/** * # v2 统一 Action API(docs 03 §48/§90/§91) * - GET /api/v2/actions Action Discovery * - GET /api/v2/actions/:id 单个 Action 契约 * - POST /api/v2/actions/:actionId 执行(六入口之一的 REST 形态) * - GET /api/v2/resources Resource Discovery */ import { Body, Controller, Get, Param, Post, Query } from "@nestjs/common"; import { z } from "zod"; import { ActionRuntime } from "../runtime/action-runtime"; import { actionRegistry } from "../registry/action-registry"; import { resourceRegistry } from "../registry/resource-registry"; import { ZodValidationPipe } from "../../common/pipes/zod-validation.pipe"; import { CurrentUser, CurrentWorkspace, Roles } from "../../common/decorators"; import type { RequestUser, RequestWorkspace } from "../../common/decorators"; import type { ActorContext } from "../foundation/types"; const ExecuteSchema = z.object({ input: z.record(z.unknown()).default({}), }); @Controller("v2") export class V2Controller { constructor(private readonly runtime: ActionRuntime) {} /** # Action Discovery */ @Get("actions") listActions(@Query("source") source?: string) { return this.runtime.discover((source as "agent" | "mcp" | undefined) ?? undefined); } /** # 单个 Action 契约 */ @Get("actions/:id") getAction(@Param("id") id: string) { const action = actionRegistry.get(id); if (!action) return { found: false }; return { found: true, id: action.id, resource: action.resource, name: action.name, description: action.description, mode: action.mode, permission: action.permission, dangerous: action.dangerous ?? false, inputSchema: action.inputSchema ?? undefined, outputSchema: action.outputSchema ?? undefined, }; } /** # Resource Discovery */ @Get("resources") listResources() { return this.runtime.discoverResources(); } /** # 执行 Action —— REST 入口(与 UI/MCP/Agent/CLI 同一 Runtime) */ @Roles("MEMBER") @Post("actions/:actionId") async execute( @Param("actionId") actionId: string, @CurrentWorkspace() ws: RequestWorkspace, @CurrentUser() user: RequestUser, @Body(new ZodValidationPipe(ExecuteSchema)) body: { input: Record }, ) { const actor: ActorContext = { type: "user", id: user.id, displayName: user.email, organizationId: ws.id, }; return this.runtime.execute(actionId, body.input, actor, { source: "user", organizationId: ws.id, }); } }