@actiondock/core 2.7.0 → 2.9.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 +77 -64
- 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 +106 -10
- package/dist/errors.js +105 -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/project/types.d.ts +5 -0
- 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 +12 -0
- package/dist/server/index.js +11 -0
- package/dist/server/mcp-endpoint.d.ts +61 -0
- package/dist/server/mcp-endpoint.js +118 -0
- package/dist/server/policy.d.ts +138 -0
- package/dist/server/policy.js +368 -0
- package/dist/server/routes/actions.js +114 -64
- package/dist/server/routes/common.d.ts +18 -33
- package/dist/server/routes/common.js +45 -65
- package/dist/server/routes/config.d.ts +1 -1
- package/dist/server/routes/config.js +55 -65
- package/dist/server/routes/doctor.d.ts +1 -1
- package/dist/server/routes/doctor.js +13 -21
- package/dist/server/routes/health.d.ts +1 -1
- package/dist/server/routes/health.js +4 -5
- package/dist/server/routes/info.d.ts +2 -2
- package/dist/server/routes/info.js +17 -24
- package/dist/server/routes/playbooks.js +23 -36
- package/dist/server/routes/runs.js +151 -141
- package/dist/server/routes/state.d.ts +1 -1
- package/dist/server/routes/state.js +64 -63
- package/dist/server/security.d.ts +11 -0
- package/dist/server/security.js +36 -19
- package/dist/server/server.d.ts +2 -2
- package/dist/server/server.js +224 -238
- package/dist/server/types.d.ts +68 -21
- 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
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { ActionDockError } from "../errors.js";
|
|
2
|
+
import type { McpEndpointHandler } from "./mcp-endpoint.js";
|
|
3
|
+
import type { EffectiveServerPolicy, ServerOptions, ServerViewOptions } from "./types.js";
|
|
4
|
+
export { extractBearerToken } from "./security.js";
|
|
5
|
+
/**
|
|
6
|
+
* 动作未列入白名单拒绝访问异常。
|
|
7
|
+
*/
|
|
8
|
+
export declare class ActionForbiddenError extends ActionDockError {
|
|
9
|
+
status: number;
|
|
10
|
+
statusCode: number;
|
|
11
|
+
constructor(actionRef: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 包未列入白名单拒绝访问异常。
|
|
15
|
+
*/
|
|
16
|
+
export declare class PackageNotAllowedError extends ActionDockError {
|
|
17
|
+
status: number;
|
|
18
|
+
statusCode: number;
|
|
19
|
+
constructor(packageId: string);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 归一化后的单个服务视图结构。
|
|
23
|
+
*/
|
|
24
|
+
export interface NormalizedServerView {
|
|
25
|
+
/** 视图唯一标识名称 */
|
|
26
|
+
name: string;
|
|
27
|
+
/** 该视图生效的统一安全策略 */
|
|
28
|
+
policy: EffectiveServerPolicy;
|
|
29
|
+
/** 该视图是否启用 MCP 端点 */
|
|
30
|
+
enableMcp: boolean;
|
|
31
|
+
/** 原始配置选项 */
|
|
32
|
+
rawOptions: ServerViewOptions;
|
|
33
|
+
/** 为该视图独立装配的 MCP 端点处理器 */
|
|
34
|
+
mcpEndpointHandler?: McpEndpointHandler;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 根据客户端携带的 Token 智能匹配已注册的视图。
|
|
38
|
+
* 使用 safeEqual 进行恒定时间比较以防范时序攻击。
|
|
39
|
+
*
|
|
40
|
+
* @param token 客户端提供的 Bearer Token
|
|
41
|
+
* @param views 可遍历的视图集合
|
|
42
|
+
* @returns 匹配成功的视图对象,未命中返回 undefined
|
|
43
|
+
*/
|
|
44
|
+
export declare function matchViewByToken(token: string, views: Iterable<NormalizedServerView>): NormalizedServerView | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* 归一化解析服务端配置的所有视图集合。
|
|
47
|
+
* 保证默认视图与显式自定义视图具有统一的数据结构。
|
|
48
|
+
*
|
|
49
|
+
* @param options 服务端配置选项
|
|
50
|
+
* @returns 默认视图与全部视图 Map 映射
|
|
51
|
+
*/
|
|
52
|
+
export declare function normalizeServerViews(options: ServerOptions): {
|
|
53
|
+
defaultView: NormalizedServerView;
|
|
54
|
+
views: Map<string, NormalizedServerView>;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* 校验目标动作是否在 actionAllowlist 允许白名单中。
|
|
58
|
+
* 支持短名 actionId 与全限定名 packageId/actionId 匹配:
|
|
59
|
+
* - 白名单项若含 '/'(如 pkg/act),则必须 packageId 与 actionId 同时精确匹配。
|
|
60
|
+
* - 白名单项若不含 '/'(如 act),则只需 actionId 匹配(不限包)。
|
|
61
|
+
*
|
|
62
|
+
* @param action 目标动作对象或引用字符串
|
|
63
|
+
* @param allowlist 允许动作白名单列表
|
|
64
|
+
* @returns 是否允许访问
|
|
65
|
+
*/
|
|
66
|
+
export declare function isActionAllowed(action: {
|
|
67
|
+
actionId?: string;
|
|
68
|
+
id?: string;
|
|
69
|
+
packageId?: string;
|
|
70
|
+
} | string | null | undefined, allowlist?: string[]): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* 根据生效策略统一判定目标动作是否允许访问。
|
|
73
|
+
* 同时综合校验策略中的 packageAllowlist 与 actionAllowlist 约束。
|
|
74
|
+
*
|
|
75
|
+
* @param action 目标动作对象或引用字符串
|
|
76
|
+
* @param policy 当前生效的服务端策略
|
|
77
|
+
* @returns 是否允许访问
|
|
78
|
+
*/
|
|
79
|
+
export declare function isActionAllowedByPolicy(action: {
|
|
80
|
+
actionId?: string;
|
|
81
|
+
id?: string;
|
|
82
|
+
packageId?: string;
|
|
83
|
+
} | string | null | undefined, policy?: EffectiveServerPolicy): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* 根据生效策略统一判定目标包标识是否允许访问。
|
|
86
|
+
*
|
|
87
|
+
* @param packageId 目标包 ID
|
|
88
|
+
* @param policy 当前生效的服务端策略
|
|
89
|
+
* @returns 是否允许访问
|
|
90
|
+
*/
|
|
91
|
+
export declare function isPackageAllowedByPolicy(packageId?: string | null, policy?: EffectiveServerPolicy): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* 根据生效策略统一判定配置与状态管理路由是否开启。
|
|
94
|
+
*
|
|
95
|
+
* @param policy 当前生效的服务端策略
|
|
96
|
+
* @returns 是否允许管理操作
|
|
97
|
+
*/
|
|
98
|
+
export declare function isManagementAllowedByPolicy(policy?: EffectiveServerPolicy): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* 根据生效策略对动作列表进行统一过滤。
|
|
101
|
+
*
|
|
102
|
+
* @param actions 待过滤的动作列表
|
|
103
|
+
* @param policy 当前生效的服务端策略
|
|
104
|
+
* @returns 过滤后允许访问的动作列表
|
|
105
|
+
*/
|
|
106
|
+
export declare function filterActionsByPolicy<T extends {
|
|
107
|
+
id: string;
|
|
108
|
+
packageId?: string;
|
|
109
|
+
actionId?: string;
|
|
110
|
+
}>(actions: T[], policy?: EffectiveServerPolicy): T[];
|
|
111
|
+
/**
|
|
112
|
+
* 根据生效策略对包列表进行统一过滤。
|
|
113
|
+
*
|
|
114
|
+
* @param packages 待过滤的包列表
|
|
115
|
+
* @param policy 当前生效的服务端策略
|
|
116
|
+
* @returns 过滤后允许访问的包列表
|
|
117
|
+
*/
|
|
118
|
+
export declare function filterPackagesByPolicy<T extends {
|
|
119
|
+
id: string;
|
|
120
|
+
}>(packages: T[], policy?: EffectiveServerPolicy): T[];
|
|
121
|
+
/**
|
|
122
|
+
* 校验目标动作是否属于允许白名单,不符合则抛出 403 异常。
|
|
123
|
+
*
|
|
124
|
+
* @param action 目标动作对象或引用
|
|
125
|
+
* @param policy 当前生效的服务端策略
|
|
126
|
+
*/
|
|
127
|
+
export declare function assertActionAllowedByPolicy(action: {
|
|
128
|
+
actionId?: string;
|
|
129
|
+
id?: string;
|
|
130
|
+
packageId?: string;
|
|
131
|
+
} | string, policy?: EffectiveServerPolicy): void;
|
|
132
|
+
/**
|
|
133
|
+
* 校验目标包是否属于允许白名单,不符合则抛出 403 异常。
|
|
134
|
+
*
|
|
135
|
+
* @param packageId 目标包 ID
|
|
136
|
+
* @param policy 当前生效的服务端策略
|
|
137
|
+
*/
|
|
138
|
+
export declare function assertPackageAllowedByPolicy(packageId: string, policy?: EffectiveServerPolicy): void;
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { parseActionRef } from "../catalog/resolve-action.js";
|
|
2
|
+
import { ActionDockError, ACTION_FORBIDDEN, PACKAGE_NOT_ALLOWED } from "../errors.js";
|
|
3
|
+
import { safeEqual } from "./security.js";
|
|
4
|
+
export { extractBearerToken } from "./security.js";
|
|
5
|
+
/**
|
|
6
|
+
* 动作未列入白名单拒绝访问异常。
|
|
7
|
+
*/
|
|
8
|
+
export class ActionForbiddenError extends ActionDockError {
|
|
9
|
+
status = 403;
|
|
10
|
+
statusCode = 403;
|
|
11
|
+
constructor(actionRef) {
|
|
12
|
+
super(ACTION_FORBIDDEN, `Action '${actionRef}' is not in the allowed action list`);
|
|
13
|
+
this.name = "ActionForbiddenError";
|
|
14
|
+
Object.setPrototypeOf(this, ActionForbiddenError.prototype);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 包未列入白名单拒绝访问异常。
|
|
19
|
+
*/
|
|
20
|
+
export class PackageNotAllowedError extends ActionDockError {
|
|
21
|
+
status = 403;
|
|
22
|
+
statusCode = 403;
|
|
23
|
+
constructor(packageId) {
|
|
24
|
+
super(PACKAGE_NOT_ALLOWED, `Package '${packageId}' is not in the allowed package list`);
|
|
25
|
+
this.name = "PackageNotAllowedError";
|
|
26
|
+
Object.setPrototypeOf(this, PackageNotAllowedError.prototype);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 根据客户端携带的 Token 智能匹配已注册的视图。
|
|
31
|
+
* 使用 safeEqual 进行恒定时间比较以防范时序攻击。
|
|
32
|
+
*
|
|
33
|
+
* @param token 客户端提供的 Bearer Token
|
|
34
|
+
* @param views 可遍历的视图集合
|
|
35
|
+
* @returns 匹配成功的视图对象,未命中返回 undefined
|
|
36
|
+
*/
|
|
37
|
+
export function matchViewByToken(token, views) {
|
|
38
|
+
const trimmed = token.trim();
|
|
39
|
+
if (!trimmed) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
let matched = undefined;
|
|
43
|
+
for (const view of views) {
|
|
44
|
+
const candidateToken = view.policy.token?.trim();
|
|
45
|
+
if (candidateToken) {
|
|
46
|
+
const isMatch = safeEqual(trimmed, candidateToken);
|
|
47
|
+
if (isMatch && !matched) {
|
|
48
|
+
matched = view;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return matched;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 归一化解析服务端配置的所有视图集合。
|
|
56
|
+
* 保证默认视图与显式自定义视图具有统一的数据结构。
|
|
57
|
+
*
|
|
58
|
+
* @param options 服务端配置选项
|
|
59
|
+
* @returns 默认视图与全部视图 Map 映射
|
|
60
|
+
*/
|
|
61
|
+
export function normalizeServerViews(options) {
|
|
62
|
+
const views = new Map();
|
|
63
|
+
// 1. 初始化基于全局配置的默认视图
|
|
64
|
+
const defaultRawOptions = {
|
|
65
|
+
name: "default",
|
|
66
|
+
token: options.token,
|
|
67
|
+
packageAllowlist: options.packageAllowlist,
|
|
68
|
+
actionAllowlist: options.actionAllowlist,
|
|
69
|
+
enableManagement: options.enableManagement,
|
|
70
|
+
enableMcp: options.enableMcp !== false,
|
|
71
|
+
mcpHandler: options.mcpHandler,
|
|
72
|
+
};
|
|
73
|
+
const customViews = new Map();
|
|
74
|
+
// 2. 解析 options.views 自定义视图
|
|
75
|
+
if (options.views) {
|
|
76
|
+
if (Array.isArray(options.views)) {
|
|
77
|
+
for (let i = 0; i < options.views.length; i++) {
|
|
78
|
+
const item = options.views[i];
|
|
79
|
+
if (!item || typeof item !== "object")
|
|
80
|
+
continue;
|
|
81
|
+
const name = item.name?.trim() || `view_${i}`;
|
|
82
|
+
if (!name || name === "." || name === "..") {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (name === "default") {
|
|
86
|
+
Object.assign(defaultRawOptions, item);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
customViews.set(name, {
|
|
90
|
+
name,
|
|
91
|
+
policy: {
|
|
92
|
+
viewName: name,
|
|
93
|
+
token: item.token,
|
|
94
|
+
packageAllowlist: item.packageAllowlist,
|
|
95
|
+
actionAllowlist: item.actionAllowlist,
|
|
96
|
+
enableManagement: item.enableManagement,
|
|
97
|
+
},
|
|
98
|
+
enableMcp: item.enableMcp !== false,
|
|
99
|
+
rawOptions: item,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (typeof options.views === "object") {
|
|
105
|
+
for (const [key, item] of Object.entries(options.views)) {
|
|
106
|
+
if (!item || typeof item !== "object")
|
|
107
|
+
continue;
|
|
108
|
+
const name = item.name?.trim() || key.trim();
|
|
109
|
+
if (!name || name === "." || name === "..") {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (name === "default") {
|
|
113
|
+
Object.assign(defaultRawOptions, item);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
customViews.set(name, {
|
|
117
|
+
name,
|
|
118
|
+
policy: {
|
|
119
|
+
viewName: name,
|
|
120
|
+
token: item.token,
|
|
121
|
+
packageAllowlist: item.packageAllowlist,
|
|
122
|
+
actionAllowlist: item.actionAllowlist,
|
|
123
|
+
enableManagement: item.enableManagement,
|
|
124
|
+
},
|
|
125
|
+
enableMcp: item.enableMcp !== false,
|
|
126
|
+
rawOptions: item,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const defaultView = {
|
|
133
|
+
name: "default",
|
|
134
|
+
policy: {
|
|
135
|
+
viewName: "default",
|
|
136
|
+
token: defaultRawOptions.token,
|
|
137
|
+
packageAllowlist: defaultRawOptions.packageAllowlist,
|
|
138
|
+
actionAllowlist: defaultRawOptions.actionAllowlist,
|
|
139
|
+
enableManagement: defaultRawOptions.enableManagement,
|
|
140
|
+
},
|
|
141
|
+
enableMcp: defaultRawOptions.enableMcp !== false,
|
|
142
|
+
rawOptions: defaultRawOptions,
|
|
143
|
+
};
|
|
144
|
+
// 默认视图置于首位,随后装载自定义视图
|
|
145
|
+
views.set("default", defaultView);
|
|
146
|
+
for (const [k, v] of customViews) {
|
|
147
|
+
views.set(k, v);
|
|
148
|
+
}
|
|
149
|
+
return { defaultView, views };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 校验目标动作是否在 actionAllowlist 允许白名单中。
|
|
153
|
+
* 支持短名 actionId 与全限定名 packageId/actionId 匹配:
|
|
154
|
+
* - 白名单项若含 '/'(如 pkg/act),则必须 packageId 与 actionId 同时精确匹配。
|
|
155
|
+
* - 白名单项若不含 '/'(如 act),则只需 actionId 匹配(不限包)。
|
|
156
|
+
*
|
|
157
|
+
* @param action 目标动作对象或引用字符串
|
|
158
|
+
* @param allowlist 允许动作白名单列表
|
|
159
|
+
* @returns 是否允许访问
|
|
160
|
+
*/
|
|
161
|
+
export function isActionAllowed(action, allowlist) {
|
|
162
|
+
if (!allowlist || !Array.isArray(allowlist) || allowlist.length === 0) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
if (!action) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
let target;
|
|
169
|
+
try {
|
|
170
|
+
if (typeof action === "string") {
|
|
171
|
+
target = parseActionRef(action);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
const rawId = action.actionId || action.id;
|
|
175
|
+
if (!rawId)
|
|
176
|
+
return false;
|
|
177
|
+
if (action.packageId) {
|
|
178
|
+
if (rawId.includes("/")) {
|
|
179
|
+
target = parseActionRef(rawId);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
target = parseActionRef({
|
|
183
|
+
packageId: action.packageId,
|
|
184
|
+
actionId: rawId,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
target = parseActionRef(rawId);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
for (const item of allowlist) {
|
|
197
|
+
if (typeof item !== "string")
|
|
198
|
+
continue;
|
|
199
|
+
const trimmed = item.trim();
|
|
200
|
+
if (!trimmed)
|
|
201
|
+
continue;
|
|
202
|
+
try {
|
|
203
|
+
const rule = parseActionRef(trimmed);
|
|
204
|
+
if (rule.packageId) {
|
|
205
|
+
if (rule.packageId === target.packageId && rule.actionId === target.actionId) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
if (rule.actionId === target.actionId) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* 根据生效策略统一判定目标动作是否允许访问。
|
|
223
|
+
* 同时综合校验策略中的 packageAllowlist 与 actionAllowlist 约束。
|
|
224
|
+
*
|
|
225
|
+
* @param action 目标动作对象或引用字符串
|
|
226
|
+
* @param policy 当前生效的服务端策略
|
|
227
|
+
* @returns 是否允许访问
|
|
228
|
+
*/
|
|
229
|
+
export function isActionAllowedByPolicy(action, policy) {
|
|
230
|
+
if (!policy) {
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
if (!action) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
let pkgId;
|
|
237
|
+
let actId;
|
|
238
|
+
try {
|
|
239
|
+
if (typeof action === "string") {
|
|
240
|
+
const parsed = parseActionRef(action);
|
|
241
|
+
pkgId = parsed.packageId;
|
|
242
|
+
actId = parsed.actionId;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
pkgId = action.packageId;
|
|
246
|
+
const rawId = action.actionId || action.id;
|
|
247
|
+
if (!rawId)
|
|
248
|
+
return false;
|
|
249
|
+
if (rawId.includes("/")) {
|
|
250
|
+
const parsed = parseActionRef(rawId);
|
|
251
|
+
pkgId = parsed.packageId || pkgId;
|
|
252
|
+
actId = parsed.actionId;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
actId = rawId;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
// 1. 若配置了 packageAllowlist,校验所属包白名单
|
|
263
|
+
if (policy.packageAllowlist && policy.packageAllowlist.length > 0) {
|
|
264
|
+
if (pkgId) {
|
|
265
|
+
if (!policy.packageAllowlist.includes(pkgId)) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
// 未指明所属包时,仅当其动作标识明确列入 actionAllowlist 时才允许放行
|
|
271
|
+
if (!policy.actionAllowlist ||
|
|
272
|
+
policy.actionAllowlist.length === 0 ||
|
|
273
|
+
!isActionAllowed({ actionId: actId }, policy.actionAllowlist)) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// 2. 若配置了 actionAllowlist,校验动作白名单
|
|
279
|
+
if (policy.actionAllowlist && policy.actionAllowlist.length > 0) {
|
|
280
|
+
if (!isActionAllowed({ packageId: pkgId, actionId: actId }, policy.actionAllowlist)) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* 根据生效策略统一判定目标包标识是否允许访问。
|
|
288
|
+
*
|
|
289
|
+
* @param packageId 目标包 ID
|
|
290
|
+
* @param policy 当前生效的服务端策略
|
|
291
|
+
* @returns 是否允许访问
|
|
292
|
+
*/
|
|
293
|
+
export function isPackageAllowedByPolicy(packageId, policy) {
|
|
294
|
+
if (!policy?.packageAllowlist || policy.packageAllowlist.length === 0) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
if (!packageId) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
return policy.packageAllowlist.includes(packageId);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* 根据生效策略统一判定配置与状态管理路由是否开启。
|
|
304
|
+
*
|
|
305
|
+
* @param policy 当前生效的服务端策略
|
|
306
|
+
* @returns 是否允许管理操作
|
|
307
|
+
*/
|
|
308
|
+
export function isManagementAllowedByPolicy(policy) {
|
|
309
|
+
return policy?.enableManagement === true;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* 根据生效策略对动作列表进行统一过滤。
|
|
313
|
+
*
|
|
314
|
+
* @param actions 待过滤的动作列表
|
|
315
|
+
* @param policy 当前生效的服务端策略
|
|
316
|
+
* @returns 过滤后允许访问的动作列表
|
|
317
|
+
*/
|
|
318
|
+
export function filterActionsByPolicy(actions, policy) {
|
|
319
|
+
if (!policy) {
|
|
320
|
+
return actions;
|
|
321
|
+
}
|
|
322
|
+
const hasPkg = policy.packageAllowlist && policy.packageAllowlist.length > 0;
|
|
323
|
+
const hasAct = policy.actionAllowlist && policy.actionAllowlist.length > 0;
|
|
324
|
+
if (!hasPkg && !hasAct) {
|
|
325
|
+
return actions;
|
|
326
|
+
}
|
|
327
|
+
return actions.filter((a) => isActionAllowedByPolicy({ packageId: a.packageId, actionId: a.actionId || a.id }, policy));
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* 根据生效策略对包列表进行统一过滤。
|
|
331
|
+
*
|
|
332
|
+
* @param packages 待过滤的包列表
|
|
333
|
+
* @param policy 当前生效的服务端策略
|
|
334
|
+
* @returns 过滤后允许访问的包列表
|
|
335
|
+
*/
|
|
336
|
+
export function filterPackagesByPolicy(packages, policy) {
|
|
337
|
+
if (!policy?.packageAllowlist || policy.packageAllowlist.length === 0) {
|
|
338
|
+
return packages;
|
|
339
|
+
}
|
|
340
|
+
return packages.filter((p) => p.id && policy.packageAllowlist.includes(p.id));
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* 校验目标动作是否属于允许白名单,不符合则抛出 403 异常。
|
|
344
|
+
*
|
|
345
|
+
* @param action 目标动作对象或引用
|
|
346
|
+
* @param policy 当前生效的服务端策略
|
|
347
|
+
*/
|
|
348
|
+
export function assertActionAllowedByPolicy(action, policy) {
|
|
349
|
+
if (!isActionAllowedByPolicy(action, policy)) {
|
|
350
|
+
const actRef = typeof action === "string"
|
|
351
|
+
? action
|
|
352
|
+
: action.packageId
|
|
353
|
+
? `${action.packageId}/${action.actionId || action.id}`
|
|
354
|
+
: action.actionId || action.id || "unknown";
|
|
355
|
+
throw new ActionForbiddenError(actRef);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* 校验目标包是否属于允许白名单,不符合则抛出 403 异常。
|
|
360
|
+
*
|
|
361
|
+
* @param packageId 目标包 ID
|
|
362
|
+
* @param policy 当前生效的服务端策略
|
|
363
|
+
*/
|
|
364
|
+
export function assertPackageAllowedByPolicy(packageId, policy) {
|
|
365
|
+
if (!isPackageAllowedByPolicy(packageId, policy)) {
|
|
366
|
+
throw new PackageNotAllowedError(packageId);
|
|
367
|
+
}
|
|
368
|
+
}
|