@actiondock/core 2.0.11 → 2.2.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 (258) hide show
  1. package/README.md +61 -30
  2. package/dist/app/app.d.ts +73 -0
  3. package/dist/app/app.js +700 -0
  4. package/dist/app/index.d.ts +2 -0
  5. package/dist/app/index.js +2 -0
  6. package/dist/app/types.d.ts +283 -0
  7. package/dist/app/types.js +1 -0
  8. package/dist/catalog/action-index.d.ts +9 -0
  9. package/dist/catalog/action-index.js +93 -0
  10. package/dist/catalog/action-resolver.d.ts +20 -0
  11. package/dist/catalog/action-resolver.js +95 -0
  12. package/dist/catalog/index.d.ts +5 -0
  13. package/dist/catalog/index.js +5 -0
  14. package/dist/catalog/location-registry.d.ts +15 -0
  15. package/dist/catalog/location-registry.js +138 -0
  16. package/dist/catalog/package-catalog.d.ts +7 -0
  17. package/dist/catalog/package-catalog.js +76 -0
  18. package/dist/catalog/types.d.ts +68 -0
  19. package/dist/catalog/types.js +1 -0
  20. package/dist/doctor/doctor.d.ts +7 -0
  21. package/dist/doctor/doctor.js +613 -0
  22. package/dist/doctor/index.d.ts +2 -0
  23. package/dist/doctor/index.js +2 -0
  24. package/dist/doctor/types.d.ts +23 -0
  25. package/dist/doctor/types.js +1 -0
  26. package/dist/errors.d.ts +122 -0
  27. package/dist/errors.js +123 -0
  28. package/dist/execution/index.d.ts +2 -0
  29. package/dist/execution/index.js +2 -0
  30. package/dist/execution/service.d.ts +79 -0
  31. package/dist/execution/service.js +633 -0
  32. package/dist/execution/types.d.ts +131 -0
  33. package/dist/execution/types.js +1 -0
  34. package/dist/export/index.d.ts +1 -0
  35. package/dist/export/index.js +1 -0
  36. package/dist/export/templates.d.ts +71 -0
  37. package/dist/export/templates.js +621 -0
  38. package/dist/filter/index.d.ts +1 -0
  39. package/dist/filter/index.js +1 -0
  40. package/dist/filter/intent.d.ts +33 -0
  41. package/dist/filter/intent.js +123 -0
  42. package/dist/host/host.d.ts +55 -0
  43. package/dist/host/host.js +740 -0
  44. package/dist/host/index.d.ts +2 -0
  45. package/dist/host/index.js +2 -0
  46. package/dist/host/types.d.ts +81 -0
  47. package/dist/host/types.js +1 -0
  48. package/dist/index.d.ts +21 -0
  49. package/dist/index.js +20 -0
  50. package/dist/ipc/diagnostic.d.ts +65 -0
  51. package/dist/ipc/diagnostic.js +129 -0
  52. package/dist/ipc/host.d.ts +7 -0
  53. package/dist/ipc/host.js +93 -0
  54. package/dist/ipc/index.d.ts +4 -0
  55. package/dist/ipc/index.js +4 -0
  56. package/dist/ipc/target.d.ts +56 -0
  57. package/dist/ipc/target.js +297 -0
  58. package/dist/ipc/types.d.ts +42 -0
  59. package/dist/ipc/types.js +1 -0
  60. package/dist/platform/default.d.ts +32 -0
  61. package/dist/platform/default.js +40 -0
  62. package/dist/platform/index.d.ts +3 -0
  63. package/dist/platform/index.js +3 -0
  64. package/dist/platform/node-fs.d.ts +37 -0
  65. package/dist/platform/node-fs.js +81 -0
  66. package/dist/platform/types.d.ts +104 -0
  67. package/dist/platform/types.js +1 -0
  68. package/dist/profile/client.d.ts +162 -0
  69. package/dist/profile/client.js +467 -0
  70. package/dist/profile/index.d.ts +3 -0
  71. package/dist/profile/index.js +3 -0
  72. package/dist/profile/manager.d.ts +47 -0
  73. package/dist/profile/manager.js +294 -0
  74. package/dist/profile/types.d.ts +61 -0
  75. package/dist/profile/types.js +1 -0
  76. package/dist/project/closure.d.ts +25 -0
  77. package/dist/project/closure.js +68 -0
  78. package/dist/project/digest.d.ts +29 -0
  79. package/dist/project/digest.js +324 -0
  80. package/dist/project/index.d.ts +10 -0
  81. package/dist/project/index.js +10 -0
  82. package/dist/project/init.d.ts +26 -0
  83. package/dist/project/init.js +175 -0
  84. package/dist/project/loader.d.ts +99 -0
  85. package/dist/project/loader.js +484 -0
  86. package/dist/project/lockfile.d.ts +55 -0
  87. package/dist/project/lockfile.js +175 -0
  88. package/dist/project/manifest.d.ts +22 -0
  89. package/dist/project/manifest.js +220 -0
  90. package/dist/project/resolver.d.ts +116 -0
  91. package/dist/project/resolver.js +376 -0
  92. package/dist/project/transactions.d.ts +103 -0
  93. package/dist/project/transactions.js +1203 -0
  94. package/dist/project/types-generator.d.ts +33 -0
  95. package/dist/project/types-generator.js +168 -0
  96. package/dist/project/types.d.ts +107 -0
  97. package/dist/project/types.js +1 -0
  98. package/dist/registry/index.d.ts +5 -0
  99. package/dist/registry/index.js +5 -0
  100. package/dist/registry/lock.d.ts +27 -0
  101. package/dist/registry/lock.js +93 -0
  102. package/dist/registry/registry.d.ts +45 -0
  103. package/dist/registry/registry.js +611 -0
  104. package/dist/registry/resolve.d.ts +62 -0
  105. package/dist/registry/resolve.js +195 -0
  106. package/dist/registry/scan.d.ts +38 -0
  107. package/dist/registry/scan.js +135 -0
  108. package/dist/registry/types.d.ts +117 -0
  109. package/dist/registry/types.js +1 -0
  110. package/dist/runtime/action-collection.d.ts +27 -0
  111. package/dist/runtime/action-collection.js +57 -0
  112. package/dist/runtime/clock.d.ts +19 -0
  113. package/dist/runtime/clock.js +17 -0
  114. package/dist/runtime/context.d.ts +86 -0
  115. package/dist/runtime/context.js +242 -0
  116. package/dist/runtime/env.d.ts +56 -0
  117. package/dist/runtime/env.js +159 -0
  118. package/dist/runtime/events.d.ts +93 -0
  119. package/dist/runtime/events.js +604 -0
  120. package/dist/runtime/execution-manager.d.ts +48 -0
  121. package/dist/runtime/execution-manager.js +66 -0
  122. package/dist/runtime/index.d.ts +10 -0
  123. package/dist/runtime/index.js +10 -0
  124. package/dist/runtime/module-loader.d.ts +34 -0
  125. package/dist/runtime/module-loader.js +16 -0
  126. package/dist/runtime/process.d.ts +9 -0
  127. package/dist/runtime/process.js +146 -0
  128. package/dist/runtime/runner.d.ts +293 -0
  129. package/dist/runtime/runner.js +984 -0
  130. package/dist/runtime/standalone.d.ts +89 -0
  131. package/dist/runtime/standalone.js +536 -0
  132. package/dist/schema/validator.d.ts +28 -0
  133. package/dist/schema/validator.js +116 -0
  134. package/dist/server/body.d.ts +40 -0
  135. package/dist/server/body.js +93 -0
  136. package/dist/server/index.d.ts +5 -0
  137. package/dist/server/index.js +5 -0
  138. package/dist/server/routes/actions.d.ts +5 -0
  139. package/dist/server/routes/actions.js +298 -0
  140. package/dist/server/routes/common.d.ts +47 -0
  141. package/dist/server/routes/common.js +104 -0
  142. package/dist/server/routes/config.d.ts +6 -0
  143. package/dist/server/routes/config.js +173 -0
  144. package/dist/server/routes/doctor.d.ts +5 -0
  145. package/dist/server/routes/doctor.js +63 -0
  146. package/dist/server/routes/health.d.ts +5 -0
  147. package/dist/server/routes/health.js +35 -0
  148. package/dist/server/routes/index.d.ts +9 -0
  149. package/dist/server/routes/index.js +9 -0
  150. package/dist/server/routes/info.d.ts +7 -0
  151. package/dist/server/routes/info.js +158 -0
  152. package/dist/server/routes/playbooks.d.ts +5 -0
  153. package/dist/server/routes/playbooks.js +134 -0
  154. package/dist/server/routes/runs.d.ts +5 -0
  155. package/dist/server/routes/runs.js +343 -0
  156. package/dist/server/routes/state.d.ts +6 -0
  157. package/dist/server/routes/state.js +169 -0
  158. package/dist/server/security.d.ts +44 -0
  159. package/dist/server/security.js +101 -0
  160. package/dist/server/server.d.ts +15 -0
  161. package/dist/server/server.js +355 -0
  162. package/dist/server/types.d.ts +78 -0
  163. package/dist/server/types.js +1 -0
  164. package/dist/storage/data-dir-lock.d.ts +119 -0
  165. package/dist/storage/data-dir-lock.js +1016 -0
  166. package/dist/storage/driver.d.ts +9 -0
  167. package/dist/storage/driver.js +59 -0
  168. package/dist/storage/index.d.ts +64 -0
  169. package/dist/storage/index.js +98 -0
  170. package/dist/storage/lazy.d.ts +8 -0
  171. package/dist/storage/lazy.js +44 -0
  172. package/dist/storage/mask.d.ts +26 -0
  173. package/dist/storage/mask.js +48 -0
  174. package/dist/storage/params.d.ts +13 -0
  175. package/dist/storage/params.js +32 -0
  176. package/dist/storage/sqlite.d.ts +79 -0
  177. package/dist/storage/sqlite.js +689 -0
  178. package/dist/storage/types.d.ts +172 -0
  179. package/dist/storage/types.js +14 -0
  180. package/dist/target/index.d.ts +4 -0
  181. package/dist/target/index.js +4 -0
  182. package/dist/target/local.d.ts +52 -0
  183. package/dist/target/local.js +343 -0
  184. package/dist/target/remote.d.ts +76 -0
  185. package/dist/target/remote.js +839 -0
  186. package/dist/target/target.d.ts +6 -0
  187. package/dist/target/target.js +35 -0
  188. package/dist/target/types.d.ts +246 -0
  189. package/dist/target/types.js +34 -0
  190. package/dist/utils/index.d.ts +49 -0
  191. package/dist/utils/index.js +180 -0
  192. package/dist/version.d.ts +4 -0
  193. package/dist/version.js +4 -0
  194. package/package.json +13 -13
  195. package/src/build/index.ts +0 -1
  196. package/src/build/templates.ts +0 -59
  197. package/src/catalog/action-index.ts +0 -92
  198. package/src/catalog/action-resolver.ts +0 -120
  199. package/src/catalog/index.ts +0 -5
  200. package/src/catalog/location-registry.ts +0 -134
  201. package/src/catalog/package-catalog.ts +0 -87
  202. package/src/catalog/types.ts +0 -75
  203. package/src/doctor/doctor.ts +0 -419
  204. package/src/doctor/index.ts +0 -2
  205. package/src/doctor/types.ts +0 -25
  206. package/src/execution/index.ts +0 -2
  207. package/src/execution/service.ts +0 -322
  208. package/src/execution/types.ts +0 -73
  209. package/src/export/index.ts +0 -1
  210. package/src/export/templates.ts +0 -590
  211. package/src/filter/index.ts +0 -1
  212. package/src/filter/intent.ts +0 -154
  213. package/src/index.ts +0 -16
  214. package/src/profile/client.ts +0 -552
  215. package/src/profile/index.ts +0 -3
  216. package/src/profile/manager.ts +0 -341
  217. package/src/profile/types.ts +0 -71
  218. package/src/project/index.ts +0 -4
  219. package/src/project/init.ts +0 -242
  220. package/src/project/loader.ts +0 -479
  221. package/src/project/manifest.ts +0 -299
  222. package/src/project/types.ts +0 -141
  223. package/src/registry/index.ts +0 -2
  224. package/src/registry/registry.ts +0 -940
  225. package/src/registry/types.ts +0 -127
  226. package/src/runtime/clock.ts +0 -41
  227. package/src/runtime/context.ts +0 -259
  228. package/src/runtime/env.ts +0 -172
  229. package/src/runtime/events.ts +0 -142
  230. package/src/runtime/execution-manager.ts +0 -74
  231. package/src/runtime/index.ts +0 -10
  232. package/src/runtime/module-loader.ts +0 -67
  233. package/src/runtime/process.ts +0 -245
  234. package/src/runtime/runner.ts +0 -527
  235. package/src/runtime/standalone.ts +0 -429
  236. package/src/schema/validator.ts +0 -61
  237. package/src/server/body.ts +0 -112
  238. package/src/server/index.ts +0 -7
  239. package/src/server/routes/actions.ts +0 -262
  240. package/src/server/routes/common.ts +0 -126
  241. package/src/server/routes/config.ts +0 -147
  242. package/src/server/routes/doctor.ts +0 -29
  243. package/src/server/routes/health.ts +0 -40
  244. package/src/server/routes/index.ts +0 -9
  245. package/src/server/routes/info.ts +0 -221
  246. package/src/server/routes/playbooks.ts +0 -109
  247. package/src/server/routes/runs.ts +0 -301
  248. package/src/server/routes/state.ts +0 -133
  249. package/src/server/runtime-registry.ts +0 -115
  250. package/src/server/security.ts +0 -115
  251. package/src/server/server.ts +0 -251
  252. package/src/server/types.ts +0 -57
  253. package/src/storage/driver.ts +0 -126
  254. package/src/storage/index.ts +0 -81
  255. package/src/storage/mask.ts +0 -34
  256. package/src/storage/sqlite.ts +0 -714
  257. package/src/storage/types.ts +0 -139
  258. package/src/utils/index.ts +0 -62
@@ -0,0 +1,633 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { ActionResolver } from "../catalog/action-resolver.js";
3
+ import { computeDigest } from "../project/digest.js";
4
+ import { InMemoryEventSink } from "../runtime/events.js";
5
+ import { ActionRunner } from "../runtime/runner.js";
6
+ import { ACTION_CANCELLED, ACTION_NOT_FOUND, ACTION_TIMEOUT, EXECUTION_FAILED, IDEMPOTENCY_CONFLICT, RUN_REPOSITORY_UNAVAILABLE, UNHANDLED_EXECUTION_ERROR, describeActionLoadFailure, } from "../errors.js";
7
+ /**
8
+ * 统一执行协调服务实现。
9
+ */
10
+ export class DefaultExecutionService {
11
+ packageId;
12
+ storage;
13
+ projectConfig;
14
+ eventSink;
15
+ maxActiveRuns;
16
+ ownerId;
17
+ hostSessionId;
18
+ _runner;
19
+ logger;
20
+ clock;
21
+ process;
22
+ platform;
23
+ actionResolver;
24
+ activeRuns = new Map();
25
+ reservedSlots = 0;
26
+ isClosing = false;
27
+ ownsStorage;
28
+ ownsGlobalStorage;
29
+ globalStorage;
30
+ constructor(options) {
31
+ this.platform = options.platform;
32
+ this.packageId = options.packageId;
33
+ this.hostSessionId = options.hostSessionId;
34
+ this.projectConfig = options.projectConfig;
35
+ this.eventSink = options.eventSink || options.platform?.eventSink || new InMemoryEventSink();
36
+ this.maxActiveRuns = options.maxActiveRuns || 32;
37
+ this.ownerId = options.ownerId || `host-${randomUUID().slice(0, 8)}`;
38
+ this.actionResolver = options.actionResolver;
39
+ this.logger = options.logger;
40
+ if (options.platform) {
41
+ this.clock = options.platform.clock;
42
+ this.process = options.platform.process;
43
+ this.storage =
44
+ options.storage ??
45
+ options.platform.storage.createStorage(this.packageId, {
46
+ projectRoot: options.projectRoot,
47
+ customHome: options.customHome,
48
+ });
49
+ }
50
+ else {
51
+ this.clock = options.clock;
52
+ this.process = options.process;
53
+ if (!options.storage) {
54
+ throw new Error("ExecutionService requires either 'storage' or 'platform' option");
55
+ }
56
+ this.storage = options.storage;
57
+ }
58
+ const globalStorage = options.platform
59
+ ? (options.globalStorage ?? options.platform.storage.createGlobalStorage({ customHome: options.customHome }))
60
+ : options.globalStorage;
61
+ this.ownsStorage = !options.storage;
62
+ this.ownsGlobalStorage = !options.globalStorage && !!options.platform;
63
+ this.globalStorage = globalStorage;
64
+ this._runner = new ActionRunner({
65
+ packageId: this.packageId,
66
+ hostSessionId: this.hostSessionId,
67
+ storage: this.storage,
68
+ globalStorage,
69
+ projectRoot: options.projectRoot,
70
+ projectConfig: this.projectConfig,
71
+ configOverrides: options.configOverrides,
72
+ actions: options.actions,
73
+ process: this.process,
74
+ clock: this.clock,
75
+ platform: options.platform,
76
+ maxCallDepth: options.maxCallDepth,
77
+ maxSubRuns: options.maxSubRuns,
78
+ actionResolver: (ref, currentPkgId) => {
79
+ const parsed = typeof ref === "string" ? ActionResolver.parseRef(ref) : ref;
80
+ if (parsed.packageId && parsed.packageId !== this.packageId) {
81
+ return undefined;
82
+ }
83
+ if (currentPkgId && currentPkgId !== this.packageId) {
84
+ return undefined;
85
+ }
86
+ return this.actionResolver ? this.actionResolver(parsed) : undefined;
87
+ },
88
+ getStorageForPackage: options.getStorageForPackage,
89
+ packageContextResolver: options.packageContextResolver,
90
+ customHome: options.customHome,
91
+ });
92
+ }
93
+ get runner() {
94
+ return this._runner;
95
+ }
96
+ registerAction(idOrAction, actionDef) {
97
+ if (typeof idOrAction === "string") {
98
+ this._runner.registerAction(idOrAction, actionDef);
99
+ }
100
+ else {
101
+ this._runner.registerAction(idOrAction);
102
+ }
103
+ }
104
+ getAction(id) {
105
+ return this._runner.getAction(id);
106
+ }
107
+ listActions() {
108
+ return this._runner.listActions();
109
+ }
110
+ getActiveHandle(runId) {
111
+ return this.activeRuns.get(runId)?.handle;
112
+ }
113
+ setPackageContextResolver(resolver) {
114
+ this._runner.setPackageContextResolver(resolver);
115
+ }
116
+ async resolveTargetAction(ref) {
117
+ const parsed = typeof ref === "string" ? ActionResolver.parseRef(ref) : ref;
118
+ const actionId = parsed.actionId;
119
+ const targetPackageId = parsed.packageId || this.packageId;
120
+ let runner = this._runner;
121
+ if (targetPackageId !== this.packageId) {
122
+ const targetRunner = await this._runner.resolveTargetPackageRunner(targetPackageId);
123
+ if (!targetRunner)
124
+ return undefined;
125
+ runner = targetRunner;
126
+ }
127
+ const fromRunner = runner.getAction(actionId);
128
+ if (fromRunner)
129
+ return fromRunner;
130
+ if (this.actionResolver && targetPackageId === this.packageId) {
131
+ return this.actionResolver(parsed);
132
+ }
133
+ return undefined;
134
+ }
135
+ async execute(ref, input, options = {}) {
136
+ const ticket = await this.start(ref, input, options);
137
+ if (!ticket.result) {
138
+ throw new Error(`Execution ticket for run '${ticket.runId}' has no result Promise`);
139
+ }
140
+ return ticket.result;
141
+ }
142
+ get activeRunsCount() {
143
+ return this.activeRuns.size + this.reservedSlots;
144
+ }
145
+ async start(ref, input, options = {}) {
146
+ if (this.isClosing) {
147
+ throw new Error("ExecutionService is closing: new tasks rejected");
148
+ }
149
+ const currentTotal = this.activeRuns.size + this.reservedSlots;
150
+ if (currentTotal >= this.maxActiveRuns) {
151
+ throw new Error(`Concurrency limit reached: ${currentTotal}/${this.maxActiveRuns} active runs`);
152
+ }
153
+ this.reservedSlots++;
154
+ let hasReservedSlot = true;
155
+ const releaseSlot = () => {
156
+ if (hasReservedSlot) {
157
+ this.reservedSlots--;
158
+ hasReservedSlot = false;
159
+ }
160
+ };
161
+ try {
162
+ let parsedRef;
163
+ try {
164
+ parsedRef = ActionResolver.parseRef(ref);
165
+ }
166
+ catch {
167
+ parsedRef = typeof ref === "object" ? ref : { actionId: ref };
168
+ }
169
+ const targetActionId = parsedRef.actionId;
170
+ const targetPackageId = parsedRef.packageId || this.packageId;
171
+ const actionRef = `${targetPackageId}/${targetActionId}`;
172
+ const effectiveClock = options.platform?.clock ?? this.clock;
173
+ // requestId 幂等检查与去重处理
174
+ const gateResult = await this.checkIdempotencyGate(input, options, actionRef, effectiveClock);
175
+ if (gateResult.ticket) {
176
+ releaseSlot();
177
+ return gateResult.ticket;
178
+ }
179
+ const designatedRunId = gateResult.designatedRunId;
180
+ const target = await this.resolveExecutionTarget(parsedRef, targetPackageId, targetActionId);
181
+ if (!target.action) {
182
+ releaseSlot();
183
+ return this.failTicketForMissingAction({
184
+ target,
185
+ input,
186
+ options,
187
+ targetPackageId,
188
+ targetActionId,
189
+ designatedRunId,
190
+ effectiveClock,
191
+ });
192
+ }
193
+ if (this.isClosing) {
194
+ throw new Error("ExecutionService is closing: new tasks rejected");
195
+ }
196
+ const controller = new AbortController();
197
+ let onAbort;
198
+ if (options.signal && typeof options.signal.addEventListener === "function") {
199
+ if (options.signal.aborted) {
200
+ controller.abort(options.signal.reason);
201
+ }
202
+ else {
203
+ onAbort = () => controller.abort(options.signal?.reason);
204
+ options.signal.addEventListener("abort", onAbort, { once: true });
205
+ }
206
+ }
207
+ const runId = designatedRunId || randomUUID();
208
+ const bridge = this.createEventBridge({ runId, options, effectiveClock });
209
+ this.registerResolvedAction(target.runner, targetPackageId, targetActionId, target.action);
210
+ const handle = target.runner.start(targetActionId, input, {
211
+ runId,
212
+ rootRunId: options.rootRunId,
213
+ parentRunId: options.parentRunId,
214
+ hostSessionId: options.hostSessionId || this.hostSessionId,
215
+ maxCallDepth: options.maxCallDepth,
216
+ configOverrides: options.config,
217
+ signal: controller.signal,
218
+ timeoutMs: options.timeoutMs,
219
+ progress: bridge.progressReporter,
220
+ logger: bridge.executionLogger,
221
+ process: options.process || options.platform?.process || this.process,
222
+ platform: options.platform || this.platform,
223
+ });
224
+ const activeItem = {
225
+ runId: handle.runId,
226
+ handle,
227
+ controller,
228
+ status: "running",
229
+ startedAt: (effectiveClock?.now() ?? new Date()).toISOString(),
230
+ signal: options.signal,
231
+ onAbort,
232
+ };
233
+ this.activeRuns.set(handle.runId, activeItem);
234
+ releaseSlot();
235
+ bridge.emitEvent({ type: "status", status: "running" });
236
+ this.watchHandleCompletion(handle, activeItem, bridge);
237
+ return {
238
+ runId: handle.runId,
239
+ status: "running",
240
+ result: handle.result,
241
+ };
242
+ }
243
+ catch (err) {
244
+ releaseSlot();
245
+ throw err;
246
+ }
247
+ }
248
+ /**
249
+ * 幂等门检查:digest 计算与冲突/重复分流(重复命中时直接返回已有票据)。
250
+ */
251
+ async checkIdempotencyGate(input, options, actionRef, effectiveClock) {
252
+ if (!options.requestId) {
253
+ return {};
254
+ }
255
+ const digestPayload = {
256
+ input,
257
+ config: options.config,
258
+ timeoutMs: options.timeoutMs,
259
+ };
260
+ const inputDigest = computeDigest(digestPayload);
261
+ const provisionalRunId = randomUUID();
262
+ if (!this.storage.checkAndRecordIdempotency) {
263
+ return {};
264
+ }
265
+ const idemp = this.storage.checkAndRecordIdempotency({
266
+ ownerId: this.ownerId,
267
+ actionRef,
268
+ requestId: options.requestId,
269
+ inputDigest,
270
+ runId: provisionalRunId,
271
+ createdAt: (effectiveClock?.now() ?? new Date()).toISOString(),
272
+ });
273
+ if (idemp.outcome === "conflict") {
274
+ const conflictError = {
275
+ code: IDEMPOTENCY_CONFLICT,
276
+ message: `Idempotency conflict for requestId '${options.requestId}': input parameters digest mismatch`,
277
+ details: {
278
+ requestId: options.requestId,
279
+ actionRef,
280
+ expectedDigest: idemp.existingDigest,
281
+ actualDigest: inputDigest,
282
+ },
283
+ };
284
+ const err = new Error(conflictError.message);
285
+ err.code = conflictError.code;
286
+ err.details = conflictError.details;
287
+ throw err;
288
+ }
289
+ if (idemp.outcome === "duplicate") {
290
+ const existingRunId = idemp.runId;
291
+ const active = this.activeRuns.get(existingRunId);
292
+ if (active) {
293
+ return {
294
+ ticket: {
295
+ runId: existingRunId,
296
+ status: active.status,
297
+ result: active.handle.result,
298
+ },
299
+ };
300
+ }
301
+ const record = await this.get(existingRunId);
302
+ if (record) {
303
+ const execRes = record.status === "success"
304
+ ? { ok: true, runId: existingRunId, data: record.output ?? null }
305
+ : {
306
+ ok: false,
307
+ runId: existingRunId,
308
+ error: record.error || {
309
+ code: EXECUTION_FAILED,
310
+ message: `Run terminated with status '${record.status}'`,
311
+ },
312
+ };
313
+ return {
314
+ ticket: {
315
+ runId: existingRunId,
316
+ status: record.status,
317
+ result: Promise.resolve(execRes),
318
+ },
319
+ };
320
+ }
321
+ }
322
+ return { designatedRunId: provisionalRunId };
323
+ }
324
+ /**
325
+ * 解析执行目标:确定目标 Runner 并解析目标 Action 定义。
326
+ */
327
+ async resolveExecutionTarget(parsedRef, targetPackageId, targetActionId) {
328
+ let runnerToUse = this._runner;
329
+ let resolveError;
330
+ if (targetPackageId && targetPackageId !== this.packageId) {
331
+ const targetRunner = await this._runner.resolveTargetPackageRunner(targetPackageId);
332
+ if (targetRunner) {
333
+ runnerToUse = targetRunner;
334
+ }
335
+ else {
336
+ resolveError = {
337
+ code: ACTION_NOT_FOUND,
338
+ message: `Target package '${targetPackageId}' not found or unresolvable`,
339
+ };
340
+ }
341
+ }
342
+ let action;
343
+ if (!resolveError) {
344
+ action = runnerToUse.getAction(targetActionId) || runnerToUse.getAction(`${targetPackageId}/${targetActionId}`);
345
+ if (!action) {
346
+ const resolution = await runnerToUse.resolveAction(parsedRef);
347
+ if (resolution.status === "found") {
348
+ action = resolution.action;
349
+ }
350
+ else if (resolution.status === "load_failed") {
351
+ resolveError = describeActionLoadFailure(resolution.error, {
352
+ actionId: targetActionId,
353
+ packageId: resolution.packageId,
354
+ projectRoot: resolution.projectRoot,
355
+ });
356
+ }
357
+ else {
358
+ const targetAction = await this.resolveTargetAction(parsedRef);
359
+ if (targetAction) {
360
+ action = targetAction;
361
+ }
362
+ else {
363
+ resolveError = {
364
+ code: ACTION_NOT_FOUND,
365
+ message: `Action '${targetActionId}' not found in package '${targetPackageId}'`,
366
+ details: resolution.reason ? { reason: resolution.reason } : undefined,
367
+ };
368
+ }
369
+ }
370
+ }
371
+ }
372
+ return { runner: runnerToUse, action, resolveError };
373
+ }
374
+ /**
375
+ * 注册解析成功的目标 Action 至目标 Runner(收敛为单一注册入口)。
376
+ */
377
+ registerResolvedAction(runner, targetPackageId, targetActionId, action) {
378
+ runner.registerAction(targetActionId, action);
379
+ }
380
+ /**
381
+ * 目标 Action 缺失时的失败票据:落库 failed 记录并补发 status 与 finish 事件。
382
+ */
383
+ failTicketForMissingAction(args) {
384
+ const { target, input, options, targetPackageId, targetActionId, designatedRunId, effectiveClock } = args;
385
+ const runId = designatedRunId || randomUUID();
386
+ const now = (effectiveClock?.now() ?? new Date()).toISOString();
387
+ const error = target.resolveError || {
388
+ code: ACTION_NOT_FOUND,
389
+ message: `Action '${targetActionId}' not found in package '${targetPackageId}'`,
390
+ };
391
+ const rootRunId = options.rootRunId || options.parentRunId || runId;
392
+ const initialRun = {
393
+ id: runId,
394
+ rootRunId,
395
+ parentRunId: options.parentRunId,
396
+ packageId: targetPackageId,
397
+ packageInstanceId: targetPackageId,
398
+ actionId: targetActionId,
399
+ generationId: "1",
400
+ ownerId: this.ownerId,
401
+ hostSessionId: options.hostSessionId || this.hostSessionId,
402
+ status: "failed",
403
+ input,
404
+ error,
405
+ startedAt: now,
406
+ finishedAt: now,
407
+ };
408
+ try {
409
+ target.runner.getStorage().createRun(initialRun);
410
+ }
411
+ catch (err) {
412
+ const repErr = new Error(`RUN_REPOSITORY_UNAVAILABLE: Failed to initialize run record in repository: ${err?.message || String(err)}`);
413
+ repErr.code = RUN_REPOSITORY_UNAVAILABLE;
414
+ repErr.details = { originalError: err?.message };
415
+ throw repErr;
416
+ }
417
+ this.eventSink.emit({
418
+ runId,
419
+ rootRunId,
420
+ sequence: 0,
421
+ timestamp: now,
422
+ type: "status",
423
+ status: "failed",
424
+ });
425
+ const errEvt = {
426
+ runId,
427
+ rootRunId,
428
+ sequence: 1,
429
+ timestamp: now,
430
+ type: "finish",
431
+ result: {
432
+ ok: false,
433
+ runId,
434
+ error,
435
+ },
436
+ };
437
+ this.eventSink.emit(errEvt);
438
+ return {
439
+ runId,
440
+ status: "failed",
441
+ result: Promise.resolve({
442
+ ok: false,
443
+ runId,
444
+ error,
445
+ }),
446
+ };
447
+ }
448
+ /**
449
+ * 创建执行事件桥:事件序列号分配、进度报告器与双写日志适配器。
450
+ */
451
+ createEventBridge(args) {
452
+ const { runId, options, effectiveClock } = args;
453
+ let sequence = 0;
454
+ const emitEvent = (payload) => {
455
+ const evt = {
456
+ ...payload,
457
+ runId,
458
+ rootRunId: options.rootRunId || options.parentRunId || runId,
459
+ sequence: sequence++,
460
+ timestamp: (effectiveClock?.now() ?? new Date()).toISOString(),
461
+ };
462
+ this.eventSink.emit(evt);
463
+ };
464
+ const progressReporter = {
465
+ report(current, total, message) {
466
+ options.progress?.report(current, total, message);
467
+ emitEvent({
468
+ type: "progress",
469
+ current,
470
+ total,
471
+ message,
472
+ });
473
+ },
474
+ };
475
+ const executionLogger = {
476
+ debug: (message, data) => {
477
+ this.logger?.debug(message, data);
478
+ options.logger?.debug(message, data);
479
+ emitEvent({
480
+ type: "log",
481
+ level: "debug",
482
+ message,
483
+ data: data,
484
+ });
485
+ },
486
+ info: (message, data) => {
487
+ this.logger?.info(message, data);
488
+ options.logger?.info(message, data);
489
+ emitEvent({
490
+ type: "log",
491
+ level: "info",
492
+ message,
493
+ data: data,
494
+ });
495
+ },
496
+ warn: (message, data) => {
497
+ this.logger?.warn(message, data);
498
+ options.logger?.warn(message, data);
499
+ emitEvent({
500
+ type: "log",
501
+ level: "warn",
502
+ message,
503
+ data: data,
504
+ });
505
+ },
506
+ error: (message, data) => {
507
+ this.logger?.error(message, data);
508
+ options.logger?.error(message, data);
509
+ emitEvent({
510
+ type: "log",
511
+ level: "error",
512
+ message,
513
+ data: data,
514
+ });
515
+ },
516
+ };
517
+ return { emitEvent, progressReporter, executionLogger };
518
+ }
519
+ /**
520
+ * 监听执行句柄终态:映射终态状态、补发 status 与 finish 事件并注销活跃运行。
521
+ */
522
+ watchHandleCompletion(handle, activeItem, bridge) {
523
+ handle.result
524
+ .then((result) => {
525
+ const finalStatus = result.ok
526
+ ? "success"
527
+ : result.error?.code === ACTION_TIMEOUT
528
+ ? "timed_out"
529
+ : result.error?.code === ACTION_CANCELLED
530
+ ? "cancelled"
531
+ : "failed";
532
+ activeItem.status = finalStatus;
533
+ bridge.emitEvent({ type: "status", status: finalStatus });
534
+ bridge.emitEvent({ type: "finish", result });
535
+ })
536
+ .catch((err) => {
537
+ activeItem.status = "failed";
538
+ bridge.emitEvent({ type: "status", status: "failed" });
539
+ bridge.emitEvent({
540
+ type: "finish",
541
+ result: {
542
+ ok: false,
543
+ runId: handle.runId,
544
+ error: {
545
+ code: UNHANDLED_EXECUTION_ERROR,
546
+ message: err?.message || String(err),
547
+ },
548
+ },
549
+ });
550
+ })
551
+ .finally(() => {
552
+ if (activeItem.signal && activeItem.onAbort) {
553
+ activeItem.signal.removeEventListener("abort", activeItem.onAbort);
554
+ activeItem.onAbort = undefined;
555
+ }
556
+ this.activeRuns.delete(handle.runId);
557
+ });
558
+ }
559
+ async get(runId) {
560
+ const record = this.storage.getRun(runId);
561
+ if (record)
562
+ return record;
563
+ // 检查所有已解析的目标包 Runner 存储
564
+ const visited = new Set([this._runner]);
565
+ const queue = [this._runner];
566
+ while (queue.length > 0) {
567
+ const runner = queue.shift();
568
+ for (const [_, childRunner] of runner.getPackageRunners()) {
569
+ if (!visited.has(childRunner)) {
570
+ visited.add(childRunner);
571
+ queue.push(childRunner);
572
+ const childRecord = childRunner.getStorage().getRun(runId);
573
+ if (childRecord) {
574
+ return childRecord;
575
+ }
576
+ }
577
+ }
578
+ }
579
+ return undefined;
580
+ }
581
+ async cancel(runId, reason) {
582
+ const active = this.activeRuns.get(runId);
583
+ if (!active) {
584
+ const record = await this.get(runId);
585
+ if (record) {
586
+ return { outcome: "already_terminal", runId, status: record.status };
587
+ }
588
+ return { outcome: "not_found", runId };
589
+ }
590
+ if (active.signal && active.onAbort) {
591
+ active.signal.removeEventListener("abort", active.onAbort);
592
+ active.onAbort = undefined;
593
+ }
594
+ active.controller.abort(new Error(reason || "Execution cancelled"));
595
+ active.handle.cancel(reason);
596
+ return { outcome: "requested", runId };
597
+ }
598
+ events(runId, options = {}) {
599
+ return this.eventSink.subscribe(runId, options);
600
+ }
601
+ async close(options = {}) {
602
+ this.isClosing = true;
603
+ this.reservedSlots = 0;
604
+ const graceMs = options.graceMs ?? 5000;
605
+ for (const [_, active] of this.activeRuns) {
606
+ active.controller.abort(new Error("Service shutting down"));
607
+ active.handle.cancel("Service shutting down");
608
+ }
609
+ if (this.activeRuns.size > 0) {
610
+ const waitPromise = Promise.all(Array.from(this.activeRuns.values()).map((a) => a.handle.result.catch(() => { })));
611
+ let timeoutTimer;
612
+ const timeoutPromise = new Promise((resolve) => {
613
+ timeoutTimer = setTimeout(resolve, graceMs);
614
+ });
615
+ try {
616
+ await Promise.race([waitPromise, timeoutPromise]);
617
+ }
618
+ finally {
619
+ if (timeoutTimer !== undefined) {
620
+ clearTimeout(timeoutTimer);
621
+ }
622
+ }
623
+ }
624
+ this.activeRuns.clear();
625
+ if (this.ownsStorage && this.storage && typeof this.storage.close === "function") {
626
+ await this.storage.close();
627
+ }
628
+ if (this.ownsGlobalStorage && this.globalStorage && typeof this.globalStorage.close === "function") {
629
+ await this.globalStorage.close();
630
+ }
631
+ }
632
+ }
633
+ export { DefaultExecutionService as ExecutionService };