@actiondock/core 2.6.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 (268) 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 +122 -10
  18. package/dist/errors.js +123 -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 -22
  33. package/dist/index.js +19 -21
  34. package/dist/input/advice.d.ts +104 -0
  35. package/dist/input/advice.js +716 -0
  36. package/dist/input/describe.d.ts +106 -0
  37. package/dist/input/describe.js +176 -0
  38. package/dist/input/file-input.d.ts +54 -0
  39. package/dist/input/file-input.js +124 -0
  40. package/dist/input/flat-decode.d.ts +11 -0
  41. package/dist/input/flat-decode.js +13 -0
  42. package/dist/input/flat-errors.d.ts +71 -0
  43. package/dist/input/flat-errors.js +87 -0
  44. package/dist/input/flat-materializer.d.ts +29 -0
  45. package/dist/input/flat-materializer.js +177 -0
  46. package/dist/input/flat-parser.d.ts +50 -0
  47. package/dist/input/flat-parser.js +169 -0
  48. package/dist/input/flat-predicates.d.ts +34 -0
  49. package/dist/input/flat-predicates.js +50 -0
  50. package/dist/input/index.d.ts +14 -0
  51. package/dist/input/index.js +14 -0
  52. package/dist/input/input-resolver.d.ts +63 -0
  53. package/dist/input/input-resolver.js +254 -0
  54. package/dist/input/json-depth-scanner.d.ts +17 -0
  55. package/dist/input/json-depth-scanner.js +49 -0
  56. package/dist/input/metadata.d.ts +99 -0
  57. package/dist/input/metadata.js +99 -0
  58. package/dist/input/stdin-input.d.ts +26 -0
  59. package/dist/input/stdin-input.js +125 -0
  60. package/dist/input/utf8.d.ts +19 -0
  61. package/dist/input/utf8.js +36 -0
  62. package/dist/input/validation-mapper.d.ts +27 -0
  63. package/dist/input/validation-mapper.js +144 -0
  64. package/dist/invocation/index.d.ts +2 -0
  65. package/dist/invocation/index.js +2 -0
  66. package/dist/invocation/policy.d.ts +157 -0
  67. package/dist/invocation/policy.js +231 -0
  68. package/dist/invocation/types.d.ts +172 -0
  69. package/dist/invocation/types.js +97 -0
  70. package/dist/ipc/diagnostic.d.ts +0 -12
  71. package/dist/ipc/diagnostic.js +0 -21
  72. package/dist/ipc/host.d.ts +4 -4
  73. package/dist/ipc/host.js +44 -47
  74. package/dist/ipc/index.d.ts +1 -1
  75. package/dist/ipc/index.js +1 -1
  76. package/dist/ipc/service.d.ts +40 -0
  77. package/dist/ipc/service.js +349 -0
  78. package/dist/ipc/types.d.ts +21 -15
  79. package/dist/json/index.d.ts +1 -0
  80. package/dist/json/index.js +1 -0
  81. package/dist/json/value-validator.d.ts +88 -0
  82. package/dist/json/value-validator.js +330 -0
  83. package/dist/node/index.d.ts +1 -0
  84. package/dist/node/index.js +1 -0
  85. package/dist/node/module-loader.d.ts +63 -0
  86. package/dist/node/module-loader.js +112 -0
  87. package/dist/package/index.d.ts +23 -0
  88. package/dist/package/index.js +18 -0
  89. package/dist/{app/app.d.ts → package/runtime.d.ts} +41 -20
  90. package/dist/{app/app.js → package/runtime.js} +140 -95
  91. package/dist/{app → package}/static-index.js +58 -41
  92. package/dist/package/storage-view.d.ts +13 -0
  93. package/dist/package/storage-view.js +22 -0
  94. package/dist/{app → package}/types.d.ts +76 -57
  95. package/dist/package/types.js +22 -0
  96. package/dist/platform/index.d.ts +1 -1
  97. package/dist/platform/index.js +1 -1
  98. package/dist/platform/node.d.ts +48 -0
  99. package/dist/platform/node.js +96 -0
  100. package/dist/platform/types.d.ts +0 -9
  101. package/dist/process/control-arbiter.d.ts +2 -1
  102. package/dist/process/control-arbiter.js +1 -2
  103. package/dist/process/driver.d.ts +9 -8
  104. package/dist/process/driver.js +11 -22
  105. package/dist/process/index.d.ts +2 -0
  106. package/dist/process/index.js +2 -0
  107. package/dist/process/managed-record.d.ts +2 -2
  108. package/dist/process/process-driver.d.ts +109 -0
  109. package/dist/process/process-driver.js +650 -0
  110. package/dist/process/process-executor.d.ts +47 -0
  111. package/dist/process/process-executor.js +296 -0
  112. package/dist/process/process-manager.js +48 -26
  113. package/dist/process/run-executor.js +12 -12
  114. package/dist/profile/client-actions.js +2 -2
  115. package/dist/profile/client-health.js +3 -2
  116. package/dist/profile/client-query.js +5 -6
  117. package/dist/profile/client-transport.d.ts +19 -17
  118. package/dist/profile/client-transport.js +48 -80
  119. package/dist/profile/client.d.ts +2 -2
  120. package/dist/profile/client.js +1 -1
  121. package/dist/profile/index.d.ts +1 -0
  122. package/dist/profile/index.js +1 -0
  123. package/dist/profile/manager.d.ts +5 -0
  124. package/dist/profile/manager.js +41 -6
  125. package/dist/project/closure.js +2 -2
  126. package/dist/project/index.d.ts +8 -1
  127. package/dist/project/index.js +10 -1
  128. package/dist/project/init.js +2 -2
  129. package/dist/project/loader.d.ts +1 -1
  130. package/dist/project/loader.js +26 -29
  131. package/dist/project/transactions.js +5 -10
  132. package/dist/registry/index.d.ts +3 -1
  133. package/dist/registry/index.js +2 -1
  134. package/dist/registry/lock.d.ts +28 -7
  135. package/dist/registry/lock.js +136 -16
  136. package/dist/registry/registry.d.ts +1 -5
  137. package/dist/registry/registry.js +1 -53
  138. package/dist/registry/scan.d.ts +0 -15
  139. package/dist/registry/scan.js +0 -59
  140. package/dist/registry/store.d.ts +42 -0
  141. package/dist/registry/store.js +43 -0
  142. package/dist/registry/types.d.ts +0 -24
  143. package/dist/runtime/action-collection.d.ts +1 -1
  144. package/dist/runtime/action-registry.d.ts +0 -4
  145. package/dist/runtime/action-registry.js +0 -7
  146. package/dist/runtime/context.js +5 -11
  147. package/dist/runtime/events.js +3 -6
  148. package/dist/runtime/identity.d.ts +22 -0
  149. package/dist/runtime/identity.js +10 -0
  150. package/dist/runtime/index.d.ts +1 -2
  151. package/dist/runtime/index.js +1 -2
  152. package/dist/runtime/module-loader.d.ts +2 -34
  153. package/dist/runtime/module-loader.js +1 -16
  154. package/dist/runtime/process.d.ts +6 -24
  155. package/dist/runtime/process.js +1 -118
  156. package/dist/runtime/run-persistence.js +2 -5
  157. package/dist/runtime/runner.d.ts +33 -103
  158. package/dist/runtime/runner.js +142 -404
  159. package/dist/runtime/standalone.d.ts +37 -9
  160. package/dist/runtime/standalone.js +195 -136
  161. package/dist/schema/index.d.ts +1 -0
  162. package/dist/schema/index.js +1 -0
  163. package/dist/schema/validator.d.ts +10 -0
  164. package/dist/schema/validator.js +48 -54
  165. package/dist/server/body.d.ts +16 -8
  166. package/dist/server/body.js +33 -16
  167. package/dist/server/dispatcher.d.ts +6 -10
  168. package/dist/server/dispatcher.js +24 -32
  169. package/dist/server/http-server.d.ts +87 -0
  170. package/dist/server/http-server.js +304 -0
  171. package/dist/server/index.d.ts +11 -0
  172. package/dist/server/index.js +10 -0
  173. package/dist/server/mcp-endpoint.d.ts +61 -0
  174. package/dist/server/mcp-endpoint.js +118 -0
  175. package/dist/server/routes/actions.js +34 -28
  176. package/dist/server/routes/common.d.ts +10 -14
  177. package/dist/server/routes/common.js +32 -56
  178. package/dist/server/routes/config.js +54 -64
  179. package/dist/server/routes/doctor.d.ts +1 -1
  180. package/dist/server/routes/doctor.js +10 -18
  181. package/dist/server/routes/health.d.ts +1 -1
  182. package/dist/server/routes/health.js +2 -3
  183. package/dist/server/routes/info.d.ts +2 -2
  184. package/dist/server/routes/info.js +12 -13
  185. package/dist/server/routes/playbooks.js +16 -29
  186. package/dist/server/routes/runs.js +95 -117
  187. package/dist/server/routes/state.js +63 -62
  188. package/dist/server/server.d.ts +2 -2
  189. package/dist/server/server.js +43 -227
  190. package/dist/server/types.d.ts +8 -19
  191. package/dist/service/factory.d.ts +12 -0
  192. package/dist/service/factory.js +85 -0
  193. package/dist/{target → service}/index.d.ts +1 -1
  194. package/dist/{target → service}/index.js +1 -1
  195. package/dist/service/local.d.ts +27 -0
  196. package/dist/service/local.js +138 -0
  197. package/dist/{target → service}/remote-errors.d.ts +3 -6
  198. package/dist/service/remote-errors.js +38 -0
  199. package/dist/{target → service}/remote-polling.d.ts +2 -2
  200. package/dist/{target → service}/remote-polling.js +8 -8
  201. package/dist/service/remote.d.ts +34 -0
  202. package/dist/service/remote.js +634 -0
  203. package/dist/service/sse-stream.d.ts +24 -0
  204. package/dist/service/sse-stream.js +139 -0
  205. package/dist/service/types.d.ts +272 -0
  206. package/dist/service/types.js +32 -0
  207. package/dist/storage/data-dir-lock.js +10 -5
  208. package/dist/storage/driver.d.ts +2 -1
  209. package/dist/storage/driver.js +3 -52
  210. package/dist/storage/index.d.ts +1 -0
  211. package/dist/storage/index.js +1 -0
  212. package/dist/storage/lock-core.d.ts +2 -6
  213. package/dist/storage/lock-core.js +4 -17
  214. package/dist/storage/params.d.ts +2 -8
  215. package/dist/storage/params.js +2 -8
  216. package/dist/storage/sqlite-driver.d.ts +40 -0
  217. package/dist/storage/sqlite-driver.js +116 -0
  218. package/dist/storage/sqlite.js +21 -6
  219. package/dist/storage/types.d.ts +4 -4
  220. package/dist/storage/types.js +2 -2
  221. package/dist/utils/index.d.ts +53 -0
  222. package/dist/utils/index.js +99 -4
  223. package/dist/utils/process.d.ts +10 -0
  224. package/dist/utils/process.js +21 -0
  225. package/dist/version.d.ts +1 -1
  226. package/dist/version.js +1 -1
  227. package/package.json +41 -3
  228. package/dist/app/index.d.ts +0 -2
  229. package/dist/app/index.js +0 -2
  230. package/dist/app/types.js +0 -1
  231. package/dist/catalog/action-index.d.ts +0 -9
  232. package/dist/catalog/action-index.js +0 -95
  233. package/dist/catalog/action-resolver.d.ts +0 -20
  234. package/dist/catalog/action-resolver.js +0 -95
  235. package/dist/catalog/location-registry.d.ts +0 -15
  236. package/dist/catalog/location-registry.js +0 -138
  237. package/dist/catalog/package-catalog.d.ts +0 -7
  238. package/dist/catalog/package-catalog.js +0 -77
  239. package/dist/catalog/types.d.ts +0 -68
  240. package/dist/catalog/types.js +0 -1
  241. package/dist/export/index.d.ts +0 -1
  242. package/dist/export/index.js +0 -1
  243. package/dist/export/templates.d.ts +0 -71
  244. package/dist/export/templates.js +0 -623
  245. package/dist/ipc/target.d.ts +0 -74
  246. package/dist/ipc/target.js +0 -365
  247. package/dist/platform/default.d.ts +0 -38
  248. package/dist/platform/default.js +0 -78
  249. package/dist/project/resolver.d.ts +0 -116
  250. package/dist/project/resolver.js +0 -376
  251. package/dist/registry/resolve.d.ts +0 -62
  252. package/dist/registry/resolve.js +0 -195
  253. package/dist/runtime/execution-manager.d.ts +0 -48
  254. package/dist/runtime/execution-manager.js +0 -66
  255. package/dist/runtime/package-runner-factory.d.ts +0 -82
  256. package/dist/runtime/package-runner-factory.js +0 -186
  257. package/dist/target/local.d.ts +0 -53
  258. package/dist/target/local.js +0 -349
  259. package/dist/target/remote-errors.js +0 -51
  260. package/dist/target/remote.d.ts +0 -86
  261. package/dist/target/remote.js +0 -818
  262. package/dist/target/target.d.ts +0 -6
  263. package/dist/target/target.js +0 -37
  264. package/dist/target/types.d.ts +0 -261
  265. package/dist/target/types.js +0 -35
  266. /package/dist/{app → package}/static-index.d.ts +0 -0
  267. /package/dist/{target → service}/sse-parser.d.ts +0 -0
  268. /package/dist/{target → service}/sse-parser.js +0 -0
@@ -0,0 +1,40 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { normalizeSqliteParams } from "./params.js";
3
+ import type { SqliteDriver, SqliteStatement } from "./types.js";
4
+ export { normalizeSqliteParams };
5
+ /**
6
+ * 基于 Node.js 内置 node:sqlite 实现的 SQLite 驱动适配器。
7
+ */
8
+ export declare class NodeSqliteDriver implements SqliteDriver {
9
+ private readonly db;
10
+ private closed;
11
+ constructor(dbPath?: string | DatabaseSync, options?: any);
12
+ /**
13
+ * 检查底层连接是否处于开启状态。
14
+ */
15
+ get isOpen(): boolean;
16
+ /**
17
+ * 获取底层原始 DatabaseSync 实例。
18
+ */
19
+ get rawDatabase(): DatabaseSync;
20
+ /**
21
+ * 执行单条或多条无需返回结果的 SQL 脚本语句。
22
+ */
23
+ exec(sql: string): void;
24
+ /**
25
+ * 预编译 SQL 语句并封装为统一的 SqliteStatement 接口。
26
+ */
27
+ prepare(sql: string): SqliteStatement;
28
+ /**
29
+ * 执行同步事务,严格拒绝并拦截异步 Promise。
30
+ */
31
+ transaction<T>(fn: () => T extends PromiseLike<unknown> ? never : T): T;
32
+ /**
33
+ * 关闭底层数据库连接并释放资源。
34
+ */
35
+ close(): void;
36
+ /**
37
+ * 断言数据库连接未关闭。
38
+ */
39
+ private assertOpen;
40
+ }
@@ -0,0 +1,116 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { normalizeSqliteParams } from "./params.js";
3
+ export { normalizeSqliteParams };
4
+ /**
5
+ * 基于 Node.js 内置 node:sqlite 实现的 SQLite 驱动适配器。
6
+ */
7
+ export class NodeSqliteDriver {
8
+ db;
9
+ closed = false;
10
+ constructor(dbPath = ":memory:", options) {
11
+ if (typeof dbPath === "string") {
12
+ this.db = options !== undefined ? new DatabaseSync(dbPath, options) : new DatabaseSync(dbPath);
13
+ }
14
+ else {
15
+ this.db = dbPath;
16
+ }
17
+ }
18
+ /**
19
+ * 检查底层连接是否处于开启状态。
20
+ */
21
+ get isOpen() {
22
+ return !this.closed;
23
+ }
24
+ /**
25
+ * 获取底层原始 DatabaseSync 实例。
26
+ */
27
+ get rawDatabase() {
28
+ return this.db;
29
+ }
30
+ /**
31
+ * 执行单条或多条无需返回结果的 SQL 脚本语句。
32
+ */
33
+ exec(sql) {
34
+ this.assertOpen();
35
+ this.db.exec(sql);
36
+ }
37
+ /**
38
+ * 预编译 SQL 语句并封装为统一的 SqliteStatement 接口。
39
+ */
40
+ prepare(sql) {
41
+ this.assertOpen();
42
+ const stmt = this.db.prepare(sql);
43
+ return {
44
+ run: (...args) => {
45
+ this.assertOpen();
46
+ const params = normalizeSqliteParams(args);
47
+ const res = stmt.run(...params);
48
+ return {
49
+ changes: Number(res.changes),
50
+ lastInsertRowid: res.lastInsertRowid,
51
+ };
52
+ },
53
+ get: (...args) => {
54
+ this.assertOpen();
55
+ const params = normalizeSqliteParams(args);
56
+ return stmt.get(...params);
57
+ },
58
+ all: (...args) => {
59
+ this.assertOpen();
60
+ const params = normalizeSqliteParams(args);
61
+ return stmt.all(...params);
62
+ },
63
+ };
64
+ }
65
+ /**
66
+ * 执行同步事务,严格拒绝并拦截异步 Promise。
67
+ */
68
+ transaction(fn) {
69
+ this.assertOpen();
70
+ this.db.exec("BEGIN");
71
+ let result;
72
+ try {
73
+ result = fn();
74
+ }
75
+ catch (err) {
76
+ try {
77
+ this.db.exec("ROLLBACK");
78
+ }
79
+ catch {
80
+ // 忽略回滚阶段发生的级联异常
81
+ }
82
+ throw err;
83
+ }
84
+ // 严格检查是否返回了异步 Promise 对象
85
+ if (result !== null &&
86
+ (typeof result === "object" || typeof result === "function") &&
87
+ typeof result.then === "function") {
88
+ try {
89
+ this.db.exec("ROLLBACK");
90
+ }
91
+ catch {
92
+ // 忽略回滚阶段发生的级联异常
93
+ }
94
+ throw new Error("Async transactions are not allowed in SQLite");
95
+ }
96
+ this.db.exec("COMMIT");
97
+ return result;
98
+ }
99
+ /**
100
+ * 关闭底层数据库连接并释放资源。
101
+ */
102
+ close() {
103
+ if (!this.closed) {
104
+ this.closed = true;
105
+ this.db.close();
106
+ }
107
+ }
108
+ /**
109
+ * 断言数据库连接未关闭。
110
+ */
111
+ assertOpen() {
112
+ if (this.closed) {
113
+ throw new Error("Database connection is closed");
114
+ }
115
+ }
116
+ }
@@ -4,6 +4,7 @@ import { decodeStateKey, encodeStateKey, escapeStateSegment, unescapeStateSegmen
4
4
  import { SystemClock } from "./clock.js";
5
5
  import { createDefaultSqliteDriver } from "./driver.js";
6
6
  import { safeParseStoredJson } from "./utils.js";
7
+ import { ActionDockError, AMBIGUOUS_STATE_KEY, STORED_ERROR_DECODE_FAILED, UNSUPPORTED_STORAGE_SCHEMA } from "../errors.js";
7
8
  import { IDEMPOTENCY_RETENTION_MS, STORAGE_SCHEMA_VERSION, isTerminalRunStatus, } from "./types.js";
8
9
  export { decodeStateKey, encodeStateKey, escapeStateSegment, unescapeStateSegment, };
9
10
  /**
@@ -76,6 +77,10 @@ export class SqliteRuntimeStorage {
76
77
  init() {
77
78
  this.driver.exec("PRAGMA journal_mode = WAL;");
78
79
  this.driver.exec("PRAGMA synchronous = NORMAL;");
80
+ // 跨进程写并发基线:SQLite 默认忙等为零,持有者与旁观写进程的写窗口重叠时
81
+ // 会立即抛 SQLITE_BUSY。统一设置 5 秒忙等,使默认同步驱动、工作线程驱动
82
+ // (worker 脚本已内置同值)与任何注入驱动的行为基线一致。
83
+ this.driver.exec("PRAGMA busy_timeout = 5000;");
79
84
  // 读取 Schema 版本号
80
85
  const versionRes = this.driver.prepare("PRAGMA user_version;").get();
81
86
  const version = Number(versionRes?.user_version ?? 0);
@@ -142,7 +147,7 @@ export class SqliteRuntimeStorage {
142
147
  }
143
148
  else if (version !== STORAGE_SCHEMA_VERSION) {
144
149
  const err = new Error(`UNSUPPORTED_STORAGE_SCHEMA: Database schema version ${version} is incompatible (expected ${STORAGE_SCHEMA_VERSION}). Silent upgrade and database overwriting are strictly prohibited.`);
145
- err.code = "UNSUPPORTED_STORAGE_SCHEMA";
150
+ err.code = UNSUPPORTED_STORAGE_SCHEMA;
146
151
  throw err;
147
152
  }
148
153
  // 确保已有历史数据库补充迁移 host_session_id 列
@@ -248,7 +253,11 @@ export class SqliteRuntimeStorage {
248
253
  if (row.expires_at) {
249
254
  const expires = new Date(row.expires_at).getTime();
250
255
  if (this.clock.now().getTime() >= expires) {
251
- this.deleteState(namespace, key).catch(() => { });
256
+ // 惰性删除失败仅降级不影响读取语义,但必须可观测,与其他存储降级点对齐
257
+ this.deleteState(namespace, key).catch((err) => {
258
+ const reason = err instanceof Error ? err.message : String(err);
259
+ console.warn(`[actiondock] expired state lazy delete failed (namespace='${namespace}' key='${key}'): ${reason}`);
260
+ });
252
261
  return undefined;
253
262
  }
254
263
  }
@@ -276,7 +285,13 @@ export class SqliteRuntimeStorage {
276
285
  const uniqueMap = new Map();
277
286
  for (const r of rawRows) {
278
287
  if (r.expires_at && now >= new Date(r.expires_at).getTime()) {
279
- this.deleteState(r.namespace, r.key).catch(() => { });
288
+ // 惰性删除失败仅降级不影响匹配语义,但必须可观测,与其他存储降级点对齐
289
+ const ns = r.namespace;
290
+ const k = r.key;
291
+ this.deleteState(ns, k).catch((err) => {
292
+ const reason = err instanceof Error ? err.message : String(err);
293
+ console.warn(`[actiondock] expired state lazy delete failed (namespace='${ns}' key='${k}'): ${reason}`);
294
+ });
280
295
  continue;
281
296
  }
282
297
  uniqueMap.set(`${r.namespace}\0${r.key}`, r);
@@ -302,7 +317,7 @@ export class SqliteRuntimeStorage {
302
317
  }
303
318
  const matchingRows = await this.findMatchingStateRows(targetKey);
304
319
  if (matchingRows.length > 1) {
305
- throw new Error(`Ambiguous state key '${targetKey}': matches ${matchingRows.length} entries (${matchingRows.map((r) => `${r.namespace}:${r.key}`).join(", ")})`);
320
+ throw new ActionDockError(AMBIGUOUS_STATE_KEY, `Ambiguous state key '${targetKey}': matches ${matchingRows.length} entries (${matchingRows.map((r) => `${r.namespace}:${r.key}`).join(", ")})`);
306
321
  }
307
322
  if (matchingRows.length === 0) {
308
323
  return undefined;
@@ -348,7 +363,7 @@ export class SqliteRuntimeStorage {
348
363
  }
349
364
  const matchingRows = await this.findMatchingStateRows(targetKey);
350
365
  if (matchingRows.length > 1) {
351
- throw new Error(`Ambiguous state key '${targetKey}': matches ${matchingRows.length} entries for deletion (${matchingRows.map((r) => `${r.namespace}:${r.key}`).join(", ")})`);
366
+ throw new ActionDockError(AMBIGUOUS_STATE_KEY, `Ambiguous state key '${targetKey}': matches ${matchingRows.length} entries for deletion (${matchingRows.map((r) => `${r.namespace}:${r.key}`).join(", ")})`);
352
367
  }
353
368
  if (matchingRows.length === 1) {
354
369
  return this.deleteState(matchingRows[0].namespace, matchingRows[0].key);
@@ -644,7 +659,7 @@ export class SqliteRuntimeStorage {
644
659
  const normalizedError = error !== undefined
645
660
  ? typeof error === "object" && error !== null && typeof error.message === "string"
646
661
  ? error
647
- : { code: "STORED_ERROR_DECODE_FAILED", message: String(error) }
662
+ : { code: STORED_ERROR_DECODE_FAILED, message: String(error) }
648
663
  : undefined;
649
664
  return {
650
665
  id: row.id,
@@ -5,8 +5,8 @@ import type { Clock } from "./clock.js";
5
5
  */
6
6
  export declare const STORAGE_SCHEMA_VERSION = 2;
7
7
  /**
8
- * 幂等去重记录保留窗口(24 小时),与 LocalActionDockTarget.info()
9
- * 声明的 idempotencyPolicy.retentionMs 承诺保持单一事实源。
8
+ * 幂等去重记录保留窗口(24 小时),与 ActionDockService.info()
9
+ * 中的 idempotencyPolicy.retentionMs 保持单一事实源。
10
10
  */
11
11
  export declare const IDEMPOTENCY_RETENTION_MS = 86400000;
12
12
  /**
@@ -34,8 +34,8 @@ export interface SqliteStatement {
34
34
  * 本契约为同步驱动:exec、prepare 与 transaction 全部以同步语义返回结果,
35
35
  * 不接受任何 Promise 或异步实现。SqliteRuntimeStorage 的公共同步方法
36
36
  * (getConfig、listConfig、getRun、listRuns、checkAndRecordIdempotency 等)
37
- * 直接依赖这一同步保证。异步驱动(如 runtime-node 的 WorkerSqliteDriver)
38
- * 不应注入本契约,需通过 RuntimeStorage 的异步外观层另行适配。
37
+ * 直接依赖这一同步保证。异步驱动不应注入本契约,
38
+ * 需通过 RuntimeStorage 的异步外观层另行适配。
39
39
  */
40
40
  export interface SqliteDriver {
41
41
  exec(sql: string): void;
@@ -4,8 +4,8 @@ import { ACTION_CANCELLED, ACTION_TIMEOUT } from "../errors.js";
4
4
  */
5
5
  export const STORAGE_SCHEMA_VERSION = 2;
6
6
  /**
7
- * 幂等去重记录保留窗口(24 小时),与 LocalActionDockTarget.info()
8
- * 声明的 idempotencyPolicy.retentionMs 承诺保持单一事实源。
7
+ * 幂等去重记录保留窗口(24 小时),与 ActionDockService.info()
8
+ * 中的 idempotencyPolicy.retentionMs 保持单一事实源。
9
9
  */
10
10
  export const IDEMPOTENCY_RETENTION_MS = 86_400_000;
11
11
  /**
@@ -1,4 +1,57 @@
1
1
  export { isLoopbackHost } from "./net.js";
2
+ export { isProcessAlive } from "./process.js";
3
+ /**
4
+ * 语义化版本元数据结构。
5
+ */
6
+ export interface SemVer {
7
+ major: number;
8
+ minor: number;
9
+ patch: number;
10
+ prerelease?: string;
11
+ }
12
+ /**
13
+ * 解析基础 SemVer 版本字符串(单一事实源)。
14
+ * 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
15
+ * 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
16
+ */
17
+ export declare function parseSemVer(v: string): SemVer | null;
18
+ /**
19
+ * 目录遍历条目描述。
20
+ */
21
+ export interface TraverseDirectoryEntry {
22
+ /** 相对遍历根目录的 POSIX 风格路径 */
23
+ relPath: string;
24
+ /** 条目绝对路径 */
25
+ fullPath: string;
26
+ /** 是否为目录 */
27
+ isDir: boolean;
28
+ }
29
+ /**
30
+ * 目录遍历配置选项。
31
+ */
32
+ export interface TraverseDirectoryOptions {
33
+ /**
34
+ * 条目忽略谓词:返回 true 的相对路径(含目录自身)不进入结果,目录不再下钻。
35
+ * 各调用方的业务忽略规则(如 node_modules、测试文件)以谓词传入保持现有行为。
36
+ */
37
+ ignore?: (relPath: string, isDir: boolean) => boolean;
38
+ /** 是否在结果中包含目录条目(默认仅返回文件) */
39
+ includeDirs?: boolean;
40
+ /** 目录不可读时的错误回调(默认静默跳过该目录) */
41
+ onReadError?: (dir: string, err: unknown) => void;
42
+ }
43
+ /**
44
+ * 递归遍历目录的单一事实源实现(同步)。
45
+ *
46
+ * 防护策略(以归档与摘要收集链路的历史行为为准):
47
+ * - 软链接越界防护:条目真实路径越出遍历根目录边界时直接跳过;
48
+ * - 循环防护:以真实路径去重,重复访问即跳过(含根目录自身入集合);
49
+ * - 无法 stat 或损坏的条目直接跳过,不中断整体遍历;
50
+ * - 同级条目按名称排序,保证遍历顺序稳定。
51
+ *
52
+ * 根目录自身不作为条目返回;目录条目(includeDirs 开启时)先于其子条目输出。
53
+ */
54
+ export declare function traverseDirectory(root: string, options?: TraverseDirectoryOptions): TraverseDirectoryEntry[];
2
55
  /**
3
56
  * 跨运行时安全查找可执行文件绝对物理路径。
4
57
  *
@@ -1,7 +1,102 @@
1
- import { existsSync, realpathSync } from "node:fs";
1
+ import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { basename, delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
+ import { ActionDockError, INVALID_PACKAGE_ID, PATH_TRAVERSAL } from "../errors.js";
4
5
  export { isLoopbackHost } from "./net.js";
6
+ export { isProcessAlive } from "./process.js";
7
+ /**
8
+ * 解析基础 SemVer 版本字符串(单一事实源)。
9
+ * 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
10
+ * 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
11
+ */
12
+ export function parseSemVer(v) {
13
+ const match = v.trim().replace(/^[v=]/, "").match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
14
+ if (!match)
15
+ return null;
16
+ return {
17
+ major: parseInt(match[1], 10),
18
+ minor: parseInt(match[2], 10),
19
+ patch: parseInt(match[3], 10),
20
+ prerelease: match[4],
21
+ };
22
+ }
23
+ /**
24
+ * 递归遍历目录的单一事实源实现(同步)。
25
+ *
26
+ * 防护策略(以归档与摘要收集链路的历史行为为准):
27
+ * - 软链接越界防护:条目真实路径越出遍历根目录边界时直接跳过;
28
+ * - 循环防护:以真实路径去重,重复访问即跳过(含根目录自身入集合);
29
+ * - 无法 stat 或损坏的条目直接跳过,不中断整体遍历;
30
+ * - 同级条目按名称排序,保证遍历顺序稳定。
31
+ *
32
+ * 根目录自身不作为条目返回;目录条目(includeDirs 开启时)先于其子条目输出。
33
+ */
34
+ export function traverseDirectory(root, options = {}) {
35
+ const results = [];
36
+ const visited = new Set();
37
+ const resolvedRoot = resolve(root);
38
+ try {
39
+ visited.add(existsSync(resolvedRoot) ? realpathSync(resolvedRoot) : resolvedRoot);
40
+ }
41
+ catch {
42
+ // 根目录真实路径解析异常时退化为不携带根真实路径的循环拦截
43
+ }
44
+ const walk = (dir) => {
45
+ let names;
46
+ try {
47
+ names = readdirSync(dir).sort();
48
+ }
49
+ catch (err) {
50
+ options.onReadError?.(dir, err);
51
+ return;
52
+ }
53
+ for (const name of names) {
54
+ const fullPath = join(dir, name);
55
+ const relPath = relative(resolvedRoot, fullPath).split(sep).join("/");
56
+ let isDir;
57
+ try {
58
+ isDir = statSync(fullPath).isDirectory();
59
+ }
60
+ catch {
61
+ // 无法访问或损坏的文件/符号链接直接跳过
62
+ continue;
63
+ }
64
+ if (options.ignore?.(relPath, isDir)) {
65
+ continue;
66
+ }
67
+ // 软链接越界防护:条目真实路径越出根目录边界时直接跳过
68
+ let real;
69
+ try {
70
+ real = existsSync(fullPath) ? realpathSync(fullPath) : fullPath;
71
+ }
72
+ catch {
73
+ continue;
74
+ }
75
+ try {
76
+ assertPathWithinRoot(resolvedRoot, real, "traverse path");
77
+ }
78
+ catch {
79
+ continue;
80
+ }
81
+ // 循环防护:真实路径重复出现即跳过
82
+ if (visited.has(real)) {
83
+ continue;
84
+ }
85
+ visited.add(real);
86
+ if (isDir) {
87
+ if (options.includeDirs) {
88
+ results.push({ relPath, fullPath, isDir: true });
89
+ }
90
+ walk(fullPath);
91
+ }
92
+ else {
93
+ results.push({ relPath, fullPath, isDir: false });
94
+ }
95
+ }
96
+ };
97
+ walk(resolvedRoot);
98
+ return results;
99
+ }
5
100
  /**
6
101
  * 跨运行时安全查找可执行文件绝对物理路径。
7
102
  *
@@ -105,7 +200,7 @@ export function assertValidPackageId(packageId) {
105
200
  typeof packageId !== "string" ||
106
201
  packageId.includes("..") ||
107
202
  (!PACKAGE_ID_REGEX.test(packageId) && !isScopedLegacy)) {
108
- throw new Error(`Invalid packageId '${packageId}': must match ${PACKAGE_ID_REGEX} and not contain path traversal characters`);
203
+ throw new ActionDockError(INVALID_PACKAGE_ID, `Invalid packageId '${packageId}': must match ${PACKAGE_ID_REGEX} and not contain path traversal characters`);
109
204
  }
110
205
  }
111
206
  /**
@@ -161,13 +256,13 @@ export function assertPathWithinRoot(rootDir, targetPath, fieldName = "path") {
161
256
  const resolvedTarget = resolve(rootDir, targetPath);
162
257
  const rel = relative(resolvedRoot, resolvedTarget);
163
258
  if (isPathOutsideBoundary(rel)) {
164
- throw new Error(`'${fieldName}' escapes boundary '${rootDir}': ${targetPath}`);
259
+ throw new ActionDockError(PATH_TRAVERSAL, `'${fieldName}' escapes boundary '${rootDir}': ${targetPath}`);
165
260
  }
166
261
  const canonicalRoot = canonicalizePath(resolvedRoot);
167
262
  const canonicalTarget = canonicalizePath(resolvedTarget);
168
263
  const relReal = relative(canonicalRoot, canonicalTarget);
169
264
  if (isPathOutsideBoundary(relReal)) {
170
- throw new Error(`'${fieldName}' symlink resolves outside boundary '${rootDir}': ${targetPath}`);
265
+ throw new ActionDockError(PATH_TRAVERSAL, `'${fieldName}' symlink resolves outside boundary '${rootDir}': ${targetPath}`);
171
266
  }
172
267
  }
173
268
  /**
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 检查目标进程是否处于存活状态(跨域通用谓词单一事实源)。
3
+ *
4
+ * 基于 process.kill(pid, 0) 探测:无异常视为存活;ESRCH(进程不存在)
5
+ * 视为已死;其他异常(如 EPERM,权限不足但进程存在)保守视为存活,
6
+ * 避免权限受限环境下误判他人持有进程死亡而抢占其锁。
7
+ *
8
+ * @param pid 待检测的进程标识符
9
+ */
10
+ export declare function isProcessAlive(pid: number): boolean;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 检查目标进程是否处于存活状态(跨域通用谓词单一事实源)。
3
+ *
4
+ * 基于 process.kill(pid, 0) 探测:无异常视为存活;ESRCH(进程不存在)
5
+ * 视为已死;其他异常(如 EPERM,权限不足但进程存在)保守视为存活,
6
+ * 避免权限受限环境下误判他人持有进程死亡而抢占其锁。
7
+ *
8
+ * @param pid 待检测的进程标识符
9
+ */
10
+ export function isProcessAlive(pid) {
11
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) {
12
+ return false;
13
+ }
14
+ try {
15
+ process.kill(pid, 0);
16
+ return true;
17
+ }
18
+ catch (err) {
19
+ return err?.code !== "ESRCH";
20
+ }
21
+ }
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * ActionDock 核心版本号单一事实源。
3
3
  */
4
- export declare const ACTIONDOCK_VERSION = "2.6.0";
4
+ export declare const ACTIONDOCK_VERSION = "2.8.0";
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * ActionDock 核心版本号单一事实源。
3
3
  */
4
- export const ACTIONDOCK_VERSION = "2.6.0";
4
+ export const ACTIONDOCK_VERSION = "2.8.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/core",
3
- "version": "2.6.0",
3
+ "version": "2.8.0",
4
4
  "description": "ActionDock Core Engine - Project loader, runtime execution, SQLite storage, standalone builder, and skill exporter",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,8 +9,45 @@
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
+ "bun": "./src/index.ts",
12
13
  "import": "./dist/index.js",
13
14
  "default": "./dist/index.js"
15
+ },
16
+ "./server": {
17
+ "types": "./dist/server/index.d.ts",
18
+ "bun": "./src/server/index.ts",
19
+ "import": "./dist/server/index.js",
20
+ "default": "./dist/server/index.js"
21
+ },
22
+ "./project": {
23
+ "types": "./dist/project/index.d.ts",
24
+ "bun": "./src/project/index.ts",
25
+ "import": "./dist/project/index.js",
26
+ "default": "./dist/project/index.js"
27
+ },
28
+ "./registry": {
29
+ "types": "./dist/registry/index.d.ts",
30
+ "bun": "./src/registry/index.ts",
31
+ "import": "./dist/registry/index.js",
32
+ "default": "./dist/registry/index.js"
33
+ },
34
+ "./profile": {
35
+ "types": "./dist/profile/index.d.ts",
36
+ "bun": "./src/profile/index.ts",
37
+ "import": "./dist/profile/index.js",
38
+ "default": "./dist/profile/index.js"
39
+ },
40
+ "./graph": {
41
+ "types": "./dist/graph/index.d.ts",
42
+ "bun": "./src/graph/index.ts",
43
+ "import": "./dist/graph/index.js",
44
+ "default": "./dist/graph/index.js"
45
+ },
46
+ "./package": {
47
+ "types": "./dist/package/index.d.ts",
48
+ "bun": "./src/package/index.ts",
49
+ "import": "./dist/package/index.js",
50
+ "default": "./dist/package/index.js"
14
51
  }
15
52
  },
16
53
  "files": [
@@ -29,9 +66,10 @@
29
66
  "test": "node ../../scripts/run-tests.ts"
30
67
  },
31
68
  "dependencies": {
32
- "@actiondock/sdk": "^2.6.0",
69
+ "@actiondock/sdk": "^2.8.0",
33
70
  "ajv": "^8.17.1",
34
- "ajv-formats": "^3.0.1"
71
+ "ajv-formats": "^3.0.1",
72
+ "undici": "^7.29.1"
35
73
  },
36
74
  "devDependencies": {
37
75
  "typescript": "^5.7.0"
@@ -1,2 +0,0 @@
1
- export * from "./types.js";
2
- export * from "./app.js";
package/dist/app/index.js DELETED
@@ -1,2 +0,0 @@
1
- export * from "./types.js";
2
- export * from "./app.js";
package/dist/app/types.js DELETED
@@ -1 +0,0 @@
1
- export {};
@@ -1,9 +0,0 @@
1
- import type { CatalogSnapshot, IndexedAction } from "./types.js";
2
- export declare class ActionIndex {
3
- private actions;
4
- constructor(snapshot: CatalogSnapshot);
5
- private indexSnapshot;
6
- private add;
7
- list(packageId?: string): IndexedAction[];
8
- find(actionId: string, packageId?: string): IndexedAction[];
9
- }