@actiondock/core 2.7.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -68
- package/dist/catalog/action-catalog.d.ts +56 -0
- package/dist/catalog/action-catalog.js +202 -0
- package/dist/catalog/discovery.d.ts +60 -0
- package/dist/catalog/discovery.js +119 -0
- package/dist/catalog/graph.d.ts +190 -0
- package/dist/catalog/graph.js +589 -0
- package/dist/catalog/index.d.ts +5 -5
- package/dist/catalog/index.js +5 -5
- package/dist/catalog/resolve-action.d.ts +43 -0
- package/dist/catalog/resolve-action.js +119 -0
- package/dist/catalog/resolve-playbook.d.ts +26 -0
- package/dist/catalog/resolve-playbook.js +114 -0
- package/dist/doctor/checks/project.js +27 -8
- package/dist/doctor/checks/registry.js +2 -2
- package/dist/doctor/checks/runtime.js +34 -1
- package/dist/errors.d.ts +102 -10
- package/dist/errors.js +103 -10
- package/dist/execution/service.d.ts +18 -16
- package/dist/execution/service.js +183 -252
- package/dist/execution/types.d.ts +25 -69
- package/dist/execution/types.js +2 -1
- package/dist/filter/intent.d.ts +8 -9
- package/dist/filter/intent.js +49 -10
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.js +4 -0
- package/dist/host/host.d.ts +74 -32
- package/dist/host/host.js +585 -336
- package/dist/host/routing.d.ts +15 -48
- package/dist/host/routing.js +90 -153
- package/dist/host/types.d.ts +50 -17
- package/dist/index.d.ts +21 -24
- package/dist/index.js +19 -23
- package/dist/input/advice.d.ts +5 -1
- package/dist/input/advice.js +163 -214
- package/dist/input/describe.d.ts +1 -1
- package/dist/input/flat-errors.d.ts +5 -6
- package/dist/input/flat-errors.js +6 -6
- package/dist/input/flat-materializer.js +42 -44
- package/dist/invocation/index.d.ts +2 -0
- package/dist/invocation/index.js +2 -0
- package/dist/invocation/policy.d.ts +157 -0
- package/dist/invocation/policy.js +231 -0
- package/dist/invocation/types.d.ts +172 -0
- package/dist/invocation/types.js +97 -0
- package/dist/ipc/diagnostic.d.ts +0 -12
- package/dist/ipc/diagnostic.js +0 -21
- package/dist/ipc/host.d.ts +4 -4
- package/dist/ipc/host.js +44 -47
- package/dist/ipc/index.d.ts +1 -1
- package/dist/ipc/index.js +1 -1
- package/dist/ipc/service.d.ts +40 -0
- package/dist/ipc/service.js +349 -0
- package/dist/ipc/types.d.ts +21 -15
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/module-loader.d.ts +63 -0
- package/dist/node/module-loader.js +112 -0
- package/dist/package/index.d.ts +23 -0
- package/dist/package/index.js +18 -0
- package/dist/{app/app.d.ts → package/runtime.d.ts} +41 -20
- package/dist/{app/app.js → package/runtime.js} +140 -95
- package/dist/{app → package}/static-index.js +58 -41
- package/dist/package/storage-view.d.ts +13 -0
- package/dist/package/storage-view.js +22 -0
- package/dist/{app → package}/types.d.ts +76 -57
- package/dist/package/types.js +22 -0
- package/dist/platform/index.d.ts +1 -1
- package/dist/platform/index.js +1 -1
- package/dist/platform/node.d.ts +48 -0
- package/dist/platform/node.js +96 -0
- package/dist/platform/types.d.ts +0 -9
- package/dist/process/control-arbiter.d.ts +2 -1
- package/dist/process/control-arbiter.js +1 -2
- package/dist/process/driver.d.ts +9 -8
- package/dist/process/driver.js +11 -22
- package/dist/process/index.d.ts +2 -0
- package/dist/process/index.js +2 -0
- package/dist/process/managed-record.d.ts +2 -2
- package/dist/process/process-driver.d.ts +109 -0
- package/dist/process/process-driver.js +650 -0
- package/dist/process/process-executor.d.ts +47 -0
- package/dist/process/process-executor.js +296 -0
- package/dist/process/process-manager.js +48 -26
- package/dist/process/run-executor.js +12 -12
- package/dist/profile/client-actions.js +2 -2
- package/dist/profile/client-health.js +2 -2
- package/dist/profile/client-query.js +5 -6
- package/dist/profile/client-transport.d.ts +19 -17
- package/dist/profile/client-transport.js +47 -80
- package/dist/profile/client.d.ts +2 -2
- package/dist/profile/client.js +1 -1
- package/dist/profile/index.d.ts +1 -0
- package/dist/profile/index.js +1 -0
- package/dist/profile/manager.d.ts +5 -0
- package/dist/profile/manager.js +41 -6
- package/dist/project/closure.js +2 -2
- package/dist/project/index.d.ts +8 -1
- package/dist/project/index.js +10 -1
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +1 -1
- package/dist/project/loader.js +26 -29
- package/dist/project/transactions.js +5 -10
- package/dist/registry/index.d.ts +3 -1
- package/dist/registry/index.js +2 -1
- package/dist/registry/lock.d.ts +28 -7
- package/dist/registry/lock.js +136 -16
- package/dist/registry/registry.d.ts +1 -5
- package/dist/registry/registry.js +1 -53
- package/dist/registry/scan.d.ts +0 -15
- package/dist/registry/scan.js +0 -59
- package/dist/registry/store.d.ts +42 -0
- package/dist/registry/store.js +43 -0
- package/dist/registry/types.d.ts +0 -24
- package/dist/runtime/action-collection.d.ts +1 -1
- package/dist/runtime/action-registry.d.ts +0 -4
- package/dist/runtime/action-registry.js +0 -7
- package/dist/runtime/context.js +5 -11
- package/dist/runtime/events.js +3 -6
- package/dist/runtime/identity.d.ts +22 -0
- package/dist/runtime/identity.js +10 -0
- package/dist/runtime/index.d.ts +1 -2
- package/dist/runtime/index.js +1 -2
- package/dist/runtime/module-loader.d.ts +2 -34
- package/dist/runtime/module-loader.js +1 -16
- package/dist/runtime/process.d.ts +6 -24
- package/dist/runtime/process.js +1 -118
- package/dist/runtime/run-persistence.js +2 -5
- package/dist/runtime/runner.d.ts +33 -93
- package/dist/runtime/runner.js +132 -364
- package/dist/runtime/standalone.d.ts +23 -8
- package/dist/runtime/standalone.js +100 -147
- package/dist/server/body.d.ts +16 -8
- package/dist/server/body.js +33 -16
- package/dist/server/dispatcher.d.ts +6 -10
- package/dist/server/dispatcher.js +24 -32
- package/dist/server/http-server.d.ts +87 -0
- package/dist/server/http-server.js +304 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.js +10 -0
- package/dist/server/mcp-endpoint.d.ts +61 -0
- package/dist/server/mcp-endpoint.js +118 -0
- package/dist/server/routes/actions.js +34 -28
- package/dist/server/routes/common.d.ts +10 -14
- package/dist/server/routes/common.js +32 -56
- package/dist/server/routes/config.js +54 -64
- package/dist/server/routes/doctor.d.ts +1 -1
- package/dist/server/routes/doctor.js +10 -18
- package/dist/server/routes/health.d.ts +1 -1
- package/dist/server/routes/health.js +2 -3
- package/dist/server/routes/info.d.ts +2 -2
- package/dist/server/routes/info.js +12 -13
- package/dist/server/routes/playbooks.js +16 -29
- package/dist/server/routes/runs.js +95 -117
- package/dist/server/routes/state.js +63 -62
- package/dist/server/server.d.ts +2 -2
- package/dist/server/server.js +43 -227
- package/dist/server/types.d.ts +8 -19
- package/dist/service/factory.d.ts +12 -0
- package/dist/service/factory.js +85 -0
- package/dist/{target → service}/index.d.ts +1 -1
- package/dist/{target → service}/index.js +1 -1
- package/dist/service/local.d.ts +27 -0
- package/dist/service/local.js +138 -0
- package/dist/{target → service}/remote-errors.d.ts +3 -6
- package/dist/service/remote-errors.js +38 -0
- package/dist/{target → service}/remote-polling.d.ts +2 -2
- package/dist/{target → service}/remote-polling.js +8 -8
- package/dist/service/remote.d.ts +34 -0
- package/dist/service/remote.js +634 -0
- package/dist/service/sse-stream.d.ts +24 -0
- package/dist/service/sse-stream.js +139 -0
- package/dist/service/types.d.ts +272 -0
- package/dist/service/types.js +32 -0
- package/dist/storage/data-dir-lock.js +10 -5
- package/dist/storage/driver.d.ts +2 -1
- package/dist/storage/driver.js +3 -52
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/lock-core.d.ts +2 -6
- package/dist/storage/lock-core.js +4 -17
- package/dist/storage/params.d.ts +2 -8
- package/dist/storage/params.js +2 -8
- package/dist/storage/sqlite-driver.d.ts +40 -0
- package/dist/storage/sqlite-driver.js +116 -0
- package/dist/storage/sqlite.js +21 -6
- package/dist/storage/types.d.ts +4 -4
- package/dist/storage/types.js +2 -2
- package/dist/utils/index.d.ts +53 -0
- package/dist/utils/index.js +99 -4
- package/dist/utils/process.d.ts +10 -0
- package/dist/utils/process.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +41 -3
- package/dist/app/index.d.ts +0 -2
- package/dist/app/index.js +0 -2
- package/dist/app/types.js +0 -1
- package/dist/catalog/action-index.d.ts +0 -9
- package/dist/catalog/action-index.js +0 -95
- package/dist/catalog/action-resolver.d.ts +0 -20
- package/dist/catalog/action-resolver.js +0 -95
- package/dist/catalog/location-registry.d.ts +0 -15
- package/dist/catalog/location-registry.js +0 -138
- package/dist/catalog/package-catalog.d.ts +0 -7
- package/dist/catalog/package-catalog.js +0 -77
- package/dist/catalog/types.d.ts +0 -68
- package/dist/catalog/types.js +0 -1
- package/dist/export/index.d.ts +0 -1
- package/dist/export/index.js +0 -1
- package/dist/export/templates.d.ts +0 -71
- package/dist/export/templates.js +0 -635
- package/dist/ipc/target.d.ts +0 -74
- package/dist/ipc/target.js +0 -365
- package/dist/platform/default.d.ts +0 -38
- package/dist/platform/default.js +0 -78
- package/dist/project/resolver.d.ts +0 -116
- package/dist/project/resolver.js +0 -376
- package/dist/registry/resolve.d.ts +0 -62
- package/dist/registry/resolve.js +0 -195
- package/dist/runtime/execution-manager.d.ts +0 -48
- package/dist/runtime/execution-manager.js +0 -66
- package/dist/runtime/package-runner-factory.d.ts +0 -82
- package/dist/runtime/package-runner-factory.js +0 -186
- package/dist/target/local.d.ts +0 -53
- package/dist/target/local.js +0 -349
- package/dist/target/remote-errors.js +0 -51
- package/dist/target/remote.d.ts +0 -86
- package/dist/target/remote.js +0 -818
- package/dist/target/target.d.ts +0 -6
- package/dist/target/target.js +0 -37
- package/dist/target/types.d.ts +0 -261
- package/dist/target/types.js +0 -35
- /package/dist/{app → package}/static-index.d.ts +0 -0
- /package/dist/{target → service}/sse-parser.d.ts +0 -0
- /package/dist/{target → service}/sse-parser.js +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享 MCP 端点处理器工厂配置选项。
|
|
3
|
+
*
|
|
4
|
+
* 覆盖 core 网关与 mcp 独立 HTTP 服务双方共同消费的配置面:
|
|
5
|
+
* - token 与 allowQueryToken 决定 Bearer 鉴权(含查询参数放行开关);
|
|
6
|
+
* - corsOrigins 决定跨域响应头解析;
|
|
7
|
+
* - maxBodyBytes 决定请求体有界读取上限(防 DoS);
|
|
8
|
+
* - corsApplyMode 决定 MCP 响应的 CORS 头部应用方式。
|
|
9
|
+
*/
|
|
10
|
+
export interface McpEndpointHandlerOptions {
|
|
11
|
+
/** 服务端预期的 Bearer Token(未配置时默认放行) */
|
|
12
|
+
token?: string;
|
|
13
|
+
/** 是否允许通过 URL 查询参数携带 Token(默认 false) */
|
|
14
|
+
allowQueryToken?: boolean;
|
|
15
|
+
/** 允许跨域请求的 CORS Origin 白名单列表 */
|
|
16
|
+
corsOrigins?: string[];
|
|
17
|
+
/** 最大允许的请求体字节限制(默认 1 MiB,防 DoS) */
|
|
18
|
+
maxBodyBytes?: number;
|
|
19
|
+
/** MCP 响应的 CORS 头部应用模式(默认 inherit 透传响应原头部) */
|
|
20
|
+
corsApplyMode?: McpCorsApplyMode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* MCP 委托处理器契约。
|
|
24
|
+
* 接收经限流重建后的标准 Request,返回 MCP 协议响应;
|
|
25
|
+
* 返回 null / undefined 表示委托方未处理,交回调用方继续后续路由。
|
|
26
|
+
*/
|
|
27
|
+
export type McpDelegateHandler = (req: Request) => Promise<Response | null | undefined> | Response | null | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* 构造 MCP 响应的 CORS 头部回写规则。
|
|
30
|
+
* - inherit:透传 MCP 响应自身头部(core 网关行为);
|
|
31
|
+
* - merge:将解析出的 CORS 头部合并覆盖到 MCP 响应(mcp 独立服务行为)。
|
|
32
|
+
*/
|
|
33
|
+
export type McpCorsApplyMode = "inherit" | "merge";
|
|
34
|
+
/**
|
|
35
|
+
* 共享 MCP 端点处理器契约。
|
|
36
|
+
*
|
|
37
|
+
* 返回 Response 表示已完整处理;返回 null / undefined 表示委托方未处理,
|
|
38
|
+
* 交回调用方继续后续路由分发。
|
|
39
|
+
*/
|
|
40
|
+
export type McpEndpointHandler = (req: Request) => Promise<Response | null | undefined>;
|
|
41
|
+
/**
|
|
42
|
+
* 构造可复用的 MCP 端点处理器。
|
|
43
|
+
*
|
|
44
|
+
* 统一承载 core 网关端点与 mcp 独立 HTTP 服务共同遵循的请求处理子序列:
|
|
45
|
+
* verifyBearerToken 鉴权(401 标准 JSON)→ readBodyWithLimit 有界读取
|
|
46
|
+
* (413 处理)→ 委托 MCP 处理器 → 按 corsApplyMode 应用 CORS 头部。
|
|
47
|
+
*
|
|
48
|
+
* CORS 头部解析与 OPTIONS 预检由外层路由分发统一承担(两端点在健康检查、
|
|
49
|
+
* 全局鉴权等前置路由上存在形态差异,不宜在此收敛);本处理器聚焦 MCP
|
|
50
|
+
* 端点自身的鉴权与请求体防护序列。
|
|
51
|
+
*
|
|
52
|
+
* 鉴权失败响应默认产出 core 网关标准错误形态;需要 JSON-RPC 错误形态的
|
|
53
|
+
* 调用方(如 mcp 独立服务)可通过 unauthorizedResponse 定制。
|
|
54
|
+
*
|
|
55
|
+
* @param delegate MCP 委托处理器(接收限流重建后的 Request)
|
|
56
|
+
* @param options 端点配置选项
|
|
57
|
+
*/
|
|
58
|
+
export declare function createMcpEndpointHandler(delegate: McpDelegateHandler, options?: McpEndpointHandlerOptions & {
|
|
59
|
+
/** 鉴权失败响应构造(默认产出 core 网关标准 401 JSON) */
|
|
60
|
+
unauthorizedResponse?: (corsHeaders: Record<string, string>) => Response;
|
|
61
|
+
}): McpEndpointHandler;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { UNAUTHORIZED, REQUEST_TOO_LARGE } from "../errors.js";
|
|
2
|
+
import { jsonResponse } from "./routes/common.js";
|
|
3
|
+
import { DEFAULT_MAX_BODY_BYTES, readBodyWithLimit, RequestTooLargeError } from "./body.js";
|
|
4
|
+
import { resolveCorsHeaders, verifyBearerToken } from "./security.js";
|
|
5
|
+
/**
|
|
6
|
+
* 构造 413 Payload Too Large JSON 响应。
|
|
7
|
+
*/
|
|
8
|
+
function requestTooLargeResponse(corsHeaders) {
|
|
9
|
+
return jsonResponse({
|
|
10
|
+
ok: false,
|
|
11
|
+
error: {
|
|
12
|
+
code: REQUEST_TOO_LARGE,
|
|
13
|
+
message: "Request body exceeds maximum allowed size",
|
|
14
|
+
},
|
|
15
|
+
}, 413, corsHeaders);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 有界读取请求体并重建 Request。
|
|
19
|
+
*
|
|
20
|
+
* 读取与超限判定统一复用 body 域的 readBodyWithLimit 单一事实源;
|
|
21
|
+
* 超限时返回 null 表示应回退 413 响应,由调用方构造错误响应。
|
|
22
|
+
* 空请求体保持 body 为 undefined,与 core 网关历史行为逐字节一致。
|
|
23
|
+
*
|
|
24
|
+
* @param req 原始请求
|
|
25
|
+
* @param maxBytes 单次请求体允许的最大字节数
|
|
26
|
+
* @returns 重建后的请求;体积超限时返回 null
|
|
27
|
+
*/
|
|
28
|
+
async function rebuildRequestWithLimit(req, maxBytes) {
|
|
29
|
+
let merged;
|
|
30
|
+
try {
|
|
31
|
+
merged = await readBodyWithLimit(req, { maxBytes });
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err instanceof RequestTooLargeError) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
const bodyInit = merged.byteLength > 0 ? new Blob([merged]) : undefined;
|
|
40
|
+
return new Request(req.url, {
|
|
41
|
+
method: req.method,
|
|
42
|
+
headers: req.headers,
|
|
43
|
+
body: bodyInit,
|
|
44
|
+
signal: req.signal,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 按指定模式为 MCP 响应附加 CORS 头部。
|
|
49
|
+
*
|
|
50
|
+
* @param response MCP 委托处理器返回的原始响应
|
|
51
|
+
* @param corsHeaders 本次请求解析出的 CORS 响应头
|
|
52
|
+
* @param mode CORS 头部应用模式(默认 inherit 透传)
|
|
53
|
+
*/
|
|
54
|
+
function applyCorsToResponse(response, corsHeaders, mode) {
|
|
55
|
+
if (mode !== "merge" || Object.keys(corsHeaders).length === 0) {
|
|
56
|
+
return response;
|
|
57
|
+
}
|
|
58
|
+
const newHeaders = new Headers(response.headers);
|
|
59
|
+
for (const [k, v] of Object.entries(corsHeaders)) {
|
|
60
|
+
newHeaders.set(k, String(v));
|
|
61
|
+
}
|
|
62
|
+
return new Response(response.body, {
|
|
63
|
+
status: response.status,
|
|
64
|
+
statusText: response.statusText,
|
|
65
|
+
headers: newHeaders,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 构造可复用的 MCP 端点处理器。
|
|
70
|
+
*
|
|
71
|
+
* 统一承载 core 网关端点与 mcp 独立 HTTP 服务共同遵循的请求处理子序列:
|
|
72
|
+
* verifyBearerToken 鉴权(401 标准 JSON)→ readBodyWithLimit 有界读取
|
|
73
|
+
* (413 处理)→ 委托 MCP 处理器 → 按 corsApplyMode 应用 CORS 头部。
|
|
74
|
+
*
|
|
75
|
+
* CORS 头部解析与 OPTIONS 预检由外层路由分发统一承担(两端点在健康检查、
|
|
76
|
+
* 全局鉴权等前置路由上存在形态差异,不宜在此收敛);本处理器聚焦 MCP
|
|
77
|
+
* 端点自身的鉴权与请求体防护序列。
|
|
78
|
+
*
|
|
79
|
+
* 鉴权失败响应默认产出 core 网关标准错误形态;需要 JSON-RPC 错误形态的
|
|
80
|
+
* 调用方(如 mcp 独立服务)可通过 unauthorizedResponse 定制。
|
|
81
|
+
*
|
|
82
|
+
* @param delegate MCP 委托处理器(接收限流重建后的 Request)
|
|
83
|
+
* @param options 端点配置选项
|
|
84
|
+
*/
|
|
85
|
+
export function createMcpEndpointHandler(delegate, options = {}) {
|
|
86
|
+
const { token, allowQueryToken, corsOrigins, maxBodyBytes, unauthorizedResponse, } = options;
|
|
87
|
+
const maxBytes = maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
88
|
+
const verifyOptions = { allowQueryToken };
|
|
89
|
+
return async (req) => {
|
|
90
|
+
const origin = req.headers.get("origin");
|
|
91
|
+
const corsHeaders = resolveCorsHeaders(origin, corsOrigins);
|
|
92
|
+
// Bearer Token 鉴权拦截
|
|
93
|
+
if (!verifyBearerToken(req, token, verifyOptions)) {
|
|
94
|
+
return (unauthorizedResponse?.(corsHeaders) ??
|
|
95
|
+
jsonResponse({
|
|
96
|
+
ok: false,
|
|
97
|
+
error: {
|
|
98
|
+
code: UNAUTHORIZED,
|
|
99
|
+
message: "Invalid or missing Bearer token",
|
|
100
|
+
},
|
|
101
|
+
}, 401, corsHeaders));
|
|
102
|
+
}
|
|
103
|
+
// 请求体体积限制保护(复用 body 域有界读取单一事实源)
|
|
104
|
+
let mcpReq = req;
|
|
105
|
+
if (req.body && req.method !== "GET" && req.method !== "HEAD") {
|
|
106
|
+
const rebuilt = await rebuildRequestWithLimit(req, maxBytes);
|
|
107
|
+
if (!rebuilt) {
|
|
108
|
+
return requestTooLargeResponse(corsHeaders);
|
|
109
|
+
}
|
|
110
|
+
mcpReq = rebuilt;
|
|
111
|
+
}
|
|
112
|
+
const mcpRes = await delegate(mcpReq);
|
|
113
|
+
if (mcpRes) {
|
|
114
|
+
return applyCorsToResponse(mcpRes, corsHeaders, options.corsApplyMode ?? "inherit");
|
|
115
|
+
}
|
|
116
|
+
return mcpRes;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ActionResolver } from "../../catalog/action-resolver.js";
|
|
1
|
+
import { parseActionRef } from "../../catalog/resolve-action.js";
|
|
3
2
|
import { filterByIntent } from "../../filter/index.js";
|
|
4
|
-
import { ACTION_NOT_FOUND, ACTION_TIMEOUT, IDEMPOTENCY_CONFLICT, INPUT_VALIDATION_FAILED, PACKAGE_NOT_FOUND } from "../../errors.js";
|
|
3
|
+
import { ACTION_EXECUTION_ERROR, ACTION_NOT_FOUND, ACTION_START_FAILED, ACTION_TIMEOUT, ACTIONS_LIST_ERROR, IDEMPOTENCY_CONFLICT, INPUT_VALIDATION_FAILED, INVALID_JSON, PACKAGE_FORBIDDEN, PACKAGE_NOT_FOUND, REQUEST_TOO_LARGE, } from "../../errors.js";
|
|
5
4
|
import { InvalidJsonError, readJsonBody, RequestTooLargeError } from "../body.js";
|
|
6
5
|
import { getSubPath, jsonResponse } from "./common.js";
|
|
7
6
|
/**
|
|
8
7
|
* 处理 Action 相关的 HTTP 路由(列表、规范查询、同步执行与异步启动)。
|
|
9
8
|
*/
|
|
10
9
|
export async function handleActionsRoutes(ctx) {
|
|
11
|
-
const { req, url, pathname, corsHeaders, options,
|
|
10
|
+
const { req, url, pathname, corsHeaders, options, service } = ctx;
|
|
12
11
|
const subpath = getSubPath(pathname);
|
|
13
12
|
// 1. Actions List: GET /api/v2/actions, GET /actions
|
|
14
13
|
if (subpath === "/actions" && req.method === "GET") {
|
|
@@ -18,7 +17,7 @@ export async function handleActionsRoutes(ctx) {
|
|
|
18
17
|
const targetPkg = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
|
|
19
18
|
const prefix = url.searchParams.get("prefix") || undefined;
|
|
20
19
|
const tags = url.searchParams.getAll("tag");
|
|
21
|
-
let actions = await
|
|
20
|
+
let actions = await service.discovery.listActions({
|
|
22
21
|
query,
|
|
23
22
|
prefix,
|
|
24
23
|
tags: tags.length > 0 ? tags : undefined,
|
|
@@ -37,7 +36,7 @@ export async function handleActionsRoutes(ctx) {
|
|
|
37
36
|
catch (err) {
|
|
38
37
|
return jsonResponse({
|
|
39
38
|
ok: false,
|
|
40
|
-
error: { code:
|
|
39
|
+
error: { code: ACTIONS_LIST_ERROR, message: err.message },
|
|
41
40
|
}, 500, corsHeaders);
|
|
42
41
|
}
|
|
43
42
|
}
|
|
@@ -51,7 +50,7 @@ export async function handleActionsRoutes(ctx) {
|
|
|
51
50
|
return jsonResponse({
|
|
52
51
|
ok: false,
|
|
53
52
|
error: {
|
|
54
|
-
code:
|
|
53
|
+
code: PACKAGE_FORBIDDEN,
|
|
55
54
|
message: `Package '${packageId}' is not in the allowed package list`,
|
|
56
55
|
},
|
|
57
56
|
}, 403, corsHeaders);
|
|
@@ -59,7 +58,7 @@ export async function handleActionsRoutes(ctx) {
|
|
|
59
58
|
}
|
|
60
59
|
const ref = `${packageId}/${actionId}`;
|
|
61
60
|
try {
|
|
62
|
-
const spec = await
|
|
61
|
+
const spec = await service.discovery.describeAction(ref);
|
|
63
62
|
return jsonResponse(spec, 200, corsHeaders);
|
|
64
63
|
}
|
|
65
64
|
catch (err) {
|
|
@@ -77,13 +76,13 @@ export async function handleActionsRoutes(ctx) {
|
|
|
77
76
|
if (actionShowMatch && req.method === "GET") {
|
|
78
77
|
const actionId = decodeURIComponent(actionShowMatch[1]);
|
|
79
78
|
try {
|
|
80
|
-
const parsed =
|
|
79
|
+
const parsed = parseActionRef(actionId);
|
|
81
80
|
if (parsed.packageId && options.packageAllowlist && options.packageAllowlist.length > 0) {
|
|
82
81
|
if (!options.packageAllowlist.includes(parsed.packageId)) {
|
|
83
82
|
return jsonResponse({
|
|
84
83
|
ok: false,
|
|
85
84
|
error: {
|
|
86
|
-
code:
|
|
85
|
+
code: PACKAGE_FORBIDDEN,
|
|
87
86
|
message: `Package '${parsed.packageId}' is not in the allowed package list`,
|
|
88
87
|
},
|
|
89
88
|
}, 403, corsHeaders);
|
|
@@ -92,14 +91,14 @@ export async function handleActionsRoutes(ctx) {
|
|
|
92
91
|
}
|
|
93
92
|
catch { }
|
|
94
93
|
try {
|
|
95
|
-
const spec = await
|
|
94
|
+
const spec = await service.discovery.describeAction(actionId);
|
|
96
95
|
if (options.packageAllowlist &&
|
|
97
96
|
options.packageAllowlist.length > 0 &&
|
|
98
97
|
(!spec.packageId || !options.packageAllowlist.includes(spec.packageId))) {
|
|
99
98
|
return jsonResponse({
|
|
100
99
|
ok: false,
|
|
101
100
|
error: {
|
|
102
|
-
code:
|
|
101
|
+
code: PACKAGE_FORBIDDEN,
|
|
103
102
|
message: `Package '${spec.packageId}' is not in the allowed package list`,
|
|
104
103
|
},
|
|
105
104
|
}, 403, corsHeaders);
|
|
@@ -138,7 +137,7 @@ export async function handleActionsRoutes(ctx) {
|
|
|
138
137
|
actionRef = decodeURIComponent(shortRunMatch[1]);
|
|
139
138
|
endpointMode = shortRunMatch[2];
|
|
140
139
|
try {
|
|
141
|
-
const parsed =
|
|
140
|
+
const parsed = parseActionRef(actionRef);
|
|
142
141
|
pkgId = parsed.packageId;
|
|
143
142
|
}
|
|
144
143
|
catch { }
|
|
@@ -147,7 +146,7 @@ export async function handleActionsRoutes(ctx) {
|
|
|
147
146
|
if (options.packageAllowlist && options.packageAllowlist.length > 0) {
|
|
148
147
|
if (!pkgId) {
|
|
149
148
|
try {
|
|
150
|
-
const spec = await
|
|
149
|
+
const spec = await service.discovery.describeAction(actionRef);
|
|
151
150
|
pkgId = spec?.packageId;
|
|
152
151
|
}
|
|
153
152
|
catch { }
|
|
@@ -155,9 +154,9 @@ export async function handleActionsRoutes(ctx) {
|
|
|
155
154
|
if (!pkgId || !options.packageAllowlist.includes(pkgId)) {
|
|
156
155
|
return jsonResponse({
|
|
157
156
|
ok: false,
|
|
158
|
-
runId:
|
|
157
|
+
runId: "",
|
|
159
158
|
error: {
|
|
160
|
-
code:
|
|
159
|
+
code: PACKAGE_FORBIDDEN,
|
|
161
160
|
message: pkgId
|
|
162
161
|
? `Package '${pkgId}' is not in the allowed package list`
|
|
163
162
|
: `Action '${actionRef}' does not belong to any allowed package`,
|
|
@@ -173,21 +172,21 @@ export async function handleActionsRoutes(ctx) {
|
|
|
173
172
|
if (err instanceof RequestTooLargeError) {
|
|
174
173
|
return jsonResponse({
|
|
175
174
|
ok: false,
|
|
176
|
-
runId:
|
|
177
|
-
error: { code:
|
|
175
|
+
runId: "",
|
|
176
|
+
error: { code: REQUEST_TOO_LARGE, message: err.message },
|
|
178
177
|
}, 413, corsHeaders);
|
|
179
178
|
}
|
|
180
179
|
if (err instanceof InvalidJsonError) {
|
|
181
180
|
return jsonResponse({
|
|
182
181
|
ok: false,
|
|
183
|
-
runId:
|
|
184
|
-
error: { code:
|
|
182
|
+
runId: "",
|
|
183
|
+
error: { code: INVALID_JSON, message: err.message },
|
|
185
184
|
}, 400, corsHeaders);
|
|
186
185
|
}
|
|
187
186
|
return jsonResponse({
|
|
188
187
|
ok: false,
|
|
189
|
-
runId:
|
|
190
|
-
error: { code:
|
|
188
|
+
runId: "",
|
|
189
|
+
error: { code: INVALID_JSON, message: `Failed to parse request body: ${err.message}` },
|
|
191
190
|
}, 400, corsHeaders);
|
|
192
191
|
}
|
|
193
192
|
const isAsync = endpointMode === "start" ||
|
|
@@ -205,9 +204,16 @@ export async function handleActionsRoutes(ctx) {
|
|
|
205
204
|
body?.requestId ||
|
|
206
205
|
body?.execution?.requestId ||
|
|
207
206
|
undefined;
|
|
207
|
+
let actionRefObj;
|
|
208
|
+
try {
|
|
209
|
+
actionRefObj = parseActionRef(actionRef);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
actionRefObj = { actionId: actionRef };
|
|
213
|
+
}
|
|
208
214
|
if (isAsync) {
|
|
209
215
|
try {
|
|
210
|
-
const ticket = await
|
|
216
|
+
const ticket = await service.execution.start(actionRefObj, input, {
|
|
211
217
|
timeoutMs,
|
|
212
218
|
config: configOverrides,
|
|
213
219
|
requestId,
|
|
@@ -236,9 +242,9 @@ export async function handleActionsRoutes(ctx) {
|
|
|
236
242
|
}
|
|
237
243
|
return jsonResponse({
|
|
238
244
|
ok: false,
|
|
239
|
-
runId:
|
|
245
|
+
runId: "",
|
|
240
246
|
error: {
|
|
241
|
-
code:
|
|
247
|
+
code: ACTION_START_FAILED,
|
|
242
248
|
message: err.message || String(err),
|
|
243
249
|
},
|
|
244
250
|
}, 500, corsHeaders);
|
|
@@ -246,7 +252,7 @@ export async function handleActionsRoutes(ctx) {
|
|
|
246
252
|
}
|
|
247
253
|
// 同步执行模式
|
|
248
254
|
try {
|
|
249
|
-
const result = await
|
|
255
|
+
const result = await service.execution.run(actionRefObj, input, {
|
|
250
256
|
signal: req.signal,
|
|
251
257
|
timeoutMs,
|
|
252
258
|
config: configOverrides,
|
|
@@ -286,9 +292,9 @@ export async function handleActionsRoutes(ctx) {
|
|
|
286
292
|
}
|
|
287
293
|
return jsonResponse({
|
|
288
294
|
ok: false,
|
|
289
|
-
runId:
|
|
295
|
+
runId: "",
|
|
290
296
|
error: {
|
|
291
|
-
code:
|
|
297
|
+
code: ACTION_EXECUTION_ERROR,
|
|
292
298
|
message: err.message || String(err),
|
|
293
299
|
},
|
|
294
300
|
}, 500, corsHeaders);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type { ActionDockTarget } from "../../target/types.js";
|
|
1
|
+
import type { PackageInfo } from "../../package/types.js";
|
|
2
|
+
import type { ActionDockService } from "../../service/types.js";
|
|
4
3
|
import type { ServerOptions } from "../types.js";
|
|
5
4
|
/**
|
|
6
5
|
* 路由处理统一上下文对象。
|
|
@@ -12,8 +11,7 @@ export interface RouteContext {
|
|
|
12
11
|
corsHeaders: Record<string, string>;
|
|
13
12
|
projectRoot: string | null;
|
|
14
13
|
customHome?: string;
|
|
15
|
-
|
|
16
|
-
target: ActionDockTarget;
|
|
14
|
+
service: ActionDockService;
|
|
17
15
|
options: ServerOptions;
|
|
18
16
|
}
|
|
19
17
|
/**
|
|
@@ -21,27 +19,25 @@ export interface RouteContext {
|
|
|
21
19
|
*/
|
|
22
20
|
export declare function jsonResponse(data: unknown, status?: number, corsHeaders?: Record<string, string>): Response;
|
|
23
21
|
/**
|
|
24
|
-
* 剥离标准版本前缀 (/api/v2/
|
|
22
|
+
* 剥离标准版本前缀 (/api/v2/),获取相对路由子路径。
|
|
25
23
|
*/
|
|
26
24
|
export declare function getSubPath(pathname: string): string;
|
|
25
|
+
import { ActionDockError } from "../../errors.js";
|
|
27
26
|
/**
|
|
28
27
|
* 包未列入白名单拒绝访问异常。
|
|
29
28
|
*/
|
|
30
|
-
export declare class PackageNotAllowedError extends
|
|
29
|
+
export declare class PackageNotAllowedError extends ActionDockError {
|
|
31
30
|
status: number;
|
|
32
31
|
statusCode: number;
|
|
33
|
-
code: string;
|
|
34
32
|
constructor(packageId: string);
|
|
35
33
|
}
|
|
36
34
|
/**
|
|
37
35
|
* 校验目标 packageId 是否属于允许包白名单。
|
|
38
36
|
* 当 options.packageAllowlist 存在且目标 packageId 不在白名单中时抛出 403 PACKAGE_NOT_ALLOWED 异常。
|
|
39
37
|
*/
|
|
40
|
-
export declare function assertPackageAllowed(packageId: string
|
|
41
|
-
packageAllowlist?: string[];
|
|
42
|
-
}): void;
|
|
38
|
+
export declare function assertPackageAllowed(packageId: string, options?: ServerOptions): void;
|
|
43
39
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
40
|
+
* 解析并确定生效的目标 packageId。
|
|
41
|
+
* 遵循严格的解析规则与白名单检查。
|
|
46
42
|
*/
|
|
47
|
-
export declare function
|
|
43
|
+
export declare function resolveTargetPackageId(packages: PackageInfo[], requestedPackageId?: string, options?: ServerOptions): string;
|
|
@@ -12,27 +12,25 @@ export function jsonResponse(data, status = 200, corsHeaders = {}) {
|
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* 剥离标准版本前缀 (/api/v2/
|
|
15
|
+
* 剥离标准版本前缀 (/api/v2/),获取相对路由子路径。
|
|
16
16
|
*/
|
|
17
17
|
export function getSubPath(pathname) {
|
|
18
18
|
if (pathname.startsWith("/api/v2/")) {
|
|
19
19
|
return pathname.slice(7);
|
|
20
20
|
}
|
|
21
|
-
if (pathname.startsWith("/api/v1/")) {
|
|
22
|
-
return pathname.slice(7);
|
|
23
|
-
}
|
|
24
21
|
return pathname;
|
|
25
22
|
}
|
|
23
|
+
import { ActionDockError, PACKAGE_NOT_FOUND, PACKAGE_NOT_ALLOWED } from "../../errors.js";
|
|
26
24
|
/**
|
|
27
25
|
* 包未列入白名单拒绝访问异常。
|
|
28
26
|
*/
|
|
29
|
-
export class PackageNotAllowedError extends
|
|
27
|
+
export class PackageNotAllowedError extends ActionDockError {
|
|
30
28
|
status = 403;
|
|
31
29
|
statusCode = 403;
|
|
32
|
-
code = "PACKAGE_NOT_ALLOWED";
|
|
33
30
|
constructor(packageId) {
|
|
34
|
-
super(`Package '${packageId}' is not in the allowed package list`);
|
|
31
|
+
super(PACKAGE_NOT_ALLOWED, `Package '${packageId}' is not in the allowed package list`);
|
|
35
32
|
this.name = "PackageNotAllowedError";
|
|
33
|
+
Object.setPrototypeOf(this, PackageNotAllowedError.prototype);
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
36
|
/**
|
|
@@ -42,63 +40,41 @@ export class PackageNotAllowedError extends Error {
|
|
|
42
40
|
export function assertPackageAllowed(packageId, options) {
|
|
43
41
|
if (options?.packageAllowlist &&
|
|
44
42
|
Array.isArray(options.packageAllowlist) &&
|
|
45
|
-
options.packageAllowlist.length > 0
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
43
|
+
options.packageAllowlist.length > 0 &&
|
|
44
|
+
!options.packageAllowlist.includes(packageId)) {
|
|
45
|
+
throw new PackageNotAllowedError(packageId);
|
|
49
46
|
}
|
|
50
47
|
}
|
|
51
48
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* 解析并确定生效的目标 packageId。
|
|
50
|
+
* 遵循严格的解析规则与白名单检查。
|
|
54
51
|
*/
|
|
55
|
-
export function
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (host) {
|
|
60
|
-
const app = host.getApp(packageIdOrPath);
|
|
61
|
-
if (app)
|
|
62
|
-
return app;
|
|
63
|
-
}
|
|
64
|
-
const innerTarget = target?.unwrap?.();
|
|
65
|
-
if (innerTarget && "packageId" in innerTarget && innerTarget.packageId === packageIdOrPath) {
|
|
66
|
-
return innerTarget;
|
|
52
|
+
export function resolveTargetPackageId(packages, requestedPackageId, options) {
|
|
53
|
+
if (requestedPackageId) {
|
|
54
|
+
if (requestedPackageId === "global") {
|
|
55
|
+
return "global";
|
|
67
56
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
assertValidPackageId(requestedPackageId);
|
|
58
|
+
assertPackageAllowed(requestedPackageId, options);
|
|
59
|
+
const matched = packages.find((p) => p.id === requestedPackageId || p.packageRoot === requestedPackageId);
|
|
60
|
+
if (!matched) {
|
|
61
|
+
throw new ActionDockError(PACKAGE_NOT_FOUND, `Unknown or unregistered package: '${requestedPackageId}'`);
|
|
72
62
|
}
|
|
73
|
-
|
|
63
|
+
return matched.id;
|
|
74
64
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (allowedApp)
|
|
82
|
-
return allowedApp;
|
|
83
|
-
}
|
|
84
|
-
if (apps.length > 0)
|
|
85
|
-
return apps[0];
|
|
65
|
+
let targetId;
|
|
66
|
+
if (options?.packageAllowlist &&
|
|
67
|
+
Array.isArray(options.packageAllowlist) &&
|
|
68
|
+
options.packageAllowlist.length > 0) {
|
|
69
|
+
const allowed = packages.find((p) => options.packageAllowlist.includes(p.id));
|
|
70
|
+
targetId = allowed ? allowed.id : (packages[0]?.id || "");
|
|
86
71
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const apps = innerTarget.listApps();
|
|
90
|
-
if (options?.packageAllowlist &&
|
|
91
|
-
Array.isArray(options.packageAllowlist) &&
|
|
92
|
-
options.packageAllowlist.length > 0) {
|
|
93
|
-
const allowedApp = apps.find((a) => options.packageAllowlist.includes(a.packageId));
|
|
94
|
-
if (allowedApp)
|
|
95
|
-
return allowedApp;
|
|
96
|
-
}
|
|
97
|
-
if (apps.length > 0)
|
|
98
|
-
return apps[0];
|
|
72
|
+
else if (packages.length > 0) {
|
|
73
|
+
targetId = packages[0].id;
|
|
99
74
|
}
|
|
100
|
-
|
|
101
|
-
|
|
75
|
+
else {
|
|
76
|
+
throw new ActionDockError(PACKAGE_NOT_FOUND, "No registered package found in service");
|
|
102
77
|
}
|
|
103
|
-
|
|
78
|
+
assertPackageAllowed(targetId, options);
|
|
79
|
+
return targetId;
|
|
104
80
|
}
|