@actiondock/core 2.9.1 → 2.11.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.
Files changed (256) hide show
  1. package/README.md +1 -2
  2. package/dist/catalog/action-catalog.js +3 -33
  3. package/dist/catalog/discovery.d.ts +0 -3
  4. package/dist/catalog/discovery.js +4 -4
  5. package/dist/catalog/graph.d.ts +0 -4
  6. package/dist/catalog/graph.js +12 -15
  7. package/dist/{profile/client-actions.d.ts → client/actions.d.ts} +1 -1
  8. package/dist/{profile/client-actions.js → client/actions.js} +2 -8
  9. package/dist/{profile/client-config.d.ts → client/config.d.ts} +1 -1
  10. package/dist/{profile/client-config.js → client/config.js} +1 -1
  11. package/dist/client/dispatcher.d.ts +4 -0
  12. package/dist/client/dispatcher.js +21 -0
  13. package/dist/client/health.d.ts +38 -0
  14. package/dist/{profile/client-health.js → client/health.js} +9 -6
  15. package/dist/client/index.d.ts +16 -0
  16. package/dist/client/index.js +20 -0
  17. package/dist/{profile/client-playbooks.d.ts → client/playbooks.d.ts} +1 -1
  18. package/dist/{profile/client-playbooks.js → client/playbooks.js} +1 -1
  19. package/dist/{profile/client-runs.d.ts → client/runs.d.ts} +4 -2
  20. package/dist/{profile/client-runs.js → client/runs.js} +7 -1
  21. package/dist/{profile/client-state.d.ts → client/state.d.ts} +1 -1
  22. package/dist/{profile/client-state.js → client/state.js} +1 -1
  23. package/dist/{profile/client-transport.d.ts → client/transport.d.ts} +29 -10
  24. package/dist/{profile/client-transport.js → client/transport.js} +125 -35
  25. package/dist/doctor/checks/index.d.ts +1 -1
  26. package/dist/doctor/checks/index.js +1 -1
  27. package/dist/doctor/checks/project.js +24 -92
  28. package/dist/doctor/checks/registry.js +1 -1
  29. package/dist/doctor/checks/runtime.d.ts +0 -4
  30. package/dist/doctor/checks/runtime.js +0 -28
  31. package/dist/doctor/doctor.js +1 -2
  32. package/dist/errors.d.ts +85 -4
  33. package/dist/errors.js +114 -4
  34. package/dist/execution/service.d.ts +55 -37
  35. package/dist/execution/service.js +601 -212
  36. package/dist/execution/types.d.ts +24 -5
  37. package/dist/host/host.d.ts +42 -22
  38. package/dist/host/host.js +565 -150
  39. package/dist/host/routing.js +6 -6
  40. package/dist/host/types.d.ts +25 -13
  41. package/dist/index.d.ts +9 -4
  42. package/dist/index.js +5 -2
  43. package/dist/input/advice.d.ts +0 -44
  44. package/dist/input/advice.js +7 -141
  45. package/dist/input/describe.d.ts +6 -0
  46. package/dist/input/describe.js +26 -1
  47. package/dist/input/flat-materializer.js +1 -1
  48. package/dist/input/flat-parser.js +1 -1
  49. package/dist/input/index.d.ts +0 -1
  50. package/dist/input/index.js +0 -1
  51. package/dist/input/input-resolver.js +2 -2
  52. package/dist/input/validation-mapper.d.ts +1 -1
  53. package/dist/invocation/policy.js +2 -0
  54. package/dist/ipc/diagnostic.d.ts +8 -21
  55. package/dist/ipc/diagnostic.js +24 -57
  56. package/dist/ipc/host.js +21 -7
  57. package/dist/ipc/service.d.ts +14 -0
  58. package/dist/ipc/service.js +132 -26
  59. package/dist/ipc/types.d.ts +0 -4
  60. package/dist/package/index.d.ts +4 -5
  61. package/dist/package/index.js +1 -2
  62. package/dist/package/runtime.d.ts +94 -23
  63. package/dist/package/runtime.js +628 -274
  64. package/dist/package/static-index.d.ts +3 -6
  65. package/dist/package/static-index.js +11 -40
  66. package/dist/package/types.d.ts +19 -4
  67. package/dist/platform/index.d.ts +2 -1
  68. package/dist/platform/index.js +2 -1
  69. package/dist/platform/loader-hook.d.ts +32 -0
  70. package/dist/platform/loader-hook.js +90 -0
  71. package/dist/{node → platform}/module-loader.d.ts +13 -0
  72. package/dist/{node → platform}/module-loader.js +51 -4
  73. package/dist/platform/node.d.ts +19 -8
  74. package/dist/platform/node.js +50 -20
  75. package/dist/platform/types.d.ts +7 -47
  76. package/dist/process/context-process.d.ts +3 -18
  77. package/dist/process/context-process.js +3 -44
  78. package/dist/process/control.d.ts +21 -0
  79. package/dist/process/control.js +34 -0
  80. package/dist/process/converters.d.ts +23 -0
  81. package/dist/process/converters.js +92 -0
  82. package/dist/process/cursor.js +2 -22
  83. package/dist/process/{diagnostics-sink.d.ts → diagnostics.d.ts} +0 -7
  84. package/dist/process/{diagnostics-sink.js → diagnostics.js} +6 -8
  85. package/dist/process/index.d.ts +1 -3
  86. package/dist/process/index.js +1 -3
  87. package/dist/process/input-dispatcher.d.ts +4 -13
  88. package/dist/process/input-dispatcher.js +48 -69
  89. package/dist/process/managed-record.d.ts +3 -31
  90. package/dist/process/metadata-store.d.ts +0 -41
  91. package/dist/process/metadata-store.js +2 -341
  92. package/dist/process/process-driver.js +2 -2
  93. package/dist/process/process-manager.d.ts +8 -50
  94. package/dist/process/process-manager.js +28 -417
  95. package/dist/process/quotas.d.ts +14 -0
  96. package/dist/process/quotas.js +29 -0
  97. package/dist/process/run-executor.js +0 -1
  98. package/dist/process/terminal-output-cache.d.ts +20 -34
  99. package/dist/process/terminal-output-cache.js +43 -64
  100. package/dist/profile/index.d.ts +1 -2
  101. package/dist/profile/index.js +1 -2
  102. package/dist/profile/manager.d.ts +2 -4
  103. package/dist/profile/manager.js +10 -40
  104. package/dist/profile/types.d.ts +1 -17
  105. package/dist/project/digest.d.ts +5 -1
  106. package/dist/project/digest.js +81 -219
  107. package/dist/project/index.d.ts +4 -5
  108. package/dist/project/index.js +4 -5
  109. package/dist/project/init.js +2 -2
  110. package/dist/project/loader.d.ts +3 -3
  111. package/dist/project/loader.js +72 -50
  112. package/dist/project/lockfile.js +6 -5
  113. package/dist/project/manifest.js +5 -4
  114. package/dist/project/transactions.d.ts +4 -42
  115. package/dist/project/transactions.js +83 -138
  116. package/dist/project/types-generator.js +2 -1
  117. package/dist/project/types.d.ts +2 -0
  118. package/dist/registry/index.d.ts +0 -1
  119. package/dist/registry/index.js +0 -1
  120. package/dist/registry/lock.d.ts +2 -11
  121. package/dist/registry/lock.js +14 -183
  122. package/dist/registry/registry.d.ts +1 -6
  123. package/dist/registry/registry.js +5 -17
  124. package/dist/runtime/action-registry.d.ts +1 -5
  125. package/dist/runtime/action-registry.js +0 -12
  126. package/dist/runtime/context.js +2 -2
  127. package/dist/runtime/events.d.ts +11 -50
  128. package/dist/runtime/events.js +88 -368
  129. package/dist/runtime/index.d.ts +0 -4
  130. package/dist/runtime/index.js +0 -4
  131. package/dist/runtime/run-persistence.d.ts +2 -0
  132. package/dist/runtime/run-persistence.js +2 -1
  133. package/dist/runtime/standalone-commands.d.ts +18 -0
  134. package/dist/runtime/standalone-commands.js +466 -0
  135. package/dist/runtime/standalone-help.d.ts +20 -0
  136. package/dist/runtime/standalone-help.js +72 -0
  137. package/dist/runtime/standalone.d.ts +1 -44
  138. package/dist/runtime/standalone.js +31 -526
  139. package/dist/schema/validator.d.ts +2 -2
  140. package/dist/schema/validator.js +2 -9
  141. package/dist/server/index.d.ts +0 -2
  142. package/dist/server/index.js +0 -2
  143. package/dist/server/routes/actions.js +1 -1
  144. package/dist/server/routes/info.js +1 -1
  145. package/dist/server/routes/playbooks.js +1 -1
  146. package/dist/server/routes/runs.js +123 -16
  147. package/dist/server/routes/state.js +43 -18
  148. package/dist/server/security.d.ts +1 -1
  149. package/dist/server/security.js +2 -2
  150. package/dist/server/server.js +3 -7
  151. package/dist/service/factory.js +4 -3
  152. package/dist/service/index.d.ts +1 -1
  153. package/dist/service/index.js +1 -1
  154. package/dist/service/remote-errors.d.ts +1 -1
  155. package/dist/service/remote-errors.js +6 -6
  156. package/dist/service/remote-polling.d.ts +3 -0
  157. package/dist/service/remote-polling.js +10 -5
  158. package/dist/service/remote.d.ts +5 -1
  159. package/dist/service/remote.js +108 -17
  160. package/dist/service/sse-stream.js +2 -2
  161. package/dist/service/types.d.ts +22 -19
  162. package/dist/service/types.js +1 -24
  163. package/dist/storage/clock.d.ts +1 -1
  164. package/dist/storage/clock.js +1 -1
  165. package/dist/storage/data-dir-lock.d.ts +10 -22
  166. package/dist/storage/data-dir-lock.js +28 -56
  167. package/dist/storage/file-lock.d.ts +64 -0
  168. package/dist/storage/file-lock.js +447 -0
  169. package/dist/storage/index.d.ts +2 -2
  170. package/dist/storage/index.js +5 -4
  171. package/dist/storage/run-liveness.d.ts +48 -0
  172. package/dist/storage/run-liveness.js +44 -0
  173. package/dist/storage/sqlite-config.d.ts +16 -0
  174. package/dist/storage/sqlite-config.js +59 -0
  175. package/dist/storage/sqlite-driver.d.ts +3 -1
  176. package/dist/storage/sqlite-driver.js +14 -4
  177. package/dist/storage/sqlite-events.d.ts +13 -0
  178. package/dist/storage/sqlite-events.js +48 -0
  179. package/dist/storage/sqlite-runs.d.ts +107 -0
  180. package/dist/storage/sqlite-runs.js +622 -0
  181. package/dist/storage/sqlite-schema.d.ts +5 -0
  182. package/dist/storage/sqlite-schema.js +143 -0
  183. package/dist/storage/sqlite-state.d.ts +33 -0
  184. package/dist/storage/sqlite-state.js +270 -0
  185. package/dist/storage/sqlite.d.ts +30 -57
  186. package/dist/storage/sqlite.js +84 -741
  187. package/dist/storage/types.d.ts +35 -2
  188. package/dist/utils/index.d.ts +18 -14
  189. package/dist/utils/index.js +37 -29
  190. package/dist/utils/semver.d.ts +18 -0
  191. package/dist/utils/semver.js +19 -0
  192. package/dist/utils/warning.d.ts +1 -0
  193. package/dist/utils/warning.js +52 -0
  194. package/dist/{json/value-validator.d.ts → value-validator.d.ts} +9 -0
  195. package/dist/value-validator.js +330 -0
  196. package/dist/version.d.ts +1 -1
  197. package/dist/version.js +1 -1
  198. package/package.json +10 -12
  199. package/dist/execution/index.d.ts +0 -2
  200. package/dist/execution/index.js +0 -2
  201. package/dist/filter/index.d.ts +0 -1
  202. package/dist/filter/index.js +0 -1
  203. package/dist/graph/index.d.ts +0 -4
  204. package/dist/graph/index.js +0 -4
  205. package/dist/input/metadata.d.ts +0 -99
  206. package/dist/input/metadata.js +0 -99
  207. package/dist/invocation/index.d.ts +0 -2
  208. package/dist/invocation/index.js +0 -2
  209. package/dist/ipc/index.d.ts +0 -4
  210. package/dist/ipc/index.js +0 -4
  211. package/dist/json/index.d.ts +0 -1
  212. package/dist/json/index.js +0 -1
  213. package/dist/json/value-validator.js +0 -330
  214. package/dist/node/index.d.ts +0 -1
  215. package/dist/node/index.js +0 -1
  216. package/dist/platform/node-fs.d.ts +0 -37
  217. package/dist/platform/node-fs.js +0 -81
  218. package/dist/process/control-arbiter.d.ts +0 -108
  219. package/dist/process/control-arbiter.js +0 -250
  220. package/dist/process/process-executor.d.ts +0 -47
  221. package/dist/process/process-executor.js +0 -296
  222. package/dist/profile/client-health.d.ts +0 -14
  223. package/dist/profile/client-query.d.ts +0 -30
  224. package/dist/profile/client-query.js +0 -69
  225. package/dist/profile/client.d.ts +0 -26
  226. package/dist/profile/client.js +0 -31
  227. package/dist/project/closure.d.ts +0 -25
  228. package/dist/project/closure.js +0 -74
  229. package/dist/registry/store.d.ts +0 -42
  230. package/dist/registry/store.js +0 -43
  231. package/dist/runtime/clock.d.ts +0 -7
  232. package/dist/runtime/clock.js +0 -7
  233. package/dist/runtime/module-loader.d.ts +0 -2
  234. package/dist/runtime/module-loader.js +0 -1
  235. package/dist/runtime/process.d.ts +0 -8
  236. package/dist/runtime/process.js +0 -1
  237. package/dist/runtime/runner.d.ts +0 -234
  238. package/dist/runtime/runner.js +0 -609
  239. package/dist/schema/index.d.ts +0 -1
  240. package/dist/schema/index.js +0 -1
  241. package/dist/server/dispatcher.d.ts +0 -25
  242. package/dist/server/dispatcher.js +0 -54
  243. package/dist/service/local.d.ts +0 -27
  244. package/dist/service/local.js +0 -141
  245. package/dist/storage/driver.d.ts +0 -10
  246. package/dist/storage/driver.js +0 -10
  247. package/dist/storage/lazy.d.ts +0 -8
  248. package/dist/storage/lazy.js +0 -45
  249. package/dist/storage/lock-core.d.ts +0 -158
  250. package/dist/storage/lock-core.js +0 -872
  251. package/dist/utils/net.d.ts +0 -10
  252. package/dist/utils/net.js +0 -16
  253. package/dist/utils/process.d.ts +0 -10
  254. package/dist/utils/process.js +0 -21
  255. /package/dist/{filter → utils}/intent.d.ts +0 -0
  256. /package/dist/{filter → utils}/intent.js +0 -0
package/dist/host/host.js CHANGED
@@ -7,15 +7,16 @@ import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
7
7
  import { hasPendingTransactions, isProjectLockHeld, recoverPendingTransactions } from "../project/transactions.js";
8
8
  import { listLinkedPackages } from "../registry/registry.js";
9
9
  import { InMemoryEventSink } from "../runtime/events.js";
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";
10
+ import { ActionDockError, ACTION_NOT_FOUND, ACTION_PACKAGE_VERSION_CONFLICT, ACTION_SUBRUN_LIMIT, EXECUTION_FAILED, INVALID_ACTION_REF, PACKAGE_ID_CONFLICT, PACKAGE_NOT_FOUND, PROJECT_BUSY, PROJECT_RECOVERY_REQUIRED, SERVICE_CLOSED, UNDECLARED_ACTION_DEPENDENCY, } from "../errors.js";
11
11
  import { InvocationPolicy } from "../invocation/policy.js";
12
+ import { createAbortError } from "../ipc/service.js";
12
13
  import { DataDirLock } from "../storage/data-dir-lock.js";
13
14
  import { DefaultActionCatalog, DefaultPackageGraph, PackageGraphBuilder, resolveAction, } from "../catalog/index.js";
15
+ import { parseActionRef } from "../catalog/resolve-action.js";
14
16
  import { buildRuntimeError, collectPackageInfos, describeActionAcrossRuntimes, describeVisiblePlaybook, isRootCallVisible, listVisiblePlaybooks, packageNotFoundMessage, parseRefLoose, resolveProjectRoot, } from "./routing.js";
15
- import { ServiceError } from "../service/types.js";
16
17
  import { CAPABILITY_UNAVAILABLE } from "../errors.js";
17
18
  import { createGlobalStorage, isSecretConfigKey } from "../storage/index.js";
18
- import { filterByIntent } from "../filter/index.js";
19
+ import { filterByIntent } from "../utils/intent.js";
19
20
  function isPackageRuntime(item) {
20
21
  return (typeof item === "object" &&
21
22
  item !== null &&
@@ -24,6 +25,36 @@ function isPackageRuntime(item) {
24
25
  "runAction" in item &&
25
26
  typeof item.runAction === "function");
26
27
  }
28
+ /** 并发解析运行记录归属包(公共端口查询,不新建执行服务) */
29
+ async function findRunOwnerRuntime(runtimes, runId) {
30
+ const results = await Promise.all(runtimes.map(async (rt) => ({ rt, record: await rt.getRun(runId).catch(() => undefined) })));
31
+ return results.find((r) => r.record !== undefined)?.rt;
32
+ }
33
+ /**
34
+ * 包装异步操作以响应 AbortSignal 取消信号。
35
+ * 若信号已中止立即抛出 AbortError,否则挂载一次性中止监听并在操作完成时注销。
36
+ */
37
+ function withAbortSignal(op, signal) {
38
+ if (signal?.aborted) {
39
+ throw createAbortError(signal.reason);
40
+ }
41
+ if (!signal) {
42
+ return op();
43
+ }
44
+ return new Promise((resolve, reject) => {
45
+ const onAbort = () => {
46
+ reject(createAbortError(signal.reason));
47
+ };
48
+ signal.addEventListener("abort", onAbort, { once: true });
49
+ op().then((res) => {
50
+ signal.removeEventListener("abort", onAbort);
51
+ resolve(res);
52
+ }, (err) => {
53
+ signal.removeEventListener("abort", onAbort);
54
+ reject(err);
55
+ });
56
+ });
57
+ }
27
58
  export class DefaultActionDockHost {
28
59
  hostSessionId;
29
60
  options;
@@ -42,7 +73,30 @@ export class DefaultActionDockHost {
42
73
  graph;
43
74
  catalog;
44
75
  globalStorage;
76
+ initPromise;
77
+ isInitialized = false;
78
+ discovery;
79
+ execution;
80
+ runs;
81
+ events;
82
+ management;
83
+ /** 自引用 host 属性,向后兼容服务实例字段访问 */
84
+ get host() {
85
+ return this;
86
+ }
87
+ /**
88
+ * @deprecated 直接使用 new DefaultActionDockHost() 构造已弃用,请迁移至 createActionDockHost() 异步工厂函数以完成完整的生命周期初始化。
89
+ */
45
90
  constructor(options = {}, internalOptions) {
91
+ if (!internalOptions?.fromFactory) {
92
+ const warnMsg = "[Host] Direct instantiation via new DefaultActionDockHost() is @deprecated and only performs minimal object assembly. Use createActionDockHost() for full lifecycle initialization.";
93
+ if (options.logger?.warn) {
94
+ options.logger.warn(warnMsg);
95
+ }
96
+ else {
97
+ console.warn(warnMsg);
98
+ }
99
+ }
46
100
  this.hostSessionId = randomUUID();
47
101
  this.options = options;
48
102
  this.maxCallDepth = options.maxCallDepth ?? 16;
@@ -56,92 +110,254 @@ export class DefaultActionDockHost {
56
110
  this.recoverOrphans = options.recoverOrphans !== false;
57
111
  this.graph = new DefaultPackageGraph(new Map());
58
112
  this.catalog = new DefaultActionCatalog(this.graph);
59
- // 当指定非内存 dataDir 时获取排他目录锁,防止并发冲突
60
- if (options.dataDir && !options.inMemory) {
61
- this.dataDirLock = DataDirLock.acquire(options.dataDir, {
62
- hostSessionId: this.hostSessionId,
63
- });
64
- }
65
- if (internalOptions?.deferInit) {
66
- return;
113
+ const self = this;
114
+ this.discovery = {
115
+ async listPackages() {
116
+ await self.ensureInitialized();
117
+ return self.info();
118
+ },
119
+ async listActions(opts) {
120
+ await self.ensureInitialized();
121
+ return self.listActions(opts);
122
+ },
123
+ async describeAction(ref) {
124
+ await self.ensureInitialized();
125
+ return self.describeAction(ref);
126
+ },
127
+ async listPlaybooks(opts) {
128
+ await self.ensureInitialized();
129
+ return self.listPlaybooks(opts);
130
+ },
131
+ async describePlaybook(id) {
132
+ await self.ensureInitialized();
133
+ return self.describePlaybook(id);
134
+ },
135
+ };
136
+ this.execution = {
137
+ async run(ref, input, opts) {
138
+ await self.ensureInitialized();
139
+ const actionRef = typeof ref === "string" ? parseActionRef(ref) : ref;
140
+ const cleanOpts = {
141
+ signal: opts?.signal,
142
+ timeoutMs: opts?.timeoutMs,
143
+ config: opts?.config,
144
+ requestId: opts?.requestId,
145
+ };
146
+ return self.runAction(actionRef, (input ?? {}), cleanOpts);
147
+ },
148
+ async start(ref, input, opts) {
149
+ await self.ensureInitialized();
150
+ const actionRef = typeof ref === "string" ? parseActionRef(ref) : ref;
151
+ const cleanOpts = {
152
+ signal: opts?.signal,
153
+ timeoutMs: opts?.timeoutMs,
154
+ config: opts?.config,
155
+ requestId: opts?.requestId,
156
+ };
157
+ return self.startAction(actionRef, (input ?? {}), cleanOpts);
158
+ },
159
+ };
160
+ this.runs = {
161
+ async list(query, options) {
162
+ await self.ensureInitialized();
163
+ const effectiveSignal = options?.signal ?? query?.signal;
164
+ return withAbortSignal(() => self.listRuns(query, effectiveSignal), effectiveSignal);
165
+ },
166
+ async count(query, options) {
167
+ await self.ensureInitialized();
168
+ const effectiveSignal = options?.signal ?? query?.signal;
169
+ return withAbortSignal(() => self.countRuns(query, effectiveSignal), effectiveSignal);
170
+ },
171
+ async get(runId, options) {
172
+ await self.ensureInitialized();
173
+ const effectiveSignal = options?.signal;
174
+ return withAbortSignal(() => self.getRun(runId, effectiveSignal), effectiveSignal);
175
+ },
176
+ async cancel(runId, reason) {
177
+ await self.ensureInitialized();
178
+ return self.cancelRun(runId, reason);
179
+ },
180
+ async clear(opts) {
181
+ await self.ensureInitialized();
182
+ return self.clearRuns(opts);
183
+ },
184
+ async cleanExpired(policy) {
185
+ await self.ensureInitialized();
186
+ return self.cleanExpiredRuns(policy);
187
+ },
188
+ };
189
+ const eventsFn = ((runId, opts) => {
190
+ return (async function* () {
191
+ await self.ensureInitialized();
192
+ yield* self.subscribeEvents(runId, opts);
193
+ })();
194
+ });
195
+ eventsFn.events = (runId, opts) => {
196
+ return (async function* () {
197
+ await self.ensureInitialized();
198
+ yield* self.subscribeEvents(runId, opts);
199
+ })();
200
+ };
201
+ this.events = eventsFn;
202
+ if (options.enableManagement !== false) {
203
+ this.management = {
204
+ config: {
205
+ async get(packageId, key) {
206
+ await self.ensureInitialized();
207
+ return self.getConfig(packageId, key);
208
+ },
209
+ async set(packageId, key, value) {
210
+ await self.ensureInitialized();
211
+ return self.setConfig(packageId, key, value);
212
+ },
213
+ async delete(packageId, key) {
214
+ await self.ensureInitialized();
215
+ return self.deleteConfig(packageId, key);
216
+ },
217
+ async list(packageId) {
218
+ await self.ensureInitialized();
219
+ return self.listConfig(packageId);
220
+ },
221
+ },
222
+ state: {
223
+ async get(packageId, actionId, key, opts) {
224
+ await self.ensureInitialized();
225
+ return self.getState(packageId, actionId, key, opts);
226
+ },
227
+ async set(packageId, actionId, key, value, opts) {
228
+ await self.ensureInitialized();
229
+ return self.setState(packageId, actionId, key, value, opts);
230
+ },
231
+ async delete(packageId, actionId, key, opts) {
232
+ await self.ensureInitialized();
233
+ return self.deleteState(packageId, actionId, key, opts);
234
+ },
235
+ async list(packageId, actionId, opts) {
236
+ await self.ensureInitialized();
237
+ return self.listStateKeys(packageId, actionId, opts);
238
+ },
239
+ async clear(packageId, actionId, opts) {
240
+ await self.ensureInitialized();
241
+ return self.clearState(packageId, actionId, opts);
242
+ },
243
+ async listEntries(packageId, opts) {
244
+ await self.ensureInitialized();
245
+ return self.listStateEntries(packageId, opts);
246
+ },
247
+ },
248
+ };
67
249
  }
250
+ }
251
+ /**
252
+ * 静态工厂方法:创建并初始化 ActionDockHost 宿主容器。
253
+ */
254
+ static async create(options = {}) {
255
+ let createdHost;
68
256
  try {
69
- this.initializeSync();
257
+ createdHost = new DefaultActionDockHost(options, { fromFactory: true });
258
+ await createdHost.ensureInitialized();
259
+ return createdHost;
70
260
  }
71
261
  catch (err) {
72
- this.rollbackSync();
262
+ if (createdHost) {
263
+ try {
264
+ await createdHost.rollbackInitialization();
265
+ }
266
+ catch {
267
+ // 忽略宿主回滚异常,确保抛出原始异常
268
+ }
269
+ }
73
270
  throw err;
74
271
  }
75
272
  }
76
- initializeSync() {
77
- // 阶段一:注册显式传入的 packages 列表
78
- this.registerExplicitPackages(this.options);
79
- // 阶段二:自动加载当前工程(若发现工程根目录且未显式禁用)
80
- if (this.options.autoLoadCurrentProject !== false) {
81
- this.loadCurrentProject(this.options);
273
+ /**
274
+ * 确保宿主容器已完成异步生命周期初始化。
275
+ * 当通过 ActionDockHost.create() / createActionDockHost() 创建时在工厂阶段预先就绪;
276
+ * 当直接通过 new DefaultActionDockHost() 构造时,在首次异步调用时惰性触发加载。
277
+ */
278
+ async ensureInitialized() {
279
+ if (this.isInitialized) {
280
+ return;
82
281
  }
83
- // 阶段三:扫描已软链接的外部包并注册至 Host
84
- if (this.options.scanLinkedPackages) {
85
- this.registerLinkedPackages(this.options);
282
+ if (this.isClosed) {
283
+ throw new ActionDockError(SERVICE_CLOSED, "ActionDockHost is closed");
86
284
  }
87
- this.rebuildGraphAndCatalog();
285
+ if (!this.initPromise) {
286
+ this.initPromise = this.performInitialization();
287
+ }
288
+ await this.initPromise;
88
289
  }
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
- }
290
+ async performInitialization() {
291
+ try {
292
+ // 当指定非内存 dataDir 且以持有者身份打开时获取排他目录锁,防止并发冲突;
293
+ // 旁观 Host(recoverOrphans: false)不获取执行宿主的排他锁,也不创建数据目录
294
+ if (this.options.dataDir &&
295
+ !this.options.inMemory &&
296
+ this.recoverOrphans &&
297
+ !this.dataDirLock) {
298
+ this.dataDirLock = DataDirLock.acquire(this.options.dataDir, {
299
+ hostSessionId: this.hostSessionId,
300
+ });
301
+ }
302
+ // 阶段一:注册显式传入的 packages 列表(仅建立身份与路由,不打开资源)
303
+ this.registerExplicitPackages(this.options);
304
+ // 阶段二:自动加载当前工程(若发现工程根目录且未显式禁用)
305
+ if (this.options.autoLoadCurrentProject !== false) {
306
+ await this.loadCurrentProject(this.options);
307
+ }
308
+ // 阶段三:扫描已软链接的外部包并注册至 Host(仅建立身份与路由)
309
+ if (this.options.scanLinkedPackages) {
310
+ this.registerLinkedPackages(this.options);
103
311
  }
104
- this.loadCurrentProject(this.options);
312
+ this.rebuildGraphAndCatalog();
313
+ // 阶段四:持有者身份在工厂返回前显式预热全部内部包的存储与运行记录恢复;
314
+ // 旁观身份(recoverOrphans: false)跳过预热,首次持久化调用时才按需打开
315
+ if (this.recoverOrphans) {
316
+ await this.prewarmManagedRuntimes();
317
+ }
318
+ this.isInitialized = true;
105
319
  }
106
- // 阶段三:扫描已软链接的外部包并注册至 Host
107
- if (this.options.scanLinkedPackages) {
108
- this.registerLinkedPackages(this.options);
320
+ catch (err) {
321
+ this.initPromise = undefined;
322
+ try {
323
+ await this.rollbackInitialization();
324
+ }
325
+ catch {
326
+ // 吞没回滚自身的副错误以确保向上抛出原始的初始化异常
327
+ }
328
+ throw err;
109
329
  }
110
- this.rebuildGraphAndCatalog();
111
330
  }
112
331
  /**
113
- * 安全回滚初始化失败时所占用的资源。
114
- *
115
- * 同步语境(构造函数路径)下对 runtime.close() 采用 fire-and-forget:
116
- * 仅附加吞没异常的 catch 处理器,不 await,避免构造链路引入异步;
117
- * 异步语境下逐个 await 全部 runtime.close() 后再依次释放全局存储与目录锁。
332
+ * 持有者预热:等待内部创建的全部包完成存储准备(含运行记录恢复),
333
+ * 并打开共享全局存储。预热的是存储,不是执行服务、进程接口或业务模块;
334
+ * 任一包准备失败按既有初始化失败时机整体回滚。
335
+ * 外部注入的 Runtime 保持既有生命周期约定,不在此预热。
118
336
  */
119
- async rollbackResources(sync) {
120
- if (sync) {
121
- // 安全关闭宿主管理的所有 Runtime 实例(同步语境 fire-and-forget)
122
- for (const runtime of this.runtimes.values()) {
123
- try {
124
- const closePromise = runtime.close();
125
- if (closePromise && typeof closePromise.catch === "function") {
126
- closePromise.catch(() => { });
127
- }
128
- }
129
- catch {
130
- // 忽略 runtime 关闭异常
131
- }
132
- }
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
- }));
337
+ async prewarmManagedRuntimes() {
338
+ const managed = Array.from(this.runtimes.values()).filter((rt) => rt instanceof DefaultPackageRuntime);
339
+ if (managed.length === 0) {
340
+ return;
144
341
  }
342
+ await Promise.all(managed.map((rt) => rt.prepareResources()));
343
+ // 持有者在返回前打开共享全局存储,保留对全局库存储错误的提前发现能力
344
+ this.getGlobalStorage();
345
+ }
346
+ /**
347
+ * 异步安全回滚初始化失败时所占用的资源。
348
+ */
349
+ async rollbackInitialization() {
350
+ this.isInitialized = false;
351
+ this.initPromise = undefined;
352
+ const allRuntimes = Array.from(this.runtimes.values());
353
+ await Promise.all(allRuntimes.map(async (runtime) => {
354
+ try {
355
+ await runtime.close();
356
+ }
357
+ catch {
358
+ // 忽略 Runtime 关闭异常
359
+ }
360
+ }));
145
361
  this.runtimes.clear();
146
362
  try {
147
363
  this.globalStorage?.close();
@@ -159,15 +375,6 @@ export class DefaultActionDockHost {
159
375
  this.dataDirLock = undefined;
160
376
  this.isClosed = true;
161
377
  }
162
- /**
163
- * 异步安全回滚初始化失败时所占用的资源。
164
- */
165
- async rollbackInitialization() {
166
- await this.rollbackResources(false);
167
- }
168
- rollbackSync() {
169
- void this.rollbackResources(true);
170
- }
171
378
  /**
172
379
  * 阶段函数:注册显式传入的 packages 列表(PackageRuntimeOptions 配置或包目录物理路径)。
173
380
  */
@@ -184,7 +391,8 @@ export class DefaultActionDockHost {
184
391
  const runtime = new DefaultPackageRuntime({
185
392
  ...packageOptions,
186
393
  hostSessionId: this.hostSessionId,
187
- globalStorage: packageOptions.globalStorage ?? this.getGlobalStorage(),
394
+ globalStorage: packageOptions.globalStorage,
395
+ globalStorageProvider: () => this.getGlobalStorage(),
188
396
  platform: packageOptions.platform ?? options.platform,
189
397
  inMemory: packageOptions.inMemory ?? options.inMemory,
190
398
  customHome: packageOptions.customHome ?? options.customHome,
@@ -210,21 +418,22 @@ export class DefaultActionDockHost {
210
418
  * - 自动探测场景(未传 projectRoot,由 findProjectRoot 发现)失败时记录实例诊断
211
419
  * failedAutoLoad 并在创建时输出单行告警,保持宿主其余能力可用但绝不无声。
212
420
  */
213
- loadCurrentProject(options) {
421
+ async loadCurrentProject(options) {
214
422
  const explicitRoot = options.projectRoot;
215
423
  const root = resolveProjectRoot(options, () => findProjectRoot());
216
424
  if (!root) {
217
425
  return;
218
426
  }
219
427
  if (isProjectLockHeld(root)) {
220
- const err = new Error("PROJECT_BUSY: Project directory is locked by another active process holding project.lock");
221
- err.code = PROJECT_BUSY;
222
- throw err;
428
+ throw new ActionDockError(PROJECT_BUSY, "PROJECT_BUSY: Project directory is locked by another active process holding project.lock");
223
429
  }
224
430
  if (hasPendingTransactions(root)) {
225
- const err = new Error(`PROJECT_RECOVERY_REQUIRED: Project directory '${root}' has pending transactions requiring recovery; use async createActionDockHost() to recover automatically`);
226
- err.code = PROJECT_RECOVERY_REQUIRED;
227
- throw err;
431
+ // 旁观路径(recoverOrphans: false)不自动恢复待恢复事务:
432
+ // 不触发依赖安装或清单回滚,直接报告恢复需求,由持有者入口完成恢复
433
+ if (!this.recoverOrphans) {
434
+ throw new ActionDockError(PROJECT_RECOVERY_REQUIRED, `PROJECT_RECOVERY_REQUIRED: Project at '${root}' has pending transactions requiring recovery. Run an owner command (e.g. 'ad add' or 'ad run') to complete recovery before querying.`);
435
+ }
436
+ await recoverPendingTransactions(root, { frozenInstall: true });
228
437
  }
229
438
  try {
230
439
  const config = loadProjectConfig(root);
@@ -249,7 +458,7 @@ export class DefaultActionDockHost {
249
458
  projectConfig: pkg.manifest,
250
459
  identity: pkg.identity,
251
460
  hostSessionId: this.hostSessionId,
252
- globalStorage: this.getGlobalStorage(),
461
+ globalStorageProvider: () => this.getGlobalStorage(),
253
462
  platform: options.platform,
254
463
  inMemory: options.inMemory,
255
464
  customHome: options.customHome,
@@ -304,7 +513,7 @@ export class DefaultActionDockHost {
304
513
  packageRoot: linked.path,
305
514
  projectConfig: config,
306
515
  hostSessionId: this.hostSessionId,
307
- globalStorage: this.getGlobalStorage(),
516
+ globalStorageProvider: () => this.getGlobalStorage(),
308
517
  platform: options.platform,
309
518
  inMemory: options.inMemory,
310
519
  customHome: options.customHome,
@@ -368,7 +577,9 @@ export class DefaultActionDockHost {
368
577
  declaredUses,
369
578
  }, { packageId: targetPackageId, actionId: targetActionId }, this.graph);
370
579
  if (authErr) {
371
- throw new ActionDockError(authErr.code, authErr.message, authErr.details);
580
+ const hint = authErr.details?.hint;
581
+ const msg = hint ? `${authErr.message}\n${hint}` : authErr.message;
582
+ throw new ActionDockError(authErr.code, msg, authErr.details, undefined, hint);
372
583
  }
373
584
  }
374
585
  // 调用嵌套深度限制校验
@@ -426,7 +637,7 @@ export class DefaultActionDockHost {
426
637
  };
427
638
  const ticket = await targetRuntime.startInvocation(targetActionId, childInput, subInvocationContext);
428
639
  if (!ticket.result) {
429
- throw new Error(`Execution ticket for run '${ticket.runId}' has no result Promise`);
640
+ throw new ActionDockError(EXECUTION_FAILED, `Execution ticket for run '${ticket.runId}' has no result Promise`);
430
641
  }
431
642
  const result = await ticket.result;
432
643
  if (!result.ok) {
@@ -480,7 +691,7 @@ export class DefaultActionDockHost {
480
691
  if (existing === runtime) {
481
692
  return;
482
693
  }
483
- throw new Error(`Package ID conflict: package '${runtime.packageId}' is already registered in host`);
694
+ throw new ActionDockError(PACKAGE_ID_CONFLICT, `Package ID conflict: package '${runtime.packageId}' is already registered in host`);
484
695
  }
485
696
  this.runtimes.set(runtime.packageId, runtime);
486
697
  if (isPublic) {
@@ -488,27 +699,18 @@ export class DefaultActionDockHost {
488
699
  }
489
700
  this.bindRuntime(runtime);
490
701
  this.rebuildGraphAndCatalog();
491
- // 接管与恢复:仅持有者身份的 Host 自动将死亡会话或遗留非终态运行收敛为 interrupted;
492
- // 旁观查询 Host(CLI state/runs/config 命令)跳过本步骤,不动其他进程的在途记录
493
- if (this.recoverOrphans) {
494
- if (typeof runtime.recoverDeadSessionRuns === "function") {
495
- try {
496
- runtime.recoverDeadSessionRuns(this.hostSessionId);
497
- }
498
- catch (err) {
499
- // 单包恢复失败不阻断整体接管流程,但必须可观测
500
- console.warn(`[actiondock] recoverDeadSessionRuns failed for package '${runtime.packageId}': ${err instanceof Error ? err.message : String(err)}`);
501
- }
502
- }
503
- }
702
+ // 注册阶段只建立身份与路由;运行记录恢复收敛到包存储准备路径(持有者工厂预热时统一执行),
703
+ // 旁观 Host 注册不触碰其他进程的在途记录
504
704
  }
505
705
  registerRuntime(runtime) {
506
706
  this.registerRuntimeInternal(runtime, true);
507
707
  }
508
708
  async info() {
709
+ await this.ensureInitialized();
509
710
  return collectPackageInfos(this.listRuntimes());
510
711
  }
511
712
  async listActions(options) {
713
+ await this.ensureInitialized();
512
714
  // 外层完成可见性筛选与跨包限定名改写后,统一委托共享过滤函数完成检索
513
715
  const results = [];
514
716
  const runtimes = this.listRuntimes();
@@ -531,9 +733,11 @@ export class DefaultActionDockHost {
531
733
  return applyActionSummaryFilters(results, options);
532
734
  }
533
735
  async describeAction(ref) {
736
+ await this.ensureInitialized();
534
737
  return describeActionAcrossRuntimes(ref, this.listRuntimes(), this.visibility(), this.failedLinkedPackages, this.catalog, this.graph);
535
738
  }
536
739
  async listPlaybooks(options) {
740
+ await this.ensureInitialized();
537
741
  let pbs = await listVisiblePlaybooks(this.listRuntimes(), this.visibility());
538
742
  if (options?.package) {
539
743
  pbs = pbs.filter((p) => p.packageId === options.package);
@@ -544,18 +748,21 @@ export class DefaultActionDockHost {
544
748
  return pbs;
545
749
  }
546
750
  async describePlaybook(id) {
751
+ await this.ensureInitialized();
547
752
  return describeVisiblePlaybook(this.listRuntimes(), id, this.visibility());
548
753
  }
549
754
  async runAction(ref, input, options = {}) {
755
+ await this.ensureInitialized();
550
756
  const ticket = await this.startAction(ref, input, options);
551
757
  if (!ticket.result) {
552
- throw new Error(`Execution ticket for run '${ticket.runId}' has no result Promise`);
758
+ throw new ActionDockError(EXECUTION_FAILED, `Execution ticket for run '${ticket.runId}' has no result Promise`);
553
759
  }
554
760
  return ticket.result;
555
761
  }
556
762
  async startAction(ref, input, options = {}) {
763
+ await this.ensureInitialized();
557
764
  if (this.isClosed) {
558
- throw new Error("ActionDockHost is closed: new tasks rejected");
765
+ throw new ActionDockError(SERVICE_CLOSED, "ActionDockHost is closed: new tasks rejected");
559
766
  }
560
767
  // - 统一基于纯领域 resolveAction 解析目标包与 Action 动作标识
561
768
  let resolved;
@@ -641,21 +848,151 @@ export class DefaultActionDockHost {
641
848
  // 调度目标包执行服务
642
849
  return targetRuntime.startInvocation(targetActionId, input, rootContext);
643
850
  }
644
- async getRun(runId) {
851
+ async getRun(runId, signal) {
852
+ await this.ensureInitialized();
853
+ if (signal?.aborted) {
854
+ throw createAbortError(signal.reason);
855
+ }
645
856
  for (const runtime of this.listRuntimes()) {
857
+ if (signal?.aborted) {
858
+ throw createAbortError(signal.reason);
859
+ }
646
860
  const record = await runtime.getRun(runId);
861
+ if (signal?.aborted) {
862
+ throw createAbortError(signal.reason);
863
+ }
647
864
  if (record)
648
865
  return record;
649
866
  }
650
867
  return undefined;
651
868
  }
652
- async listRuns(query) {
869
+ async listRuns(query, signal) {
870
+ await this.ensureInitialized();
871
+ const effectiveSignal = signal ?? query?.signal;
872
+ if (effectiveSignal?.aborted) {
873
+ throw createAbortError(effectiveSignal.reason);
874
+ }
653
875
  const runtimes = query?.packageId
654
876
  ? [this.getRuntime(query.packageId)].filter(Boolean)
655
- : this.listRuntimes();
877
+ : query?.packageAllowlist && query.packageAllowlist.length > 0
878
+ ? query.packageAllowlist.map((id) => this.getRuntime(id)).filter(Boolean)
879
+ : this.listRuntimes();
880
+ // 当存在 intent 过滤时:分批读取并在内存中累积命中记录,先过滤后分页
881
+ if (query?.intent) {
882
+ const intentStr = query.intent;
883
+ const matchedRecords = [];
884
+ const BATCH_SIZE = 100;
885
+ const targetLimit = query.limit;
886
+ const targetOffset = query.offset ?? 0;
887
+ const neededCount = targetLimit !== undefined ? targetOffset + targetLimit : Infinity;
888
+ if (runtimes.length === 1) {
889
+ const runtime = runtimes[0];
890
+ let batchOffset = 0;
891
+ while (true) {
892
+ if (effectiveSignal?.aborted) {
893
+ throw createAbortError(effectiveSignal.reason);
894
+ }
895
+ const batch = await runtime.listRuns({
896
+ actionId: query.actionId,
897
+ status: query.status,
898
+ requestIds: query.requestIds,
899
+ limit: BATCH_SIZE,
900
+ offset: batchOffset,
901
+ signal: effectiveSignal,
902
+ });
903
+ if (effectiveSignal?.aborted) {
904
+ throw createAbortError(effectiveSignal.reason);
905
+ }
906
+ if (batch.length === 0)
907
+ break;
908
+ batchOffset += batch.length;
909
+ const normalized = batch.map((r) => ({
910
+ ...r,
911
+ packageId: r.packageId || runtime.packageId,
912
+ }));
913
+ const filtered = filterByIntent(normalized, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
914
+ matchedRecords.push(...filtered);
915
+ if (matchedRecords.length >= neededCount) {
916
+ break;
917
+ }
918
+ if (batch.length < BATCH_SIZE) {
919
+ break;
920
+ }
921
+ }
922
+ }
923
+ else {
924
+ for (const runtime of runtimes) {
925
+ if (effectiveSignal?.aborted) {
926
+ throw createAbortError(effectiveSignal.reason);
927
+ }
928
+ let batchOffset = 0;
929
+ while (true) {
930
+ if (effectiveSignal?.aborted) {
931
+ throw createAbortError(effectiveSignal.reason);
932
+ }
933
+ const batch = await runtime.listRuns({
934
+ actionId: query.actionId,
935
+ status: query.status,
936
+ requestIds: query.requestIds,
937
+ limit: BATCH_SIZE,
938
+ offset: batchOffset,
939
+ signal: effectiveSignal,
940
+ });
941
+ if (effectiveSignal?.aborted) {
942
+ throw createAbortError(effectiveSignal.reason);
943
+ }
944
+ if (batch.length === 0)
945
+ break;
946
+ batchOffset += batch.length;
947
+ const normalized = batch.map((r) => ({
948
+ ...r,
949
+ packageId: r.packageId || runtime.packageId,
950
+ }));
951
+ const filtered = filterByIntent(normalized, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
952
+ matchedRecords.push(...filtered);
953
+ if (batch.length < BATCH_SIZE) {
954
+ break;
955
+ }
956
+ }
957
+ }
958
+ matchedRecords.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
959
+ }
960
+ const end = targetLimit !== undefined ? targetOffset + targetLimit : undefined;
961
+ return matchedRecords.slice(targetOffset, end);
962
+ }
963
+ // 当不存在 intent 过滤时:
964
+ if (runtimes.length === 1) {
965
+ if (effectiveSignal?.aborted) {
966
+ throw createAbortError(effectiveSignal.reason);
967
+ }
968
+ const runtime = runtimes[0];
969
+ const recs = await runtime.listRuns(query ? { ...query, signal: effectiveSignal } : { signal: effectiveSignal });
970
+ if (effectiveSignal?.aborted) {
971
+ throw createAbortError(effectiveSignal.reason);
972
+ }
973
+ return recs.map((r) => ({
974
+ ...r,
975
+ packageId: r.packageId || runtime.packageId,
976
+ }));
977
+ }
978
+ // 跨包联合查询:下推 limit/offset 或合并排序
656
979
  const records = [];
980
+ const fetchLimit = query?.limit !== undefined
981
+ ? (query.offset ?? 0) + query.limit
982
+ : undefined;
657
983
  for (const runtime of runtimes) {
658
- const recs = await runtime.listRuns(query);
984
+ if (effectiveSignal?.aborted) {
985
+ throw createAbortError(effectiveSignal.reason);
986
+ }
987
+ const recs = await runtime.listRuns({
988
+ ...query,
989
+ offset: 0,
990
+ limit: fetchLimit,
991
+ signal: effectiveSignal,
992
+ });
993
+ if (effectiveSignal?.aborted) {
994
+ throw createAbortError(effectiveSignal.reason);
995
+ }
659
996
  for (const r of recs) {
660
997
  records.push({
661
998
  ...r,
@@ -664,16 +1001,87 @@ export class DefaultActionDockHost {
664
1001
  }
665
1002
  }
666
1003
  records.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
667
- let result = records;
1004
+ const start = query?.offset ?? 0;
1005
+ const end = query?.limit !== undefined ? start + query.limit : undefined;
1006
+ return records.slice(start, end);
1007
+ }
1008
+ async countRuns(query, signal) {
1009
+ await this.ensureInitialized();
1010
+ const effectiveSignal = signal ?? query?.signal;
1011
+ if (effectiveSignal?.aborted) {
1012
+ throw createAbortError(effectiveSignal.reason);
1013
+ }
1014
+ const runtimes = query?.packageId
1015
+ ? [this.getRuntime(query.packageId)].filter(Boolean)
1016
+ : query?.packageAllowlist && query.packageAllowlist.length > 0
1017
+ ? query.packageAllowlist.map((id) => this.getRuntime(id)).filter(Boolean)
1018
+ : this.listRuntimes();
668
1019
  if (query?.intent) {
669
- result = filterByIntent(result, query.intent, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
1020
+ const intentStr = query.intent;
1021
+ let count = 0;
1022
+ const BATCH_SIZE = 100;
1023
+ for (const runtime of runtimes) {
1024
+ if (effectiveSignal?.aborted) {
1025
+ throw createAbortError(effectiveSignal.reason);
1026
+ }
1027
+ let batchOffset = 0;
1028
+ while (true) {
1029
+ if (effectiveSignal?.aborted) {
1030
+ throw createAbortError(effectiveSignal.reason);
1031
+ }
1032
+ const batch = await runtime.listRuns({
1033
+ actionId: query.actionId,
1034
+ status: query.status,
1035
+ requestIds: query.requestIds,
1036
+ limit: BATCH_SIZE,
1037
+ offset: batchOffset,
1038
+ signal: effectiveSignal,
1039
+ });
1040
+ if (effectiveSignal?.aborted) {
1041
+ throw createAbortError(effectiveSignal.reason);
1042
+ }
1043
+ if (batch.length === 0)
1044
+ break;
1045
+ batchOffset += batch.length;
1046
+ const normalized = batch.map((r) => ({
1047
+ ...r,
1048
+ packageId: r.packageId || runtime.packageId,
1049
+ }));
1050
+ const filtered = filterByIntent(normalized, intentStr, [(r) => r.id, (r) => r.actionId, (r) => r.status, (r) => r.packageId], false);
1051
+ count += filtered.length;
1052
+ if (batch.length < BATCH_SIZE) {
1053
+ break;
1054
+ }
1055
+ }
1056
+ }
1057
+ return count;
670
1058
  }
671
- if (query?.limit && result.length > query.limit) {
672
- result.length = query.limit;
1059
+ let total = 0;
1060
+ for (const runtime of runtimes) {
1061
+ if (effectiveSignal?.aborted) {
1062
+ throw createAbortError(effectiveSignal.reason);
1063
+ }
1064
+ if (typeof runtime.countRuns === "function") {
1065
+ total += await runtime.countRuns(query ? { ...query, signal: effectiveSignal } : { signal: effectiveSignal });
1066
+ }
1067
+ else {
1068
+ const all = await runtime.listRuns({
1069
+ actionId: query?.actionId,
1070
+ status: query?.status,
1071
+ requestIds: query?.requestIds,
1072
+ limit: 100_000,
1073
+ signal: effectiveSignal,
1074
+ });
1075
+ if (effectiveSignal?.aborted) {
1076
+ throw createAbortError(effectiveSignal.reason);
1077
+ }
1078
+ total += all.length;
1079
+ }
673
1080
  }
674
- return result;
1081
+ return total;
675
1082
  }
676
1083
  async clearRuns(options) {
1084
+ await this.ensureInitialized();
677
1085
  const runtimes = options?.packageId
678
1086
  ? [this.getRuntime(options.packageId)].filter(Boolean)
679
1087
  : this.listRuntimes();
@@ -685,6 +1093,7 @@ export class DefaultActionDockHost {
685
1093
  return total;
686
1094
  }
687
1095
  async cleanExpiredRuns(policy) {
1096
+ await this.ensureInitialized();
688
1097
  let total = 0;
689
1098
  for (const runtime of this.listRuntimes()) {
690
1099
  if (typeof runtime.cleanExpiredRuns === "function") {
@@ -695,6 +1104,7 @@ export class DefaultActionDockHost {
695
1104
  return total;
696
1105
  }
697
1106
  async cancelRun(runId, reason) {
1107
+ await this.ensureInitialized();
698
1108
  for (const runtime of this.listRuntimes()) {
699
1109
  const res = await runtime.cancelRun(runId, reason);
700
1110
  if (res.outcome !== "not_found") {
@@ -703,27 +1113,38 @@ export class DefaultActionDockHost {
703
1113
  }
704
1114
  return { outcome: "not_found", runId };
705
1115
  }
706
- events(runId, options) {
707
- for (const runtime of this.listRuntimes()) {
708
- const storageRecord = runtime.storage?.getRun?.(runId);
709
- if (storageRecord) {
710
- return runtime.events(runId, options);
1116
+ subscribeEvents(runId, options) {
1117
+ const self = this;
1118
+ // 事件订阅可异步解析包归属:逐包并发查询运行记录(公共端口,不新建执行服务),
1119
+ // 命中则使用该包事件源,全部未命中回退 Host 事件源
1120
+ return (async function* () {
1121
+ const runtimes = self.listRuntimes();
1122
+ const owner = await findRunOwnerRuntime(runtimes, runId);
1123
+ if (owner) {
1124
+ yield* owner.events(runId, options);
1125
+ return;
711
1126
  }
712
- }
713
- const firstRuntime = this.listRuntimes()[0];
714
- if (firstRuntime) {
715
- return firstRuntime.events(runId, options);
716
- }
717
- return this.eventSink.subscribe(runId, options);
1127
+ // Host 共享事件源作为统一回退(与执行服务装配使用同一 sink,完成事件不丢失)
1128
+ yield* self.eventSink.subscribe(runId, options);
1129
+ })();
718
1130
  }
719
1131
  getGlobalStorage() {
720
1132
  if (!this.globalStorage) {
721
- const hasInMemoryPackage = this.options.packages?.some((item) => typeof item !== "string" && !isPackageRuntime(item) && item.inMemory);
1133
+ const hasInMemoryPackage = this.options.packages?.some((item) => {
1134
+ if (typeof item === "string")
1135
+ return false;
1136
+ if (isPackageRuntime(item)) {
1137
+ return Boolean(item.inMemory || item.options?.inMemory);
1138
+ }
1139
+ return Boolean(item.inMemory);
1140
+ }) ||
1141
+ Array.from(this.runtimes.values()).some((rt) => Boolean(rt.inMemory || rt.options?.inMemory));
722
1142
  const inMemory = Boolean(this.options.inMemory || hasInMemoryPackage);
723
1143
  this.globalStorage =
724
1144
  this.options.platform?.storage?.createGlobalStorage?.({
725
1145
  dataDir: this.options.dataDir,
726
1146
  customHome: this.options.customHome,
1147
+ ...(inMemory ? { inMemory: true } : {}),
727
1148
  }) ??
728
1149
  createGlobalStorage({
729
1150
  dataDir: this.options.dataDir,
@@ -758,11 +1179,12 @@ export class DefaultActionDockHost {
758
1179
  requireRuntime(packageId) {
759
1180
  const runtime = this.resolveRuntime(packageId);
760
1181
  if (!runtime) {
761
- throw new Error(`Package '${packageId}' not found in host`);
1182
+ throw new ActionDockError(PACKAGE_NOT_FOUND, `Package '${packageId}' not found in host`);
762
1183
  }
763
1184
  return runtime;
764
1185
  }
765
1186
  async getConfig(packageId, key) {
1187
+ await this.ensureInitialized();
766
1188
  if (packageId === "global") {
767
1189
  const globalStorage = this.getGlobalStorage();
768
1190
  const val = globalStorage.getConfig(key);
@@ -780,6 +1202,7 @@ export class DefaultActionDockHost {
780
1202
  return this.requireRuntime(packageId).getConfig(key);
781
1203
  }
782
1204
  async setConfig(packageId, key, value) {
1205
+ await this.ensureInitialized();
783
1206
  if (packageId === "global") {
784
1207
  await this.getGlobalStorage().setConfig(key, value);
785
1208
  return;
@@ -787,12 +1210,14 @@ export class DefaultActionDockHost {
787
1210
  await this.requireRuntime(packageId).setConfig(key, value);
788
1211
  }
789
1212
  async deleteConfig(packageId, key) {
1213
+ await this.ensureInitialized();
790
1214
  if (packageId === "global") {
791
1215
  return await this.getGlobalStorage().deleteConfig(key);
792
1216
  }
793
1217
  return await this.requireRuntime(packageId).deleteConfig(key);
794
1218
  }
795
1219
  async listConfig(packageId) {
1220
+ await this.ensureInitialized();
796
1221
  if (packageId === "global") {
797
1222
  const globalStorage = this.getGlobalStorage();
798
1223
  const all = globalStorage.listConfig();
@@ -811,11 +1236,13 @@ export class DefaultActionDockHost {
811
1236
  return this.requireRuntime(packageId).listConfig();
812
1237
  }
813
1238
  async getState(packageId, actionId, key, options) {
1239
+ await this.ensureInitialized();
814
1240
  return this.requireRuntime(packageId).getState(actionId, key, options);
815
1241
  }
816
1242
  async setState(packageId, actionId, key, value, options) {
1243
+ await this.ensureInitialized();
817
1244
  const runtime = this.requireRuntime(packageId);
818
- if (actionId) {
1245
+ if (actionId !== undefined) {
819
1246
  await runtime.setActionState(actionId, key, value, options);
820
1247
  }
821
1248
  else {
@@ -823,18 +1250,22 @@ export class DefaultActionDockHost {
823
1250
  }
824
1251
  }
825
1252
  async deleteState(packageId, actionId, key, options) {
1253
+ await this.ensureInitialized();
826
1254
  return this.requireRuntime(packageId).deleteState(actionId, key, options);
827
1255
  }
828
1256
  async listStateKeys(packageId, actionId, options) {
1257
+ await this.ensureInitialized();
829
1258
  return this.requireRuntime(packageId).listStateKeys(actionId, options);
830
1259
  }
831
1260
  async clearState(packageId, actionId, options) {
1261
+ await this.ensureInitialized();
832
1262
  return this.requireRuntime(packageId).clearState(actionId, options);
833
1263
  }
834
1264
  async listStateEntries(packageId, options) {
1265
+ await this.ensureInitialized();
835
1266
  const runtime = this.resolveRuntime(packageId);
836
1267
  if (!runtime) {
837
- throw new ServiceError(CAPABILITY_UNAVAILABLE, `CAPABILITY_UNAVAILABLE: Package '${packageId}' not found in host`);
1268
+ throw new ActionDockError(CAPABILITY_UNAVAILABLE, `CAPABILITY_UNAVAILABLE: Package '${packageId}' not found in host`);
838
1269
  }
839
1270
  if (!runtime.listStateEntries) {
840
1271
  return [];
@@ -880,21 +1311,5 @@ export class DefaultActionDockHost {
880
1311
  * 工厂函数:创建并初始化 ActionDockHost 宿主容器。
881
1312
  */
882
1313
  export async function createActionDockHost(options = {}) {
883
- let createdHost;
884
- try {
885
- createdHost = new DefaultActionDockHost(options, { deferInit: true });
886
- await createdHost.initializeAsync();
887
- return createdHost;
888
- }
889
- catch (err) {
890
- if (createdHost) {
891
- try {
892
- await createdHost.rollbackInitialization();
893
- }
894
- catch {
895
- // 忽略宿主回滚异常,确保抛出原始异常
896
- }
897
- }
898
- throw err;
899
- }
1314
+ return DefaultActionDockHost.create(options);
900
1315
  }