v2 platform foundation (docs 01/02/03): Action Runtime + Registry + unified API

- v2 foundation: ActionResult/DevError/ActorContext/ResourceRef/Execution types
- ActionRegistry + ResourceRegistry (namespaced types, FAIL FAST on duplicates)
- ActionRuntime pipeline: validate -> authorize -> middleware -> handler ->
  output validation -> audit -> event -> execution persisted (devos_action_executions)
- Legacy bridge: v1 services registered as devops.*/core.* resources & actions (47 actions, 9 resources)
- Unified Action API: POST /api/v2/actions/:actionId, GET discovery (actions/resources)
- 10 v2 unit tests (registry fail-fast, validation, authz, failure audit, events)
This commit is contained in:
phptits
2026-09-10 21:09:44 +00:00
parent 0d6cd37579
commit b1f17cf637
30 changed files with 60110 additions and 3 deletions
+77
View File
@@ -0,0 +1,77 @@
/**
* # 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<string, unknown> },
) {
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,
});
}
}