@actiondock/core 2.6.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 +122 -10
- package/dist/errors.js +123 -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 -22
- package/dist/index.js +19 -21
- package/dist/input/advice.d.ts +104 -0
- package/dist/input/advice.js +716 -0
- package/dist/input/describe.d.ts +106 -0
- package/dist/input/describe.js +176 -0
- package/dist/input/file-input.d.ts +54 -0
- package/dist/input/file-input.js +124 -0
- package/dist/input/flat-decode.d.ts +11 -0
- package/dist/input/flat-decode.js +13 -0
- package/dist/input/flat-errors.d.ts +71 -0
- package/dist/input/flat-errors.js +87 -0
- package/dist/input/flat-materializer.d.ts +29 -0
- package/dist/input/flat-materializer.js +177 -0
- package/dist/input/flat-parser.d.ts +50 -0
- package/dist/input/flat-parser.js +169 -0
- package/dist/input/flat-predicates.d.ts +34 -0
- package/dist/input/flat-predicates.js +50 -0
- package/dist/input/index.d.ts +14 -0
- package/dist/input/index.js +14 -0
- package/dist/input/input-resolver.d.ts +63 -0
- package/dist/input/input-resolver.js +254 -0
- package/dist/input/json-depth-scanner.d.ts +17 -0
- package/dist/input/json-depth-scanner.js +49 -0
- package/dist/input/metadata.d.ts +99 -0
- package/dist/input/metadata.js +99 -0
- package/dist/input/stdin-input.d.ts +26 -0
- package/dist/input/stdin-input.js +125 -0
- package/dist/input/utf8.d.ts +19 -0
- package/dist/input/utf8.js +36 -0
- package/dist/input/validation-mapper.d.ts +27 -0
- package/dist/input/validation-mapper.js +144 -0
- 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/json/index.d.ts +1 -0
- package/dist/json/index.js +1 -0
- package/dist/json/value-validator.d.ts +88 -0
- package/dist/json/value-validator.js +330 -0
- 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 +3 -2
- package/dist/profile/client-query.js +5 -6
- package/dist/profile/client-transport.d.ts +19 -17
- package/dist/profile/client-transport.js +48 -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 -103
- package/dist/runtime/runner.js +142 -404
- package/dist/runtime/standalone.d.ts +37 -9
- package/dist/runtime/standalone.js +195 -136
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/validator.d.ts +10 -0
- package/dist/schema/validator.js +48 -54
- 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 -623
- 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
package/dist/runtime/runner.js
CHANGED
|
@@ -1,79 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { resolveActionProject } from "../registry/registry.js";
|
|
6
|
-
import { ACTION_CALL_CYCLE, ACTION_CYCLE_DETECTED, ACTION_FAILED, ACTION_MAX_DEPTH_EXCEEDED, ACTION_NOT_FOUND, ACTION_SUBRUN_LIMIT, ACTION_TIMEOUT, describeActionLoadFailure, INPUT_NOT_JSON, MAX_SUBRUNS_REACHED, OUTPUT_NOT_JSON, ACTION_CANCELLED, PACKAGE_NOT_FOUND, INPUT_VALIDATION_FAILED, OUTPUT_VALIDATION_FAILED, UNDECLARED_ACTION_DEPENDENCY, } from "../errors.js";
|
|
7
|
-
import { validateSchema } from "../schema/validator.js";
|
|
1
|
+
import { parseActionRef } from "../catalog/resolve-action.js";
|
|
2
|
+
import { ACTION_FAILED, ACTION_NOT_FOUND, ACTION_TIMEOUT, INPUT_NOT_JSON, OUTPUT_NOT_JSON, ACTION_CANCELLED, INPUT_VALIDATION_FAILED, OUTPUT_VALIDATION_FAILED, INVOCATION_UNSUPPORTED, ActionDockError, } from "../errors.js";
|
|
3
|
+
import { validateSchemaOnly } from "../schema/validator.js";
|
|
4
|
+
import { validateActionInputValue } from "../json/value-validator.js";
|
|
8
5
|
import { createActionContext, StderrLogger } from "./context.js";
|
|
9
|
-
import {
|
|
6
|
+
import { createDefaultProcessOwner } from "../invocation/types.js";
|
|
7
|
+
import { ActionRegistry, findLocalAction, isActionDefinitionObject, resolveAnonymousActionId, } from "./action-registry.js";
|
|
10
8
|
import { buildInitialRunRecord, createRunFinalizer, createRunOrThrow, tryCreateRun, } from "./run-persistence.js";
|
|
11
|
-
import {
|
|
12
|
-
// 错误码常量已收敛至 src/errors.ts 单一事实源,此处保留 re-export 以维持既有导入路径兼容。
|
|
13
|
-
export { RUN_REPOSITORY_UNAVAILABLE, RUN_PERSISTENCE_FAILED, INPUT_NOT_JSON, OUTPUT_NOT_JSON, ACTION_SUBRUN_LIMIT, MAX_SUBRUNS_REACHED, ACTION_CALL_CYCLE, ACTION_CYCLE_DETECTED, ACTION_MAX_DEPTH_EXCEEDED, } from "../errors.js";
|
|
9
|
+
import { validateJsonValue } from "../json/value-validator.js";
|
|
14
10
|
/**
|
|
15
|
-
*
|
|
11
|
+
* 将输入值校验结果映射为标准运行时错误(单一事实源)。
|
|
16
12
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* json-value 分支映射 INPUT_NOT_JSON(非 JSON 类型值),input-policy 分支映射
|
|
14
|
+
* INPUT_VALIDATION_FAILED(策略违规)并携带 details 数组;start 同步路径与
|
|
15
|
+
* raceExecutionAndAbort 延迟解析路径共用,确保错误码与 details 结构一致。
|
|
19
16
|
*/
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
return {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return { valid: false, reason: `Number is non-finite or NaN (${val})` };
|
|
27
|
-
}
|
|
28
|
-
return { valid: true };
|
|
29
|
-
}
|
|
30
|
-
if (typeof val === "undefined" || typeof val === "function" || typeof val === "symbol" || typeof val === "bigint") {
|
|
31
|
-
return { valid: false, reason: `Unsupported JSON type '${typeof val}'` };
|
|
32
|
-
}
|
|
33
|
-
if (typeof val === "object") {
|
|
34
|
-
if (stack.has(val)) {
|
|
35
|
-
return { valid: false, reason: "Circular reference detected in object structure" };
|
|
36
|
-
}
|
|
37
|
-
stack.add(val);
|
|
38
|
-
try {
|
|
39
|
-
if (Array.isArray(val)) {
|
|
40
|
-
for (const item of val) {
|
|
41
|
-
const res = validateJsonValue(item, stack);
|
|
42
|
-
if (!res.valid)
|
|
43
|
-
return res;
|
|
44
|
-
}
|
|
45
|
-
return { valid: true };
|
|
46
|
-
}
|
|
47
|
-
for (const v of Object.values(val)) {
|
|
48
|
-
if (v !== undefined) {
|
|
49
|
-
const res = validateJsonValue(v, stack);
|
|
50
|
-
if (!res.valid)
|
|
51
|
-
return res;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return { valid: true };
|
|
55
|
-
}
|
|
56
|
-
finally {
|
|
57
|
-
// 无论正常返回还是提前返回,均须将当前对象移出路径栈,避免祖先对象被误判为环路
|
|
58
|
-
stack.delete(val);
|
|
59
|
-
}
|
|
17
|
+
function buildInputValidationError(targetActionId, check) {
|
|
18
|
+
if (check.kind === "json-value") {
|
|
19
|
+
return {
|
|
20
|
+
code: INPUT_NOT_JSON,
|
|
21
|
+
message: `Input validation failed for action '${targetActionId}': ${check.reason}`,
|
|
22
|
+
};
|
|
60
23
|
}
|
|
61
|
-
return {
|
|
24
|
+
return {
|
|
25
|
+
code: INPUT_VALIDATION_FAILED,
|
|
26
|
+
message: `Input validation failed for action '${targetActionId}': ${check.reason}`,
|
|
27
|
+
details: [check.reason],
|
|
28
|
+
};
|
|
62
29
|
}
|
|
63
30
|
/**
|
|
64
31
|
* ActionDock 核心执行引擎(ActionRunner)。
|
|
65
32
|
*
|
|
66
33
|
* 本类为执行编排壳:负责 Action 执行的全生命周期编排(校验、深度与环路检测、
|
|
67
34
|
* 超时与取消竞态、子调用 uses 授权与错误分类),并将单一职责域委托至独立模块:
|
|
68
|
-
* - Action 注册与检索委托
|
|
35
|
+
* - Action 注册与检索委托 ActionRegistry(action-registry.ts)
|
|
69
36
|
* - RunRecord 构造与落库委托 run-persistence.ts
|
|
70
|
-
* -
|
|
37
|
+
* - 跨包动作调用委托注入的 ActionInvoker 并重返目标包执行服务
|
|
71
38
|
*
|
|
72
39
|
* 入参 (inputSchema) 与出参 (outputSchema) 的 JSON Schema 严格校验、嵌套 Action
|
|
73
40
|
* 相互调用的环路检测(Cycle Detection)、超时 (Timeout) 与中断信号 (AbortSignal)
|
|
74
41
|
* 竞态控制与运行记录持久化编排仍由本类承担。
|
|
75
42
|
*/
|
|
76
43
|
export class ActionRunner {
|
|
44
|
+
identity;
|
|
77
45
|
packageId;
|
|
78
46
|
packageInstanceId;
|
|
79
47
|
generationId;
|
|
@@ -85,76 +53,41 @@ export class ActionRunner {
|
|
|
85
53
|
registry;
|
|
86
54
|
clock;
|
|
87
55
|
process;
|
|
88
|
-
platform;
|
|
89
|
-
maxCallDepth;
|
|
90
|
-
maxSubRuns;
|
|
91
|
-
activeSubRuns = 0;
|
|
92
|
-
packageFactory;
|
|
93
|
-
actionResolver;
|
|
94
|
-
getStorageForPackage;
|
|
95
56
|
customHome;
|
|
57
|
+
actionResolver;
|
|
96
58
|
hostSessionId;
|
|
59
|
+
actionInvoker;
|
|
97
60
|
constructor(options) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
61
|
+
if (!options.identity) {
|
|
62
|
+
throw new Error("ActionRunner requires 'identity' PackageIdentity option");
|
|
63
|
+
}
|
|
64
|
+
this.identity = options.identity;
|
|
65
|
+
this.packageId = this.identity.id;
|
|
66
|
+
this.packageInstanceId = this.identity.instanceId;
|
|
67
|
+
this.generationId = this.identity.generation;
|
|
101
68
|
this.hostSessionId = options.hostSessionId;
|
|
102
69
|
this.projectRoot = options.projectRoot;
|
|
103
70
|
this.projectConfig = options.projectConfig;
|
|
104
71
|
this.configOverrides = options.configOverrides || {};
|
|
105
72
|
this.registry = new ActionRegistry(options.actions);
|
|
106
73
|
this.customHome = options.customHome;
|
|
107
|
-
this.
|
|
108
|
-
if (options.platform) {
|
|
109
|
-
this.clock = options.platform.clock;
|
|
110
|
-
this.process = options.platform.process;
|
|
111
|
-
this.storage = options.storage ?? options.platform.storage.createStorage(this.packageId, {
|
|
112
|
-
projectRoot: this.projectRoot,
|
|
113
|
-
customHome: this.customHome,
|
|
114
|
-
// Runner 作为执行宿主组件属于持有者路径,打开时收割遗留孤儿运行
|
|
115
|
-
recoverOrphans: true,
|
|
116
|
-
});
|
|
117
|
-
this.globalStorage = options.globalStorage ?? options.platform.storage.createGlobalStorage({
|
|
118
|
-
customHome: this.customHome,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
if (!options.storage) {
|
|
123
|
-
throw new Error("ActionRunner requires either 'storage' or 'platform' option");
|
|
124
|
-
}
|
|
125
|
-
this.storage = options.storage;
|
|
126
|
-
this.globalStorage = options.globalStorage;
|
|
127
|
-
this.process = options.process;
|
|
128
|
-
this.clock = options.clock;
|
|
129
|
-
}
|
|
130
|
-
this.maxCallDepth = options.maxCallDepth ?? 16;
|
|
131
|
-
this.maxSubRuns = options.maxSubRuns ?? 64;
|
|
74
|
+
this.actionInvoker = options.actionInvoker;
|
|
132
75
|
this.actionResolver = options.actionResolver;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
maxCallDepth: this.maxCallDepth,
|
|
141
|
-
maxSubRuns: this.maxSubRuns,
|
|
142
|
-
actionResolver: this.actionResolver,
|
|
143
|
-
getStorageForPackage: this.getStorageForPackage,
|
|
144
|
-
packageContextResolver: options.packageContextResolver,
|
|
145
|
-
customHome: this.customHome,
|
|
146
|
-
}, (opts) => new ActionRunner(opts));
|
|
76
|
+
if (!options.storage) {
|
|
77
|
+
throw new Error("ActionRunner requires 'storage' option");
|
|
78
|
+
}
|
|
79
|
+
this.storage = options.storage;
|
|
80
|
+
this.globalStorage = options.globalStorage;
|
|
81
|
+
this.clock = options.clock;
|
|
82
|
+
this.process = options.process;
|
|
147
83
|
}
|
|
148
|
-
/** 本地 Action
|
|
84
|
+
/** 本地 Action 注册表底层映射 */
|
|
149
85
|
get actions() {
|
|
150
86
|
return this.registry.map;
|
|
151
87
|
}
|
|
152
88
|
getStorage() {
|
|
153
89
|
return this.storage;
|
|
154
90
|
}
|
|
155
|
-
getPackageRunners() {
|
|
156
|
-
return this.packageFactory.runners;
|
|
157
|
-
}
|
|
158
91
|
registerAction(idOrAction, actionDef) {
|
|
159
92
|
this.registry.registerAction(idOrAction, actionDef);
|
|
160
93
|
}
|
|
@@ -164,11 +97,17 @@ export class ActionRunner {
|
|
|
164
97
|
getAction(id) {
|
|
165
98
|
return this.registry.getAction(id);
|
|
166
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* 设置子任务动作调用委托器。
|
|
102
|
+
*/
|
|
103
|
+
setActionInvoker(invoker) {
|
|
104
|
+
this.actionInvoker = invoker;
|
|
105
|
+
}
|
|
167
106
|
/**
|
|
168
107
|
* 动态解析 Action(支持本地注册表、自定义解析器委托与已链接包目录索引检索)。
|
|
169
108
|
*
|
|
170
109
|
* @param actionOrRef Action 定义对象、引用或标识符
|
|
171
|
-
* @returns 解析判别联合结果(found | not_found
|
|
110
|
+
* @returns 解析判别联合结果(found | not_found)
|
|
172
111
|
*/
|
|
173
112
|
async resolveAction(actionOrRef) {
|
|
174
113
|
if (isActionDefinitionObject(actionOrRef)) {
|
|
@@ -184,24 +123,26 @@ export class ActionRunner {
|
|
|
184
123
|
}
|
|
185
124
|
const targetActionId = parsed.actionId;
|
|
186
125
|
const targetPackageId = parsed.packageId;
|
|
126
|
+
// 铁律:ActionRunner 仅限执行当前包局部 Action,任何跨包请求直接拒绝,禁止跨包解析和执行
|
|
127
|
+
if (targetPackageId && targetPackageId !== this.packageId) {
|
|
128
|
+
return {
|
|
129
|
+
status: "not_found",
|
|
130
|
+
reason: `Cross-package action '${targetPackageId}/${targetActionId}' cannot be resolved by ActionRunner of package '${this.packageId}'`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
187
133
|
// 本地 actions 映射表优先检索
|
|
188
134
|
const localMatched = findLocalAction(this.actions, parsed, this.packageId);
|
|
189
135
|
if (localMatched) {
|
|
190
136
|
return { status: "found", action: localMatched };
|
|
191
137
|
}
|
|
192
|
-
//
|
|
138
|
+
// 外部注入的局部 actionResolver 调度
|
|
193
139
|
if (this.actionResolver) {
|
|
194
140
|
try {
|
|
195
|
-
const customResolved = await this.actionResolver(
|
|
141
|
+
const customResolved = await this.actionResolver(targetActionId);
|
|
196
142
|
if (customResolved) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
else {
|
|
201
|
-
this.actions.set(targetActionId, customResolved);
|
|
202
|
-
if (this.packageId) {
|
|
203
|
-
this.actions.set(`${this.packageId}/${targetActionId}`, customResolved);
|
|
204
|
-
}
|
|
143
|
+
this.actions.set(targetActionId, customResolved);
|
|
144
|
+
if (this.packageId) {
|
|
145
|
+
this.actions.set(`${this.packageId}/${targetActionId}`, customResolved);
|
|
205
146
|
}
|
|
206
147
|
return { status: "found", action: customResolved };
|
|
207
148
|
}
|
|
@@ -210,61 +151,10 @@ export class ActionRunner {
|
|
|
210
151
|
return { status: "not_found", reason: err.message };
|
|
211
152
|
}
|
|
212
153
|
}
|
|
213
|
-
//
|
|
214
|
-
const identifier = targetPackageId
|
|
215
|
-
? `${targetPackageId}/${targetActionId}`
|
|
216
|
-
: targetActionId;
|
|
217
|
-
let resolved;
|
|
218
|
-
try {
|
|
219
|
-
resolved = await resolveActionProject(identifier, this.projectRoot, this.customHome);
|
|
220
|
-
}
|
|
221
|
-
catch (err) {
|
|
222
|
-
return { status: "not_found", reason: err.message };
|
|
223
|
-
}
|
|
224
|
-
if (!resolved || !existsSync(resolved.projectRoot)) {
|
|
225
|
-
return {
|
|
226
|
-
status: "not_found",
|
|
227
|
-
reason: `Project root not found for action '${identifier}'`,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
let config;
|
|
231
|
-
try {
|
|
232
|
-
config = loadProjectConfig(resolved.projectRoot);
|
|
233
|
-
}
|
|
234
|
-
catch (err) {
|
|
235
|
-
return {
|
|
236
|
-
status: "load_failed",
|
|
237
|
-
error: err instanceof Error ? err : new Error(String(err)),
|
|
238
|
-
packageId: resolved.packageId,
|
|
239
|
-
projectRoot: resolved.projectRoot,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
let actionsMap;
|
|
243
|
-
try {
|
|
244
|
-
actionsMap = await loadActions(resolved.projectRoot, config.actionsDir, {
|
|
245
|
-
loader: this.platform?.modules,
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
catch (err) {
|
|
249
|
-
return {
|
|
250
|
-
status: "load_failed",
|
|
251
|
-
error: err instanceof Error ? err : new Error(String(err)),
|
|
252
|
-
packageId: resolved.packageId,
|
|
253
|
-
projectRoot: resolved.projectRoot,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
const matched = actionsMap.get(resolved.actionId);
|
|
257
|
-
if (matched) {
|
|
258
|
-
this.actions.set(`${resolved.packageId}/${resolved.actionId}`, matched);
|
|
259
|
-
// 仅当目标包就是当前项目时才注册短标识符,避免跨包动态载入污染全局短标识符
|
|
260
|
-
if (!targetPackageId || resolved.packageId === this.packageId) {
|
|
261
|
-
this.actions.set(resolved.actionId, matched);
|
|
262
|
-
}
|
|
263
|
-
return { status: "found", action: matched };
|
|
264
|
-
}
|
|
154
|
+
// 铁律 4:Runner 不找 Package:只能执行自己所属 Package 的 Action
|
|
265
155
|
return {
|
|
266
156
|
status: "not_found",
|
|
267
|
-
reason: `Action '${
|
|
157
|
+
reason: `Action '${targetActionId}' not found in package '${this.packageId}'`,
|
|
268
158
|
};
|
|
269
159
|
}
|
|
270
160
|
/**
|
|
@@ -273,22 +163,6 @@ export class ActionRunner {
|
|
|
273
163
|
listActions() {
|
|
274
164
|
return this.registry.listActions();
|
|
275
165
|
}
|
|
276
|
-
/**
|
|
277
|
-
* 注入或更新跨包运行上下文解析委托。
|
|
278
|
-
*/
|
|
279
|
-
setPackageContextResolver(resolver) {
|
|
280
|
-
this.packageFactory.setPackageContextResolver(resolver);
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* 跨包运行时解析与获取(确保跨包执行具备独立的配置、存储、状态与 Action 注册表)。
|
|
284
|
-
*
|
|
285
|
-
* 缓存 miss 后存在长异步窗口(加载配置、扫描 Action、打开存储),并发调用会重复构建
|
|
286
|
-
* Runner、重复打开 SQLite 连接且子任务限流计数分裂;故以 in-flight Promise 去重,
|
|
287
|
-
* 并发方 await 同一构建任务,构建失败时移除该 Promise 以便后续重试。
|
|
288
|
-
*/
|
|
289
|
-
async resolveTargetPackageRunner(targetPackageId) {
|
|
290
|
-
return this.packageFactory.resolveTargetPackageRunner(targetPackageId);
|
|
291
|
-
}
|
|
292
166
|
/**
|
|
293
167
|
* 异步启动 Action 的执行并立即返回 ExecutionHandle 句柄。
|
|
294
168
|
*
|
|
@@ -300,14 +174,11 @@ export class ActionRunner {
|
|
|
300
174
|
start(actionOrId, input = {}, options = {}) {
|
|
301
175
|
const runCtx = this.prepareRunContext(actionOrId, input, options);
|
|
302
176
|
const { runId, targetActionId, targetPackageId } = runCtx;
|
|
303
|
-
// 输入参数 JSON 格式与合法性防御校验(拦截 NaN/Infinity/循环引用等非 JSON
|
|
304
|
-
const inputCheck =
|
|
177
|
+
// 输入参数 JSON 格式与合法性防御校验(拦截 NaN/Infinity/循环引用等非 JSON 类型及非法原型键策略违规)
|
|
178
|
+
const inputCheck = validateActionInputValue(input);
|
|
305
179
|
if (!inputCheck.valid) {
|
|
306
|
-
const error =
|
|
307
|
-
|
|
308
|
-
message: `Input validation failed for action '${targetActionId}': ${inputCheck.reason}`,
|
|
309
|
-
};
|
|
310
|
-
// 安全记录 failed 状态(不可序列化的非法 input 严禁直接写入持久化存储)
|
|
180
|
+
const error = buildInputValidationError(targetActionId, inputCheck);
|
|
181
|
+
// 安全记录 failed 状态(不可序列化或策略违规的非法 input 严禁直接写入持久化存储)
|
|
311
182
|
tryCreateRun(this.storage, buildInitialRunRecord(this.buildRunPersistenceInput(runCtx), "failed", error));
|
|
312
183
|
return {
|
|
313
184
|
runId,
|
|
@@ -318,27 +189,15 @@ export class ActionRunner {
|
|
|
318
189
|
// 始终优先将执行尝试持久化到存储中(确保任意异常与终态都可追溯)
|
|
319
190
|
createRunOrThrow(this.storage, buildInitialRunRecord(this.buildRunPersistenceInput(runCtx), "running"));
|
|
320
191
|
const finalizer = createRunFinalizer(this.storage, runCtx.runId);
|
|
321
|
-
//
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
// 环路死锁检测 (Cycle Detection)
|
|
332
|
-
const cycle = this.computeCallKey(runCtx.callStack, targetActionId, targetPackageId);
|
|
333
|
-
if (cycle.error) {
|
|
334
|
-
finalizer.finalize("failed", undefined, cycle.error);
|
|
335
|
-
return {
|
|
336
|
-
runId,
|
|
337
|
-
result: Promise.resolve({ ok: false, runId, error: cycle.error }),
|
|
338
|
-
cancel: () => false,
|
|
339
|
-
};
|
|
192
|
+
// 记录调用栈快照(供子任务追溯,若已处于调用栈顶则不重复追加)
|
|
193
|
+
const callKey = targetPackageId ? `${targetPackageId}/${targetActionId}` : targetActionId;
|
|
194
|
+
const lastStackItem = runCtx.callStack[runCtx.callStack.length - 1];
|
|
195
|
+
const isAlreadyAtTop = lastStackItem === callKey ||
|
|
196
|
+
(targetActionId && lastStackItem === targetActionId) ||
|
|
197
|
+
(targetPackageId && lastStackItem === `${targetPackageId}/${targetActionId}`);
|
|
198
|
+
if (!isAlreadyAtTop) {
|
|
199
|
+
runCtx.callStack.push(callKey);
|
|
340
200
|
}
|
|
341
|
-
runCtx.callStack.push(cycle.callKey);
|
|
342
201
|
// 输入参数 JSON Schema 校验(若 action 已就绪)
|
|
343
202
|
const schemaError = this.checkActionInputSchema(runCtx.action, targetActionId, input);
|
|
344
203
|
if (schemaError) {
|
|
@@ -408,9 +267,9 @@ export class ActionRunner {
|
|
|
408
267
|
* 预解析执行目标并装配运行期共享上下文对象(runId、目标标识、时钟源与调用栈快照)。
|
|
409
268
|
*/
|
|
410
269
|
prepareRunContext(actionOrId, input, options) {
|
|
411
|
-
const runId = options.runId || randomUUID();
|
|
412
|
-
const effectiveClock = options.
|
|
413
|
-
const effectiveProcess = options.process ||
|
|
270
|
+
const runId = options.runId || crypto.randomUUID();
|
|
271
|
+
const effectiveClock = options.clock ?? this.clock;
|
|
272
|
+
const effectiveProcess = options.process || this.process;
|
|
414
273
|
const startedAt = effectiveClock?.now().toISOString() ||
|
|
415
274
|
(typeof this.storage.clock?.now === "function"
|
|
416
275
|
? this.storage.clock.now().toISOString()
|
|
@@ -474,51 +333,15 @@ export class ActionRunner {
|
|
|
474
333
|
runnerHostSessionId: this.hostSessionId,
|
|
475
334
|
};
|
|
476
335
|
}
|
|
477
|
-
/**
|
|
478
|
-
* 调用嵌套深度限制检测(超限时返回 ACTION_CALL_CYCLE 错误)。
|
|
479
|
-
*/
|
|
480
|
-
checkCallDepth(callStack, targetActionId, options) {
|
|
481
|
-
// 执行级 options.maxCallDepth 可覆盖构造级默认值,保留原覆盖契约
|
|
482
|
-
const maxDepth = options.maxCallDepth ?? this.maxCallDepth;
|
|
483
|
-
if (callStack.length >= maxDepth) {
|
|
484
|
-
return {
|
|
485
|
-
code: ACTION_CALL_CYCLE,
|
|
486
|
-
message: `Maximum call depth of ${maxDepth} exceeded: ${callStack.join(" -> ")} -> ${targetActionId}`,
|
|
487
|
-
details: { alias: ACTION_MAX_DEPTH_EXCEEDED, reason: "depth_exceeded", maxDepth, callStack: [...callStack] },
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
return undefined;
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* 计算调用键并执行环路死锁检测(命中时返回携带错误的调用键结果)。
|
|
494
|
-
*/
|
|
495
|
-
computeCallKey(callStack, targetActionId, targetPackageId) {
|
|
496
|
-
const isExternal = Boolean(targetPackageId && targetPackageId !== this.packageId);
|
|
497
|
-
const callKey = isExternal
|
|
498
|
-
? `${targetPackageId}/${targetActionId}`
|
|
499
|
-
: targetActionId;
|
|
500
|
-
const hasCycle = isExternal
|
|
501
|
-
? callStack.includes(callKey)
|
|
502
|
-
: (callStack.includes(callKey) || (this.packageId ? callStack.includes(`${this.packageId}/${targetActionId}`) : false));
|
|
503
|
-
if (hasCycle) {
|
|
504
|
-
return {
|
|
505
|
-
callKey,
|
|
506
|
-
error: {
|
|
507
|
-
code: ACTION_CALL_CYCLE,
|
|
508
|
-
message: `Cycle detected in action invocation: ${callStack.join(" -> ")} -> ${callKey}`,
|
|
509
|
-
details: { alias: ACTION_CYCLE_DETECTED, reason: "cycle_detected", callStack: [...callStack], target: callKey },
|
|
510
|
-
},
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
return { callKey };
|
|
514
|
-
}
|
|
515
336
|
/**
|
|
516
337
|
* 输入参数 JSON Schema 校验(若 action 已就绪,返回校验错误或 undefined)。
|
|
517
338
|
*/
|
|
518
339
|
checkActionInputSchema(action, targetActionId, input) {
|
|
519
|
-
const targetInputSchema = action?.inputSchema
|
|
520
|
-
|
|
521
|
-
|
|
340
|
+
const targetInputSchema = action?.inputSchema !== undefined
|
|
341
|
+
? action.inputSchema
|
|
342
|
+
: this.projectConfig?.actions?.[targetActionId]?.inputSchema;
|
|
343
|
+
if (targetInputSchema !== undefined) {
|
|
344
|
+
const val = validateSchemaOnly(targetInputSchema, input);
|
|
522
345
|
if (!val.valid) {
|
|
523
346
|
return {
|
|
524
347
|
code: INPUT_VALIDATION_FAILED,
|
|
@@ -535,12 +358,13 @@ export class ActionRunner {
|
|
|
535
358
|
buildActionContext(args) {
|
|
536
359
|
const { runCtx, controller, rootRunId } = args;
|
|
537
360
|
const { options, targetPackageId, targetActionId, effectiveProcess } = runCtx;
|
|
538
|
-
const effectiveOwner = options.owner ||
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
361
|
+
const effectiveOwner = options.owner ||
|
|
362
|
+
createDefaultProcessOwner({
|
|
363
|
+
tenantId: options.tenantId,
|
|
364
|
+
principalId: options.principalId || options.ownerId,
|
|
365
|
+
packageInstanceId: options.packageInstanceId || this.packageInstanceId,
|
|
366
|
+
generationId: options.generationId || this.generationId,
|
|
367
|
+
});
|
|
544
368
|
return createActionContext({
|
|
545
369
|
actionId: targetActionId,
|
|
546
370
|
storage: this.storage,
|
|
@@ -566,121 +390,51 @@ export class ActionRunner {
|
|
|
566
390
|
});
|
|
567
391
|
}
|
|
568
392
|
/**
|
|
569
|
-
* 子 Action
|
|
393
|
+
* 子 Action 调用委托:
|
|
394
|
+
* 必须委托 Host 动作调用器(走 Host 执行主链与 InvocationPolicy 鉴权);
|
|
395
|
+
* 缺少 actionInvoker 时直接抛出 INVOCATION_UNSUPPORTED,严禁跨包或同包无约束执行逃逸。
|
|
570
396
|
*/
|
|
571
397
|
async invokeChildAction(args) {
|
|
572
398
|
const { runCtx, controller, rootRunId, childAction, childInput, parentRunId } = args;
|
|
573
|
-
const { options,
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
const childActionId = parsed.actionId;
|
|
586
|
-
const effectiveParentOwner = options.owner || {
|
|
587
|
-
tenantId: options.tenantId || "default",
|
|
588
|
-
principalId: options.principalId || options.ownerId || "default",
|
|
589
|
-
packageInstanceId: options.packageInstanceId || this.packageInstanceId || this.packageId,
|
|
590
|
-
generationId: options.generationId || this.generationId || "1",
|
|
591
|
-
};
|
|
592
|
-
const isSamePackage = !childPackageId || childPackageId === this.packageId;
|
|
593
|
-
this.activeSubRuns++;
|
|
594
|
-
try {
|
|
595
|
-
const runnerToUse = await this.resolveChildRunner(childPackageId, childActionId);
|
|
596
|
-
if (runnerToUse !== this) {
|
|
597
|
-
this.assertDeclaredUses(runCtx.action, targetActionId, childPackageId, childActionId);
|
|
598
|
-
}
|
|
599
|
-
const childPackageInstanceId = isSamePackage
|
|
600
|
-
? effectiveParentOwner.packageInstanceId
|
|
601
|
-
: (runnerToUse !== this ? runnerToUse.packageInstanceId : childPackageId);
|
|
602
|
-
const childGenerationId = isSamePackage
|
|
603
|
-
? effectiveParentOwner.generationId
|
|
604
|
-
: (runnerToUse !== this ? runnerToUse.generationId : "1");
|
|
605
|
-
const childOwner = {
|
|
606
|
-
tenantId: effectiveParentOwner.tenantId,
|
|
607
|
-
principalId: effectiveParentOwner.principalId,
|
|
608
|
-
packageInstanceId: childPackageInstanceId,
|
|
609
|
-
generationId: childGenerationId,
|
|
610
|
-
};
|
|
611
|
-
const childResult = await runnerToUse.execute(childAction, childInput, {
|
|
612
|
-
rootRunId,
|
|
613
|
-
parentRunId,
|
|
614
|
-
callStack,
|
|
615
|
-
signal: controller.signal,
|
|
616
|
-
process: effectiveProcess,
|
|
617
|
-
platform: options.platform || this.platform,
|
|
618
|
-
progress: options.progress,
|
|
619
|
-
logger: options.logger,
|
|
620
|
-
configOverrides: options.configOverrides,
|
|
621
|
-
maxCallDepth: options.maxCallDepth ?? this.maxCallDepth,
|
|
622
|
-
tenantId: childOwner.tenantId,
|
|
623
|
-
principalId: childOwner.principalId,
|
|
624
|
-
ownerId: childOwner.principalId,
|
|
625
|
-
packageInstanceId: childOwner.packageInstanceId,
|
|
626
|
-
generationId: childOwner.generationId,
|
|
627
|
-
owner: childOwner,
|
|
628
|
-
hostSessionId: options.hostSessionId ?? this.hostSessionId,
|
|
399
|
+
const { options, effectiveProcess, callStack } = runCtx;
|
|
400
|
+
const invoker = options.actionInvoker || this.actionInvoker;
|
|
401
|
+
if (!invoker) {
|
|
402
|
+
throw new ActionDockError(INVOCATION_UNSUPPORTED, "Nested action invocation requires a Host ActionInvoker");
|
|
403
|
+
}
|
|
404
|
+
const effectiveParentOwner = options.owner ||
|
|
405
|
+
createDefaultProcessOwner({
|
|
406
|
+
tenantId: options.tenantId,
|
|
407
|
+
principalId: options.principalId || options.ownerId,
|
|
408
|
+
packageInstanceId: this.packageInstanceId,
|
|
409
|
+
generationId: this.generationId,
|
|
629
410
|
});
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
err.details = {
|
|
658
|
-
caller: `${this.packageId}/${targetActionId}`,
|
|
659
|
-
target: targetRef,
|
|
660
|
-
declaredUses: rawUses,
|
|
661
|
-
};
|
|
662
|
-
throw err;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* 解析子调用应使用的 Runner(本包或已解析的目标包 Runner)。
|
|
667
|
-
*/
|
|
668
|
-
async resolveChildRunner(childPackageId, childActionId) {
|
|
669
|
-
if (childPackageId && childPackageId !== this.packageId) {
|
|
670
|
-
const targetRunner = await this.resolveTargetPackageRunner(childPackageId);
|
|
671
|
-
if (targetRunner) {
|
|
672
|
-
return targetRunner;
|
|
673
|
-
}
|
|
674
|
-
if (this.actions.has(`${childPackageId}/${childActionId}`)) {
|
|
675
|
-
return this;
|
|
676
|
-
}
|
|
677
|
-
if (!this.actionResolver) {
|
|
678
|
-
const err = new Error(`Package '${childPackageId}' could not be resolved`);
|
|
679
|
-
err.code = PACKAGE_NOT_FOUND;
|
|
680
|
-
throw err;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
return this;
|
|
411
|
+
const childRunId = crypto.randomUUID();
|
|
412
|
+
const invocationContext = {
|
|
413
|
+
runId: childRunId,
|
|
414
|
+
rootRunId,
|
|
415
|
+
parentRunId: parentRunId || runCtx.runId,
|
|
416
|
+
caller: {
|
|
417
|
+
packageId: this.packageId,
|
|
418
|
+
actionId: runCtx.targetActionId,
|
|
419
|
+
runId: runCtx.runId,
|
|
420
|
+
declaredUses: runCtx.action?.uses ||
|
|
421
|
+
this.projectConfig?.actions?.[runCtx.targetActionId]?.uses,
|
|
422
|
+
},
|
|
423
|
+
callStack: [...callStack],
|
|
424
|
+
package: this.identity,
|
|
425
|
+
signal: controller.signal,
|
|
426
|
+
timeoutMs: options.timeoutMs,
|
|
427
|
+
maxCallDepth: options.maxCallDepth,
|
|
428
|
+
config: options.configOverrides,
|
|
429
|
+
tenantId: effectiveParentOwner.tenantId,
|
|
430
|
+
principalId: effectiveParentOwner.principalId,
|
|
431
|
+
hostSessionId: options.hostSessionId || this.hostSessionId,
|
|
432
|
+
logger: options.logger,
|
|
433
|
+
progress: options.progress,
|
|
434
|
+
process: effectiveProcess,
|
|
435
|
+
owner: effectiveParentOwner,
|
|
436
|
+
};
|
|
437
|
+
return await invoker(childAction, childInput, invocationContext);
|
|
684
438
|
}
|
|
685
439
|
/**
|
|
686
440
|
* 执行 Action 业务逻辑并与取消/超时信号进行竞态,返回执行结果信封 Promise。
|
|
@@ -710,16 +464,6 @@ export class ActionRunner {
|
|
|
710
464
|
currentAction = resolution.action;
|
|
711
465
|
runCtx.action = currentAction;
|
|
712
466
|
}
|
|
713
|
-
else if (resolution.status === "load_failed") {
|
|
714
|
-
const cause = resolution.error;
|
|
715
|
-
const error = describeActionLoadFailure(cause, {
|
|
716
|
-
actionId: targetActionId,
|
|
717
|
-
packageId: resolution.packageId,
|
|
718
|
-
projectRoot: resolution.projectRoot,
|
|
719
|
-
});
|
|
720
|
-
finalizer.finalize("failed", undefined, error);
|
|
721
|
-
return { ok: false, runId, error };
|
|
722
|
-
}
|
|
723
467
|
else {
|
|
724
468
|
const error = {
|
|
725
469
|
code: ACTION_NOT_FOUND,
|
|
@@ -729,12 +473,9 @@ export class ActionRunner {
|
|
|
729
473
|
finalizer.finalize("failed", undefined, error);
|
|
730
474
|
return { ok: false, runId, error };
|
|
731
475
|
}
|
|
732
|
-
const inputCheck =
|
|
476
|
+
const inputCheck = validateActionInputValue(input);
|
|
733
477
|
if (!inputCheck.valid) {
|
|
734
|
-
const error =
|
|
735
|
-
code: INPUT_NOT_JSON,
|
|
736
|
-
message: `Input validation failed for action '${targetActionId}': ${inputCheck.reason}`,
|
|
737
|
-
};
|
|
478
|
+
const error = buildInputValidationError(targetActionId, inputCheck);
|
|
738
479
|
finalizer.finalize("failed", undefined, error);
|
|
739
480
|
return { ok: false, runId, error };
|
|
740
481
|
}
|
|
@@ -795,9 +536,11 @@ export class ActionRunner {
|
|
|
795
536
|
* 输出结果 Schema 校验(返回校验错误或 undefined)。
|
|
796
537
|
*/
|
|
797
538
|
checkActionOutputSchema(action, targetActionId, rawOutput) {
|
|
798
|
-
const targetOutputSchema = action?.outputSchema
|
|
799
|
-
|
|
800
|
-
|
|
539
|
+
const targetOutputSchema = action?.outputSchema !== undefined
|
|
540
|
+
? action.outputSchema
|
|
541
|
+
: this.projectConfig?.actions?.[targetActionId]?.outputSchema;
|
|
542
|
+
if (targetOutputSchema !== undefined) {
|
|
543
|
+
const outVal = validateSchemaOnly(targetOutputSchema, rawOutput);
|
|
801
544
|
if (!outVal.valid) {
|
|
802
545
|
return {
|
|
803
546
|
code: OUTPUT_VALIDATION_FAILED,
|
|
@@ -849,15 +592,10 @@ export class ActionRunner {
|
|
|
849
592
|
};
|
|
850
593
|
}
|
|
851
594
|
/**
|
|
852
|
-
* 释放 Runner
|
|
853
|
-
*
|
|
854
|
-
* 幂等且并发安全:重复调用直接复用首次 Promise;子包存储异常不阻断其余释放;
|
|
855
|
-
* 仅关闭由本 Runner 构建子包 Runner 时独立创建的存储,外部注入的存储(如经
|
|
856
|
-
* packageContextResolver 传入的目标包自身存储)生命周期归所有者管理,不在此误关。
|
|
595
|
+
* 释放 Runner 持有的资源。
|
|
596
|
+
* 铁律 4:Runner 仅执行自己所属 Package 的 Action,不管理外部包生命周期。
|
|
857
597
|
*/
|
|
858
|
-
async dispose() {
|
|
859
|
-
return this.packageFactory.dispose();
|
|
860
|
-
}
|
|
598
|
+
async dispose() { }
|
|
861
599
|
/**
|
|
862
600
|
* 同步等待方式执行指定 Action,直接返回 ExecutionResult 信封结果。
|
|
863
601
|
*
|