@actiondock/core 2.5.1 → 2.7.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/dist/app/app.d.ts +14 -1
- package/dist/app/app.js +88 -222
- package/dist/app/static-index.d.ts +39 -0
- package/dist/app/static-index.js +164 -0
- package/dist/app/types.d.ts +5 -0
- package/dist/catalog/action-index.js +6 -4
- package/dist/catalog/package-catalog.js +2 -1
- package/dist/doctor/checks/index.d.ts +5 -0
- package/dist/doctor/checks/index.js +5 -0
- package/dist/doctor/checks/project-locate.d.ts +6 -0
- package/dist/doctor/checks/project-locate.js +36 -0
- package/dist/doctor/checks/project.d.ts +6 -0
- package/dist/doctor/checks/project.js +383 -0
- package/dist/doctor/checks/registry.d.ts +13 -0
- package/dist/doctor/checks/registry.js +207 -0
- package/dist/doctor/checks/runtime.d.ts +13 -0
- package/dist/doctor/checks/runtime.js +90 -0
- package/dist/doctor/checks/storage.d.ts +5 -0
- package/dist/doctor/checks/storage.js +34 -0
- package/dist/doctor/context.d.ts +35 -0
- package/dist/doctor/context.js +11 -0
- package/dist/doctor/doctor.d.ts +5 -0
- package/dist/doctor/doctor.js +33 -602
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +22 -0
- package/dist/execution/service.js +14 -9
- package/dist/export/templates.js +42 -30
- package/dist/host/host.d.ts +33 -0
- package/dist/host/host.js +222 -362
- package/dist/host/routing.d.ts +92 -0
- package/dist/host/routing.js +238 -0
- package/dist/host/types.d.ts +8 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/input/advice.d.ts +100 -0
- package/dist/input/advice.js +767 -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 +72 -0
- package/dist/input/flat-errors.js +87 -0
- package/dist/input/flat-materializer.d.ts +29 -0
- package/dist/input/flat-materializer.js +179 -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/ipc/host.js +38 -0
- 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/platform/types.d.ts +7 -0
- package/dist/process/control-arbiter.d.ts +107 -0
- package/dist/process/control-arbiter.js +251 -0
- package/dist/process/diagnostics-sink.d.ts +26 -0
- package/dist/process/diagnostics-sink.js +35 -0
- package/dist/process/index.d.ts +7 -0
- package/dist/process/index.js +7 -0
- package/dist/process/input-dispatcher.d.ts +88 -0
- package/dist/process/input-dispatcher.js +240 -0
- package/dist/process/managed-record.d.ts +78 -0
- package/dist/process/managed-record.js +1 -0
- package/dist/process/process-manager.d.ts +15 -56
- package/dist/process/process-manager.js +119 -707
- package/dist/process/reservation-table.d.ts +39 -0
- package/dist/process/reservation-table.js +80 -0
- package/dist/process/run-executor.d.ts +16 -0
- package/dist/process/run-executor.js +155 -0
- package/dist/process/terminal-output-cache.d.ts +66 -0
- package/dist/process/terminal-output-cache.js +132 -0
- package/dist/profile/client-actions.d.ts +56 -0
- package/dist/profile/client-actions.js +215 -0
- package/dist/profile/client-config.d.ts +20 -0
- package/dist/profile/client-config.js +51 -0
- package/dist/profile/client-health.d.ts +14 -0
- package/dist/profile/client-health.js +69 -0
- package/dist/profile/client-playbooks.d.ts +19 -0
- package/dist/profile/client-playbooks.js +36 -0
- package/dist/profile/client-query.d.ts +30 -0
- package/dist/profile/client-query.js +70 -0
- package/dist/profile/client-runs.d.ts +42 -0
- package/dist/profile/client-runs.js +58 -0
- package/dist/profile/client-state.d.ts +56 -0
- package/dist/profile/client-state.js +83 -0
- package/dist/profile/client-transport.d.ts +61 -0
- package/dist/profile/client-transport.js +156 -0
- package/dist/profile/client.d.ts +23 -142
- package/dist/profile/client.js +28 -588
- package/dist/profile/manager.js +1 -1
- package/dist/project/closure.d.ts +4 -4
- package/dist/project/closure.js +11 -5
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +0 -25
- package/dist/project/loader.js +1 -154
- package/dist/project/transactions.d.ts +12 -14
- package/dist/project/transactions.js +60 -836
- package/dist/registry/registry.js +6 -4
- package/dist/registry/resolve.js +1 -1
- package/dist/runtime/action-registry.d.ts +45 -0
- package/dist/runtime/action-registry.js +108 -0
- package/dist/runtime/clock.d.ts +5 -17
- package/dist/runtime/clock.js +5 -15
- package/dist/runtime/context.d.ts +3 -0
- package/dist/runtime/context.js +17 -56
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/package-runner-factory.d.ts +82 -0
- package/dist/runtime/package-runner-factory.js +186 -0
- package/dist/runtime/run-persistence.d.ts +85 -0
- package/dist/runtime/run-persistence.js +111 -0
- package/dist/runtime/runner.d.ts +25 -45
- package/dist/runtime/runner.js +130 -354
- package/dist/runtime/standalone.d.ts +25 -2
- package/dist/runtime/standalone.js +305 -87
- 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/dispatcher.d.ts +8 -3
- package/dist/server/dispatcher.js +16 -40
- package/dist/server/routes/runs.js +6 -4
- package/dist/server/security.d.ts +1 -7
- package/dist/server/security.js +2 -13
- package/dist/storage/clock.d.ts +25 -0
- package/dist/storage/clock.js +23 -0
- package/dist/storage/data-dir-lock.d.ts +3 -36
- package/dist/storage/data-dir-lock.js +48 -881
- package/dist/storage/index.d.ts +8 -1
- package/dist/storage/index.js +8 -2
- package/dist/storage/lazy.js +1 -0
- package/dist/storage/lock-core.d.ts +162 -0
- package/dist/storage/lock-core.js +885 -0
- package/dist/storage/sqlite.d.ts +19 -6
- package/dist/storage/sqlite.js +81 -89
- package/dist/storage/types.d.ts +27 -5
- package/dist/storage/types.js +21 -0
- package/dist/storage/utils.d.ts +22 -0
- package/dist/storage/utils.js +34 -0
- package/dist/target/local.d.ts +1 -0
- package/dist/target/local.js +23 -17
- package/dist/target/remote-errors.d.ts +21 -0
- package/dist/target/remote-errors.js +51 -0
- package/dist/target/remote-polling.d.ts +35 -0
- package/dist/target/remote-polling.js +104 -0
- package/dist/target/remote.d.ts +4 -0
- package/dist/target/remote.js +108 -301
- package/dist/target/sse-parser.d.ts +35 -0
- package/dist/target/sse-parser.js +163 -0
- package/dist/target/target.js +2 -0
- package/dist/target/types.d.ts +6 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/net.d.ts +10 -0
- package/dist/utils/net.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/host/host.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { DefaultActionDockApp } from "../app/app.js";
|
|
4
|
-
import { ActionResolver } from "../catalog/action-resolver.js";
|
|
5
4
|
import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
|
|
6
5
|
import { ActionPackageResolver } from "../project/resolver.js";
|
|
7
6
|
import { hasPendingTransactions, isProjectLockHeld, recoverPendingTransactions } from "../project/transactions.js";
|
|
@@ -9,6 +8,7 @@ import { listLinkedPackages } from "../registry/registry.js";
|
|
|
9
8
|
import { InMemoryEventSink } from "../runtime/events.js";
|
|
10
9
|
import { ACTION_CALL_CYCLE, ACTION_MAX_DEPTH_EXCEEDED, ACTION_NOT_FOUND, ACTION_PACKAGE_VERSION_CONFLICT, ACTION_SUBRUN_LIMIT, INVALID_ACTION_REF, MAX_SUBRUNS_REACHED, PACKAGE_NOT_FOUND, PROJECT_BUSY, PROJECT_RECOVERY_REQUIRED, UNDECLARED_ACTION_DEPENDENCY, } from "../errors.js";
|
|
11
10
|
import { DataDirLock } from "../storage/data-dir-lock.js";
|
|
11
|
+
import { ambiguousActionMessage, buildRuntimeError, collectPackageInfos, describeActionAcrossApps, describeVisiblePlaybook, isRootCallVisible, listVisiblePlaybooks, packageNotFoundMessage, parseRefLoose, resolveProjectRoot, resolveShortRef, } from "./routing.js";
|
|
12
12
|
function isActionDockApp(item) {
|
|
13
13
|
return (typeof item === "object" &&
|
|
14
14
|
item !== null &&
|
|
@@ -17,29 +17,13 @@ function isActionDockApp(item) {
|
|
|
17
17
|
"runAction" in item &&
|
|
18
18
|
typeof item.runAction === "function");
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* 判定异常是否为 not-found 语义(包内确实不存在该 Action)。
|
|
22
|
-
*
|
|
23
|
-
* 遍历匹配短标识符时仅跳过此类错误;存储损坏、模块加载失败等内部错误必须向调用方透传,
|
|
24
|
-
* 严禁被吞没后伪装成 ACTION_NOT_FOUND。
|
|
25
|
-
*/
|
|
26
|
-
function isActionNotFoundLikeError(err) {
|
|
27
|
-
if (!err) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
if (err.code === ACTION_NOT_FOUND || err.code === "NOT_FOUND") {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
const message = typeof err.message === "string" ? err.message : String(err);
|
|
34
|
-
return (message.startsWith("ACTION_NOT_FOUND:") ||
|
|
35
|
-
/Action '[^']*' not found in package/.test(message));
|
|
36
|
-
}
|
|
37
20
|
/**
|
|
38
21
|
* ActionDock 统一多包宿主容器默认实现。
|
|
39
22
|
* 负责聚合与调度多个 ActionDockApp 实例,提供跨包引用路由、依赖声明校验与资源配额控制。
|
|
40
23
|
*/
|
|
41
24
|
export class DefaultActionDockHost {
|
|
42
25
|
hostSessionId;
|
|
26
|
+
options;
|
|
43
27
|
apps = new Map();
|
|
44
28
|
internallyCreatedApps = new Set();
|
|
45
29
|
activeSubRunsPerRoot = new Map();
|
|
@@ -51,11 +35,17 @@ export class DefaultActionDockHost {
|
|
|
51
35
|
isClosed = false;
|
|
52
36
|
dataDirLock;
|
|
53
37
|
failedLinkedPackages = new Map();
|
|
38
|
+
failedAutoLoad;
|
|
39
|
+
/** 透传给内部创建 App 的存储收割开关(Host 默认持有者身份,显式可关) */
|
|
40
|
+
recoverOrphans;
|
|
54
41
|
constructor(options = {}) {
|
|
55
42
|
this.hostSessionId = randomUUID();
|
|
43
|
+
this.options = options;
|
|
56
44
|
this.maxCallDepth = options.maxCallDepth ?? 16;
|
|
57
45
|
this.maxSubRuns = options.maxSubRuns ?? 64;
|
|
58
46
|
this.eventSink = options.eventSink ?? options.platform?.eventSink ?? new InMemoryEventSink();
|
|
47
|
+
// Host 默认声明数据目录持有者身份;查询旁观方(CLI 查询命令)显式置 false
|
|
48
|
+
this.recoverOrphans = options.recoverOrphans !== false;
|
|
59
49
|
// 当指定非内存 dataDir 时获取排他目录锁,防止并发冲突
|
|
60
50
|
if (options.dataDir && !options.inMemory) {
|
|
61
51
|
this.dataDirLock = DataDirLock.acquire(options.dataDir, {
|
|
@@ -63,140 +53,15 @@ export class DefaultActionDockHost {
|
|
|
63
53
|
});
|
|
64
54
|
}
|
|
65
55
|
try {
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (isActionDockApp(item)) {
|
|
70
|
-
this.registerAppInternal(item, true);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
const app = new DefaultActionDockApp({
|
|
74
|
-
...item,
|
|
75
|
-
hostSessionId: this.hostSessionId,
|
|
76
|
-
platform: item.platform ?? options.platform,
|
|
77
|
-
inMemory: item.inMemory ?? options.inMemory,
|
|
78
|
-
customHome: item.customHome ?? options.customHome,
|
|
79
|
-
dataDir: item.dataDir ?? options.dataDir,
|
|
80
|
-
clock: item.clock ?? options.clock,
|
|
81
|
-
process: item.process ?? options.process,
|
|
82
|
-
logger: item.logger ?? options.logger,
|
|
83
|
-
eventSink: item.eventSink ?? this.eventSink,
|
|
84
|
-
maxCallDepth: item.maxCallDepth ?? this.maxCallDepth,
|
|
85
|
-
maxSubRuns: item.maxSubRuns ?? this.maxSubRuns,
|
|
86
|
-
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
87
|
-
});
|
|
88
|
-
this.internallyCreatedApps.add(app);
|
|
89
|
-
this.registerAppInternal(app, true);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// 自动加载当前工程(若发现工程根目录且未显式禁用)
|
|
56
|
+
// 阶段一:注册显式传入的 packages 列表
|
|
57
|
+
this.registerExplicitPackages(options);
|
|
58
|
+
// 阶段二:自动加载当前工程(若发现工程根目录且未显式禁用)
|
|
94
59
|
if (options.autoLoadCurrentProject !== false) {
|
|
95
|
-
|
|
96
|
-
if (!root) {
|
|
97
|
-
const detected = findProjectRoot();
|
|
98
|
-
if (detected) {
|
|
99
|
-
root = detected;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (root && existsSync(root)) {
|
|
103
|
-
if (isProjectLockHeld(root)) {
|
|
104
|
-
const err = new Error("PROJECT_BUSY: Project directory is locked by another active process holding project.lock");
|
|
105
|
-
err.code = PROJECT_BUSY;
|
|
106
|
-
throw err;
|
|
107
|
-
}
|
|
108
|
-
if (hasPendingTransactions(root)) {
|
|
109
|
-
const err = new Error(`PROJECT_RECOVERY_REQUIRED: Project directory '${root}' has pending transactions requiring recovery; use async createActionDockHost() to recover automatically`);
|
|
110
|
-
err.code = PROJECT_RECOVERY_REQUIRED;
|
|
111
|
-
throw err;
|
|
112
|
-
}
|
|
113
|
-
try {
|
|
114
|
-
const config = loadProjectConfig(root);
|
|
115
|
-
this.hostPublicPackageIds.add(config.id);
|
|
116
|
-
this.resolver = new ActionPackageResolver({
|
|
117
|
-
projectRoot: root,
|
|
118
|
-
manifest: config,
|
|
119
|
-
allowDevLinks: options.scanLinkedPackages,
|
|
120
|
-
customHome: options.customHome,
|
|
121
|
-
});
|
|
122
|
-
const graph = this.resolver.resolveSync();
|
|
123
|
-
this.hostPublicPackageIds.add(graph.rootPackageId);
|
|
124
|
-
for (const depId of graph.directDependencyIds) {
|
|
125
|
-
this.hostPublicPackageIds.add(depId);
|
|
126
|
-
}
|
|
127
|
-
for (const pkg of graph.packages.values()) {
|
|
128
|
-
if (!this.apps.has(pkg.packageId)) {
|
|
129
|
-
const isDirectOrRoot = this.hostPublicPackageIds.has(pkg.packageId);
|
|
130
|
-
const app = new DefaultActionDockApp({
|
|
131
|
-
packageRoot: pkg.packageRoot,
|
|
132
|
-
projectConfig: pkg.manifest,
|
|
133
|
-
hostSessionId: this.hostSessionId,
|
|
134
|
-
platform: options.platform,
|
|
135
|
-
inMemory: options.inMemory,
|
|
136
|
-
customHome: options.customHome,
|
|
137
|
-
dataDir: options.dataDir,
|
|
138
|
-
clock: options.clock,
|
|
139
|
-
process: options.process,
|
|
140
|
-
logger: options.logger,
|
|
141
|
-
eventSink: this.eventSink,
|
|
142
|
-
maxCallDepth: this.maxCallDepth,
|
|
143
|
-
maxSubRuns: this.maxSubRuns,
|
|
144
|
-
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
145
|
-
});
|
|
146
|
-
this.internallyCreatedApps.add(app);
|
|
147
|
-
this.registerAppInternal(app, isDirectOrRoot);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
catch (err) {
|
|
152
|
-
if (err?.code === ACTION_PACKAGE_VERSION_CONFLICT ||
|
|
153
|
-
err?.code === PROJECT_RECOVERY_REQUIRED ||
|
|
154
|
-
err?.code === PROJECT_BUSY) {
|
|
155
|
-
throw err;
|
|
156
|
-
}
|
|
157
|
-
// 忽略非 ActionDock 工程目录解析异常
|
|
158
|
-
}
|
|
159
|
-
}
|
|
60
|
+
this.loadCurrentProject(options);
|
|
160
61
|
}
|
|
161
|
-
//
|
|
62
|
+
// 阶段三:扫描已软链接的外部包并注册至 Host
|
|
162
63
|
if (options.scanLinkedPackages) {
|
|
163
|
-
|
|
164
|
-
if (!this.apps.has(linked.id)) {
|
|
165
|
-
if (!existsSync(linked.path)) {
|
|
166
|
-
const err = `Linked package '${linked.id}' path does not exist on disk: '${linked.path}'`;
|
|
167
|
-
this.failedLinkedPackages.set(linked.id, { path: linked.path, error: err });
|
|
168
|
-
options.logger?.warn?.(`[Host] ${err}`);
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
try {
|
|
172
|
-
const config = loadProjectConfig(linked.path);
|
|
173
|
-
this.hostPublicPackageIds.add(linked.id);
|
|
174
|
-
const app = new DefaultActionDockApp({
|
|
175
|
-
packageRoot: linked.path,
|
|
176
|
-
projectConfig: config,
|
|
177
|
-
hostSessionId: this.hostSessionId,
|
|
178
|
-
platform: options.platform,
|
|
179
|
-
inMemory: options.inMemory,
|
|
180
|
-
customHome: options.customHome,
|
|
181
|
-
dataDir: options.dataDir,
|
|
182
|
-
clock: options.clock,
|
|
183
|
-
process: options.process,
|
|
184
|
-
logger: options.logger,
|
|
185
|
-
eventSink: this.eventSink,
|
|
186
|
-
maxCallDepth: this.maxCallDepth,
|
|
187
|
-
maxSubRuns: this.maxSubRuns,
|
|
188
|
-
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
189
|
-
});
|
|
190
|
-
this.internallyCreatedApps.add(app);
|
|
191
|
-
this.registerAppInternal(app, true);
|
|
192
|
-
}
|
|
193
|
-
catch (err) {
|
|
194
|
-
const errDetail = err?.message || String(err);
|
|
195
|
-
this.failedLinkedPackages.set(linked.id, { path: linked.path, error: errDetail });
|
|
196
|
-
options.logger?.warn?.(`[Host] Failed to load linked package '${linked.id}' from '${linked.path}': ${errDetail}`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
64
|
+
this.registerLinkedPackages(options);
|
|
200
65
|
}
|
|
201
66
|
}
|
|
202
67
|
catch (err) {
|
|
@@ -224,6 +89,164 @@ export class DefaultActionDockHost {
|
|
|
224
89
|
throw err;
|
|
225
90
|
}
|
|
226
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* 阶段函数:注册显式传入的 packages 列表(现成 App 实例或 AppOptions 配置)。
|
|
94
|
+
*/
|
|
95
|
+
registerExplicitPackages(options) {
|
|
96
|
+
if (!options.packages || !Array.isArray(options.packages)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
for (const item of options.packages) {
|
|
100
|
+
if (isActionDockApp(item)) {
|
|
101
|
+
this.registerAppInternal(item, true);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const app = new DefaultActionDockApp({
|
|
105
|
+
...item,
|
|
106
|
+
hostSessionId: this.hostSessionId,
|
|
107
|
+
platform: item.platform ?? options.platform,
|
|
108
|
+
inMemory: item.inMemory ?? options.inMemory,
|
|
109
|
+
customHome: item.customHome ?? options.customHome,
|
|
110
|
+
dataDir: item.dataDir ?? options.dataDir,
|
|
111
|
+
clock: item.clock ?? options.clock,
|
|
112
|
+
process: item.process ?? options.process,
|
|
113
|
+
logger: item.logger ?? options.logger,
|
|
114
|
+
eventSink: item.eventSink ?? this.eventSink,
|
|
115
|
+
maxCallDepth: item.maxCallDepth ?? this.maxCallDepth,
|
|
116
|
+
maxSubRuns: item.maxSubRuns ?? this.maxSubRuns,
|
|
117
|
+
recoverOrphans: this.recoverOrphans,
|
|
118
|
+
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
119
|
+
});
|
|
120
|
+
this.internallyCreatedApps.add(app);
|
|
121
|
+
this.registerAppInternal(app, true);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 阶段函数:自动加载当前工程并注册依赖闭包内的全部包。
|
|
127
|
+
*
|
|
128
|
+
* 失败语义区分场景:
|
|
129
|
+
* - 显式传入 projectRoot 时,任何解析失败(损坏 actiondock.json、文件系统错误等)
|
|
130
|
+
* 必须向上抛出,由调用方感知,严禁吞没;
|
|
131
|
+
* - 自动探测场景(未传 projectRoot,由 findProjectRoot 发现)失败时记录实例诊断
|
|
132
|
+
* failedAutoLoad 并在创建时输出单行告警,保持宿主其余能力可用但绝不无声。
|
|
133
|
+
*/
|
|
134
|
+
loadCurrentProject(options) {
|
|
135
|
+
const explicitRoot = options.projectRoot;
|
|
136
|
+
const root = resolveProjectRoot(options, () => findProjectRoot());
|
|
137
|
+
if (!root) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (isProjectLockHeld(root)) {
|
|
141
|
+
const err = new Error("PROJECT_BUSY: Project directory is locked by another active process holding project.lock");
|
|
142
|
+
err.code = PROJECT_BUSY;
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
if (hasPendingTransactions(root)) {
|
|
146
|
+
const err = new Error(`PROJECT_RECOVERY_REQUIRED: Project directory '${root}' has pending transactions requiring recovery; use async createActionDockHost() to recover automatically`);
|
|
147
|
+
err.code = PROJECT_RECOVERY_REQUIRED;
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const config = loadProjectConfig(root);
|
|
152
|
+
this.hostPublicPackageIds.add(config.id);
|
|
153
|
+
this.resolver = new ActionPackageResolver({
|
|
154
|
+
projectRoot: root,
|
|
155
|
+
manifest: config,
|
|
156
|
+
allowDevLinks: options.scanLinkedPackages,
|
|
157
|
+
customHome: options.customHome,
|
|
158
|
+
});
|
|
159
|
+
const graph = this.resolver.resolveSync();
|
|
160
|
+
this.hostPublicPackageIds.add(graph.rootPackageId);
|
|
161
|
+
for (const depId of graph.directDependencyIds) {
|
|
162
|
+
this.hostPublicPackageIds.add(depId);
|
|
163
|
+
}
|
|
164
|
+
for (const pkg of graph.packages.values()) {
|
|
165
|
+
if (!this.apps.has(pkg.packageId)) {
|
|
166
|
+
const isDirectOrRoot = this.hostPublicPackageIds.has(pkg.packageId);
|
|
167
|
+
const app = new DefaultActionDockApp({
|
|
168
|
+
packageRoot: pkg.packageRoot,
|
|
169
|
+
projectConfig: pkg.manifest,
|
|
170
|
+
hostSessionId: this.hostSessionId,
|
|
171
|
+
platform: options.platform,
|
|
172
|
+
inMemory: options.inMemory,
|
|
173
|
+
customHome: options.customHome,
|
|
174
|
+
dataDir: options.dataDir,
|
|
175
|
+
clock: options.clock,
|
|
176
|
+
process: options.process,
|
|
177
|
+
logger: options.logger,
|
|
178
|
+
eventSink: this.eventSink,
|
|
179
|
+
maxCallDepth: this.maxCallDepth,
|
|
180
|
+
maxSubRuns: this.maxSubRuns,
|
|
181
|
+
recoverOrphans: this.recoverOrphans,
|
|
182
|
+
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
183
|
+
});
|
|
184
|
+
this.internallyCreatedApps.add(app);
|
|
185
|
+
this.registerAppInternal(app, isDirectOrRoot);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
if (err?.code === ACTION_PACKAGE_VERSION_CONFLICT ||
|
|
191
|
+
err?.code === PROJECT_RECOVERY_REQUIRED ||
|
|
192
|
+
err?.code === PROJECT_BUSY) {
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
195
|
+
if (explicitRoot) {
|
|
196
|
+
// 显式指定的工程根目录解析失败必须抛出:调用方明确指定了位置,损坏配置不允许被吞没
|
|
197
|
+
throw err;
|
|
198
|
+
}
|
|
199
|
+
// 自动探测场景:记录实例诊断并输出单行告警,避免损坏工程被无声跳过
|
|
200
|
+
const detail = err?.message || String(err);
|
|
201
|
+
this.failedAutoLoad = { projectRoot: root, error: detail };
|
|
202
|
+
console.warn(`[Host] Failed to auto-load project at '${root}': ${detail}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 阶段函数:扫描全局注册表中已软链接的外部包并注册至 Host。
|
|
207
|
+
* 加载失败的链接包记录至 failedLinkedPackages 诊断并在调阅时透传精准失败原因。
|
|
208
|
+
*/
|
|
209
|
+
registerLinkedPackages(options) {
|
|
210
|
+
for (const linked of listLinkedPackages(options.customHome)) {
|
|
211
|
+
if (this.apps.has(linked.id)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!existsSync(linked.path)) {
|
|
215
|
+
const err = `Linked package '${linked.id}' path does not exist on disk: '${linked.path}'`;
|
|
216
|
+
this.failedLinkedPackages.set(linked.id, { path: linked.path, error: err });
|
|
217
|
+
options.logger?.warn?.(`[Host] ${err}`);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const config = loadProjectConfig(linked.path);
|
|
222
|
+
this.hostPublicPackageIds.add(linked.id);
|
|
223
|
+
const app = new DefaultActionDockApp({
|
|
224
|
+
packageRoot: linked.path,
|
|
225
|
+
projectConfig: config,
|
|
226
|
+
hostSessionId: this.hostSessionId,
|
|
227
|
+
platform: options.platform,
|
|
228
|
+
inMemory: options.inMemory,
|
|
229
|
+
customHome: options.customHome,
|
|
230
|
+
dataDir: options.dataDir,
|
|
231
|
+
clock: options.clock,
|
|
232
|
+
process: options.process,
|
|
233
|
+
logger: options.logger,
|
|
234
|
+
eventSink: this.eventSink,
|
|
235
|
+
maxCallDepth: this.maxCallDepth,
|
|
236
|
+
maxSubRuns: this.maxSubRuns,
|
|
237
|
+
recoverOrphans: this.recoverOrphans,
|
|
238
|
+
packageContextResolver: this.resolvePackageContext.bind(this),
|
|
239
|
+
});
|
|
240
|
+
this.internallyCreatedApps.add(app);
|
|
241
|
+
this.registerAppInternal(app, true);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
const errDetail = err?.message || String(err);
|
|
245
|
+
this.failedLinkedPackages.set(linked.id, { path: linked.path, error: errDetail });
|
|
246
|
+
options.logger?.warn?.(`[Host] Failed to load linked package '${linked.id}' from '${linked.path}': ${errDetail}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
227
250
|
resolvePackageContext(packageId) {
|
|
228
251
|
const targetApp = this.getApp(packageId);
|
|
229
252
|
if (!targetApp)
|
|
@@ -243,12 +266,26 @@ export class DefaultActionDockHost {
|
|
|
243
266
|
runner.setPackageContextResolver(this.resolvePackageContext.bind(this));
|
|
244
267
|
}
|
|
245
268
|
}
|
|
269
|
+
/** 路由可见性上下文(公开包集合与依赖解析器) */
|
|
270
|
+
visibility() {
|
|
271
|
+
return {
|
|
272
|
+
hostPublicPackageIds: this.hostPublicPackageIds,
|
|
273
|
+
resolver: this.resolver,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
246
276
|
getApp(packageId) {
|
|
247
277
|
return this.apps.get(packageId);
|
|
248
278
|
}
|
|
249
279
|
listApps() {
|
|
250
280
|
return Array.from(this.apps.values());
|
|
251
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* 获取自动加载工程的失败诊断信息。
|
|
284
|
+
* 仅自动探测场景会记录此诊断;显式传入 projectRoot 的加载失败会直接抛出,不产生此诊断。
|
|
285
|
+
*/
|
|
286
|
+
getAutoLoadFailure() {
|
|
287
|
+
return this.failedAutoLoad;
|
|
288
|
+
}
|
|
252
289
|
registerAppInternal(app, isPublic) {
|
|
253
290
|
if (this.apps.has(app.packageId)) {
|
|
254
291
|
const existing = this.apps.get(app.packageId);
|
|
@@ -262,22 +299,18 @@ export class DefaultActionDockHost {
|
|
|
262
299
|
this.hostPublicPackageIds.add(app.packageId);
|
|
263
300
|
}
|
|
264
301
|
this.bindApp(app);
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
st.recoverRunningRuns(this.hostSessionId);
|
|
278
|
-
}
|
|
279
|
-
catch {
|
|
280
|
-
// 忽略单包恢复异常
|
|
302
|
+
// 接管与恢复:仅持有者身份的 Host 自动将死亡会话或遗留非终态运行收敛为 interrupted;
|
|
303
|
+
// 旁观查询 Host(CLI state/runs/config 命令)跳过本步骤,不动其他进程的在途记录
|
|
304
|
+
if (this.recoverOrphans) {
|
|
305
|
+
const st = app.storage;
|
|
306
|
+
if (st && typeof st.recoverDeadSessionRuns === "function") {
|
|
307
|
+
try {
|
|
308
|
+
st.recoverDeadSessionRuns(this.hostSessionId);
|
|
309
|
+
}
|
|
310
|
+
catch (err) {
|
|
311
|
+
// 单包恢复失败不阻断整体接管流程,但必须可观测
|
|
312
|
+
console.warn(`[actiondock] recoverDeadSessionRuns failed for package '${app.packageId}': ${err instanceof Error ? err.message : String(err)}`);
|
|
313
|
+
}
|
|
281
314
|
}
|
|
282
315
|
}
|
|
283
316
|
}
|
|
@@ -285,16 +318,15 @@ export class DefaultActionDockHost {
|
|
|
285
318
|
this.registerAppInternal(app, true);
|
|
286
319
|
}
|
|
287
320
|
async info() {
|
|
288
|
-
return
|
|
321
|
+
return collectPackageInfos(this.listApps());
|
|
289
322
|
}
|
|
290
323
|
async listActions(options) {
|
|
291
324
|
let results = [];
|
|
292
325
|
const apps = this.listApps();
|
|
293
326
|
for (const app of apps) {
|
|
294
|
-
const isPublic = this.hostPublicPackageIds.has(app.packageId);
|
|
295
327
|
const appSummaries = await app.listActions();
|
|
296
328
|
for (const item of appSummaries) {
|
|
297
|
-
if (!
|
|
329
|
+
if (!isRootCallVisible(app.packageId, item.id, this.hostPublicPackageIds, this.resolver)) {
|
|
298
330
|
continue;
|
|
299
331
|
}
|
|
300
332
|
const qualifiedId = apps.length > 1 && !item.id.includes("/")
|
|
@@ -321,123 +353,13 @@ export class DefaultActionDockHost {
|
|
|
321
353
|
return results;
|
|
322
354
|
}
|
|
323
355
|
async describeAction(ref) {
|
|
324
|
-
|
|
325
|
-
try {
|
|
326
|
-
parsed = ActionResolver.parseRef(ref);
|
|
327
|
-
}
|
|
328
|
-
catch {
|
|
329
|
-
parsed = typeof ref === "object" ? ref : { actionId: ref };
|
|
330
|
-
}
|
|
331
|
-
if (parsed.packageId) {
|
|
332
|
-
const isPublic = this.hostPublicPackageIds.has(parsed.packageId);
|
|
333
|
-
if (!isPublic && this.resolver && !this.resolver.canRootCall(parsed.packageId, parsed.actionId)) {
|
|
334
|
-
throw new Error(`UNDECLARED_ACTION_DEPENDENCY: Action '${parsed.packageId}/${parsed.actionId}' is not declared as a direct dependency in actiondock.json and is not delegated by a visible playbook`);
|
|
335
|
-
}
|
|
336
|
-
const app = this.getApp(parsed.packageId);
|
|
337
|
-
if (!app) {
|
|
338
|
-
const failed = this.failedLinkedPackages.get(parsed.packageId);
|
|
339
|
-
if (failed) {
|
|
340
|
-
throw new Error(`Package '${parsed.packageId}' not found in host (failed to load from '${failed.path}': ${failed.error})`);
|
|
341
|
-
}
|
|
342
|
-
throw new Error(`Package '${parsed.packageId}' not found in host`);
|
|
343
|
-
}
|
|
344
|
-
const spec = await app.describeAction(parsed.actionId);
|
|
345
|
-
return {
|
|
346
|
-
...spec,
|
|
347
|
-
packageId: app.packageId,
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
const matches = [];
|
|
351
|
-
for (const app of this.listApps()) {
|
|
352
|
-
try {
|
|
353
|
-
const isPublic = this.hostPublicPackageIds.has(app.packageId);
|
|
354
|
-
if (!isPublic && this.resolver && !this.resolver.canRootCall(app.packageId, parsed.actionId)) {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
const spec = await app.describeAction(parsed.actionId);
|
|
358
|
-
matches.push({ app, spec: { ...spec, packageId: app.packageId } });
|
|
359
|
-
}
|
|
360
|
-
catch (err) {
|
|
361
|
-
// 仅将 not-found 语义视为「包内无此 Action」;其余异常必须透传,避免内部错误伪装成 ACTION_NOT_FOUND
|
|
362
|
-
if (!isActionNotFoundLikeError(err)) {
|
|
363
|
-
throw err;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
if (matches.length === 1) {
|
|
368
|
-
return {
|
|
369
|
-
...matches[0].spec,
|
|
370
|
-
packageId: matches[0].app.packageId,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
if (matches.length > 1) {
|
|
374
|
-
const candidates = matches.map((m) => `${m.app.packageId}/${parsed.actionId}`).join(", ");
|
|
375
|
-
const err = new Error(`INVALID_ACTION_REF: Action '${parsed.actionId}' is ambiguous and provided by multiple packages: ${candidates}. Please specify '<package-id>/${parsed.actionId}'. (AMBIGUOUS_ACTION_REF)`);
|
|
376
|
-
err.code = "INVALID_ACTION_REF";
|
|
377
|
-
err.details = { alias: "AMBIGUOUS_ACTION_REF", candidates: matches.map((m) => m.app.packageId) };
|
|
378
|
-
throw err;
|
|
379
|
-
}
|
|
380
|
-
throw new Error(`ACTION_NOT_FOUND: Action '${parsed.actionId}' not found in any registered package`);
|
|
356
|
+
return describeActionAcrossApps(ref, this.listApps(), this.visibility(), this.failedLinkedPackages);
|
|
381
357
|
}
|
|
382
358
|
async listPlaybooks() {
|
|
383
|
-
|
|
384
|
-
const apps = this.listApps();
|
|
385
|
-
for (const app of apps) {
|
|
386
|
-
const isPublic = this.hostPublicPackageIds.has(app.packageId);
|
|
387
|
-
if (!isPublic && this.resolver) {
|
|
388
|
-
const graph = this.resolver.resolveSync();
|
|
389
|
-
if (!graph.directDependencyIds.has(app.packageId)) {
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
const appPlaybooks = await app.listPlaybooks();
|
|
394
|
-
for (const item of appPlaybooks) {
|
|
395
|
-
const qualifiedId = apps.length > 1 && !item.id.includes("/")
|
|
396
|
-
? `${app.packageId}/${item.id}`
|
|
397
|
-
: item.id;
|
|
398
|
-
results.push({
|
|
399
|
-
...item,
|
|
400
|
-
id: qualifiedId,
|
|
401
|
-
packageId: app.packageId,
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
return results;
|
|
359
|
+
return listVisiblePlaybooks(this.listApps(), this.visibility());
|
|
406
360
|
}
|
|
407
361
|
async describePlaybook(id) {
|
|
408
|
-
|
|
409
|
-
const lastSlashIndex = id.lastIndexOf("/");
|
|
410
|
-
const packageId = id.slice(0, lastSlashIndex);
|
|
411
|
-
const playbookId = id.slice(lastSlashIndex + 1);
|
|
412
|
-
const isPublic = this.hostPublicPackageIds.has(packageId);
|
|
413
|
-
if (!isPublic && this.resolver) {
|
|
414
|
-
const graph = this.resolver.resolveSync();
|
|
415
|
-
if (!graph.directDependencyIds.has(packageId)) {
|
|
416
|
-
throw new Error(`UNDECLARED_ACTION_DEPENDENCY: Playbook '${id}' belongs to undeclared transitive package '${packageId}'`);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
const app = this.getApp(packageId);
|
|
420
|
-
if (!app) {
|
|
421
|
-
throw new Error(`Package '${packageId}' not found in host`);
|
|
422
|
-
}
|
|
423
|
-
return app.describePlaybook(playbookId);
|
|
424
|
-
}
|
|
425
|
-
for (const app of this.listApps()) {
|
|
426
|
-
try {
|
|
427
|
-
const isPublic = this.hostPublicPackageIds.has(app.packageId);
|
|
428
|
-
if (!isPublic && this.resolver) {
|
|
429
|
-
const graph = this.resolver.resolveSync();
|
|
430
|
-
if (!graph.directDependencyIds.has(app.packageId)) {
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return await app.describePlaybook(id);
|
|
435
|
-
}
|
|
436
|
-
catch {
|
|
437
|
-
// 忽略未匹配的包
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
throw new Error(`Playbook '${id}' not found in any registered package`);
|
|
362
|
+
return describeVisiblePlaybook(this.listApps(), id, this.visibility());
|
|
441
363
|
}
|
|
442
364
|
async runAction(ref, input, options = {}) {
|
|
443
365
|
const ticket = await this.startAction(ref, input, options);
|
|
@@ -451,13 +373,7 @@ export class DefaultActionDockHost {
|
|
|
451
373
|
throw new Error("ActionDockHost is closed: new tasks rejected");
|
|
452
374
|
}
|
|
453
375
|
// - 解析目标包与 Action 动作标识
|
|
454
|
-
|
|
455
|
-
try {
|
|
456
|
-
parsed = ActionResolver.parseRef(ref);
|
|
457
|
-
}
|
|
458
|
-
catch {
|
|
459
|
-
parsed = typeof ref === "object" ? ref : { actionId: ref };
|
|
460
|
-
}
|
|
376
|
+
const parsed = parseRefLoose(ref);
|
|
461
377
|
let targetApp;
|
|
462
378
|
let targetActionId = parsed.actionId;
|
|
463
379
|
let targetPackageId = parsed.packageId;
|
|
@@ -465,14 +381,7 @@ export class DefaultActionDockHost {
|
|
|
465
381
|
targetApp = this.getApp(targetPackageId);
|
|
466
382
|
if (!targetApp) {
|
|
467
383
|
const runId = randomUUID();
|
|
468
|
-
const
|
|
469
|
-
const errorMsg = failed
|
|
470
|
-
? `Package '${targetPackageId}' not found in host (failed to load from '${failed.path}': ${failed.error})`
|
|
471
|
-
: `Package '${targetPackageId}' not found in host`;
|
|
472
|
-
const error = {
|
|
473
|
-
code: PACKAGE_NOT_FOUND,
|
|
474
|
-
message: errorMsg,
|
|
475
|
-
};
|
|
384
|
+
const error = buildRuntimeError(PACKAGE_NOT_FOUND, packageNotFoundMessage(targetPackageId, this.failedLinkedPackages));
|
|
476
385
|
return {
|
|
477
386
|
runId,
|
|
478
387
|
status: "failed",
|
|
@@ -481,35 +390,14 @@ export class DefaultActionDockHost {
|
|
|
481
390
|
}
|
|
482
391
|
}
|
|
483
392
|
else {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const isPublic = this.hostPublicPackageIds.has(app.packageId);
|
|
488
|
-
if (!isPublic && this.resolver && !this.resolver.canRootCall(app.packageId, targetActionId)) {
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
await app.describeAction(targetActionId);
|
|
492
|
-
matches.push(app);
|
|
493
|
-
}
|
|
494
|
-
catch (err) {
|
|
495
|
-
// 仅将 not-found 语义视为「包内无此 Action」;其余异常必须透传,避免内部错误伪装成 ACTION_NOT_FOUND
|
|
496
|
-
if (!isActionNotFoundLikeError(err)) {
|
|
497
|
-
throw err;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
if (matches.length === 1) {
|
|
502
|
-
targetApp = matches[0];
|
|
393
|
+
const resolution = await resolveShortRef(this.listApps(), targetActionId, this.visibility());
|
|
394
|
+
if (resolution.kind === "unique") {
|
|
395
|
+
targetApp = resolution.app;
|
|
503
396
|
targetPackageId = targetApp.packageId;
|
|
504
397
|
}
|
|
505
|
-
else if (
|
|
506
|
-
const candidates = matches.map((m) => `${m.packageId}/${targetActionId}`).join(", ");
|
|
398
|
+
else if (resolution.kind === "ambiguous") {
|
|
507
399
|
const runId = randomUUID();
|
|
508
|
-
const error = {
|
|
509
|
-
code: INVALID_ACTION_REF,
|
|
510
|
-
message: `Action '${targetActionId}' is ambiguous and provided by multiple packages: ${candidates}. Please specify '<package-id>/${targetActionId}'.`,
|
|
511
|
-
details: { alias: "AMBIGUOUS_ACTION_REF", candidates: matches.map((m) => m.packageId) },
|
|
512
|
-
};
|
|
400
|
+
const error = buildRuntimeError(INVALID_ACTION_REF, ambiguousActionMessage(targetActionId, resolution.candidates), { alias: "AMBIGUOUS_ACTION_REF", candidates: resolution.candidates });
|
|
513
401
|
return {
|
|
514
402
|
runId,
|
|
515
403
|
status: "failed",
|
|
@@ -518,10 +406,7 @@ export class DefaultActionDockHost {
|
|
|
518
406
|
}
|
|
519
407
|
else {
|
|
520
408
|
const runId = randomUUID();
|
|
521
|
-
const error = {
|
|
522
|
-
code: ACTION_NOT_FOUND,
|
|
523
|
-
message: `Action '${targetActionId}' not found in any registered package`,
|
|
524
|
-
};
|
|
409
|
+
const error = buildRuntimeError(ACTION_NOT_FOUND, `Action '${targetActionId}' not found in any registered package`);
|
|
525
410
|
return {
|
|
526
411
|
runId,
|
|
527
412
|
status: "failed",
|
|
@@ -532,16 +417,9 @@ export class DefaultActionDockHost {
|
|
|
532
417
|
// 根调用可见性鉴权
|
|
533
418
|
const parentRunId = options.parentRunId;
|
|
534
419
|
if (!parentRunId && targetPackageId) {
|
|
535
|
-
|
|
536
|
-
if (!isPublic && this.resolver && !this.resolver.canRootCall(targetPackageId, targetActionId)) {
|
|
420
|
+
if (!isRootCallVisible(targetPackageId, targetActionId, this.hostPublicPackageIds, this.resolver)) {
|
|
537
421
|
const runId = randomUUID();
|
|
538
|
-
const error = {
|
|
539
|
-
code: UNDECLARED_ACTION_DEPENDENCY,
|
|
540
|
-
message: `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`,
|
|
541
|
-
details: {
|
|
542
|
-
target: `${targetPackageId}/${targetActionId}`,
|
|
543
|
-
},
|
|
544
|
-
};
|
|
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}` });
|
|
545
423
|
return {
|
|
546
424
|
runId,
|
|
547
425
|
status: "failed",
|
|
@@ -570,15 +448,11 @@ export class DefaultActionDockHost {
|
|
|
570
448
|
: usesList.some((u) => u === targetRef || u === `${targetPackageId}/*` || u === targetPackageId);
|
|
571
449
|
if (!isAllowed) {
|
|
572
450
|
const runId = randomUUID();
|
|
573
|
-
const error = {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
target: targetRef,
|
|
579
|
-
declaredUses: usesList,
|
|
580
|
-
},
|
|
581
|
-
};
|
|
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
|
+
});
|
|
582
456
|
return {
|
|
583
457
|
runId,
|
|
584
458
|
status: "failed",
|
|
@@ -602,11 +476,7 @@ export class DefaultActionDockHost {
|
|
|
602
476
|
}
|
|
603
477
|
if (depth >= this.maxCallDepth) {
|
|
604
478
|
const runId = randomUUID();
|
|
605
|
-
const error = {
|
|
606
|
-
code: ACTION_CALL_CYCLE,
|
|
607
|
-
message: `Maximum call depth of ${this.maxCallDepth} exceeded`,
|
|
608
|
-
details: { alias: ACTION_MAX_DEPTH_EXCEEDED, reason: "depth_exceeded", maxDepth: this.maxCallDepth },
|
|
609
|
-
};
|
|
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 });
|
|
610
480
|
return {
|
|
611
481
|
runId,
|
|
612
482
|
status: "failed",
|
|
@@ -618,11 +488,7 @@ export class DefaultActionDockHost {
|
|
|
618
488
|
const currentSubRuns = this.activeSubRunsPerRoot.get(effectiveRootRunId) || 0;
|
|
619
489
|
if (currentSubRuns >= this.maxSubRuns) {
|
|
620
490
|
const runId = randomUUID();
|
|
621
|
-
const error = {
|
|
622
|
-
code: ACTION_SUBRUN_LIMIT,
|
|
623
|
-
message: `Maximum concurrent sub-runs (${this.maxSubRuns}) reached for root run '${effectiveRootRunId}'`,
|
|
624
|
-
details: { alias: MAX_SUBRUNS_REACHED, limit: this.maxSubRuns },
|
|
625
|
-
};
|
|
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 });
|
|
626
492
|
return {
|
|
627
493
|
runId,
|
|
628
494
|
status: "failed",
|
|
@@ -745,14 +611,8 @@ export async function createActionDockHost(options = {}) {
|
|
|
745
611
|
let createdHost;
|
|
746
612
|
try {
|
|
747
613
|
if (options.autoLoadCurrentProject !== false) {
|
|
748
|
-
|
|
749
|
-
if (
|
|
750
|
-
const detected = findProjectRoot();
|
|
751
|
-
if (detected) {
|
|
752
|
-
root = detected;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
if (root && existsSync(root)) {
|
|
614
|
+
const root = resolveProjectRoot(options, () => findProjectRoot());
|
|
615
|
+
if (root) {
|
|
756
616
|
if (isProjectLockHeld(root)) {
|
|
757
617
|
const err = new Error("PROJECT_BUSY: Project directory is locked by another active process holding project.lock");
|
|
758
618
|
err.code = PROJECT_BUSY;
|