@actiondock/core 2.10.0 → 2.12.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 (252) 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 +8 -14
  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 +20 -88
  28. package/dist/doctor/checks/runtime.d.ts +0 -4
  29. package/dist/doctor/checks/runtime.js +0 -28
  30. package/dist/doctor/doctor.js +1 -2
  31. package/dist/errors.d.ts +53 -3
  32. package/dist/errors.js +53 -3
  33. package/dist/execution/service.d.ts +55 -37
  34. package/dist/execution/service.js +601 -212
  35. package/dist/execution/types.d.ts +24 -5
  36. package/dist/host/host.d.ts +42 -22
  37. package/dist/host/host.js +562 -149
  38. package/dist/host/routing.js +6 -6
  39. package/dist/host/types.d.ts +25 -13
  40. package/dist/index.d.ts +9 -4
  41. package/dist/index.js +5 -2
  42. package/dist/input/advice.d.ts +0 -44
  43. package/dist/input/advice.js +7 -141
  44. package/dist/input/flat-materializer.js +1 -1
  45. package/dist/input/flat-parser.js +1 -1
  46. package/dist/input/index.d.ts +0 -1
  47. package/dist/input/index.js +0 -1
  48. package/dist/input/input-resolver.js +1 -1
  49. package/dist/input/validation-mapper.d.ts +1 -1
  50. package/dist/ipc/diagnostic.d.ts +8 -21
  51. package/dist/ipc/diagnostic.js +24 -57
  52. package/dist/ipc/host.js +21 -7
  53. package/dist/ipc/service.d.ts +14 -0
  54. package/dist/ipc/service.js +132 -26
  55. package/dist/ipc/types.d.ts +0 -4
  56. package/dist/package/index.d.ts +4 -5
  57. package/dist/package/index.js +1 -2
  58. package/dist/package/runtime.d.ts +94 -23
  59. package/dist/package/runtime.js +628 -274
  60. package/dist/package/static-index.d.ts +3 -6
  61. package/dist/package/static-index.js +11 -40
  62. package/dist/package/types.d.ts +19 -4
  63. package/dist/platform/index.d.ts +2 -1
  64. package/dist/platform/index.js +2 -1
  65. package/dist/platform/loader-hook.d.ts +32 -0
  66. package/dist/platform/loader-hook.js +90 -0
  67. package/dist/{node → platform}/module-loader.d.ts +13 -0
  68. package/dist/{node → platform}/module-loader.js +51 -4
  69. package/dist/platform/node.d.ts +19 -8
  70. package/dist/platform/node.js +50 -20
  71. package/dist/platform/types.d.ts +7 -47
  72. package/dist/process/context-process.d.ts +3 -18
  73. package/dist/process/context-process.js +3 -44
  74. package/dist/process/control.d.ts +21 -0
  75. package/dist/process/control.js +34 -0
  76. package/dist/process/converters.d.ts +23 -0
  77. package/dist/process/converters.js +92 -0
  78. package/dist/process/cursor.js +2 -22
  79. package/dist/process/{diagnostics-sink.d.ts → diagnostics.d.ts} +0 -7
  80. package/dist/process/{diagnostics-sink.js → diagnostics.js} +6 -8
  81. package/dist/process/index.d.ts +1 -3
  82. package/dist/process/index.js +1 -3
  83. package/dist/process/input-dispatcher.d.ts +4 -13
  84. package/dist/process/input-dispatcher.js +48 -69
  85. package/dist/process/managed-record.d.ts +3 -31
  86. package/dist/process/metadata-store.d.ts +0 -41
  87. package/dist/process/metadata-store.js +2 -341
  88. package/dist/process/process-driver.js +2 -2
  89. package/dist/process/process-manager.d.ts +8 -50
  90. package/dist/process/process-manager.js +28 -417
  91. package/dist/process/quotas.d.ts +14 -0
  92. package/dist/process/quotas.js +29 -0
  93. package/dist/process/run-executor.js +0 -1
  94. package/dist/process/terminal-output-cache.d.ts +20 -34
  95. package/dist/process/terminal-output-cache.js +43 -64
  96. package/dist/profile/index.d.ts +1 -2
  97. package/dist/profile/index.js +1 -2
  98. package/dist/profile/manager.d.ts +2 -4
  99. package/dist/profile/manager.js +10 -40
  100. package/dist/profile/types.d.ts +1 -17
  101. package/dist/project/digest.d.ts +5 -1
  102. package/dist/project/digest.js +81 -219
  103. package/dist/project/index.d.ts +4 -5
  104. package/dist/project/index.js +4 -5
  105. package/dist/project/init.js +2 -2
  106. package/dist/project/loader.d.ts +3 -3
  107. package/dist/project/loader.js +72 -50
  108. package/dist/project/lockfile.js +6 -5
  109. package/dist/project/manifest.js +5 -4
  110. package/dist/project/transactions.d.ts +4 -42
  111. package/dist/project/transactions.js +83 -138
  112. package/dist/project/types-generator.js +2 -1
  113. package/dist/project/types.d.ts +2 -0
  114. package/dist/registry/index.d.ts +0 -1
  115. package/dist/registry/index.js +0 -1
  116. package/dist/registry/lock.d.ts +2 -11
  117. package/dist/registry/lock.js +14 -183
  118. package/dist/registry/registry.d.ts +1 -6
  119. package/dist/registry/registry.js +5 -17
  120. package/dist/runtime/action-registry.d.ts +1 -5
  121. package/dist/runtime/action-registry.js +0 -12
  122. package/dist/runtime/context.js +2 -2
  123. package/dist/runtime/events.d.ts +11 -50
  124. package/dist/runtime/events.js +88 -368
  125. package/dist/runtime/index.d.ts +0 -4
  126. package/dist/runtime/index.js +0 -4
  127. package/dist/runtime/run-persistence.d.ts +2 -0
  128. package/dist/runtime/run-persistence.js +2 -1
  129. package/dist/runtime/standalone-commands.d.ts +18 -0
  130. package/dist/runtime/standalone-commands.js +466 -0
  131. package/dist/runtime/standalone-help.d.ts +20 -0
  132. package/dist/runtime/standalone-help.js +72 -0
  133. package/dist/runtime/standalone.d.ts +1 -44
  134. package/dist/runtime/standalone.js +32 -573
  135. package/dist/schema/validator.d.ts +2 -2
  136. package/dist/schema/validator.js +2 -9
  137. package/dist/server/index.d.ts +0 -2
  138. package/dist/server/index.js +0 -2
  139. package/dist/server/routes/actions.js +1 -1
  140. package/dist/server/routes/info.js +1 -1
  141. package/dist/server/routes/playbooks.js +1 -1
  142. package/dist/server/routes/runs.js +123 -16
  143. package/dist/server/routes/state.js +43 -18
  144. package/dist/server/security.d.ts +1 -1
  145. package/dist/server/security.js +2 -2
  146. package/dist/server/server.js +3 -7
  147. package/dist/service/factory.js +4 -3
  148. package/dist/service/index.d.ts +1 -1
  149. package/dist/service/index.js +1 -1
  150. package/dist/service/remote-errors.d.ts +1 -1
  151. package/dist/service/remote-errors.js +6 -6
  152. package/dist/service/remote-polling.d.ts +3 -0
  153. package/dist/service/remote-polling.js +10 -5
  154. package/dist/service/remote.d.ts +5 -1
  155. package/dist/service/remote.js +108 -17
  156. package/dist/service/sse-stream.js +2 -2
  157. package/dist/service/types.d.ts +22 -19
  158. package/dist/service/types.js +1 -24
  159. package/dist/storage/clock.d.ts +1 -1
  160. package/dist/storage/clock.js +1 -1
  161. package/dist/storage/data-dir-lock.d.ts +10 -22
  162. package/dist/storage/data-dir-lock.js +28 -56
  163. package/dist/storage/file-lock.d.ts +64 -0
  164. package/dist/storage/file-lock.js +447 -0
  165. package/dist/storage/index.d.ts +2 -2
  166. package/dist/storage/index.js +5 -4
  167. package/dist/storage/run-liveness.d.ts +48 -0
  168. package/dist/storage/run-liveness.js +44 -0
  169. package/dist/storage/sqlite-config.d.ts +16 -0
  170. package/dist/storage/sqlite-config.js +59 -0
  171. package/dist/storage/sqlite-driver.d.ts +3 -1
  172. package/dist/storage/sqlite-driver.js +14 -4
  173. package/dist/storage/sqlite-events.d.ts +13 -0
  174. package/dist/storage/sqlite-events.js +48 -0
  175. package/dist/storage/sqlite-runs.d.ts +107 -0
  176. package/dist/storage/sqlite-runs.js +622 -0
  177. package/dist/storage/sqlite-schema.d.ts +5 -0
  178. package/dist/storage/sqlite-schema.js +143 -0
  179. package/dist/storage/sqlite-state.d.ts +33 -0
  180. package/dist/storage/sqlite-state.js +270 -0
  181. package/dist/storage/sqlite.d.ts +30 -57
  182. package/dist/storage/sqlite.js +84 -741
  183. package/dist/storage/types.d.ts +35 -2
  184. package/dist/utils/index.d.ts +18 -14
  185. package/dist/utils/index.js +37 -29
  186. package/dist/utils/semver.d.ts +18 -0
  187. package/dist/utils/semver.js +19 -0
  188. package/dist/utils/warning.d.ts +1 -0
  189. package/dist/utils/warning.js +52 -0
  190. package/dist/{json/value-validator.d.ts → value-validator.d.ts} +9 -0
  191. package/dist/value-validator.js +330 -0
  192. package/dist/version.d.ts +1 -1
  193. package/dist/version.js +1 -1
  194. package/package.json +10 -12
  195. package/dist/execution/index.d.ts +0 -2
  196. package/dist/execution/index.js +0 -2
  197. package/dist/filter/index.d.ts +0 -1
  198. package/dist/filter/index.js +0 -1
  199. package/dist/graph/index.d.ts +0 -4
  200. package/dist/graph/index.js +0 -4
  201. package/dist/input/metadata.d.ts +0 -99
  202. package/dist/input/metadata.js +0 -99
  203. package/dist/invocation/index.d.ts +0 -2
  204. package/dist/invocation/index.js +0 -2
  205. package/dist/ipc/index.d.ts +0 -4
  206. package/dist/ipc/index.js +0 -4
  207. package/dist/json/index.d.ts +0 -1
  208. package/dist/json/index.js +0 -1
  209. package/dist/json/value-validator.js +0 -330
  210. package/dist/node/index.d.ts +0 -1
  211. package/dist/node/index.js +0 -1
  212. package/dist/platform/node-fs.d.ts +0 -37
  213. package/dist/platform/node-fs.js +0 -81
  214. package/dist/process/control-arbiter.d.ts +0 -108
  215. package/dist/process/control-arbiter.js +0 -250
  216. package/dist/process/process-executor.d.ts +0 -47
  217. package/dist/process/process-executor.js +0 -296
  218. package/dist/profile/client-health.d.ts +0 -14
  219. package/dist/profile/client-query.d.ts +0 -30
  220. package/dist/profile/client-query.js +0 -69
  221. package/dist/profile/client.d.ts +0 -26
  222. package/dist/profile/client.js +0 -31
  223. package/dist/project/closure.d.ts +0 -25
  224. package/dist/project/closure.js +0 -74
  225. package/dist/registry/store.d.ts +0 -42
  226. package/dist/registry/store.js +0 -43
  227. package/dist/runtime/clock.d.ts +0 -7
  228. package/dist/runtime/clock.js +0 -7
  229. package/dist/runtime/module-loader.d.ts +0 -2
  230. package/dist/runtime/module-loader.js +0 -1
  231. package/dist/runtime/process.d.ts +0 -8
  232. package/dist/runtime/process.js +0 -1
  233. package/dist/runtime/runner.d.ts +0 -234
  234. package/dist/runtime/runner.js +0 -609
  235. package/dist/schema/index.d.ts +0 -1
  236. package/dist/schema/index.js +0 -1
  237. package/dist/server/dispatcher.d.ts +0 -25
  238. package/dist/server/dispatcher.js +0 -54
  239. package/dist/service/local.d.ts +0 -27
  240. package/dist/service/local.js +0 -141
  241. package/dist/storage/driver.d.ts +0 -10
  242. package/dist/storage/driver.js +0 -10
  243. package/dist/storage/lazy.d.ts +0 -8
  244. package/dist/storage/lazy.js +0 -45
  245. package/dist/storage/lock-core.d.ts +0 -158
  246. package/dist/storage/lock-core.js +0 -872
  247. package/dist/utils/net.d.ts +0 -10
  248. package/dist/utils/net.js +0 -16
  249. package/dist/utils/process.d.ts +0 -10
  250. package/dist/utils/process.js +0 -21
  251. /package/dist/{filter → utils}/intent.d.ts +0 -0
  252. /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,
@@ -428,7 +637,7 @@ export class DefaultActionDockHost {
428
637
  };
429
638
  const ticket = await targetRuntime.startInvocation(targetActionId, childInput, subInvocationContext);
430
639
  if (!ticket.result) {
431
- 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`);
432
641
  }
433
642
  const result = await ticket.result;
434
643
  if (!result.ok) {
@@ -482,7 +691,7 @@ export class DefaultActionDockHost {
482
691
  if (existing === runtime) {
483
692
  return;
484
693
  }
485
- 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`);
486
695
  }
487
696
  this.runtimes.set(runtime.packageId, runtime);
488
697
  if (isPublic) {
@@ -490,27 +699,18 @@ export class DefaultActionDockHost {
490
699
  }
491
700
  this.bindRuntime(runtime);
492
701
  this.rebuildGraphAndCatalog();
493
- // 接管与恢复:仅持有者身份的 Host 自动将死亡会话或遗留非终态运行收敛为 interrupted;
494
- // 旁观查询 Host(CLI state/runs/config 命令)跳过本步骤,不动其他进程的在途记录
495
- if (this.recoverOrphans) {
496
- if (typeof runtime.recoverDeadSessionRuns === "function") {
497
- try {
498
- runtime.recoverDeadSessionRuns(this.hostSessionId);
499
- }
500
- catch (err) {
501
- // 单包恢复失败不阻断整体接管流程,但必须可观测
502
- console.warn(`[actiondock] recoverDeadSessionRuns failed for package '${runtime.packageId}': ${err instanceof Error ? err.message : String(err)}`);
503
- }
504
- }
505
- }
702
+ // 注册阶段只建立身份与路由;运行记录恢复收敛到包存储准备路径(持有者工厂预热时统一执行),
703
+ // 旁观 Host 注册不触碰其他进程的在途记录
506
704
  }
507
705
  registerRuntime(runtime) {
508
706
  this.registerRuntimeInternal(runtime, true);
509
707
  }
510
708
  async info() {
709
+ await this.ensureInitialized();
511
710
  return collectPackageInfos(this.listRuntimes());
512
711
  }
513
712
  async listActions(options) {
713
+ await this.ensureInitialized();
514
714
  // 外层完成可见性筛选与跨包限定名改写后,统一委托共享过滤函数完成检索
515
715
  const results = [];
516
716
  const runtimes = this.listRuntimes();
@@ -533,9 +733,11 @@ export class DefaultActionDockHost {
533
733
  return applyActionSummaryFilters(results, options);
534
734
  }
535
735
  async describeAction(ref) {
736
+ await this.ensureInitialized();
536
737
  return describeActionAcrossRuntimes(ref, this.listRuntimes(), this.visibility(), this.failedLinkedPackages, this.catalog, this.graph);
537
738
  }
538
739
  async listPlaybooks(options) {
740
+ await this.ensureInitialized();
539
741
  let pbs = await listVisiblePlaybooks(this.listRuntimes(), this.visibility());
540
742
  if (options?.package) {
541
743
  pbs = pbs.filter((p) => p.packageId === options.package);
@@ -546,18 +748,21 @@ export class DefaultActionDockHost {
546
748
  return pbs;
547
749
  }
548
750
  async describePlaybook(id) {
751
+ await this.ensureInitialized();
549
752
  return describeVisiblePlaybook(this.listRuntimes(), id, this.visibility());
550
753
  }
551
754
  async runAction(ref, input, options = {}) {
755
+ await this.ensureInitialized();
552
756
  const ticket = await this.startAction(ref, input, options);
553
757
  if (!ticket.result) {
554
- 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`);
555
759
  }
556
760
  return ticket.result;
557
761
  }
558
762
  async startAction(ref, input, options = {}) {
763
+ await this.ensureInitialized();
559
764
  if (this.isClosed) {
560
- throw new Error("ActionDockHost is closed: new tasks rejected");
765
+ throw new ActionDockError(SERVICE_CLOSED, "ActionDockHost is closed: new tasks rejected");
561
766
  }
562
767
  // - 统一基于纯领域 resolveAction 解析目标包与 Action 动作标识
563
768
  let resolved;
@@ -643,21 +848,151 @@ export class DefaultActionDockHost {
643
848
  // 调度目标包执行服务
644
849
  return targetRuntime.startInvocation(targetActionId, input, rootContext);
645
850
  }
646
- async getRun(runId) {
851
+ async getRun(runId, signal) {
852
+ await this.ensureInitialized();
853
+ if (signal?.aborted) {
854
+ throw createAbortError(signal.reason);
855
+ }
647
856
  for (const runtime of this.listRuntimes()) {
857
+ if (signal?.aborted) {
858
+ throw createAbortError(signal.reason);
859
+ }
648
860
  const record = await runtime.getRun(runId);
861
+ if (signal?.aborted) {
862
+ throw createAbortError(signal.reason);
863
+ }
649
864
  if (record)
650
865
  return record;
651
866
  }
652
867
  return undefined;
653
868
  }
654
- 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
+ }
655
875
  const runtimes = query?.packageId
656
876
  ? [this.getRuntime(query.packageId)].filter(Boolean)
657
- : 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 或合并排序
658
979
  const records = [];
980
+ const fetchLimit = query?.limit !== undefined
981
+ ? (query.offset ?? 0) + query.limit
982
+ : undefined;
659
983
  for (const runtime of runtimes) {
660
- 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
+ }
661
996
  for (const r of recs) {
662
997
  records.push({
663
998
  ...r,
@@ -666,16 +1001,87 @@ export class DefaultActionDockHost {
666
1001
  }
667
1002
  }
668
1003
  records.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
669
- 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();
670
1019
  if (query?.intent) {
671
- 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;
672
1058
  }
673
- if (query?.limit && result.length > query.limit) {
674
- 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
+ }
675
1080
  }
676
- return result;
1081
+ return total;
677
1082
  }
678
1083
  async clearRuns(options) {
1084
+ await this.ensureInitialized();
679
1085
  const runtimes = options?.packageId
680
1086
  ? [this.getRuntime(options.packageId)].filter(Boolean)
681
1087
  : this.listRuntimes();
@@ -687,6 +1093,7 @@ export class DefaultActionDockHost {
687
1093
  return total;
688
1094
  }
689
1095
  async cleanExpiredRuns(policy) {
1096
+ await this.ensureInitialized();
690
1097
  let total = 0;
691
1098
  for (const runtime of this.listRuntimes()) {
692
1099
  if (typeof runtime.cleanExpiredRuns === "function") {
@@ -697,6 +1104,7 @@ export class DefaultActionDockHost {
697
1104
  return total;
698
1105
  }
699
1106
  async cancelRun(runId, reason) {
1107
+ await this.ensureInitialized();
700
1108
  for (const runtime of this.listRuntimes()) {
701
1109
  const res = await runtime.cancelRun(runId, reason);
702
1110
  if (res.outcome !== "not_found") {
@@ -705,27 +1113,38 @@ export class DefaultActionDockHost {
705
1113
  }
706
1114
  return { outcome: "not_found", runId };
707
1115
  }
708
- events(runId, options) {
709
- for (const runtime of this.listRuntimes()) {
710
- const storageRecord = runtime.storage?.getRun?.(runId);
711
- if (storageRecord) {
712
- 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;
713
1126
  }
714
- }
715
- const firstRuntime = this.listRuntimes()[0];
716
- if (firstRuntime) {
717
- return firstRuntime.events(runId, options);
718
- }
719
- return this.eventSink.subscribe(runId, options);
1127
+ // Host 共享事件源作为统一回退(与执行服务装配使用同一 sink,完成事件不丢失)
1128
+ yield* self.eventSink.subscribe(runId, options);
1129
+ })();
720
1130
  }
721
1131
  getGlobalStorage() {
722
1132
  if (!this.globalStorage) {
723
- 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));
724
1142
  const inMemory = Boolean(this.options.inMemory || hasInMemoryPackage);
725
1143
  this.globalStorage =
726
1144
  this.options.platform?.storage?.createGlobalStorage?.({
727
1145
  dataDir: this.options.dataDir,
728
1146
  customHome: this.options.customHome,
1147
+ ...(inMemory ? { inMemory: true } : {}),
729
1148
  }) ??
730
1149
  createGlobalStorage({
731
1150
  dataDir: this.options.dataDir,
@@ -760,11 +1179,12 @@ export class DefaultActionDockHost {
760
1179
  requireRuntime(packageId) {
761
1180
  const runtime = this.resolveRuntime(packageId);
762
1181
  if (!runtime) {
763
- throw new Error(`Package '${packageId}' not found in host`);
1182
+ throw new ActionDockError(PACKAGE_NOT_FOUND, `Package '${packageId}' not found in host`);
764
1183
  }
765
1184
  return runtime;
766
1185
  }
767
1186
  async getConfig(packageId, key) {
1187
+ await this.ensureInitialized();
768
1188
  if (packageId === "global") {
769
1189
  const globalStorage = this.getGlobalStorage();
770
1190
  const val = globalStorage.getConfig(key);
@@ -782,6 +1202,7 @@ export class DefaultActionDockHost {
782
1202
  return this.requireRuntime(packageId).getConfig(key);
783
1203
  }
784
1204
  async setConfig(packageId, key, value) {
1205
+ await this.ensureInitialized();
785
1206
  if (packageId === "global") {
786
1207
  await this.getGlobalStorage().setConfig(key, value);
787
1208
  return;
@@ -789,12 +1210,14 @@ export class DefaultActionDockHost {
789
1210
  await this.requireRuntime(packageId).setConfig(key, value);
790
1211
  }
791
1212
  async deleteConfig(packageId, key) {
1213
+ await this.ensureInitialized();
792
1214
  if (packageId === "global") {
793
1215
  return await this.getGlobalStorage().deleteConfig(key);
794
1216
  }
795
1217
  return await this.requireRuntime(packageId).deleteConfig(key);
796
1218
  }
797
1219
  async listConfig(packageId) {
1220
+ await this.ensureInitialized();
798
1221
  if (packageId === "global") {
799
1222
  const globalStorage = this.getGlobalStorage();
800
1223
  const all = globalStorage.listConfig();
@@ -813,11 +1236,13 @@ export class DefaultActionDockHost {
813
1236
  return this.requireRuntime(packageId).listConfig();
814
1237
  }
815
1238
  async getState(packageId, actionId, key, options) {
1239
+ await this.ensureInitialized();
816
1240
  return this.requireRuntime(packageId).getState(actionId, key, options);
817
1241
  }
818
1242
  async setState(packageId, actionId, key, value, options) {
1243
+ await this.ensureInitialized();
819
1244
  const runtime = this.requireRuntime(packageId);
820
- if (actionId) {
1245
+ if (actionId !== undefined) {
821
1246
  await runtime.setActionState(actionId, key, value, options);
822
1247
  }
823
1248
  else {
@@ -825,18 +1250,22 @@ export class DefaultActionDockHost {
825
1250
  }
826
1251
  }
827
1252
  async deleteState(packageId, actionId, key, options) {
1253
+ await this.ensureInitialized();
828
1254
  return this.requireRuntime(packageId).deleteState(actionId, key, options);
829
1255
  }
830
1256
  async listStateKeys(packageId, actionId, options) {
1257
+ await this.ensureInitialized();
831
1258
  return this.requireRuntime(packageId).listStateKeys(actionId, options);
832
1259
  }
833
1260
  async clearState(packageId, actionId, options) {
1261
+ await this.ensureInitialized();
834
1262
  return this.requireRuntime(packageId).clearState(actionId, options);
835
1263
  }
836
1264
  async listStateEntries(packageId, options) {
1265
+ await this.ensureInitialized();
837
1266
  const runtime = this.resolveRuntime(packageId);
838
1267
  if (!runtime) {
839
- 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`);
840
1269
  }
841
1270
  if (!runtime.listStateEntries) {
842
1271
  return [];
@@ -882,21 +1311,5 @@ export class DefaultActionDockHost {
882
1311
  * 工厂函数:创建并初始化 ActionDockHost 宿主容器。
883
1312
  */
884
1313
  export async function createActionDockHost(options = {}) {
885
- let createdHost;
886
- try {
887
- createdHost = new DefaultActionDockHost(options, { deferInit: true });
888
- await createdHost.initializeAsync();
889
- return createdHost;
890
- }
891
- catch (err) {
892
- if (createdHost) {
893
- try {
894
- await createdHost.rollbackInitialization();
895
- }
896
- catch {
897
- // 忽略宿主回滚异常,确保抛出原始异常
898
- }
899
- }
900
- throw err;
901
- }
1314
+ return DefaultActionDockHost.create(options);
902
1315
  }