@actiondock/core 2.10.0 → 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 (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/ipc/host.js CHANGED
@@ -16,8 +16,9 @@ const IPC_METHOD_HANDLERS = {
16
16
  describePlaybook: async (service, a) => service.discovery.describePlaybook(a[0]),
17
17
  runAction: async (service, a) => service.execution.run(a[0], a[1], a[2]),
18
18
  startAction: async (service, a) => service.execution.start(a[0], a[1], a[2]),
19
- listRuns: async (service, a) => service.runs.list(a[0]),
20
- getRun: async (service, a) => service.runs.get(a[0]),
19
+ listRuns: async (service, a) => service.runs.list(a[0], a[1]),
20
+ countRuns: async (service, a) => typeof service.runs.count === "function" ? service.runs.count(a[0], a[1]) : undefined,
21
+ getRun: async (service, a) => service.runs.get(a[0], a[1]),
21
22
  cancelRun: async (service, a) => service.runs.cancel(a[0], a[1]),
22
23
  clearRuns: async (service, a) => service.runs.clear ? await service.runs.clear(a[0]) : 0,
23
24
  cleanExpiredRuns: async (service, a) => service.runs.cleanExpired ? await service.runs.cleanExpired(a[0]) : 0,
@@ -75,8 +76,11 @@ export async function serveParentIpc(service) {
75
76
  return arg;
76
77
  }
77
78
  const { [IPC_SIGNAL_MARKER]: _marker, ...rest } = arg;
78
- const controller = new AbortController();
79
- activeControllers.set(id, controller);
79
+ let controller = activeControllers.get(id);
80
+ if (!controller) {
81
+ controller = new AbortController();
82
+ activeControllers.set(id, controller);
83
+ }
80
84
  return { ...rest, signal: controller.signal };
81
85
  };
82
86
  // 监听父进程断连或退出信号
@@ -119,15 +123,23 @@ export async function serveParentIpc(service) {
119
123
  }
120
124
  }
121
125
  catch (err) {
126
+ const isAbort = err?.name === "AbortError" ||
127
+ err?.code === "ABORT_ERR" ||
128
+ err?.code === "EXECUTION_ABORTED" ||
129
+ err?.code === "OPERATION_ABORTED";
122
130
  const response = {
123
131
  id,
124
132
  type: "response",
125
133
  ok: false,
126
134
  error: {
127
- code: err?.code || SERVICE_ERROR,
135
+ code: err?.code || (isAbort ? "OPERATION_ABORTED" : SERVICE_ERROR),
128
136
  message: err?.message || String(err),
129
137
  stack: err?.stack,
130
- details: err?.details,
138
+ details: err?.details !== undefined
139
+ ? (typeof err?.details === "object" && err?.details !== null && err?.name === "AbortError"
140
+ ? { name: err?.name, ...err.details }
141
+ : err.details)
142
+ : (err?.name === "AbortError" ? { name: err?.name } : undefined),
131
143
  },
132
144
  };
133
145
  if (process.send) {
@@ -144,7 +156,9 @@ export async function serveParentIpc(service) {
144
156
  const abortMsg = msg;
145
157
  const controller = activeControllers.get(abortMsg.id);
146
158
  if (controller) {
147
- controller.abort(new Error(abortMsg.reason || "Execution was aborted via IPC"));
159
+ const abortError = new Error(abortMsg.reason || "Execution was aborted via IPC");
160
+ abortError.name = "AbortError";
161
+ controller.abort(abortError);
148
162
  }
149
163
  }
150
164
  else if (msg.type === "close") {
@@ -2,6 +2,10 @@ import { type ChildProcess } from "node:child_process";
2
2
  import type { PackageInfo } from "../package/types.js";
3
3
  import type { ActionDockService, ConfigPort, DiscoveryPort, ExecutionPort, RunsPort, StatePort } from "../service/types.js";
4
4
  import type { IpcServiceOptions } from "./types.js";
5
+ /**
6
+ * 创建标准 AbortError 异常对象。
7
+ */
8
+ export declare function createAbortError(reason?: unknown): Error;
5
9
  /**
6
10
  * 跨进程取消信号占位标记字段名。
7
11
  * AbortSignal 不可序列化,序列化 options 时把 signal 字段替换为该标记,
@@ -12,6 +16,16 @@ export declare const IPC_SIGNAL_MARKER = "__ipcSignal";
12
16
  * 判断执行选项中是否携带跨进程取消信号占位标记。
13
17
  */
14
18
  export declare function hasIpcSignalMarker(value: unknown): value is Record<string, unknown>;
19
+ /**
20
+ * 序列化执行选项与查询选项的边界收敛函数。
21
+ *
22
+ * 序列化边界决策:
23
+ * - 剥离不可序列化对象:AbortSignal 无法通过 Node IPC 通道传递,在此转换为 IPC_SIGNAL_MARKER 占位标记,由远端宿主根据标记重建 AbortController 接入取消链路。
24
+ * - 过滤非安全值:全面剔除函数、undefined、Symbol 与 BigInt 等无法安全跨进程传递的值,防止 IPC 序列化异常或意外的远端反射。
25
+ * - 循环引用防御:递归遍历嵌套对象(如 config 配置项或 filter 筛选条件)时基于调用栈祖先集合进行环检测,阻断循环引用进入 IPC 通道,且不误伤跨字段复用的合法对象。
26
+ * - 支持跨进程选项透传:安全保留 RunOptions 与 RunReadOptions、ListRunsOptions 中的合法字段(包含 timeoutMs、config、requestId、limit、offset、packageId、action、status、since、until、requestIds、filter 等)。
27
+ */
28
+ export declare function markIpcSignal<T extends Record<string, any>>(options?: T): Record<string, unknown> | undefined;
15
29
  /**
16
30
  * 基于 Node IPC 监督进程通信通道的 ActionDockService 实现。
17
31
  * 遵循标准 Service Ports 体系,隔离子进程生命周期并暴露结构化服务端口。
@@ -1,7 +1,20 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { fork } from "node:child_process";
3
3
  import { DiagnosticForwarder } from "./diagnostic.js";
4
- import { ActionDockError, CAPABILITY_UNAVAILABLE, EXECUTION_ABORTED, HOST_PROCESS_EXITED, IPC_ERROR, SERVICE_CLOSED, } from "../errors.js";
4
+ import { ActionDockError, CAPABILITY_UNAVAILABLE, EXECUTION_ABORTED, HOST_PROCESS_EXITED, INVALID_ARGUMENT, IPC_ERROR, SERVICE_CLOSED, } from "../errors.js";
5
+ /**
6
+ * 创建标准 AbortError 异常对象。
7
+ */
8
+ export function createAbortError(reason) {
9
+ const message = reason instanceof Error
10
+ ? (reason.message || "The operation was aborted")
11
+ : reason !== undefined
12
+ ? String(reason)
13
+ : "The operation was aborted";
14
+ const err = new Error(message, reason !== undefined ? { cause: reason } : undefined);
15
+ err.name = "AbortError";
16
+ return err;
17
+ }
5
18
  /**
6
19
  * 跨进程取消信号占位标记字段名。
7
20
  * AbortSignal 不可序列化,序列化 options 时把 signal 字段替换为该标记,
@@ -17,22 +30,79 @@ export function hasIpcSignalMarker(value) {
17
30
  value[IPC_SIGNAL_MARKER] === true);
18
31
  }
19
32
  /**
20
- * 序列化执行选项:剥离不可序列化的 AbortSignal 并写入占位标记。
33
+ * 安全收敛可跨进程传递的数据值,过滤函数、undefined、Symbol、BigInt 并防御循环引用。
34
+ */
35
+ function sanitizeIpcValue(value, ancestors) {
36
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
37
+ return value;
38
+ }
39
+ if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
40
+ return undefined;
41
+ }
42
+ if (typeof value === "object") {
43
+ if (ancestors.has(value)) {
44
+ return undefined;
45
+ }
46
+ ancestors.add(value);
47
+ try {
48
+ if (Array.isArray(value)) {
49
+ const result = [];
50
+ for (const item of value) {
51
+ const sanitized = sanitizeIpcValue(item, ancestors);
52
+ if (sanitized !== undefined) {
53
+ result.push(sanitized);
54
+ }
55
+ }
56
+ return result;
57
+ }
58
+ const result = {};
59
+ for (const [key, val] of Object.entries(value)) {
60
+ const sanitized = sanitizeIpcValue(val, ancestors);
61
+ if (sanitized !== undefined) {
62
+ result[key] = sanitized;
63
+ }
64
+ }
65
+ return result;
66
+ }
67
+ finally {
68
+ ancestors.delete(value);
69
+ }
70
+ }
71
+ return undefined;
72
+ }
73
+ /**
74
+ * 序列化执行选项与查询选项的边界收敛函数。
75
+ *
76
+ * 序列化边界决策:
77
+ * - 剥离不可序列化对象:AbortSignal 无法通过 Node IPC 通道传递,在此转换为 IPC_SIGNAL_MARKER 占位标记,由远端宿主根据标记重建 AbortController 接入取消链路。
78
+ * - 过滤非安全值:全面剔除函数、undefined、Symbol 与 BigInt 等无法安全跨进程传递的值,防止 IPC 序列化异常或意外的远端反射。
79
+ * - 循环引用防御:递归遍历嵌套对象(如 config 配置项或 filter 筛选条件)时基于调用栈祖先集合进行环检测,阻断循环引用进入 IPC 通道,且不误伤跨字段复用的合法对象。
80
+ * - 支持跨进程选项透传:安全保留 RunOptions 与 RunReadOptions、ListRunsOptions 中的合法字段(包含 timeoutMs、config、requestId、limit、offset、packageId、action、status、since、until、requestIds、filter 等)。
21
81
  */
22
- function markIpcSignal(options) {
23
- if (!options)
82
+ export function markIpcSignal(options) {
83
+ if (!options || typeof options !== "object")
24
84
  return undefined;
25
- const clean = {};
26
- if (options.timeoutMs !== undefined)
27
- clean.timeoutMs = options.timeoutMs;
28
- if (options.config !== undefined)
29
- clean.config = options.config;
30
- if (options.requestId !== undefined)
31
- clean.requestId = options.requestId;
32
- if (options.signal) {
33
- clean[IPC_SIGNAL_MARKER] = true;
85
+ const ancestors = new Set();
86
+ ancestors.add(options);
87
+ try {
88
+ const clean = {};
89
+ for (const [key, value] of Object.entries(options)) {
90
+ if (key === "signal") {
91
+ if (value) {
92
+ clean[IPC_SIGNAL_MARKER] = true;
93
+ }
94
+ continue;
95
+ }
96
+ const sanitized = sanitizeIpcValue(value, ancestors);
97
+ if (sanitized !== undefined) {
98
+ clean[key] = sanitized;
99
+ }
100
+ }
101
+ return clean;
102
+ }
103
+ finally {
104
+ ancestors.delete(options);
34
105
  }
35
- return clean;
36
106
  }
37
107
  /**
38
108
  * 基于 Node IPC 监督进程通信通道的 ActionDockService 实现。
@@ -51,8 +121,6 @@ export class IpcActionDockService {
51
121
  management;
52
122
  constructor(options) {
53
123
  this.forwarder = new DiagnosticForwarder({
54
- maxBytes: options.maxDiagnosticBytes,
55
- maxRateBytesPerSec: options.maxDiagnosticRate,
56
124
  target: options.diagnosticTarget,
57
125
  });
58
126
  if (options.childProcess) {
@@ -66,7 +134,7 @@ export class IpcActionDockService {
66
134
  });
67
135
  }
68
136
  else {
69
- throw new Error("IpcActionDockService requires either 'childProcess' or 'scriptPath'");
137
+ throw new ActionDockError(INVALID_ARGUMENT, "IpcActionDockService requires either 'childProcess' or 'scriptPath'");
70
138
  }
71
139
  if (this.child.stdout) {
72
140
  this.forwarder.attach(this.child.stdout, "stdout");
@@ -93,7 +161,13 @@ export class IpcActionDockService {
93
161
  pending.resolve(resp.data);
94
162
  }
95
163
  else {
96
- const err = new ActionDockError(resp.error?.code || IPC_ERROR, resp.error?.message || "IPC Service call failed", resp.error?.details);
164
+ const isAbort = resp.error?.code === "EXECUTION_ABORTED" ||
165
+ resp.error?.code === "OPERATION_ABORTED" ||
166
+ resp.error?.code === "ABORT_ERR" ||
167
+ resp.error?.details?.name === "AbortError";
168
+ const err = isAbort
169
+ ? createAbortError(resp.error?.message)
170
+ : new ActionDockError(resp.error?.code || IPC_ERROR, resp.error?.message || "IPC Service call failed", resp.error?.details);
97
171
  pending.reject(err);
98
172
  }
99
173
  }
@@ -129,7 +203,7 @@ export class IpcActionDockService {
129
203
  * 生成方法级端口转发器:把端口方法调用映射为 callRemote 远端调用。
130
204
  * 方法名与参数列表的对应关系即唯一事实源,杜绝手写转发样板膨胀。
131
205
  */
132
- const forward = (method, args) => self.callRemote(method, args);
206
+ const forward = (method, args, signal) => self.callRemote(method, args, signal);
133
207
  this.discovery = {
134
208
  async listPackages() {
135
209
  return forward("listPackages", []);
@@ -171,11 +245,38 @@ export class IpcActionDockService {
171
245
  },
172
246
  };
173
247
  this.runs = {
174
- async list(query) {
175
- return forward("listRuns", [query]);
248
+ async list(query, options) {
249
+ const signal = options?.signal ?? query?.signal;
250
+ if (signal?.aborted) {
251
+ throw createAbortError(signal.reason);
252
+ }
253
+ const serializableQuery = markIpcSignal(query);
254
+ const serializableOptions = markIpcSignal(options);
255
+ const args = serializableOptions !== undefined
256
+ ? [serializableQuery ?? {}, serializableOptions]
257
+ : [serializableQuery];
258
+ return forward("listRuns", args, signal);
259
+ },
260
+ async count(query, options) {
261
+ const signal = options?.signal ?? query?.signal;
262
+ if (signal?.aborted) {
263
+ throw createAbortError(signal.reason);
264
+ }
265
+ const serializableQuery = markIpcSignal(query);
266
+ const serializableOptions = markIpcSignal(options);
267
+ const args = serializableOptions !== undefined
268
+ ? [serializableQuery ?? {}, serializableOptions]
269
+ : [serializableQuery];
270
+ return forward("countRuns", args, signal);
176
271
  },
177
- async get(runId) {
178
- const res = await forward("getRun", [runId]);
272
+ async get(runId, options) {
273
+ const signal = options?.signal ?? options?.query?.signal;
274
+ if (signal?.aborted) {
275
+ throw createAbortError(signal.reason);
276
+ }
277
+ const serializableOptions = markIpcSignal(options);
278
+ const args = serializableOptions !== undefined ? [runId, serializableOptions] : [runId];
279
+ const res = await forward("getRun", args, signal);
179
280
  return res ?? undefined;
180
281
  },
181
282
  async cancel(runId, reason) {
@@ -243,9 +344,7 @@ export class IpcActionDockService {
243
344
  }
244
345
  await this.readyPromise;
245
346
  if (signal?.aborted) {
246
- const err = new Error(signal.reason ? String(signal.reason) : "Operation aborted");
247
- err.name = "AbortError";
248
- throw err;
347
+ throw createAbortError(signal.reason);
249
348
  }
250
349
  const id = randomUUID();
251
350
  const message = {
@@ -258,6 +357,13 @@ export class IpcActionDockService {
258
357
  if (signal && !signal.aborted) {
259
358
  onAbort = () => {
260
359
  this.sendAbort(id);
360
+ if (method !== "runAction") {
361
+ const pending = this.pendingCalls.get(id);
362
+ if (pending) {
363
+ this.pendingCalls.delete(id);
364
+ pending.reject(createAbortError(signal.reason));
365
+ }
366
+ }
261
367
  };
262
368
  signal.addEventListener("abort", onAbort, { once: true });
263
369
  }
@@ -46,10 +46,6 @@ export interface IpcServiceOptions {
46
46
  cwd?: string;
47
47
  /** 环境变量 */
48
48
  env?: NodeJS.ProcessEnv;
49
- /** 诊断流最大缓冲字节数 */
50
- maxDiagnosticBytes?: number;
51
- /** 诊断流每秒最大字节速率 */
52
- maxDiagnosticRate?: number;
53
49
  /** 诊断流目标输出 */
54
50
  diagnosticTarget?: any;
55
51
  /** 是否启用管理端口(默认 true) */
@@ -10,13 +10,12 @@ export { createPackageRuntime, DefaultPackageRuntime, } from "./runtime.js";
10
10
  export { createNonClosingStorageView } from "./storage-view.js";
11
11
  export { findExecutable } from "../utils/index.js";
12
12
  export type { PackageRuntime, PackageRuntimeOptions, PackageInfo, ActionSpec, ActionSummary, PlaybookSpec, PlaybookSummary, } from "./types.js";
13
- export type { RuntimePlatform, FileSystem, StorageFactory, StorageFactoryOptions, GlobalStorageFactoryOptions, } from "../platform/types.js";
14
- export { type Clock, SystemClock, } from "../runtime/clock.js";
15
- export type { ModuleLoader } from "../node/module-loader.js";
13
+ export type { RuntimePlatform, StorageFactory, StorageFactoryOptions, GlobalStorageFactoryOptions, } from "../platform/types.js";
14
+ export { type Clock, SystemClock, } from "../storage/clock.js";
15
+ export type { ModuleLoader } from "../platform/module-loader.js";
16
16
  export { InMemoryEventSink, type EventSink, } from "../runtime/events.js";
17
17
  export { ProcessManager, type ProcessOwner, } from "../process/process-manager.js";
18
- export type { ProcessExecutor } from "../runtime/process.js";
19
- export { NodeProcessExecutor } from "../process/process-executor.js";
18
+ export type { ProcessAPI as ProcessExecutor } from "@actiondock/sdk";
20
19
  export type { ProcessDriver, ProcessDriverCallbacks, ProcessDriverHandle, ProcessHandle, ProcessObserver, } from "../process/driver.js";
21
20
  export { createStorage, resolveDatabasePath, } from "../storage/index.js";
22
21
  export { SqliteRuntimeStorage } from "../storage/sqlite.js";
@@ -9,10 +9,9 @@
9
9
  export { createPackageRuntime, DefaultPackageRuntime, } from "./runtime.js";
10
10
  export { createNonClosingStorageView } from "./storage-view.js";
11
11
  export { findExecutable } from "../utils/index.js";
12
- export { SystemClock, } from "../runtime/clock.js";
12
+ export { SystemClock, } from "../storage/clock.js";
13
13
  export { InMemoryEventSink, } from "../runtime/events.js";
14
14
  export { ProcessManager, } from "../process/process-manager.js";
15
- export { NodeProcessExecutor } from "../process/process-executor.js";
16
15
  // 存储契约与驱动
17
16
  export { createStorage, resolveDatabasePath, } from "../storage/index.js";
18
17
  export { SqliteRuntimeStorage } from "../storage/sqlite.js";
@@ -7,6 +7,13 @@ import type { ActionSpec, ActionSummary, ConfigValueView, HostManagedPackageRunt
7
7
  /**
8
8
  * ActionDock 统一包运行时默认实现。
9
9
  * 封装并管理单个 Action Package 的执行引擎、静态元数据索引、配置与状态存储生命周期。
10
+ *
11
+ * 资源按需装配契约:
12
+ * - 构造函数只完成身份、清单与内存注入动作的归一化,不打开数据库、
13
+ * 不创建进程接口、不注册模块加载钩子、不实例化执行服务;
14
+ * - 包存储、执行服务、全局存储与配置解析器拆分为内部资源槽位,
15
+ * 首次实际调用时准备并缓存复用;
16
+ * - info / list / describe / 规程发现等静态查询全程零资源创建。
10
17
  */
11
18
  export declare class DefaultPackageRuntime implements HostManagedPackageRuntime {
12
19
  readonly identity: PackageIdentity;
@@ -15,15 +22,20 @@ export declare class DefaultPackageRuntime implements HostManagedPackageRuntime
15
22
  readonly generationId: string;
16
23
  readonly packageRoot?: string;
17
24
  readonly projectConfig: ProjectConfig;
25
+ readonly inMemory: boolean;
18
26
  private readonly platform;
19
- private readonly storage;
20
- private readonly globalStorage?;
21
- private readonly executionService;
22
27
  private readonly actionsMap;
23
28
  private readonly options;
24
- private runtimeConfig;
25
29
  private isClosed;
26
- private injectedGlobalStorage;
30
+ private storageSlot;
31
+ private executionSlot;
32
+ /** 全局存储:显式注入或首次准备后固定,配合 ownership 判定关闭归属 */
33
+ private globalStorageRes?;
34
+ private runtimeConfig?;
35
+ /** 执行委托暂存:执行服务尚未创建时只保存,不触发资源初始化 */
36
+ private pendingInvoker?;
37
+ /** Host 会话级恢复标记:存储准备阶段收敛执行一次,避免重复执行恢复链 */
38
+ private recoveredForHostSession;
27
39
  /** 静态清单索引缓存:包静态事实(根目录、配置、内存注入集合)在实例生命周期内不变,解析结果同实例内复用 */
28
40
  private staticActionIndex?;
29
41
  private staticPlaybookIndex?;
@@ -39,6 +51,58 @@ export declare class DefaultPackageRuntime implements HostManagedPackageRuntime
39
51
  * 聚合逻辑委托 static-index 单一事实源;同实例内复用首次解析结果。
40
52
  */
41
53
  private getStaticPlaybookMap;
54
+ private assertOpen;
55
+ /**
56
+ * 准备包存储:打开(或复用注入的)包数据库,等待可选的 ensureInitialized,
57
+ * 并在持有者模式下完成运行记录恢复(允许返回 Promise 的恢复接口必须被等待)。
58
+ * 并发首次调用共享同一在途 Promise;失败时释放新建资源、清空失败 Promise、透传原始错误。
59
+ */
60
+ private ensureStorage;
61
+ private openStorageAsync;
62
+ /** 构造包存储工厂选项(单一事实源,异步与同步打开路径共用) */
63
+ private buildStorageOptions;
64
+ /**
65
+ * Host 会话级运行记录恢复:同实例生命周期内最多执行一次。
66
+ * 允许返回 Promise 的恢复接口被等待:恢复完成(或失败透传)前存储不发布;
67
+ * 恢复失败沿用既有可观测处理(告警透传),不因异步化静默丢失。
68
+ */
69
+ private recoverForHostSessionOnce;
70
+ /**
71
+ * 准备全局存储:显式注入优先,其次 Host 共享提供函数,最后独立使用平台存储工厂。
72
+ * 同步契约:RuntimeConfig 与 ctx.config.get() 的读取是同步的,故全局库必须同步可得。
73
+ */
74
+ private ensureGlobalStorageSync;
75
+ /**
76
+ * 异步准备全局存储:供执行服务装配前调用,保证可选的异步初始化完成。
77
+ * 同步路径(RuntimeConfig 读取)仍走 ensureGlobalStorageSync 复用同一实例。
78
+ */
79
+ private ensureGlobalStorage;
80
+ /**
81
+ * 准备配置解析器:依赖包存储与全局存储(五层优先级链既有实现不变)。
82
+ * 同步契约:RuntimeConfig 与 ctx.config.get() 的读取是同步的,故同步打开包库;
83
+ * 若异步准备已在途,同步路径仅在实例已发布后复用,避免双打开竞态。
84
+ */
85
+ private ensureRuntimeConfig;
86
+ /**
87
+ * 获取已就绪的包存储(同步契约专用):
88
+ * - 已发布实例直接复用;
89
+ * - 异步准备在途时等待其结算后复用同一实例;
90
+ * - 未准备时同步打开并登记在途 Promise 已完成状态。
91
+ * 单一事实源:同步打开也走 buildStorageOptions,并补齐会话恢复等待语义的同步近似。
92
+ */
93
+ private getStorageAlreadyPrepared;
94
+ /** 同步打开包存储(仅当异步准备从未发起时使用) */
95
+ private openStorageSync;
96
+ /**
97
+ * 准备执行服务:等待存储与全局存储就绪后创建唯一 DefaultExecutionService。
98
+ * 失败时只回收执行初始化独自新建的资源,不关闭仍被状态查询使用的包存储。
99
+ */
100
+ private ensureExecutionService;
101
+ private buildExecutionService;
102
+ /** 异步等待存储准备完成(供执行准备与异步入口复用;单一赋值入口) */
103
+ private prepareStorageAsync;
104
+ /** 获取已创建的执行服务(若存在),不触发创建 */
105
+ private peekExecutionService;
42
106
  info(options?: {
43
107
  exposeDebugInfo?: boolean;
44
108
  }): Promise<PackageInfo>;
@@ -50,6 +114,8 @@ export declare class DefaultPackageRuntime implements HostManagedPackageRuntime
50
114
  runAction(id: string, input: JsonValue, options?: RunOptions): Promise<ExecutionResult>;
51
115
  startAction(id: string, input: JsonValue, options?: RunOptions): Promise<ExecutionTicket>;
52
116
  startInvocation(actionId: string, input: JsonValue, context: InvocationContext): Promise<ExecutionTicket>;
117
+ /** 执行前统一准备:关闭检查与执行服务就绪 */
118
+ private prepareExecution;
53
119
  getRun(runId: string): Promise<RunRecord | undefined>;
54
120
  cancelRun(runId: string, reason?: string): Promise<CancelResult>;
55
121
  events(runId: string, options?: {
@@ -58,42 +124,47 @@ export declare class DefaultPackageRuntime implements HostManagedPackageRuntime
58
124
  maxQueueSize?: number;
59
125
  }): AsyncIterable<ExecutionEvent>;
60
126
  setActionInvoker(invoker?: ActionInvoker): void;
127
+ /**
128
+ * Host 持有者路径预热入口:完成包存储准备(含运行记录恢复),不创建执行服务。
129
+ * 延迟装配下旁观路径零资源创建,持有者工厂在返回前调用本方法保持既有初始化时机。
130
+ */
131
+ prepareResources(): Promise<void>;
132
+ /**
133
+ * 显式触发运行记录恢复(Host 持有者路径兼容入口)。
134
+ * 延迟装配下恢复已收敛到存储准备路径,本方法确保存储已就绪后按需补一次会话恢复。
135
+ */
61
136
  recoverDeadSessionRuns(sessionId?: string): void;
62
137
  listConfig(): Promise<ConfigValueView[]>;
63
138
  getConfig(key: string): Promise<ConfigValueView>;
64
139
  setConfig(key: string, value: JsonValue): Promise<void>;
65
140
  deleteConfig(key: string): Promise<boolean>;
66
141
  /**
67
- * 状态作用域统一解析辅助函数(单一事实源)。
68
- *
69
- * 完成重载消歧后委托 mergeStateScope 计算最终生效的命名空间:
70
- * - (actionId, key, options) 形态:位置 actionId 参与冲突校验;
71
- * - (key, options) 扁平形态:首参是状态键,options.actionId 属合法显式指定。
72
- */
73
- private resolveStateScope;
74
- /**
75
- * 合并位置 actionId 与 options.actionId 并计算最终命名空间(单一事实源)。
76
- *
77
- * - positionalActionId 非 undefined:调用形态携带位置 actionId,
78
- * options.actionId 与其冲突时抛出异常(空串同样参与冲突校验);
79
- * - positionalActionId 为 undefined:无位置 actionId 的扁平调用形态,
80
- * options.actionId 属合法显式指定,直接采纳。
142
+ * 将可能携带命名空间前缀的状态键解码为 (namespace, key)。
143
+ * 解码失败时按无命名空间处理。
81
144
  */
82
- private mergeStateScope;
145
+ private decodeStateKeyParts;
146
+ private hasExplicitStateScope;
147
+ private computeStateNamespace;
148
+ private getStateInternal;
149
+ private setStateInternal;
150
+ private deleteStateInternal;
151
+ private listStateKeysInternal;
152
+ private clearStateInternal;
83
153
  getState<T extends JsonValue = JsonValue>(key: string, options?: StateScopeOptions): Promise<T | undefined>;
84
154
  getState<T extends JsonValue = JsonValue>(actionId: string, key: string, options?: StateScopeOptions): Promise<T | undefined>;
85
155
  setState<T extends JsonValue = JsonValue>(key: string, value: T, options?: StateScopeOptions): Promise<void>;
86
- setState<T extends JsonValue = JsonValue>(actionId: string, key: string, value: T, options: StateScopeOptions): Promise<void>;
156
+ setState<T extends JsonValue = JsonValue>(actionId: string, key: string, value: T, options?: StateScopeOptions): Promise<void>;
87
157
  deleteState(key: string, options?: StateScopeOptions): Promise<boolean>;
88
158
  deleteState(actionId: string, key: string, options?: StateScopeOptions): Promise<boolean>;
89
159
  getActionState<T extends JsonValue = JsonValue>(actionId: string, key: string, options?: StateScopeOptions): Promise<T | undefined>;
90
160
  setActionState<T extends JsonValue = JsonValue>(actionId: string, key: string, value: T, options?: StateScopeOptions): Promise<void>;
91
161
  deleteActionState(actionId: string, key: string, options?: StateScopeOptions): Promise<boolean>;
92
162
  listStateKeys(options?: StateScopeOptions): Promise<string[]>;
93
- listStateKeys(actionId: string, options?: StateScopeOptions): Promise<string[]>;
163
+ listStateKeys(actionId?: string, options?: StateScopeOptions): Promise<string[]>;
94
164
  clearState(options?: StateScopeOptions): Promise<number>;
95
- clearState(actionId: string, options?: StateScopeOptions): Promise<number>;
165
+ clearState(actionId?: string, options?: StateScopeOptions): Promise<number>;
96
166
  listRuns(options?: ListRunsOptions): Promise<RunRecord[]>;
167
+ countRuns(options?: ListRunsOptions): Promise<number>;
97
168
  clearRuns(options?: {
98
169
  actionId?: string;
99
170
  status?: string;