@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
package/dist/host/host.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
-
import {
|
|
3
|
+
import { DefaultPackageRuntime } from "../package/runtime.js";
|
|
4
|
+
import { applyActionSummaryFilters } from "../package/types.js";
|
|
5
|
+
import { createRootInvocationContext, } from "../invocation/types.js";
|
|
4
6
|
import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
|
|
5
|
-
import { ActionPackageResolver } from "../project/resolver.js";
|
|
6
7
|
import { hasPendingTransactions, isProjectLockHeld, recoverPendingTransactions } from "../project/transactions.js";
|
|
7
8
|
import { listLinkedPackages } from "../registry/registry.js";
|
|
8
9
|
import { InMemoryEventSink } from "../runtime/events.js";
|
|
9
|
-
import {
|
|
10
|
+
import { ActionDockError, ACTION_NOT_FOUND, ACTION_PACKAGE_VERSION_CONFLICT, ACTION_SUBRUN_LIMIT, INVALID_ACTION_REF, PACKAGE_NOT_FOUND, PROJECT_BUSY, PROJECT_RECOVERY_REQUIRED, UNDECLARED_ACTION_DEPENDENCY, } from "../errors.js";
|
|
11
|
+
import { InvocationPolicy } from "../invocation/policy.js";
|
|
10
12
|
import { DataDirLock } from "../storage/data-dir-lock.js";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
+
import { DefaultActionCatalog, DefaultPackageGraph, PackageGraphBuilder, resolveAction, } from "../catalog/index.js";
|
|
14
|
+
import { buildRuntimeError, collectPackageInfos, describeActionAcrossRuntimes, describeVisiblePlaybook, isRootCallVisible, listVisiblePlaybooks, packageNotFoundMessage, parseRefLoose, resolveProjectRoot, } from "./routing.js";
|
|
15
|
+
import { ServiceError } from "../service/types.js";
|
|
16
|
+
import { CAPABILITY_UNAVAILABLE } from "../errors.js";
|
|
17
|
+
import { createGlobalStorage, isSecretConfigKey } from "../storage/index.js";
|
|
18
|
+
import { filterByIntent } from "../filter/index.js";
|
|
19
|
+
function isPackageRuntime(item) {
|
|
13
20
|
return (typeof item === "object" &&
|
|
14
21
|
item !== null &&
|
|
15
22
|
"info" in item &&
|
|
@@ -17,18 +24,12 @@ function isActionDockApp(item) {
|
|
|
17
24
|
"runAction" in item &&
|
|
18
25
|
typeof item.runAction === "function");
|
|
19
26
|
}
|
|
20
|
-
/**
|
|
21
|
-
* ActionDock 统一多包宿主容器默认实现。
|
|
22
|
-
* 负责聚合与调度多个 ActionDockApp 实例,提供跨包引用路由、依赖声明校验与资源配额控制。
|
|
23
|
-
*/
|
|
24
27
|
export class DefaultActionDockHost {
|
|
25
28
|
hostSessionId;
|
|
26
29
|
options;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
activeSubRunsPerRoot = new Map();
|
|
30
|
+
runtimes = new Map();
|
|
31
|
+
policy;
|
|
30
32
|
hostPublicPackageIds = new Set();
|
|
31
|
-
resolver;
|
|
32
33
|
maxCallDepth;
|
|
33
34
|
maxSubRuns;
|
|
34
35
|
eventSink;
|
|
@@ -36,89 +37,167 @@ export class DefaultActionDockHost {
|
|
|
36
37
|
dataDirLock;
|
|
37
38
|
failedLinkedPackages = new Map();
|
|
38
39
|
failedAutoLoad;
|
|
39
|
-
/** 透传给内部创建
|
|
40
|
+
/** 透传给内部创建 Runtime 的存储收割开关(Host 默认持有者身份,显式可关) */
|
|
40
41
|
recoverOrphans;
|
|
41
|
-
|
|
42
|
+
graph;
|
|
43
|
+
catalog;
|
|
44
|
+
globalStorage;
|
|
45
|
+
constructor(options = {}, internalOptions) {
|
|
42
46
|
this.hostSessionId = randomUUID();
|
|
43
47
|
this.options = options;
|
|
44
48
|
this.maxCallDepth = options.maxCallDepth ?? 16;
|
|
45
49
|
this.maxSubRuns = options.maxSubRuns ?? 64;
|
|
50
|
+
this.policy = new InvocationPolicy({
|
|
51
|
+
maxCallDepth: this.maxCallDepth,
|
|
52
|
+
maxSubRuns: this.maxSubRuns,
|
|
53
|
+
});
|
|
46
54
|
this.eventSink = options.eventSink ?? options.platform?.eventSink ?? new InMemoryEventSink();
|
|
47
55
|
// Host 默认声明数据目录持有者身份;查询旁观方(CLI 查询命令)显式置 false
|
|
48
56
|
this.recoverOrphans = options.recoverOrphans !== false;
|
|
57
|
+
this.graph = new DefaultPackageGraph(new Map());
|
|
58
|
+
this.catalog = new DefaultActionCatalog(this.graph);
|
|
49
59
|
// 当指定非内存 dataDir 时获取排他目录锁,防止并发冲突
|
|
50
60
|
if (options.dataDir && !options.inMemory) {
|
|
51
61
|
this.dataDirLock = DataDirLock.acquire(options.dataDir, {
|
|
52
62
|
hostSessionId: this.hostSessionId,
|
|
53
63
|
});
|
|
54
64
|
}
|
|
65
|
+
if (internalOptions?.deferInit) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
55
68
|
try {
|
|
56
|
-
|
|
57
|
-
this.registerExplicitPackages(options);
|
|
58
|
-
// 阶段二:自动加载当前工程(若发现工程根目录且未显式禁用)
|
|
59
|
-
if (options.autoLoadCurrentProject !== false) {
|
|
60
|
-
this.loadCurrentProject(options);
|
|
61
|
-
}
|
|
62
|
-
// 阶段三:扫描已软链接的外部包并注册至 Host
|
|
63
|
-
if (options.scanLinkedPackages) {
|
|
64
|
-
this.registerLinkedPackages(options);
|
|
65
|
-
}
|
|
69
|
+
this.initializeSync();
|
|
66
70
|
}
|
|
67
71
|
catch (err) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
this.rollbackSync();
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
initializeSync() {
|
|
77
|
+
// 阶段一:注册显式传入的 packages 列表
|
|
78
|
+
this.registerExplicitPackages(this.options);
|
|
79
|
+
// 阶段二:自动加载当前工程(若发现工程根目录且未显式禁用)
|
|
80
|
+
if (this.options.autoLoadCurrentProject !== false) {
|
|
81
|
+
this.loadCurrentProject(this.options);
|
|
82
|
+
}
|
|
83
|
+
// 阶段三:扫描已软链接的外部包并注册至 Host
|
|
84
|
+
if (this.options.scanLinkedPackages) {
|
|
85
|
+
this.registerLinkedPackages(this.options);
|
|
86
|
+
}
|
|
87
|
+
this.rebuildGraphAndCatalog();
|
|
88
|
+
}
|
|
89
|
+
async initializeAsync() {
|
|
90
|
+
// 阶段一:注册显式传入的 packages 列表
|
|
91
|
+
this.registerExplicitPackages(this.options);
|
|
92
|
+
// 阶段二:自动加载当前工程(若发现工程根目录且未显式禁用)
|
|
93
|
+
if (this.options.autoLoadCurrentProject !== false) {
|
|
94
|
+
const root = resolveProjectRoot(this.options, () => findProjectRoot());
|
|
95
|
+
if (root) {
|
|
96
|
+
if (isProjectLockHeld(root)) {
|
|
97
|
+
throw new ActionDockError(PROJECT_BUSY, "PROJECT_BUSY: Project directory is locked by another active process holding project.lock");
|
|
98
|
+
}
|
|
99
|
+
// 依据事务日志恢复未完成提交的悬空事务(锁持有者存活时严禁判定为崩溃事务并禁止自动恢复)
|
|
100
|
+
if (hasPendingTransactions(root)) {
|
|
101
|
+
await recoverPendingTransactions(root, { frozenInstall: true });
|
|
102
|
+
}
|
|
73
103
|
}
|
|
74
|
-
this.
|
|
75
|
-
|
|
76
|
-
|
|
104
|
+
this.loadCurrentProject(this.options);
|
|
105
|
+
}
|
|
106
|
+
// 阶段三:扫描已软链接的外部包并注册至 Host
|
|
107
|
+
if (this.options.scanLinkedPackages) {
|
|
108
|
+
this.registerLinkedPackages(this.options);
|
|
109
|
+
}
|
|
110
|
+
this.rebuildGraphAndCatalog();
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 安全回滚初始化失败时所占用的资源。
|
|
114
|
+
*
|
|
115
|
+
* 同步语境(构造函数路径)下对 runtime.close() 采用 fire-and-forget:
|
|
116
|
+
* 仅附加吞没异常的 catch 处理器,不 await,避免构造链路引入异步;
|
|
117
|
+
* 异步语境下逐个 await 全部 runtime.close() 后再依次释放全局存储与目录锁。
|
|
118
|
+
*/
|
|
119
|
+
async rollbackResources(sync) {
|
|
120
|
+
if (sync) {
|
|
121
|
+
// 安全关闭宿主管理的所有 Runtime 实例(同步语境 fire-and-forget)
|
|
122
|
+
for (const runtime of this.runtimes.values()) {
|
|
77
123
|
try {
|
|
78
|
-
const closePromise =
|
|
124
|
+
const closePromise = runtime.close();
|
|
79
125
|
if (closePromise && typeof closePromise.catch === "function") {
|
|
80
126
|
closePromise.catch(() => { });
|
|
81
127
|
}
|
|
82
128
|
}
|
|
83
129
|
catch {
|
|
84
|
-
// 忽略
|
|
130
|
+
// 忽略 runtime 关闭异常
|
|
85
131
|
}
|
|
86
132
|
}
|
|
87
|
-
this.internallyCreatedApps.clear();
|
|
88
|
-
this.apps.clear();
|
|
89
|
-
throw err;
|
|
90
133
|
}
|
|
134
|
+
else {
|
|
135
|
+
const allRuntimes = Array.from(this.runtimes.values());
|
|
136
|
+
await Promise.all(allRuntimes.map(async (runtime) => {
|
|
137
|
+
try {
|
|
138
|
+
await runtime.close();
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// 忽略 Runtime 关闭异常
|
|
142
|
+
}
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
this.runtimes.clear();
|
|
146
|
+
try {
|
|
147
|
+
this.globalStorage?.close();
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// 忽略全局存储关闭异常
|
|
151
|
+
}
|
|
152
|
+
this.globalStorage = undefined;
|
|
153
|
+
try {
|
|
154
|
+
this.dataDirLock?.release();
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// 忽略排他锁释放异常
|
|
158
|
+
}
|
|
159
|
+
this.dataDirLock = undefined;
|
|
160
|
+
this.isClosed = true;
|
|
91
161
|
}
|
|
92
162
|
/**
|
|
93
|
-
*
|
|
163
|
+
* 异步安全回滚初始化失败时所占用的资源。
|
|
164
|
+
*/
|
|
165
|
+
async rollbackInitialization() {
|
|
166
|
+
await this.rollbackResources(false);
|
|
167
|
+
}
|
|
168
|
+
rollbackSync() {
|
|
169
|
+
void this.rollbackResources(true);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 阶段函数:注册显式传入的 packages 列表(PackageRuntimeOptions 配置或包目录物理路径)。
|
|
94
173
|
*/
|
|
95
174
|
registerExplicitPackages(options) {
|
|
96
175
|
if (!options.packages || !Array.isArray(options.packages)) {
|
|
97
176
|
return;
|
|
98
177
|
}
|
|
99
178
|
for (const item of options.packages) {
|
|
100
|
-
if (
|
|
101
|
-
this.
|
|
179
|
+
if (isPackageRuntime(item)) {
|
|
180
|
+
this.registerRuntimeInternal(item, true);
|
|
102
181
|
}
|
|
103
182
|
else {
|
|
104
|
-
const
|
|
105
|
-
|
|
183
|
+
const packageOptions = typeof item === "string" ? { packageRoot: item } : item;
|
|
184
|
+
const runtime = new DefaultPackageRuntime({
|
|
185
|
+
...packageOptions,
|
|
106
186
|
hostSessionId: this.hostSessionId,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
187
|
+
globalStorage: packageOptions.globalStorage ?? this.getGlobalStorage(),
|
|
188
|
+
platform: packageOptions.platform ?? options.platform,
|
|
189
|
+
inMemory: packageOptions.inMemory ?? options.inMemory,
|
|
190
|
+
customHome: packageOptions.customHome ?? options.customHome,
|
|
191
|
+
dataDir: packageOptions.dataDir ?? options.dataDir,
|
|
192
|
+
clock: packageOptions.clock ?? options.clock,
|
|
193
|
+
process: packageOptions.process ?? options.process,
|
|
194
|
+
logger: packageOptions.logger ?? options.logger,
|
|
195
|
+
eventSink: packageOptions.eventSink ?? this.eventSink,
|
|
196
|
+
maxCallDepth: packageOptions.maxCallDepth ?? this.maxCallDepth,
|
|
197
|
+
maxSubRuns: packageOptions.maxSubRuns ?? this.maxSubRuns,
|
|
117
198
|
recoverOrphans: this.recoverOrphans,
|
|
118
|
-
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
119
199
|
});
|
|
120
|
-
this.
|
|
121
|
-
this.registerAppInternal(app, true);
|
|
200
|
+
this.registerRuntimeInternal(runtime, true);
|
|
122
201
|
}
|
|
123
202
|
}
|
|
124
203
|
}
|
|
@@ -149,25 +228,28 @@ export class DefaultActionDockHost {
|
|
|
149
228
|
}
|
|
150
229
|
try {
|
|
151
230
|
const config = loadProjectConfig(root);
|
|
152
|
-
|
|
153
|
-
this.resolver = new ActionPackageResolver({
|
|
231
|
+
const builder = new PackageGraphBuilder({
|
|
154
232
|
projectRoot: root,
|
|
155
233
|
manifest: config,
|
|
156
234
|
allowDevLinks: options.scanLinkedPackages,
|
|
157
235
|
customHome: options.customHome,
|
|
158
236
|
});
|
|
159
|
-
const graph =
|
|
160
|
-
|
|
237
|
+
const graph = builder.buildSync();
|
|
238
|
+
if (graph.rootPackageId) {
|
|
239
|
+
this.hostPublicPackageIds.add(graph.rootPackageId);
|
|
240
|
+
}
|
|
161
241
|
for (const depId of graph.directDependencyIds) {
|
|
162
242
|
this.hostPublicPackageIds.add(depId);
|
|
163
243
|
}
|
|
164
244
|
for (const pkg of graph.packages.values()) {
|
|
165
|
-
if (!this.
|
|
245
|
+
if (!this.runtimes.has(pkg.packageId)) {
|
|
166
246
|
const isDirectOrRoot = this.hostPublicPackageIds.has(pkg.packageId);
|
|
167
|
-
const
|
|
168
|
-
packageRoot: pkg.
|
|
247
|
+
const runtime = new DefaultPackageRuntime({
|
|
248
|
+
packageRoot: pkg.root,
|
|
169
249
|
projectConfig: pkg.manifest,
|
|
250
|
+
identity: pkg.identity,
|
|
170
251
|
hostSessionId: this.hostSessionId,
|
|
252
|
+
globalStorage: this.getGlobalStorage(),
|
|
171
253
|
platform: options.platform,
|
|
172
254
|
inMemory: options.inMemory,
|
|
173
255
|
customHome: options.customHome,
|
|
@@ -179,10 +261,8 @@ export class DefaultActionDockHost {
|
|
|
179
261
|
maxCallDepth: this.maxCallDepth,
|
|
180
262
|
maxSubRuns: this.maxSubRuns,
|
|
181
263
|
recoverOrphans: this.recoverOrphans,
|
|
182
|
-
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
183
264
|
});
|
|
184
|
-
this.
|
|
185
|
-
this.registerAppInternal(app, isDirectOrRoot);
|
|
265
|
+
this.registerRuntimeInternal(runtime, isDirectOrRoot);
|
|
186
266
|
}
|
|
187
267
|
}
|
|
188
268
|
}
|
|
@@ -208,7 +288,7 @@ export class DefaultActionDockHost {
|
|
|
208
288
|
*/
|
|
209
289
|
registerLinkedPackages(options) {
|
|
210
290
|
for (const linked of listLinkedPackages(options.customHome)) {
|
|
211
|
-
if (this.
|
|
291
|
+
if (this.runtimes.has(linked.id)) {
|
|
212
292
|
continue;
|
|
213
293
|
}
|
|
214
294
|
if (!existsSync(linked.path)) {
|
|
@@ -220,10 +300,11 @@ export class DefaultActionDockHost {
|
|
|
220
300
|
try {
|
|
221
301
|
const config = loadProjectConfig(linked.path);
|
|
222
302
|
this.hostPublicPackageIds.add(linked.id);
|
|
223
|
-
const
|
|
303
|
+
const runtime = new DefaultPackageRuntime({
|
|
224
304
|
packageRoot: linked.path,
|
|
225
305
|
projectConfig: config,
|
|
226
306
|
hostSessionId: this.hostSessionId,
|
|
307
|
+
globalStorage: this.getGlobalStorage(),
|
|
227
308
|
platform: options.platform,
|
|
228
309
|
inMemory: options.inMemory,
|
|
229
310
|
customHome: options.customHome,
|
|
@@ -235,10 +316,8 @@ export class DefaultActionDockHost {
|
|
|
235
316
|
maxCallDepth: this.maxCallDepth,
|
|
236
317
|
maxSubRuns: this.maxSubRuns,
|
|
237
318
|
recoverOrphans: this.recoverOrphans,
|
|
238
|
-
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
239
319
|
});
|
|
240
|
-
this.
|
|
241
|
-
this.registerAppInternal(app, true);
|
|
320
|
+
this.registerRuntimeInternal(runtime, true);
|
|
242
321
|
}
|
|
243
322
|
catch (err) {
|
|
244
323
|
const errDetail = err?.message || String(err);
|
|
@@ -247,119 +326,225 @@ export class DefaultActionDockHost {
|
|
|
247
326
|
}
|
|
248
327
|
}
|
|
249
328
|
}
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
projectRoot: targetApp.packageRoot,
|
|
256
|
-
projectConfig: targetApp.projectConfig,
|
|
257
|
-
storage: targetApp.storage,
|
|
258
|
-
actions: targetApp.actionsMap,
|
|
259
|
-
packageInstanceId: targetApp.packageInstanceId,
|
|
260
|
-
generationId: targetApp.generationId,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
bindApp(app) {
|
|
264
|
-
const runner = app.executionService?.runner;
|
|
265
|
-
if (runner && typeof runner.setPackageContextResolver === "function") {
|
|
266
|
-
runner.setPackageContextResolver(this.resolvePackageContext.bind(this));
|
|
329
|
+
bindRuntime(runtime) {
|
|
330
|
+
const invoker = this.createActionInvoker(runtime);
|
|
331
|
+
if ("setActionInvoker" in runtime && typeof runtime.setActionInvoker === "function") {
|
|
332
|
+
runtime.setActionInvoker(invoker);
|
|
267
333
|
}
|
|
268
334
|
}
|
|
269
|
-
/**
|
|
335
|
+
/**
|
|
336
|
+
* 构造应用专属动作调用委托器。
|
|
337
|
+
* 铁律 2:跨包调用改走 ActionInvoker 回到 Host 执行主链,经由唯一的 InvocationPolicy 授权后调用目标 Package 的 ExecutionService。
|
|
338
|
+
*/
|
|
339
|
+
createActionInvoker(callerRuntime) {
|
|
340
|
+
return async (childAction, childInput, context) => {
|
|
341
|
+
const resolved = resolveAction(childAction, {
|
|
342
|
+
caller: callerRuntime.packageId,
|
|
343
|
+
graph: this.graph,
|
|
344
|
+
catalog: this.catalog,
|
|
345
|
+
});
|
|
346
|
+
const targetPackageId = resolved.package.id;
|
|
347
|
+
const targetActionId = resolved.ref.actionId;
|
|
348
|
+
const isSamePackage = targetPackageId === callerRuntime.packageId;
|
|
349
|
+
const targetRuntime = isSamePackage ? callerRuntime : this.getRuntime(targetPackageId);
|
|
350
|
+
if (!targetRuntime) {
|
|
351
|
+
throw new ActionDockError(PACKAGE_NOT_FOUND, packageNotFoundMessage(targetPackageId, this.failedLinkedPackages));
|
|
352
|
+
}
|
|
353
|
+
// 跨包 uses 依赖声明校验(统一委托 InvocationPolicy 单一事实源)
|
|
354
|
+
if (!isSamePackage) {
|
|
355
|
+
let declaredUses = context.caller?.declaredUses;
|
|
356
|
+
if (!declaredUses) {
|
|
357
|
+
try {
|
|
358
|
+
const callerSpec = await callerRuntime.describeAction(context.caller?.actionId || "");
|
|
359
|
+
declaredUses = callerSpec.uses;
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
// 忽略规范提取异常
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const authErr = this.policy.checkUsesAuthorization({
|
|
366
|
+
packageId: callerRuntime.packageId,
|
|
367
|
+
actionId: context.caller?.actionId || "",
|
|
368
|
+
declaredUses,
|
|
369
|
+
}, { packageId: targetPackageId, actionId: targetActionId }, this.graph);
|
|
370
|
+
if (authErr) {
|
|
371
|
+
throw new ActionDockError(authErr.code, authErr.message, authErr.details);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// 调用嵌套深度限制校验
|
|
375
|
+
const depthErr = this.policy.checkCallDepth(context.callStack, targetActionId, context.maxCallDepth);
|
|
376
|
+
if (depthErr) {
|
|
377
|
+
throw new ActionDockError(depthErr.code, depthErr.message, depthErr.details);
|
|
378
|
+
}
|
|
379
|
+
// 调用链环路死锁检测
|
|
380
|
+
const cycle = this.policy.checkCycle(context.callStack, targetActionId, targetPackageId, callerRuntime.packageId);
|
|
381
|
+
if (cycle.error) {
|
|
382
|
+
throw new ActionDockError(cycle.error.code, cycle.error.message, cycle.error.details);
|
|
383
|
+
}
|
|
384
|
+
// 针对根运行的并发子任务配额校验与申请
|
|
385
|
+
const rootRunId = context.rootRunId;
|
|
386
|
+
const quotaErr = this.policy.checkSubRunQuota(rootRunId);
|
|
387
|
+
if (quotaErr) {
|
|
388
|
+
throw new ActionDockError(quotaErr.code, quotaErr.message, quotaErr.details);
|
|
389
|
+
}
|
|
390
|
+
if (!this.policy.acquireSubRun(rootRunId)) {
|
|
391
|
+
throw new ActionDockError(ACTION_SUBRUN_LIMIT, `Maximum concurrent sub-runs (${this.policy.maxSubRuns}) reached`, { limit: this.policy.maxSubRuns });
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
const nextCallStack = [...context.callStack, cycle.callKey];
|
|
395
|
+
const targetOwner = context.owner
|
|
396
|
+
? {
|
|
397
|
+
tenantId: context.owner.tenantId,
|
|
398
|
+
principalId: context.owner.principalId,
|
|
399
|
+
packageInstanceId: isSamePackage
|
|
400
|
+
? (context.owner.packageInstanceId || callerRuntime.identity.instanceId)
|
|
401
|
+
: targetRuntime.identity.instanceId,
|
|
402
|
+
generationId: isSamePackage
|
|
403
|
+
? (context.owner.generationId || callerRuntime.identity.generation)
|
|
404
|
+
: targetRuntime.identity.generation,
|
|
405
|
+
}
|
|
406
|
+
: undefined;
|
|
407
|
+
const subInvocationContext = {
|
|
408
|
+
runId: context.runId,
|
|
409
|
+
rootRunId: context.rootRunId,
|
|
410
|
+
parentRunId: context.parentRunId,
|
|
411
|
+
caller: context.caller,
|
|
412
|
+
callStack: nextCallStack,
|
|
413
|
+
package: targetRuntime.identity,
|
|
414
|
+
signal: context.signal,
|
|
415
|
+
timeoutMs: context.timeoutMs,
|
|
416
|
+
config: context.config,
|
|
417
|
+
requestId: context.requestId,
|
|
418
|
+
tenantId: context.tenantId,
|
|
419
|
+
principalId: context.principalId,
|
|
420
|
+
hostSessionId: context.hostSessionId || this.hostSessionId,
|
|
421
|
+
maxCallDepth: context.maxCallDepth ?? this.maxCallDepth,
|
|
422
|
+
logger: context.logger,
|
|
423
|
+
progress: context.progress,
|
|
424
|
+
process: context.process,
|
|
425
|
+
owner: targetOwner,
|
|
426
|
+
};
|
|
427
|
+
const ticket = await targetRuntime.startInvocation(targetActionId, childInput, subInvocationContext);
|
|
428
|
+
if (!ticket.result) {
|
|
429
|
+
throw new Error(`Execution ticket for run '${ticket.runId}' has no result Promise`);
|
|
430
|
+
}
|
|
431
|
+
const result = await ticket.result;
|
|
432
|
+
if (!result.ok) {
|
|
433
|
+
throw new ActionDockError(result.error.code, result.error.message, result.error.details);
|
|
434
|
+
}
|
|
435
|
+
return result.data;
|
|
436
|
+
}
|
|
437
|
+
finally {
|
|
438
|
+
this.policy.releaseSubRun(rootRunId);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/** 路由可见性上下文(公开包集合与包依赖图) */
|
|
270
443
|
visibility() {
|
|
271
444
|
return {
|
|
272
445
|
hostPublicPackageIds: this.hostPublicPackageIds,
|
|
273
|
-
|
|
446
|
+
graph: this.graph,
|
|
274
447
|
};
|
|
275
448
|
}
|
|
276
|
-
|
|
277
|
-
|
|
449
|
+
rebuildGraphAndCatalog() {
|
|
450
|
+
const discovered = Array.from(this.runtimes.values()).map((runtime) => ({
|
|
451
|
+
id: runtime.packageId,
|
|
452
|
+
root: runtime.packageRoot || "",
|
|
453
|
+
manifest: runtime.projectConfig,
|
|
454
|
+
identity: runtime.identity,
|
|
455
|
+
isCurrentProject: this.hostPublicPackageIds.has(runtime.packageId),
|
|
456
|
+
}));
|
|
457
|
+
const rootRuntime = Array.from(this.runtimes.values()).find((runtime) => this.hostPublicPackageIds.has(runtime.packageId));
|
|
458
|
+
const builder = new PackageGraphBuilder({
|
|
459
|
+
packages: discovered,
|
|
460
|
+
root: rootRuntime?.packageRoot || rootRuntime?.packageId,
|
|
461
|
+
});
|
|
462
|
+
this.graph = builder.buildSync();
|
|
463
|
+
this.catalog = new DefaultActionCatalog(this.graph, (pkgId) => {
|
|
464
|
+
const runtime = this.getRuntime(pkgId);
|
|
465
|
+
if (!runtime)
|
|
466
|
+
return undefined;
|
|
467
|
+
return new Map(runtime.actionsMap);
|
|
468
|
+
});
|
|
469
|
+
this.policy.setVisibilityContext(this.visibility());
|
|
278
470
|
}
|
|
279
|
-
|
|
280
|
-
return
|
|
471
|
+
getRuntime(packageId) {
|
|
472
|
+
return this.runtimes.get(packageId);
|
|
281
473
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
* 仅自动探测场景会记录此诊断;显式传入 projectRoot 的加载失败会直接抛出,不产生此诊断。
|
|
285
|
-
*/
|
|
286
|
-
getAutoLoadFailure() {
|
|
287
|
-
return this.failedAutoLoad;
|
|
474
|
+
listRuntimes() {
|
|
475
|
+
return Array.from(this.runtimes.values());
|
|
288
476
|
}
|
|
289
|
-
|
|
290
|
-
if (this.
|
|
291
|
-
const existing = this.
|
|
292
|
-
if (existing ===
|
|
477
|
+
registerRuntimeInternal(runtime, isPublic) {
|
|
478
|
+
if (this.runtimes.has(runtime.packageId)) {
|
|
479
|
+
const existing = this.runtimes.get(runtime.packageId);
|
|
480
|
+
if (existing === runtime) {
|
|
293
481
|
return;
|
|
294
482
|
}
|
|
295
|
-
throw new Error(`Package ID conflict: package '${
|
|
483
|
+
throw new Error(`Package ID conflict: package '${runtime.packageId}' is already registered in host`);
|
|
296
484
|
}
|
|
297
|
-
this.
|
|
485
|
+
this.runtimes.set(runtime.packageId, runtime);
|
|
298
486
|
if (isPublic) {
|
|
299
|
-
this.hostPublicPackageIds.add(
|
|
487
|
+
this.hostPublicPackageIds.add(runtime.packageId);
|
|
300
488
|
}
|
|
301
|
-
this.
|
|
489
|
+
this.bindRuntime(runtime);
|
|
490
|
+
this.rebuildGraphAndCatalog();
|
|
302
491
|
// 接管与恢复:仅持有者身份的 Host 自动将死亡会话或遗留非终态运行收敛为 interrupted;
|
|
303
492
|
// 旁观查询 Host(CLI state/runs/config 命令)跳过本步骤,不动其他进程的在途记录
|
|
304
493
|
if (this.recoverOrphans) {
|
|
305
|
-
|
|
306
|
-
if (st && typeof st.recoverDeadSessionRuns === "function") {
|
|
494
|
+
if (typeof runtime.recoverDeadSessionRuns === "function") {
|
|
307
495
|
try {
|
|
308
|
-
|
|
496
|
+
runtime.recoverDeadSessionRuns(this.hostSessionId);
|
|
309
497
|
}
|
|
310
498
|
catch (err) {
|
|
311
499
|
// 单包恢复失败不阻断整体接管流程,但必须可观测
|
|
312
|
-
console.warn(`[actiondock] recoverDeadSessionRuns failed for package '${
|
|
500
|
+
console.warn(`[actiondock] recoverDeadSessionRuns failed for package '${runtime.packageId}': ${err instanceof Error ? err.message : String(err)}`);
|
|
313
501
|
}
|
|
314
502
|
}
|
|
315
503
|
}
|
|
316
504
|
}
|
|
317
|
-
|
|
318
|
-
this.
|
|
505
|
+
registerRuntime(runtime) {
|
|
506
|
+
this.registerRuntimeInternal(runtime, true);
|
|
319
507
|
}
|
|
320
508
|
async info() {
|
|
321
|
-
return collectPackageInfos(this.
|
|
509
|
+
return collectPackageInfos(this.listRuntimes());
|
|
322
510
|
}
|
|
323
511
|
async listActions(options) {
|
|
324
|
-
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
512
|
+
// 外层完成可见性筛选与跨包限定名改写后,统一委托共享过滤函数完成检索
|
|
513
|
+
const results = [];
|
|
514
|
+
const runtimes = this.listRuntimes();
|
|
515
|
+
for (const runtime of runtimes) {
|
|
516
|
+
const runtimeSummaries = await runtime.listActions();
|
|
517
|
+
for (const item of runtimeSummaries) {
|
|
518
|
+
if (!isRootCallVisible(runtime.packageId, item.id, this.hostPublicPackageIds, this.graph)) {
|
|
330
519
|
continue;
|
|
331
520
|
}
|
|
332
|
-
const qualifiedId =
|
|
333
|
-
? `${
|
|
521
|
+
const qualifiedId = runtimes.length > 1 && !item.id.includes("/")
|
|
522
|
+
? `${runtime.packageId}/${item.id}`
|
|
334
523
|
: item.id;
|
|
335
524
|
results.push({
|
|
336
525
|
...item,
|
|
337
526
|
id: qualifiedId,
|
|
338
|
-
packageId:
|
|
527
|
+
packageId: runtime.packageId,
|
|
339
528
|
});
|
|
340
529
|
}
|
|
341
530
|
}
|
|
342
|
-
|
|
343
|
-
results = results.filter((s) => options.tags.every((t) => s.tags?.includes(t)));
|
|
344
|
-
}
|
|
345
|
-
if (options?.query) {
|
|
346
|
-
const q = options.query.toLowerCase();
|
|
347
|
-
results = results.filter((s) => s.id.toLowerCase().includes(q) ||
|
|
348
|
-
(s.description && s.description.toLowerCase().includes(q)));
|
|
349
|
-
}
|
|
350
|
-
if (options?.prefix) {
|
|
351
|
-
results = results.filter((s) => s.id.startsWith(options.prefix));
|
|
352
|
-
}
|
|
353
|
-
return results;
|
|
531
|
+
return applyActionSummaryFilters(results, options);
|
|
354
532
|
}
|
|
355
533
|
async describeAction(ref) {
|
|
356
|
-
return
|
|
534
|
+
return describeActionAcrossRuntimes(ref, this.listRuntimes(), this.visibility(), this.failedLinkedPackages, this.catalog, this.graph);
|
|
357
535
|
}
|
|
358
|
-
async listPlaybooks() {
|
|
359
|
-
|
|
536
|
+
async listPlaybooks(options) {
|
|
537
|
+
let pbs = await listVisiblePlaybooks(this.listRuntimes(), this.visibility());
|
|
538
|
+
if (options?.package) {
|
|
539
|
+
pbs = pbs.filter((p) => p.packageId === options.package);
|
|
540
|
+
}
|
|
541
|
+
if (options?.intent) {
|
|
542
|
+
pbs = filterByIntent(pbs, options.intent, [(p) => p.id, (p) => p.description || "", (p) => p.packageId || "", (p) => (p.actions || []).join(" ")], false);
|
|
543
|
+
}
|
|
544
|
+
return pbs;
|
|
360
545
|
}
|
|
361
546
|
async describePlaybook(id) {
|
|
362
|
-
return describeVisiblePlaybook(this.
|
|
547
|
+
return describeVisiblePlaybook(this.listRuntimes(), id, this.visibility());
|
|
363
548
|
}
|
|
364
549
|
async runAction(ref, input, options = {}) {
|
|
365
550
|
const ticket = await this.startAction(ref, input, options);
|
|
@@ -372,185 +557,136 @@ export class DefaultActionDockHost {
|
|
|
372
557
|
if (this.isClosed) {
|
|
373
558
|
throw new Error("ActionDockHost is closed: new tasks rejected");
|
|
374
559
|
}
|
|
375
|
-
// - 解析目标包与 Action 动作标识
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (!targetApp) {
|
|
383
|
-
const runId = randomUUID();
|
|
384
|
-
const error = buildRuntimeError(PACKAGE_NOT_FOUND, packageNotFoundMessage(targetPackageId, this.failedLinkedPackages));
|
|
385
|
-
return {
|
|
386
|
-
runId,
|
|
387
|
-
status: "failed",
|
|
388
|
-
result: Promise.resolve({ ok: false, runId, error }),
|
|
389
|
-
};
|
|
390
|
-
}
|
|
560
|
+
// - 统一基于纯领域 resolveAction 解析目标包与 Action 动作标识
|
|
561
|
+
let resolved;
|
|
562
|
+
try {
|
|
563
|
+
resolved = resolveAction(ref, {
|
|
564
|
+
graph: this.graph,
|
|
565
|
+
catalog: this.catalog,
|
|
566
|
+
});
|
|
391
567
|
}
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
runId,
|
|
403
|
-
status: "failed",
|
|
404
|
-
result: Promise.resolve({ ok: false, runId, error }),
|
|
405
|
-
};
|
|
568
|
+
catch (err) {
|
|
569
|
+
const runId = randomUUID();
|
|
570
|
+
let code = err.code || ACTION_NOT_FOUND;
|
|
571
|
+
let message = err.message || String(err);
|
|
572
|
+
if (code === PACKAGE_NOT_FOUND) {
|
|
573
|
+
code = PACKAGE_NOT_FOUND;
|
|
574
|
+
const parsed = parseRefLoose(ref);
|
|
575
|
+
if (parsed.packageId) {
|
|
576
|
+
message = packageNotFoundMessage(parsed.packageId, this.failedLinkedPackages);
|
|
577
|
+
}
|
|
406
578
|
}
|
|
407
|
-
else {
|
|
408
|
-
const runId = randomUUID();
|
|
409
|
-
const error = buildRuntimeError(ACTION_NOT_FOUND, `Action '${targetActionId}' not found in any registered package`);
|
|
579
|
+
else if (code === INVALID_ACTION_REF && err.details?.alias === "AMBIGUOUS_ACTION_REF") {
|
|
410
580
|
return {
|
|
411
581
|
runId,
|
|
412
582
|
status: "failed",
|
|
413
|
-
result: Promise.resolve({
|
|
583
|
+
result: Promise.resolve({
|
|
584
|
+
ok: false,
|
|
585
|
+
runId,
|
|
586
|
+
error: buildRuntimeError(INVALID_ACTION_REF, message, err.details),
|
|
587
|
+
}),
|
|
414
588
|
};
|
|
415
589
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const runId = randomUUID();
|
|
422
|
-
const error = buildRuntimeError(UNDECLARED_ACTION_DEPENDENCY, `Root call to action '${targetPackageId}/${targetActionId}' is not allowed: package '${targetPackageId}' is not declared as a direct dependency in actiondock.json and is not delegated by a visible playbook`, { target: `${targetPackageId}/${targetActionId}` });
|
|
423
|
-
return {
|
|
590
|
+
return {
|
|
591
|
+
runId,
|
|
592
|
+
status: "failed",
|
|
593
|
+
result: Promise.resolve({
|
|
594
|
+
ok: false,
|
|
424
595
|
runId,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
if (
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
try {
|
|
443
|
-
const callerSpec = await callerApp.describeAction(callerActionId);
|
|
444
|
-
const usesList = callerSpec.uses || [];
|
|
445
|
-
const targetRef = `${targetPackageId}/${targetActionId}`;
|
|
446
|
-
const isAllowed = this.resolver
|
|
447
|
-
? this.resolver.canCascadeCall(callerPackageId, callerActionId, targetPackageId, targetActionId)
|
|
448
|
-
: usesList.some((u) => u === targetRef || u === `${targetPackageId}/*` || u === targetPackageId);
|
|
449
|
-
if (!isAllowed) {
|
|
450
|
-
const runId = randomUUID();
|
|
451
|
-
const error = buildRuntimeError(UNDECLARED_ACTION_DEPENDENCY, `Action '${callerPackageId}/${callerActionId}' does not declare dependency on '${targetRef}' in 'uses'`, {
|
|
452
|
-
caller: `${callerPackageId}/${callerActionId}`,
|
|
453
|
-
target: targetRef,
|
|
454
|
-
declaredUses: usesList,
|
|
455
|
-
});
|
|
456
|
-
return {
|
|
457
|
-
runId,
|
|
458
|
-
status: "failed",
|
|
459
|
-
result: Promise.resolve({ ok: false, runId, error }),
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
catch {
|
|
464
|
-
// 忽略规范提取异常,交由执行服务执行
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
// 调用嵌套深度限制校验
|
|
469
|
-
let depth = 1;
|
|
470
|
-
let cur = parentRun;
|
|
471
|
-
while (cur && cur.parentRunId) {
|
|
472
|
-
depth++;
|
|
473
|
-
if (depth > this.maxCallDepth)
|
|
474
|
-
break;
|
|
475
|
-
cur = await this.getRun(cur.parentRunId);
|
|
476
|
-
}
|
|
477
|
-
if (depth >= this.maxCallDepth) {
|
|
478
|
-
const runId = randomUUID();
|
|
479
|
-
const error = buildRuntimeError(ACTION_CALL_CYCLE, `Maximum call depth of ${this.maxCallDepth} exceeded`, { alias: ACTION_MAX_DEPTH_EXCEEDED, reason: "depth_exceeded", maxDepth: this.maxCallDepth });
|
|
480
|
-
return {
|
|
481
|
-
runId,
|
|
482
|
-
status: "failed",
|
|
483
|
-
result: Promise.resolve({ ok: false, runId, error }),
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
// 针对根运行的并发子任务数限制校验
|
|
487
|
-
if (effectiveRootRunId) {
|
|
488
|
-
const currentSubRuns = this.activeSubRunsPerRoot.get(effectiveRootRunId) || 0;
|
|
489
|
-
if (currentSubRuns >= this.maxSubRuns) {
|
|
490
|
-
const runId = randomUUID();
|
|
491
|
-
const error = buildRuntimeError(ACTION_SUBRUN_LIMIT, `Maximum concurrent sub-runs (${this.maxSubRuns}) reached for root run '${effectiveRootRunId}'`, { alias: MAX_SUBRUNS_REACHED, limit: this.maxSubRuns });
|
|
492
|
-
return {
|
|
493
|
-
runId,
|
|
494
|
-
status: "failed",
|
|
495
|
-
result: Promise.resolve({ ok: false, runId, error }),
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
// - 构造子运行参数并调度至目标 App 执行
|
|
502
|
-
const execOptions = {
|
|
503
|
-
...options,
|
|
504
|
-
rootRunId: effectiveRootRunId,
|
|
505
|
-
parentRunId,
|
|
506
|
-
hostSessionId: this.hostSessionId,
|
|
507
|
-
maxCallDepth: options.maxCallDepth ?? this.maxCallDepth,
|
|
508
|
-
};
|
|
509
|
-
if (parentRunId && effectiveRootRunId) {
|
|
510
|
-
this.activeSubRunsPerRoot.set(effectiveRootRunId, (this.activeSubRunsPerRoot.get(effectiveRootRunId) || 0) + 1);
|
|
511
|
-
}
|
|
512
|
-
let ticket;
|
|
596
|
+
error: buildRuntimeError(code, message),
|
|
597
|
+
}),
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
const targetPackageId = resolved.package.id;
|
|
601
|
+
const targetActionId = resolved.ref.actionId;
|
|
602
|
+
const targetRuntime = this.getRuntime(targetPackageId);
|
|
603
|
+
if (!targetRuntime) {
|
|
604
|
+
const runId = randomUUID();
|
|
605
|
+
const error = buildRuntimeError(PACKAGE_NOT_FOUND, packageNotFoundMessage(targetPackageId, this.failedLinkedPackages));
|
|
606
|
+
return {
|
|
607
|
+
runId,
|
|
608
|
+
status: "failed",
|
|
609
|
+
result: Promise.resolve({ ok: false, runId, error }),
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
// 根调用可见性鉴权(统一委托 InvocationPolicy 单一事实源断言)
|
|
513
613
|
try {
|
|
514
|
-
|
|
614
|
+
this.policy.assertRootVisibility(resolved, this.visibility());
|
|
515
615
|
}
|
|
516
616
|
catch (err) {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
617
|
+
const runId = randomUUID();
|
|
618
|
+
return {
|
|
619
|
+
runId,
|
|
620
|
+
status: "failed",
|
|
621
|
+
result: Promise.resolve({
|
|
622
|
+
ok: false,
|
|
623
|
+
runId,
|
|
624
|
+
error: buildRuntimeError(err.code || UNDECLARED_ACTION_DEPENDENCY, err.message, err.details),
|
|
625
|
+
}),
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
// 在边界安全组装受信任的根调用上下文(纯 Root Call,无父级血缘)
|
|
629
|
+
const rootKey = `${targetPackageId}/${targetActionId}`;
|
|
630
|
+
const rootContext = createRootInvocationContext({
|
|
631
|
+
targetPackage: targetRuntime.identity,
|
|
632
|
+
callStack: [rootKey],
|
|
633
|
+
signal: options.signal,
|
|
634
|
+
timeoutMs: options.timeoutMs,
|
|
635
|
+
config: options.config,
|
|
636
|
+
requestId: options.requestId,
|
|
637
|
+
hostSessionId: this.hostSessionId,
|
|
638
|
+
maxCallDepth: this.maxCallDepth,
|
|
639
|
+
process: this.options.process,
|
|
640
|
+
});
|
|
641
|
+
// 调度目标包执行服务
|
|
642
|
+
return targetRuntime.startInvocation(targetActionId, input, rootContext);
|
|
542
643
|
}
|
|
543
644
|
async getRun(runId) {
|
|
544
|
-
for (const
|
|
545
|
-
const record = await
|
|
645
|
+
for (const runtime of this.listRuntimes()) {
|
|
646
|
+
const record = await runtime.getRun(runId);
|
|
546
647
|
if (record)
|
|
547
648
|
return record;
|
|
548
649
|
}
|
|
549
650
|
return undefined;
|
|
550
651
|
}
|
|
652
|
+
async listRuns(query) {
|
|
653
|
+
const runtimes = query?.packageId
|
|
654
|
+
? [this.getRuntime(query.packageId)].filter(Boolean)
|
|
655
|
+
: this.listRuntimes();
|
|
656
|
+
const records = [];
|
|
657
|
+
for (const runtime of runtimes) {
|
|
658
|
+
const recs = await runtime.listRuns(query);
|
|
659
|
+
for (const r of recs) {
|
|
660
|
+
records.push({
|
|
661
|
+
...r,
|
|
662
|
+
packageId: r.packageId || runtime.packageId,
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
records.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
|
|
667
|
+
let result = records;
|
|
668
|
+
if (query?.intent) {
|
|
669
|
+
result = filterByIntent(result, query.intent, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
|
|
670
|
+
}
|
|
671
|
+
if (query?.limit && result.length > query.limit) {
|
|
672
|
+
result.length = query.limit;
|
|
673
|
+
}
|
|
674
|
+
return result;
|
|
675
|
+
}
|
|
676
|
+
async clearRuns(options) {
|
|
677
|
+
const runtimes = options?.packageId
|
|
678
|
+
? [this.getRuntime(options.packageId)].filter(Boolean)
|
|
679
|
+
: this.listRuntimes();
|
|
680
|
+
let total = 0;
|
|
681
|
+
for (const runtime of runtimes) {
|
|
682
|
+
const res = await runtime.clearRuns(options);
|
|
683
|
+
total += res;
|
|
684
|
+
}
|
|
685
|
+
return total;
|
|
686
|
+
}
|
|
551
687
|
async cancelRun(runId, reason) {
|
|
552
|
-
for (const
|
|
553
|
-
const res = await
|
|
688
|
+
for (const runtime of this.listRuntimes()) {
|
|
689
|
+
const res = await runtime.cancelRun(runId, reason);
|
|
554
690
|
if (res.outcome !== "not_found") {
|
|
555
691
|
return res;
|
|
556
692
|
}
|
|
@@ -558,43 +694,169 @@ export class DefaultActionDockHost {
|
|
|
558
694
|
return { outcome: "not_found", runId };
|
|
559
695
|
}
|
|
560
696
|
events(runId, options) {
|
|
561
|
-
for (const
|
|
562
|
-
|
|
563
|
-
|
|
697
|
+
for (const runtime of this.listRuntimes()) {
|
|
698
|
+
const storageRecord = runtime.storage?.getRun?.(runId);
|
|
699
|
+
if (storageRecord) {
|
|
700
|
+
return runtime.events(runId, options);
|
|
564
701
|
}
|
|
565
702
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
703
|
+
const firstRuntime = this.listRuntimes()[0];
|
|
704
|
+
if (firstRuntime) {
|
|
705
|
+
return firstRuntime.events(runId, options);
|
|
706
|
+
}
|
|
707
|
+
return this.eventSink.subscribe(runId, options);
|
|
708
|
+
}
|
|
709
|
+
getGlobalStorage() {
|
|
710
|
+
if (!this.globalStorage) {
|
|
711
|
+
const hasInMemoryPackage = this.options.packages?.some((item) => typeof item !== "string" && !isPackageRuntime(item) && item.inMemory);
|
|
712
|
+
const inMemory = Boolean(this.options.inMemory || hasInMemoryPackage);
|
|
713
|
+
this.globalStorage =
|
|
714
|
+
this.options.platform?.storage?.createGlobalStorage?.({
|
|
715
|
+
dataDir: this.options.dataDir,
|
|
716
|
+
customHome: this.options.customHome,
|
|
717
|
+
}) ??
|
|
718
|
+
createGlobalStorage({
|
|
719
|
+
dataDir: this.options.dataDir,
|
|
720
|
+
customHome: this.options.customHome,
|
|
721
|
+
inMemory,
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
return this.globalStorage;
|
|
725
|
+
}
|
|
726
|
+
findDeclaredConfigItem(key) {
|
|
727
|
+
let foundItem;
|
|
728
|
+
for (const runtime of this.listRuntimes()) {
|
|
729
|
+
const item = runtime.projectConfig?.config?.[key];
|
|
730
|
+
if (item) {
|
|
731
|
+
if (item.secret)
|
|
732
|
+
return item;
|
|
733
|
+
foundItem = item;
|
|
570
734
|
}
|
|
571
735
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
736
|
+
return foundItem;
|
|
737
|
+
}
|
|
738
|
+
resolveRuntime(packageId) {
|
|
739
|
+
if (packageId) {
|
|
740
|
+
return this.getRuntime(packageId);
|
|
575
741
|
}
|
|
576
|
-
|
|
742
|
+
const runtimes = this.listRuntimes();
|
|
743
|
+
return runtimes.length === 1 ? runtimes[0] : undefined;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* 解析目标包运行时:未命中时抛出统一的包未找到错误。
|
|
747
|
+
*/
|
|
748
|
+
requireRuntime(packageId) {
|
|
749
|
+
const runtime = this.resolveRuntime(packageId);
|
|
750
|
+
if (!runtime) {
|
|
751
|
+
throw new Error(`Package '${packageId}' not found in host`);
|
|
752
|
+
}
|
|
753
|
+
return runtime;
|
|
754
|
+
}
|
|
755
|
+
async getConfig(packageId, key) {
|
|
756
|
+
if (packageId === "global") {
|
|
757
|
+
const globalStorage = this.getGlobalStorage();
|
|
758
|
+
const val = globalStorage.getConfig(key);
|
|
759
|
+
const configured = val !== undefined;
|
|
760
|
+
const declaredItem = this.findDeclaredConfigItem(key);
|
|
761
|
+
const isSecret = isSecretConfigKey(key, declaredItem);
|
|
762
|
+
return {
|
|
763
|
+
key,
|
|
764
|
+
configured,
|
|
765
|
+
secret: isSecret,
|
|
766
|
+
source: configured ? "global" : "default",
|
|
767
|
+
value: !isSecret && configured ? val : undefined,
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
return this.requireRuntime(packageId).getConfig(key);
|
|
771
|
+
}
|
|
772
|
+
async setConfig(packageId, key, value) {
|
|
773
|
+
if (packageId === "global") {
|
|
774
|
+
await this.getGlobalStorage().setConfig(key, value);
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
await this.requireRuntime(packageId).setConfig(key, value);
|
|
778
|
+
}
|
|
779
|
+
async deleteConfig(packageId, key) {
|
|
780
|
+
if (packageId === "global") {
|
|
781
|
+
return await this.getGlobalStorage().deleteConfig(key);
|
|
782
|
+
}
|
|
783
|
+
return await this.requireRuntime(packageId).deleteConfig(key);
|
|
784
|
+
}
|
|
785
|
+
async listConfig(packageId) {
|
|
786
|
+
if (packageId === "global") {
|
|
787
|
+
const globalStorage = this.getGlobalStorage();
|
|
788
|
+
const all = globalStorage.listConfig();
|
|
789
|
+
return Object.entries(all).map(([key, val]) => {
|
|
790
|
+
const declaredItem = this.findDeclaredConfigItem(key);
|
|
791
|
+
const isSecret = isSecretConfigKey(key, declaredItem);
|
|
792
|
+
return {
|
|
793
|
+
key,
|
|
794
|
+
configured: true,
|
|
795
|
+
secret: isSecret,
|
|
796
|
+
source: "global",
|
|
797
|
+
value: isSecret ? undefined : val,
|
|
798
|
+
};
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
return this.requireRuntime(packageId).listConfig();
|
|
802
|
+
}
|
|
803
|
+
async getState(packageId, actionId, key, options) {
|
|
804
|
+
return this.requireRuntime(packageId).getState(actionId, key, options);
|
|
805
|
+
}
|
|
806
|
+
async setState(packageId, actionId, key, value, options) {
|
|
807
|
+
const runtime = this.requireRuntime(packageId);
|
|
808
|
+
if (actionId) {
|
|
809
|
+
await runtime.setActionState(actionId, key, value, options);
|
|
810
|
+
}
|
|
811
|
+
else {
|
|
812
|
+
await runtime.setState(key, value, options);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
async deleteState(packageId, actionId, key, options) {
|
|
816
|
+
return this.requireRuntime(packageId).deleteState(actionId, key, options);
|
|
817
|
+
}
|
|
818
|
+
async listStateKeys(packageId, actionId, options) {
|
|
819
|
+
return this.requireRuntime(packageId).listStateKeys(actionId, options);
|
|
820
|
+
}
|
|
821
|
+
async clearState(packageId, actionId, options) {
|
|
822
|
+
return this.requireRuntime(packageId).clearState(actionId, options);
|
|
823
|
+
}
|
|
824
|
+
async listStateEntries(packageId, options) {
|
|
825
|
+
const runtime = this.resolveRuntime(packageId);
|
|
826
|
+
if (!runtime) {
|
|
827
|
+
throw new ServiceError(CAPABILITY_UNAVAILABLE, `CAPABILITY_UNAVAILABLE: Package '${packageId}' not found in host`);
|
|
828
|
+
}
|
|
829
|
+
if (!runtime.listStateEntries) {
|
|
830
|
+
return [];
|
|
831
|
+
}
|
|
832
|
+
return runtime.listStateEntries(options);
|
|
577
833
|
}
|
|
578
834
|
/**
|
|
579
835
|
* 优雅关闭宿主容器。
|
|
580
|
-
* 仅对内部创建的
|
|
581
|
-
* 外部传入借用的
|
|
836
|
+
* 仅对内部创建的 Runtime 实例执行 close 并安全释放底层资源;
|
|
837
|
+
* 外部传入借用的 Runtime 实例生命周期完全由调用方负责管理,Host 关闭时仅解绑引用并清理自身内部实例。
|
|
582
838
|
*/
|
|
583
839
|
async close(options) {
|
|
584
840
|
if (this.isClosed)
|
|
585
841
|
return;
|
|
586
842
|
this.isClosed = true;
|
|
587
|
-
const
|
|
588
|
-
await Promise.all(
|
|
843
|
+
const allRuntimes = Array.from(this.runtimes.values());
|
|
844
|
+
await Promise.all(allRuntimes.map(async (runtime) => {
|
|
589
845
|
try {
|
|
590
|
-
await
|
|
846
|
+
await runtime.close(options);
|
|
591
847
|
}
|
|
592
848
|
catch {
|
|
593
|
-
//
|
|
849
|
+
// 忽略 Runtime 关闭异常,确保全部安全释放
|
|
594
850
|
}
|
|
595
851
|
}));
|
|
596
|
-
this.
|
|
597
|
-
|
|
852
|
+
this.runtimes.clear();
|
|
853
|
+
try {
|
|
854
|
+
this.globalStorage?.close();
|
|
855
|
+
}
|
|
856
|
+
catch {
|
|
857
|
+
// 忽略全局存储关闭异常
|
|
858
|
+
}
|
|
859
|
+
this.globalStorage = undefined;
|
|
598
860
|
try {
|
|
599
861
|
this.dataDirLock?.release();
|
|
600
862
|
}
|
|
@@ -610,30 +872,17 @@ export class DefaultActionDockHost {
|
|
|
610
872
|
export async function createActionDockHost(options = {}) {
|
|
611
873
|
let createdHost;
|
|
612
874
|
try {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
if (root) {
|
|
616
|
-
if (isProjectLockHeld(root)) {
|
|
617
|
-
const err = new Error("PROJECT_BUSY: Project directory is locked by another active process holding project.lock");
|
|
618
|
-
err.code = PROJECT_BUSY;
|
|
619
|
-
throw err;
|
|
620
|
-
}
|
|
621
|
-
// 依据事务日志恢复未完成提交的悬空事务(锁持有者存活时严禁判定为崩溃事务并禁止自动恢复)
|
|
622
|
-
if (hasPendingTransactions(root)) {
|
|
623
|
-
await recoverPendingTransactions(root, { frozenInstall: true });
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
createdHost = new DefaultActionDockHost(options);
|
|
875
|
+
createdHost = new DefaultActionDockHost(options, { deferInit: true });
|
|
876
|
+
await createdHost.initializeAsync();
|
|
628
877
|
return createdHost;
|
|
629
878
|
}
|
|
630
879
|
catch (err) {
|
|
631
880
|
if (createdHost) {
|
|
632
881
|
try {
|
|
633
|
-
await createdHost.
|
|
882
|
+
await createdHost.rollbackInitialization();
|
|
634
883
|
}
|
|
635
884
|
catch {
|
|
636
|
-
//
|
|
885
|
+
// 忽略宿主回滚异常,确保抛出原始异常
|
|
637
886
|
}
|
|
638
887
|
}
|
|
639
888
|
throw err;
|