@actiondock/core 2.7.0 → 2.8.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 (236) hide show
  1. package/README.md +61 -68
  2. package/dist/catalog/action-catalog.d.ts +56 -0
  3. package/dist/catalog/action-catalog.js +202 -0
  4. package/dist/catalog/discovery.d.ts +60 -0
  5. package/dist/catalog/discovery.js +119 -0
  6. package/dist/catalog/graph.d.ts +190 -0
  7. package/dist/catalog/graph.js +589 -0
  8. package/dist/catalog/index.d.ts +5 -5
  9. package/dist/catalog/index.js +5 -5
  10. package/dist/catalog/resolve-action.d.ts +43 -0
  11. package/dist/catalog/resolve-action.js +119 -0
  12. package/dist/catalog/resolve-playbook.d.ts +26 -0
  13. package/dist/catalog/resolve-playbook.js +114 -0
  14. package/dist/doctor/checks/project.js +27 -8
  15. package/dist/doctor/checks/registry.js +2 -2
  16. package/dist/doctor/checks/runtime.js +34 -1
  17. package/dist/errors.d.ts +102 -10
  18. package/dist/errors.js +103 -10
  19. package/dist/execution/service.d.ts +18 -16
  20. package/dist/execution/service.js +183 -252
  21. package/dist/execution/types.d.ts +25 -69
  22. package/dist/execution/types.js +2 -1
  23. package/dist/filter/intent.d.ts +8 -9
  24. package/dist/filter/intent.js +49 -10
  25. package/dist/graph/index.d.ts +4 -0
  26. package/dist/graph/index.js +4 -0
  27. package/dist/host/host.d.ts +74 -32
  28. package/dist/host/host.js +585 -336
  29. package/dist/host/routing.d.ts +15 -48
  30. package/dist/host/routing.js +90 -153
  31. package/dist/host/types.d.ts +50 -17
  32. package/dist/index.d.ts +21 -24
  33. package/dist/index.js +19 -23
  34. package/dist/input/advice.d.ts +5 -1
  35. package/dist/input/advice.js +163 -214
  36. package/dist/input/describe.d.ts +1 -1
  37. package/dist/input/flat-errors.d.ts +5 -6
  38. package/dist/input/flat-errors.js +6 -6
  39. package/dist/input/flat-materializer.js +42 -44
  40. package/dist/invocation/index.d.ts +2 -0
  41. package/dist/invocation/index.js +2 -0
  42. package/dist/invocation/policy.d.ts +157 -0
  43. package/dist/invocation/policy.js +231 -0
  44. package/dist/invocation/types.d.ts +172 -0
  45. package/dist/invocation/types.js +97 -0
  46. package/dist/ipc/diagnostic.d.ts +0 -12
  47. package/dist/ipc/diagnostic.js +0 -21
  48. package/dist/ipc/host.d.ts +4 -4
  49. package/dist/ipc/host.js +44 -47
  50. package/dist/ipc/index.d.ts +1 -1
  51. package/dist/ipc/index.js +1 -1
  52. package/dist/ipc/service.d.ts +40 -0
  53. package/dist/ipc/service.js +349 -0
  54. package/dist/ipc/types.d.ts +21 -15
  55. package/dist/node/index.d.ts +1 -0
  56. package/dist/node/index.js +1 -0
  57. package/dist/node/module-loader.d.ts +63 -0
  58. package/dist/node/module-loader.js +112 -0
  59. package/dist/package/index.d.ts +23 -0
  60. package/dist/package/index.js +18 -0
  61. package/dist/{app/app.d.ts → package/runtime.d.ts} +41 -20
  62. package/dist/{app/app.js → package/runtime.js} +140 -95
  63. package/dist/{app → package}/static-index.js +58 -41
  64. package/dist/package/storage-view.d.ts +13 -0
  65. package/dist/package/storage-view.js +22 -0
  66. package/dist/{app → package}/types.d.ts +76 -57
  67. package/dist/package/types.js +22 -0
  68. package/dist/platform/index.d.ts +1 -1
  69. package/dist/platform/index.js +1 -1
  70. package/dist/platform/node.d.ts +48 -0
  71. package/dist/platform/node.js +96 -0
  72. package/dist/platform/types.d.ts +0 -9
  73. package/dist/process/control-arbiter.d.ts +2 -1
  74. package/dist/process/control-arbiter.js +1 -2
  75. package/dist/process/driver.d.ts +9 -8
  76. package/dist/process/driver.js +11 -22
  77. package/dist/process/index.d.ts +2 -0
  78. package/dist/process/index.js +2 -0
  79. package/dist/process/managed-record.d.ts +2 -2
  80. package/dist/process/process-driver.d.ts +109 -0
  81. package/dist/process/process-driver.js +650 -0
  82. package/dist/process/process-executor.d.ts +47 -0
  83. package/dist/process/process-executor.js +296 -0
  84. package/dist/process/process-manager.js +48 -26
  85. package/dist/process/run-executor.js +12 -12
  86. package/dist/profile/client-actions.js +2 -2
  87. package/dist/profile/client-health.js +2 -2
  88. package/dist/profile/client-query.js +5 -6
  89. package/dist/profile/client-transport.d.ts +19 -17
  90. package/dist/profile/client-transport.js +47 -80
  91. package/dist/profile/client.d.ts +2 -2
  92. package/dist/profile/client.js +1 -1
  93. package/dist/profile/index.d.ts +1 -0
  94. package/dist/profile/index.js +1 -0
  95. package/dist/profile/manager.d.ts +5 -0
  96. package/dist/profile/manager.js +41 -6
  97. package/dist/project/closure.js +2 -2
  98. package/dist/project/index.d.ts +8 -1
  99. package/dist/project/index.js +10 -1
  100. package/dist/project/init.js +2 -2
  101. package/dist/project/loader.d.ts +1 -1
  102. package/dist/project/loader.js +26 -29
  103. package/dist/project/transactions.js +5 -10
  104. package/dist/registry/index.d.ts +3 -1
  105. package/dist/registry/index.js +2 -1
  106. package/dist/registry/lock.d.ts +28 -7
  107. package/dist/registry/lock.js +136 -16
  108. package/dist/registry/registry.d.ts +1 -5
  109. package/dist/registry/registry.js +1 -53
  110. package/dist/registry/scan.d.ts +0 -15
  111. package/dist/registry/scan.js +0 -59
  112. package/dist/registry/store.d.ts +42 -0
  113. package/dist/registry/store.js +43 -0
  114. package/dist/registry/types.d.ts +0 -24
  115. package/dist/runtime/action-collection.d.ts +1 -1
  116. package/dist/runtime/action-registry.d.ts +0 -4
  117. package/dist/runtime/action-registry.js +0 -7
  118. package/dist/runtime/context.js +5 -11
  119. package/dist/runtime/events.js +3 -6
  120. package/dist/runtime/identity.d.ts +22 -0
  121. package/dist/runtime/identity.js +10 -0
  122. package/dist/runtime/index.d.ts +1 -2
  123. package/dist/runtime/index.js +1 -2
  124. package/dist/runtime/module-loader.d.ts +2 -34
  125. package/dist/runtime/module-loader.js +1 -16
  126. package/dist/runtime/process.d.ts +6 -24
  127. package/dist/runtime/process.js +1 -118
  128. package/dist/runtime/run-persistence.js +2 -5
  129. package/dist/runtime/runner.d.ts +33 -93
  130. package/dist/runtime/runner.js +132 -364
  131. package/dist/runtime/standalone.d.ts +23 -8
  132. package/dist/runtime/standalone.js +100 -147
  133. package/dist/server/body.d.ts +16 -8
  134. package/dist/server/body.js +33 -16
  135. package/dist/server/dispatcher.d.ts +6 -10
  136. package/dist/server/dispatcher.js +24 -32
  137. package/dist/server/http-server.d.ts +87 -0
  138. package/dist/server/http-server.js +304 -0
  139. package/dist/server/index.d.ts +11 -0
  140. package/dist/server/index.js +10 -0
  141. package/dist/server/mcp-endpoint.d.ts +61 -0
  142. package/dist/server/mcp-endpoint.js +118 -0
  143. package/dist/server/routes/actions.js +34 -28
  144. package/dist/server/routes/common.d.ts +10 -14
  145. package/dist/server/routes/common.js +32 -56
  146. package/dist/server/routes/config.js +54 -64
  147. package/dist/server/routes/doctor.d.ts +1 -1
  148. package/dist/server/routes/doctor.js +10 -18
  149. package/dist/server/routes/health.d.ts +1 -1
  150. package/dist/server/routes/health.js +2 -3
  151. package/dist/server/routes/info.d.ts +2 -2
  152. package/dist/server/routes/info.js +12 -13
  153. package/dist/server/routes/playbooks.js +16 -29
  154. package/dist/server/routes/runs.js +95 -117
  155. package/dist/server/routes/state.js +63 -62
  156. package/dist/server/server.d.ts +2 -2
  157. package/dist/server/server.js +43 -227
  158. package/dist/server/types.d.ts +8 -19
  159. package/dist/service/factory.d.ts +12 -0
  160. package/dist/service/factory.js +85 -0
  161. package/dist/{target → service}/index.d.ts +1 -1
  162. package/dist/{target → service}/index.js +1 -1
  163. package/dist/service/local.d.ts +27 -0
  164. package/dist/service/local.js +138 -0
  165. package/dist/{target → service}/remote-errors.d.ts +3 -6
  166. package/dist/service/remote-errors.js +38 -0
  167. package/dist/{target → service}/remote-polling.d.ts +2 -2
  168. package/dist/{target → service}/remote-polling.js +8 -8
  169. package/dist/service/remote.d.ts +34 -0
  170. package/dist/service/remote.js +634 -0
  171. package/dist/service/sse-stream.d.ts +24 -0
  172. package/dist/service/sse-stream.js +139 -0
  173. package/dist/service/types.d.ts +272 -0
  174. package/dist/service/types.js +32 -0
  175. package/dist/storage/data-dir-lock.js +10 -5
  176. package/dist/storage/driver.d.ts +2 -1
  177. package/dist/storage/driver.js +3 -52
  178. package/dist/storage/index.d.ts +1 -0
  179. package/dist/storage/index.js +1 -0
  180. package/dist/storage/lock-core.d.ts +2 -6
  181. package/dist/storage/lock-core.js +4 -17
  182. package/dist/storage/params.d.ts +2 -8
  183. package/dist/storage/params.js +2 -8
  184. package/dist/storage/sqlite-driver.d.ts +40 -0
  185. package/dist/storage/sqlite-driver.js +116 -0
  186. package/dist/storage/sqlite.js +21 -6
  187. package/dist/storage/types.d.ts +4 -4
  188. package/dist/storage/types.js +2 -2
  189. package/dist/utils/index.d.ts +53 -0
  190. package/dist/utils/index.js +99 -4
  191. package/dist/utils/process.d.ts +10 -0
  192. package/dist/utils/process.js +21 -0
  193. package/dist/version.d.ts +1 -1
  194. package/dist/version.js +1 -1
  195. package/package.json +41 -3
  196. package/dist/app/index.d.ts +0 -2
  197. package/dist/app/index.js +0 -2
  198. package/dist/app/types.js +0 -1
  199. package/dist/catalog/action-index.d.ts +0 -9
  200. package/dist/catalog/action-index.js +0 -95
  201. package/dist/catalog/action-resolver.d.ts +0 -20
  202. package/dist/catalog/action-resolver.js +0 -95
  203. package/dist/catalog/location-registry.d.ts +0 -15
  204. package/dist/catalog/location-registry.js +0 -138
  205. package/dist/catalog/package-catalog.d.ts +0 -7
  206. package/dist/catalog/package-catalog.js +0 -77
  207. package/dist/catalog/types.d.ts +0 -68
  208. package/dist/catalog/types.js +0 -1
  209. package/dist/export/index.d.ts +0 -1
  210. package/dist/export/index.js +0 -1
  211. package/dist/export/templates.d.ts +0 -71
  212. package/dist/export/templates.js +0 -635
  213. package/dist/ipc/target.d.ts +0 -74
  214. package/dist/ipc/target.js +0 -365
  215. package/dist/platform/default.d.ts +0 -38
  216. package/dist/platform/default.js +0 -78
  217. package/dist/project/resolver.d.ts +0 -116
  218. package/dist/project/resolver.js +0 -376
  219. package/dist/registry/resolve.d.ts +0 -62
  220. package/dist/registry/resolve.js +0 -195
  221. package/dist/runtime/execution-manager.d.ts +0 -48
  222. package/dist/runtime/execution-manager.js +0 -66
  223. package/dist/runtime/package-runner-factory.d.ts +0 -82
  224. package/dist/runtime/package-runner-factory.js +0 -186
  225. package/dist/target/local.d.ts +0 -53
  226. package/dist/target/local.js +0 -349
  227. package/dist/target/remote-errors.js +0 -51
  228. package/dist/target/remote.d.ts +0 -86
  229. package/dist/target/remote.js +0 -818
  230. package/dist/target/target.d.ts +0 -6
  231. package/dist/target/target.js +0 -37
  232. package/dist/target/types.d.ts +0 -261
  233. package/dist/target/types.js +0 -35
  234. /package/dist/{app → package}/static-index.d.ts +0 -0
  235. /package/dist/{target → service}/sse-parser.d.ts +0 -0
  236. /package/dist/{target → service}/sse-parser.js +0 -0
@@ -1,6 +1,10 @@
1
- import { mkdir, rename, rm, stat } from "node:fs/promises";
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFile, mkdir, rename, rm, stat, utimes, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { isProcessAlive } from "../utils/process.js";
2
5
  /**
3
- * 锁目录创建后超过该时长仍未释放,视为持有进程异常退出的残留锁。
6
+ * 锁目录最近一次心跳(创建或续期)后超过该时长且持有进程已死亡,
7
+ * 才视为持有进程异常退出的残留锁。
4
8
  */
5
9
  export const REGISTRY_LOCK_STALE_MS = 10000;
6
10
  /**
@@ -12,29 +16,120 @@ export const REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS = 15000;
12
16
  * 两次获取尝试之间的退避间隔,休眠让出事件循环,避免忙等阻塞。
13
17
  */
14
18
  export const REGISTRY_LOCK_RETRY_DELAY_MS = 25;
19
+ /**
20
+ * 持锁期间的心跳续期间隔。持有者定期 touch 锁目录刷新 mtime,
21
+ * 使长耗时持锁操作(如 linkPackage 的深度扫描)不会仅因耗时超过
22
+ * stale 阈值而被等待方误判为残留锁抢占。
23
+ */
24
+ export const REGISTRY_LOCK_HEARTBEAT_MS = 3000;
25
+ /**
26
+ * 锁目录内的持有者元数据文件名。
27
+ */
28
+ const LOCK_METADATA_FILE = "metadata.json";
15
29
  function sleep(ms) {
16
30
  return new Promise((r) => setTimeout(r, ms));
17
31
  }
32
+ /**
33
+ * 读取锁目录中的持有者元数据;缺失、不可读或结构非法时返回 undefined。
34
+ * 元数据信息缺失时陈旧判定退化为仅看 mtime 年龄(与历史行为兼容)。
35
+ */
36
+ async function readLockMetadata(lockDir) {
37
+ let raw;
38
+ try {
39
+ raw = await readFile(join(lockDir, LOCK_METADATA_FILE), "utf-8");
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ try {
45
+ const parsed = JSON.parse(raw);
46
+ if (parsed &&
47
+ typeof parsed === "object" &&
48
+ typeof parsed.pid === "number" &&
49
+ typeof parsed.token === "string") {
50
+ return {
51
+ pid: parsed.pid,
52
+ token: parsed.token,
53
+ createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : "",
54
+ };
55
+ }
56
+ }
57
+ catch {
58
+ // 损坏的元数据视同缺失
59
+ }
60
+ return undefined;
61
+ }
62
+ /**
63
+ * 本进程当前持有的注册表锁目录集合。
64
+ * 用于区分「同进程在途持锁」(不可回收,需等待)与「同进程历史泄漏锁」
65
+ * (释放阶段 rm 失败遗留,pid 存活复核下仍应可回收),
66
+ * 避免存活 pid 保守判定把自身泄漏锁变成永久死锁。
67
+ */
68
+ const activeLockDirs = new Set();
69
+ /**
70
+ * 判定锁目录是否为可回收的残留锁。
71
+ *
72
+ * 判定语义(保守优先,保护并发正确性):
73
+ * - mtime 未超龄:一律不回收(正常持锁或心跳间隔内的活跃锁)。
74
+ * - mtime 超龄 且元数据含存活 pid:不回收(慢磁盘下长扫描的持锁者仍活着;
75
+ * 同进程在途持锁同样不回收,交由释放后重试获取)。
76
+ * - mtime 超龄 且元数据 pid 已死:回收。
77
+ * - mtime 超龄 且元数据缺失/非法:仅看 mtime 维持历史行为回收
78
+ * (兼容旧版本无元数据锁目录与竞态窗口内的半初始化锁目录)。
79
+ */
80
+ async function isStaleLock(lockDir, staleMs = REGISTRY_LOCK_STALE_MS) {
81
+ let lockAgeMs;
82
+ try {
83
+ const info = await stat(lockDir);
84
+ lockAgeMs = Date.now() - info.mtimeMs;
85
+ }
86
+ catch (err) {
87
+ // ENOENT:持有者恰好在两次探测之间释放,交由调用方立即重试获取
88
+ throw err;
89
+ }
90
+ if (lockAgeMs <= staleMs) {
91
+ return false;
92
+ }
93
+ const metadata = await readLockMetadata(lockDir);
94
+ if (metadata === undefined) {
95
+ return true;
96
+ }
97
+ if (metadata.pid === process.pid) {
98
+ // 同进程:仅在途持锁不可回收,历史泄漏锁(不在活跃集合)可回收
99
+ return !activeLockDirs.has(lockDir);
100
+ }
101
+ return !isProcessAlive(metadata.pid);
102
+ }
18
103
  /**
19
104
  * 异步注册表锁(防止多进程并发读写导致 registry.json 损坏)。
20
105
  *
21
- * 获取方式:fs/promises mkdir 的原子性保证同一时刻仅一个进程能创建锁目录。
22
- * 冲突处理:EEXIST 时 stat 检查锁目录年龄,超过 stale 阈值则先原子 rename 到
23
- * 隔离名再删除——rename 仅有一个进程能成功,失败方回到等待重试,消除
24
- * 「stat 检查后删除再重建」的竞态窗口;未过期的锁通过 sleep 让出事件循环后重试。
106
+ * 获取方式:fs/promises mkdir 的原子性保证同一时刻仅一个进程能创建锁目录,
107
+ * 创建成功后立即写入含 pid 与随机 token 的 metadata.json。
108
+ * 冲突处理:EEXIST 时按保守语义判定残留(见 isStaleLock):mtime 超龄且
109
+ * pid 已死才抢占——抢占仍通过原子 rename 到隔离名实现,rename 仅有一个
110
+ * 进程能成功,失败方回到等待重试,消除「检查后删除再重建」的竞态窗口;
111
+ * 未过期的锁通过 sleep 让出事件循环后重试。
112
+ * 心跳续期:持锁期间以固定间隔 touch 锁目录刷新 mtime,使 linkPackage 等
113
+ * 长耗时持锁操作不会仅因耗时被误判残留;fn 完成后停止心跳并删除锁目录。
25
114
  *
26
115
  * @param filePath 注册表文件路径,锁目录为其同级 `${filePath}.lock`
27
116
  * @param fn 持锁期间执行的操作(同步或异步)
117
+ * @param options 可选锁超时与重试配置
28
118
  * @returns fn 的返回值
29
- * @throws 超过 REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS 仍未获取时抛出描述性错误
119
+ * @throws 超过 acquireTimeoutMs 仍未获取时抛出描述性错误
30
120
  */
31
- export async function withRegistryLock(filePath, fn) {
121
+ export async function withRegistryLock(filePath, fn, options) {
122
+ const staleMs = options?.staleMs ?? REGISTRY_LOCK_STALE_MS;
123
+ const acquireTimeoutMs = options?.acquireTimeoutMs ?? REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS;
124
+ const retryDelayMs = options?.retryDelayMs ?? REGISTRY_LOCK_RETRY_DELAY_MS;
125
+ const heartbeatMs = options?.heartbeatMs ?? REGISTRY_LOCK_HEARTBEAT_MS;
32
126
  const lockDir = `${filePath}.lock`;
33
- const deadline = Date.now() + REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS;
127
+ const deadline = Date.now() + acquireTimeoutMs;
34
128
  while (true) {
35
129
  try {
36
130
  // 原子获取:目录创建成功即持有锁,无需二次确认
37
131
  await mkdir(lockDir);
132
+ activeLockDirs.add(lockDir);
38
133
  break;
39
134
  }
40
135
  catch (err) {
@@ -42,11 +137,10 @@ export async function withRegistryLock(filePath, fn) {
42
137
  throw err;
43
138
  }
44
139
  }
45
- // 锁被占用,检查是否为残留的过期锁
46
- let lockAgeMs;
140
+ // 锁被占用,检查是否为可回收的残留锁
141
+ let stale;
47
142
  try {
48
- const info = await stat(lockDir);
49
- lockAgeMs = Date.now() - info.mtimeMs;
143
+ stale = await isStaleLock(lockDir, staleMs);
50
144
  }
51
145
  catch (err) {
52
146
  if (err.code === "ENOENT") {
@@ -55,7 +149,7 @@ export async function withRegistryLock(filePath, fn) {
55
149
  }
56
150
  throw err;
57
151
  }
58
- if (lockAgeMs > REGISTRY_LOCK_STALE_MS) {
152
+ if (stale) {
59
153
  // 原子抢占:rename 到含进程号与时间戳的隔离名,仅一个进程能成功;
60
154
  // 失败方说明已被其他等待者抢先处理,回到等待重试即可
61
155
  const quarantine = `${lockDir}.stale.${process.pid}.${Date.now()}`;
@@ -73,15 +167,41 @@ export async function withRegistryLock(filePath, fn) {
73
167
  }
74
168
  }
75
169
  if (Date.now() > deadline) {
76
- throw new Error(`Failed to acquire registry lock '${lockDir}' within ${REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS}ms. ` +
170
+ throw new Error(`Failed to acquire registry lock '${lockDir}' within ${acquireTimeoutMs}ms. ` +
77
171
  `The lock may be held by another ActionDock process; retry after it exits or remove the stale lock directory manually.`);
78
172
  }
79
- await sleep(REGISTRY_LOCK_RETRY_DELAY_MS);
173
+ await sleep(retryDelayMs);
174
+ }
175
+ // 持有者元数据写入失败不阻断持锁(退化为仅 mtime 判定),但保证可观测
176
+ try {
177
+ const metadata = {
178
+ pid: process.pid,
179
+ token: randomUUID(),
180
+ createdAt: new Date().toISOString(),
181
+ };
182
+ await writeFile(join(lockDir, LOCK_METADATA_FILE), JSON.stringify(metadata, null, 2), "utf-8");
183
+ }
184
+ catch (err) {
185
+ const reason = err instanceof Error ? err.message : String(err);
186
+ console.warn(`[Registry] Failed to write lock metadata in '${lockDir}': ${reason}`);
80
187
  }
188
+ // 心跳续期:定期 touch 锁目录刷新 mtime,防止长耗时持锁操作被误判残留。
189
+ // 单次 touch 失败仅告警不中断(mtime 未刷新仅会提前暴露为陈旧候选,
190
+ // 由 pid 存活复核兜底,不会破坏互斥)。
191
+ const heartbeat = setInterval(() => {
192
+ const now = new Date();
193
+ utimes(lockDir, now, now).catch((err) => {
194
+ const reason = err instanceof Error ? err.message : String(err);
195
+ console.warn(`[Registry] Failed to refresh lock heartbeat for '${lockDir}': ${reason}`);
196
+ });
197
+ }, heartbeatMs);
198
+ heartbeat.unref?.();
81
199
  try {
82
200
  return await fn();
83
201
  }
84
202
  finally {
203
+ clearInterval(heartbeat);
204
+ activeLockDirs.delete(lockDir);
85
205
  try {
86
206
  await rm(lockDir, { recursive: true, force: true });
87
207
  }
@@ -1,4 +1,4 @@
1
- import type { GlobalRegistryData, LinkedPackageEntry, LinkedWorkspaceEntry, LinkResult, PruneResult, RegistryStatusReport, ResolvedActionProject, ResolvedPlaybookProject, UnlinkResult } from "./types.js";
1
+ import type { GlobalRegistryData, LinkedPackageEntry, LinkedWorkspaceEntry, LinkResult, PruneResult, RegistryStatusReport, UnlinkResult } from "./types.js";
2
2
  /**
3
3
  * 注册表内容解析与 links 迁移(纯函数,同步与异步加载共享的唯一事实源)。
4
4
  *
@@ -34,11 +34,7 @@ export declare function linkPackage(targetPath?: string, customHome?: string, op
34
34
  }): Promise<LinkResult>;
35
35
  export declare function unlinkPackage(identifier?: string, customHome?: string): Promise<UnlinkResult | null>;
36
36
  export declare function listLinkedPackages(customHome?: string): LinkedPackageEntry[];
37
- export declare function listLinkedPackagesAsync(customHome?: string): Promise<LinkedPackageEntry[]>;
38
37
  export declare function listLinkedWorkspaces(customHome?: string): LinkedWorkspaceEntry[];
39
- export declare function resolveActionProjectSync(actionIdentifier: string, cwd?: string, customHome?: string): ResolvedActionProject;
40
- export declare function resolveActionProject(actionIdentifier: string, cwd?: string, customHome?: string): Promise<ResolvedActionProject>;
41
- export declare function resolvePlaybookProject(playbookIdentifier: string, cwd?: string, customHome?: string): ResolvedPlaybookProject;
42
38
  export declare function resolvePackageRoot(packageIdOrPath?: string, cwd?: string, customHome?: string): string | null;
43
39
  export declare function getRegistryStatus(customHome?: string): RegistryStatusReport;
44
40
  export declare function pruneRegistry(customHome?: string): Promise<PruneResult>;
@@ -4,8 +4,7 @@ import { basename, dirname, join, resolve, sep } from "node:path";
4
4
  import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
5
5
  import { getActionDockHome, getPackageSlug } from "../utils/index.js";
6
6
  import { withRegistryLock } from "./lock.js";
7
- import { buildLinkedPackageEntry, discoverProjectConfigs, discoverProjectConfigsAsync, discoverProjects, pathExistsAsync, } from "./scan.js";
8
- import { probeActionAsync, probeActionSync, probePlaybook, resolveEntityFlow, runFlowAsync, runFlowSync, } from "./resolve.js";
7
+ import { buildLinkedPackageEntry, discoverProjectConfigs, discoverProjects, } from "./scan.js";
9
8
  /**
10
9
  * 空注册表初始结构。
11
10
  */
@@ -400,61 +399,10 @@ export function listLinkedPackages(customHome) {
400
399
  }
401
400
  return Object.values(result);
402
401
  }
403
- export async function listLinkedPackagesAsync(customHome) {
404
- const registry = await loadRegistryAsync(customHome);
405
- const result = { ...registry.packages };
406
- if (registry.workspaces) {
407
- for (const ws of Object.values(registry.workspaces)) {
408
- if (!(await pathExistsAsync(ws.path)))
409
- continue;
410
- for (const { root, config } of await discoverProjectConfigsAsync(ws.path)) {
411
- if (!result[config.id] || result[config.id].workspaceRoot === ws.path) {
412
- result[config.id] = buildLinkedPackageEntry(config, root, ws.linkedAt, ws.path);
413
- }
414
- }
415
- }
416
- }
417
- return Object.values(result);
418
- }
419
402
  export function listLinkedWorkspaces(customHome) {
420
403
  const registry = loadRegistry(customHome);
421
404
  return Object.values(registry.workspaces || {});
422
405
  }
423
- export function resolveActionProjectSync(actionIdentifier, cwd = process.cwd(), customHome) {
424
- return runFlowSync(resolveEntityFlow({
425
- identifier: actionIdentifier,
426
- cwd,
427
- entityNoun: "Action",
428
- listLinkedPackages: () => listLinkedPackages(customHome),
429
- probe: (root, config, id) => probeActionSync(root, config, id),
430
- buildResult: (projectRoot, packageId, actionId) => ({ projectRoot, packageId, actionId }),
431
- }));
432
- }
433
- export async function resolveActionProject(actionIdentifier, cwd = process.cwd(), customHome) {
434
- return runFlowAsync(resolveEntityFlow({
435
- identifier: actionIdentifier,
436
- cwd,
437
- entityNoun: "Action",
438
- listLinkedPackages: () => listLinkedPackagesAsync(customHome),
439
- probe: (root, config, id) => probeActionAsync(root, config, id),
440
- buildResult: (projectRoot, packageId, actionId) => ({ projectRoot, packageId, actionId }),
441
- }));
442
- }
443
- export function resolvePlaybookProject(playbookIdentifier, cwd = process.cwd(), customHome) {
444
- return runFlowSync(resolveEntityFlow({
445
- identifier: playbookIdentifier,
446
- cwd,
447
- entityNoun: "Playbook",
448
- listLinkedPackages: () => listLinkedPackages(customHome),
449
- probe: (root, config, id) => probePlaybook(root, config, id),
450
- buildResult: (projectRoot, packageId, playbookId, playbook) => ({
451
- projectRoot,
452
- packageId,
453
- playbookId,
454
- playbook,
455
- }),
456
- }));
457
- }
458
406
  export function resolvePackageRoot(packageIdOrPath, cwd, customHome) {
459
407
  if (!packageIdOrPath) {
460
408
  return findProjectRoot(cwd);
@@ -8,10 +8,6 @@ export declare const IGNORED_SCAN_DIRS: Set<string>;
8
8
  * 递归扫描包含 actiondock.json 的子项目根目录(同步版本)。
9
9
  */
10
10
  export declare function discoverProjects(dir: string, maxDepth?: number): string[];
11
- /**
12
- * 递归扫描包含 actiondock.json 的子项目根目录(异步版本,逻辑与同步版一致)。
13
- */
14
- export declare function discoverProjectsAsync(dir: string, maxDepth?: number): Promise<string[]>;
15
11
  /**
16
12
  * 扫描并加载全部子项目配置(同步版本)。
17
13
  * 损坏项目(配置缺失或非法)跳过不中断:注册表操作面对的是外部任意目录,
@@ -21,18 +17,7 @@ export declare function discoverProjectConfigs(dir: string, maxDepth?: number):
21
17
  root: string;
22
18
  config: ReturnType<typeof loadProjectConfig>;
23
19
  }>;
24
- /**
25
- * 扫描并加载全部子项目配置(异步版本,跳过策略与同步版一致)。
26
- */
27
- export declare function discoverProjectConfigsAsync(dir: string, maxDepth?: number): Promise<Array<{
28
- root: string;
29
- config: ReturnType<typeof loadProjectConfig>;
30
- }>>;
31
20
  /**
32
21
  * 由项目配置构造注册表链接条目(纯函数,无副作用)。
33
22
  */
34
23
  export declare function buildLinkedPackageEntry(config: ReturnType<typeof loadProjectConfig>, path: string, linkedAt: string, workspaceRoot?: string): LinkedPackageEntry;
35
- /**
36
- * 异步探测路径是否存在。
37
- */
38
- export declare function pathExistsAsync(path: string): Promise<boolean>;
@@ -1,5 +1,4 @@
1
1
  import { readdirSync } from "node:fs";
2
- import { readdir, stat } from "node:fs/promises";
3
2
  import { join, resolve } from "node:path";
4
3
  import { loadProjectConfig } from "../project/loader.js";
5
4
  /**
@@ -45,37 +44,6 @@ export function discoverProjects(dir, maxDepth = 3) {
45
44
  walk(resolvedDir, 1);
46
45
  return results;
47
46
  }
48
- /**
49
- * 递归扫描包含 actiondock.json 的子项目根目录(异步版本,逻辑与同步版一致)。
50
- */
51
- export async function discoverProjectsAsync(dir, maxDepth = 3) {
52
- const results = [];
53
- const resolvedDir = resolve(dir);
54
- async function walk(currentDir, currentDepth) {
55
- if (currentDepth > maxDepth)
56
- return;
57
- let entries;
58
- try {
59
- entries = await readdir(currentDir, { withFileTypes: true });
60
- }
61
- catch {
62
- // 忽略无法读取的目录:扫描是尽力而为的探测,单目录不可读不应中断整体发现
63
- return;
64
- }
65
- const hasActiondock = entries.some((e) => e.isFile() && e.name === "actiondock.json");
66
- if (hasActiondock && currentDir !== resolvedDir) {
67
- results.push(currentDir);
68
- return; // 不再向项目内部子目录递归
69
- }
70
- for (const entry of entries) {
71
- if (entry.isDirectory() && !IGNORED_SCAN_DIRS.has(entry.name)) {
72
- await walk(join(currentDir, entry.name), currentDepth + 1);
73
- }
74
- }
75
- }
76
- await walk(resolvedDir, 1);
77
- return results;
78
- }
79
47
  /**
80
48
  * 扫描并加载全部子项目配置(同步版本)。
81
49
  * 损坏项目(配置缺失或非法)跳过不中断:注册表操作面对的是外部任意目录,
@@ -93,21 +61,6 @@ export function discoverProjectConfigs(dir, maxDepth = 3) {
93
61
  }
94
62
  return results;
95
63
  }
96
- /**
97
- * 扫描并加载全部子项目配置(异步版本,跳过策略与同步版一致)。
98
- */
99
- export async function discoverProjectConfigsAsync(dir, maxDepth = 3) {
100
- const results = [];
101
- for (const root of await discoverProjectsAsync(dir, maxDepth)) {
102
- try {
103
- results.push({ root, config: loadProjectConfig(root) });
104
- }
105
- catch {
106
- // 跳过损坏项目:其 actiondock.json 缺失必要字段或非法,无法构成有效链接条目
107
- }
108
- }
109
- return results;
110
- }
111
64
  /**
112
65
  * 由项目配置构造注册表链接条目(纯函数,无副作用)。
113
66
  */
@@ -121,15 +74,3 @@ export function buildLinkedPackageEntry(config, path, linkedAt, workspaceRoot) {
121
74
  ...(workspaceRoot ? { workspaceRoot } : {}),
122
75
  };
123
76
  }
124
- /**
125
- * 异步探测路径是否存在。
126
- */
127
- export async function pathExistsAsync(path) {
128
- try {
129
- await stat(path);
130
- return true;
131
- }
132
- catch {
133
- return false;
134
- }
135
- }
@@ -0,0 +1,42 @@
1
+ import type { GlobalRegistryData, LinkedPackageEntry, LinkedWorkspaceEntry, LinkResult, PruneResult, RegistryStatusReport, UnlinkResult } from "./types.js";
2
+ /**
3
+ * 用户显式注册位置存储契约。
4
+ * 职责严格限制为存储与读取用户显式 link/unlink 的物理位置(packages 与 workspaces)。
5
+ */
6
+ export interface RegistryStore {
7
+ readonly customHome?: string;
8
+ getFilePath(): string;
9
+ load(): GlobalRegistryData;
10
+ loadAsync(): Promise<GlobalRegistryData>;
11
+ save(data: GlobalRegistryData): Promise<void>;
12
+ listPackages(): LinkedPackageEntry[];
13
+ listWorkspaces(): LinkedWorkspaceEntry[];
14
+ link(path: string, options?: {
15
+ recursive?: boolean;
16
+ }): Promise<LinkResult>;
17
+ unlink(idOrPath: string): Promise<UnlinkResult | null>;
18
+ prune(): Promise<PruneResult>;
19
+ getStatus(): RegistryStatusReport;
20
+ }
21
+ /**
22
+ * 默认注册表物理位置存储实现。
23
+ * 直接基于文件系统注册表文件(~/.actiondock/registry.json)读写;
24
+ * 全部方法均为向 registry.ts 自由函数透传 customHome 的零逻辑转发,
25
+ * 收敛为单行委托以消除重复样板。
26
+ */
27
+ export declare class DefaultRegistryStore implements RegistryStore {
28
+ readonly customHome?: string;
29
+ constructor(customHome?: string);
30
+ getFilePath(): string;
31
+ load(): GlobalRegistryData;
32
+ loadAsync(): Promise<GlobalRegistryData>;
33
+ save(data: GlobalRegistryData): Promise<void>;
34
+ listPackages(): LinkedPackageEntry[];
35
+ listWorkspaces(): LinkedWorkspaceEntry[];
36
+ link(path: string, options?: {
37
+ recursive?: boolean;
38
+ }): Promise<LinkResult>;
39
+ unlink(idOrPath: string): Promise<UnlinkResult | null>;
40
+ prune(): Promise<PruneResult>;
41
+ getStatus(): RegistryStatusReport;
42
+ }
@@ -0,0 +1,43 @@
1
+ import { getRegistryFilePath, getRegistryStatus, linkPackage, listLinkedPackages, listLinkedWorkspaces, loadRegistry, loadRegistryAsync, pruneRegistry, saveRegistry, unlinkPackage, } from "./registry.js";
2
+ /**
3
+ * 默认注册表物理位置存储实现。
4
+ * 直接基于文件系统注册表文件(~/.actiondock/registry.json)读写;
5
+ * 全部方法均为向 registry.ts 自由函数透传 customHome 的零逻辑转发,
6
+ * 收敛为单行委托以消除重复样板。
7
+ */
8
+ export class DefaultRegistryStore {
9
+ customHome;
10
+ constructor(customHome) {
11
+ this.customHome = customHome;
12
+ }
13
+ getFilePath() {
14
+ return getRegistryFilePath(this.customHome);
15
+ }
16
+ load() {
17
+ return loadRegistry(this.customHome);
18
+ }
19
+ loadAsync() {
20
+ return loadRegistryAsync(this.customHome);
21
+ }
22
+ save(data) {
23
+ return saveRegistry(data, this.customHome);
24
+ }
25
+ listPackages() {
26
+ return listLinkedPackages(this.customHome);
27
+ }
28
+ listWorkspaces() {
29
+ return listLinkedWorkspaces(this.customHome);
30
+ }
31
+ link(path, options) {
32
+ return linkPackage(path, this.customHome, options);
33
+ }
34
+ unlink(idOrPath) {
35
+ return unlinkPackage(idOrPath, this.customHome);
36
+ }
37
+ prune() {
38
+ return pruneRegistry(this.customHome);
39
+ }
40
+ getStatus() {
41
+ return getRegistryStatus(this.customHome);
42
+ }
43
+ }
@@ -91,27 +91,3 @@ export interface PruneResult {
91
91
  prunedPackages: LinkedPackageEntry[];
92
92
  prunedWorkspaces: LinkedWorkspaceEntry[];
93
93
  }
94
- /**
95
- * Action 归属项目解析结果。
96
- */
97
- export interface ResolvedActionProject {
98
- /** 所在项目的根目录绝对路径 */
99
- projectRoot: string;
100
- /** 所属 Package ID */
101
- packageId: string;
102
- /** 匹配到的 Action ID */
103
- actionId: string;
104
- }
105
- /**
106
- * Playbook 归属项目解析结果。
107
- */
108
- export interface ResolvedPlaybookProject {
109
- /** 所在项目的根目录绝对路径 */
110
- projectRoot: string;
111
- /** 所属 Package ID */
112
- packageId: string;
113
- /** 匹配到的 Playbook ID */
114
- playbookId: string;
115
- /** Playbook 实体数据 */
116
- playbook: import("../project/types.js").PlaybookDefinition;
117
- }
@@ -1,5 +1,5 @@
1
1
  import type { ActionDefinition } from "@actiondock/sdk";
2
- import type { ActionSpec } from "../app/types.js";
2
+ import type { ActionSpec } from "../package/types.js";
3
3
  /**
4
4
  * 归一化 Action 集合的产物结构。
5
5
  */
@@ -39,7 +39,3 @@ export declare function findLocalAction(actions: Map<string, ActionDefinition>,
39
39
  * 不额外吞异或静默改道,保持边界行为零变化。
40
40
  */
41
41
  export declare function isActionDefinitionObject(value: unknown): value is ActionDefinition;
42
- /**
43
- * 解析 Action 引用或标识符(解析失败抛出原始异常,由调用方分类处理)。
44
- */
45
- export declare function parseActionRef(ref: ActionRef | string): ActionRef;
@@ -1,4 +1,3 @@
1
- import { ActionResolver } from "../catalog/action-resolver.js";
2
1
  /**
3
2
  * Action 注册表(单一职责模块)。
4
3
  *
@@ -100,9 +99,3 @@ export function isActionDefinitionObject(value) {
100
99
  "run" in value &&
101
100
  typeof value.run === "function");
102
101
  }
103
- /**
104
- * 解析 Action 引用或标识符(解析失败抛出原始异常,由调用方分类处理)。
105
- */
106
- export function parseActionRef(ref) {
107
- return ActionResolver.parseRef(ref);
108
- }
@@ -1,6 +1,7 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import { MemoryProcessDriver, ProcessManager } from "../process/index.js";
2
+ import { createDefaultProcessOwner } from "../invocation/types.js";
3
3
  import { resolveEnvValue } from "./env.js";
4
+ import { ActionDockError, INVALID_ACTION_REF } from "../errors.js";
4
5
  /**
5
6
  * 生产级配置解析器实现。
6
7
  * 严格践行 5 层配置解析优先级链:
@@ -166,14 +167,12 @@ export function createActionContext(options) {
166
167
  const state = new RuntimeStateStore(options.storage);
167
168
  const log = options.logger || new StderrLogger();
168
169
  const signal = options.signal ?? new AbortController().signal;
169
- const currentRunId = options.runId || randomUUID();
170
+ const currentRunId = options.runId || crypto.randomUUID();
170
171
  const currentRootRunId = options.rootRunId || options.parentRunId || currentRunId;
171
172
  const invoke = async (action, input) => {
172
173
  if (typeof action !== "string" &&
173
174
  (!action || typeof action !== "object" || typeof action.run === "function" || !("actionId" in action))) {
174
- const err = new Error("INVALID_ACTION_REF: ctx.actions.invoke strictly accepts only ActionRef or string, passing ActionDefinition or function is prohibited");
175
- err.code = "INVALID_ACTION_REF";
176
- throw err;
175
+ throw new ActionDockError(INVALID_ACTION_REF, "INVALID_ACTION_REF: ctx.actions.invoke strictly accepts only ActionRef or string, passing ActionDefinition or function is prohibited");
177
176
  }
178
177
  if (options.onActionInvoke) {
179
178
  return (await options.onActionInvoke(action, input, currentRunId));
@@ -182,12 +181,7 @@ export function createActionContext(options) {
182
181
  };
183
182
  const invokerFn = (ref, input) => invoke(ref, input);
184
183
  const invoker = Object.assign(invokerFn, { invoke });
185
- const defaultOwner = {
186
- tenantId: "default",
187
- principalId: "default",
188
- packageInstanceId: "default",
189
- generationId: "default",
190
- };
184
+ const defaultOwner = createDefaultProcessOwner();
191
185
  const effectiveOwner = options.owner || defaultOwner;
192
186
  // 外部注入的可能是平台级共享 ContextProcessAPI(未绑定 runId),直接复用会让
193
187
  // 多个 run 共享同一隔离与回收状态;此处派生 run 级实例保证隔离与自动回收均以 run 为单位
@@ -1,4 +1,4 @@
1
- import { EVENT_BACKPRESSURE_LIMIT, EVENT_CURSOR_EXPIRED } from "../errors.js";
1
+ import { ActionDockError, EVENT_BACKPRESSURE_LIMIT, EVENT_CURSOR_EXPIRED } from "../errors.js";
2
2
  /**
3
3
  * 订阅者通道:封装实时事件队列、监听器与终止状态。
4
4
  * 职责:
@@ -409,16 +409,13 @@ export class InMemoryEventSink {
409
409
  const earliestSeq = run.earliestRetainedSequence ?? 0;
410
410
  if (afterSequence !== undefined && afterSequence >= 0 && afterSequence < earliestSeq - 1) {
411
411
  const earliestCursor = run.earliestRetainedEventId ?? String(earliestSeq);
412
- const expiredErr = new Error(`Event cursor '${after}' has expired; earliest available cursor is '${earliestCursor}'`);
413
- expiredErr.code = EVENT_CURSOR_EXPIRED;
414
- expiredErr.details = {
412
+ throw new ActionDockError(EVENT_CURSOR_EXPIRED, `Event cursor '${after}' has expired; earliest available cursor is '${earliestCursor}'`, {
415
413
  cursor: after,
416
414
  earliestCursor,
417
415
  earliestRetainedCursor: earliestCursor,
418
416
  earliestSequence: earliestSeq,
419
417
  earliestEventId: run.earliestRetainedEventId,
420
- };
421
- throw expiredErr;
418
+ });
422
419
  }
423
420
  }
424
421
  /**
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 包唯一物理与快照身份标识值对象(PackageIdentity)。
3
+ *
4
+ * 铁律 7:只在 PackageRuntime 创建时生成一次,统一传递给 ExecutionService、
5
+ * Runner、ProcessOwner、RunRecord 等,禁止内部组件自行 fallback。
6
+ */
7
+ export interface PackageIdentity {
8
+ /** 包唯一逻辑标识符 */
9
+ readonly id: string;
10
+ /** 包物理实例标识符(单次加载/运行周期的全局唯一物理实例标识) */
11
+ readonly instanceId: string;
12
+ /** 快照代次标识符 */
13
+ readonly generation: string;
14
+ }
15
+ /**
16
+ * 创建 PackageIdentity 值对象单一事实工厂。
17
+ */
18
+ export declare function createPackageIdentity(options: {
19
+ id: string;
20
+ instanceId?: string;
21
+ generation?: string;
22
+ }): PackageIdentity;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 创建 PackageIdentity 值对象单一事实工厂。
3
+ */
4
+ export function createPackageIdentity(options) {
5
+ return Object.freeze({
6
+ id: options.id,
7
+ instanceId: options.instanceId || options.id,
8
+ generation: options.generation || "1",
9
+ });
10
+ }
@@ -1,10 +1,9 @@
1
+ export * from "./identity.js";
1
2
  export * from "./context.js";
2
3
  export * from "./action-collection.js";
3
4
  export * from "./action-registry.js";
4
5
  export * from "./run-persistence.js";
5
- export * from "./package-runner-factory.js";
6
6
  export * from "./runner.js";
7
- export * from "./execution-manager.js";
8
7
  export * from "./standalone.js";
9
8
  export * from "./env.js";
10
9
  export * from "./clock.js";