@actiondock/core 2.5.1 → 2.7.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 (175) hide show
  1. package/dist/app/app.d.ts +14 -1
  2. package/dist/app/app.js +88 -222
  3. package/dist/app/static-index.d.ts +39 -0
  4. package/dist/app/static-index.js +164 -0
  5. package/dist/app/types.d.ts +5 -0
  6. package/dist/catalog/action-index.js +6 -4
  7. package/dist/catalog/package-catalog.js +2 -1
  8. package/dist/doctor/checks/index.d.ts +5 -0
  9. package/dist/doctor/checks/index.js +5 -0
  10. package/dist/doctor/checks/project-locate.d.ts +6 -0
  11. package/dist/doctor/checks/project-locate.js +36 -0
  12. package/dist/doctor/checks/project.d.ts +6 -0
  13. package/dist/doctor/checks/project.js +383 -0
  14. package/dist/doctor/checks/registry.d.ts +13 -0
  15. package/dist/doctor/checks/registry.js +207 -0
  16. package/dist/doctor/checks/runtime.d.ts +13 -0
  17. package/dist/doctor/checks/runtime.js +90 -0
  18. package/dist/doctor/checks/storage.d.ts +5 -0
  19. package/dist/doctor/checks/storage.js +34 -0
  20. package/dist/doctor/context.d.ts +35 -0
  21. package/dist/doctor/context.js +11 -0
  22. package/dist/doctor/doctor.d.ts +5 -0
  23. package/dist/doctor/doctor.js +33 -602
  24. package/dist/errors.d.ts +22 -0
  25. package/dist/errors.js +22 -0
  26. package/dist/execution/service.js +14 -9
  27. package/dist/export/templates.js +42 -30
  28. package/dist/host/host.d.ts +33 -0
  29. package/dist/host/host.js +222 -362
  30. package/dist/host/routing.d.ts +92 -0
  31. package/dist/host/routing.js +238 -0
  32. package/dist/host/types.d.ts +8 -0
  33. package/dist/index.d.ts +3 -1
  34. package/dist/index.js +3 -1
  35. package/dist/input/advice.d.ts +100 -0
  36. package/dist/input/advice.js +767 -0
  37. package/dist/input/describe.d.ts +106 -0
  38. package/dist/input/describe.js +176 -0
  39. package/dist/input/file-input.d.ts +54 -0
  40. package/dist/input/file-input.js +124 -0
  41. package/dist/input/flat-decode.d.ts +11 -0
  42. package/dist/input/flat-decode.js +13 -0
  43. package/dist/input/flat-errors.d.ts +72 -0
  44. package/dist/input/flat-errors.js +87 -0
  45. package/dist/input/flat-materializer.d.ts +29 -0
  46. package/dist/input/flat-materializer.js +179 -0
  47. package/dist/input/flat-parser.d.ts +50 -0
  48. package/dist/input/flat-parser.js +169 -0
  49. package/dist/input/flat-predicates.d.ts +34 -0
  50. package/dist/input/flat-predicates.js +50 -0
  51. package/dist/input/index.d.ts +14 -0
  52. package/dist/input/index.js +14 -0
  53. package/dist/input/input-resolver.d.ts +63 -0
  54. package/dist/input/input-resolver.js +254 -0
  55. package/dist/input/json-depth-scanner.d.ts +17 -0
  56. package/dist/input/json-depth-scanner.js +49 -0
  57. package/dist/input/metadata.d.ts +99 -0
  58. package/dist/input/metadata.js +99 -0
  59. package/dist/input/stdin-input.d.ts +26 -0
  60. package/dist/input/stdin-input.js +125 -0
  61. package/dist/input/utf8.d.ts +19 -0
  62. package/dist/input/utf8.js +36 -0
  63. package/dist/input/validation-mapper.d.ts +27 -0
  64. package/dist/input/validation-mapper.js +144 -0
  65. package/dist/ipc/host.js +38 -0
  66. package/dist/json/index.d.ts +1 -0
  67. package/dist/json/index.js +1 -0
  68. package/dist/json/value-validator.d.ts +88 -0
  69. package/dist/json/value-validator.js +330 -0
  70. package/dist/platform/types.d.ts +7 -0
  71. package/dist/process/control-arbiter.d.ts +107 -0
  72. package/dist/process/control-arbiter.js +251 -0
  73. package/dist/process/diagnostics-sink.d.ts +26 -0
  74. package/dist/process/diagnostics-sink.js +35 -0
  75. package/dist/process/index.d.ts +7 -0
  76. package/dist/process/index.js +7 -0
  77. package/dist/process/input-dispatcher.d.ts +88 -0
  78. package/dist/process/input-dispatcher.js +240 -0
  79. package/dist/process/managed-record.d.ts +78 -0
  80. package/dist/process/managed-record.js +1 -0
  81. package/dist/process/process-manager.d.ts +15 -56
  82. package/dist/process/process-manager.js +119 -707
  83. package/dist/process/reservation-table.d.ts +39 -0
  84. package/dist/process/reservation-table.js +80 -0
  85. package/dist/process/run-executor.d.ts +16 -0
  86. package/dist/process/run-executor.js +155 -0
  87. package/dist/process/terminal-output-cache.d.ts +66 -0
  88. package/dist/process/terminal-output-cache.js +132 -0
  89. package/dist/profile/client-actions.d.ts +56 -0
  90. package/dist/profile/client-actions.js +215 -0
  91. package/dist/profile/client-config.d.ts +20 -0
  92. package/dist/profile/client-config.js +51 -0
  93. package/dist/profile/client-health.d.ts +14 -0
  94. package/dist/profile/client-health.js +69 -0
  95. package/dist/profile/client-playbooks.d.ts +19 -0
  96. package/dist/profile/client-playbooks.js +36 -0
  97. package/dist/profile/client-query.d.ts +30 -0
  98. package/dist/profile/client-query.js +70 -0
  99. package/dist/profile/client-runs.d.ts +42 -0
  100. package/dist/profile/client-runs.js +58 -0
  101. package/dist/profile/client-state.d.ts +56 -0
  102. package/dist/profile/client-state.js +83 -0
  103. package/dist/profile/client-transport.d.ts +61 -0
  104. package/dist/profile/client-transport.js +156 -0
  105. package/dist/profile/client.d.ts +23 -142
  106. package/dist/profile/client.js +28 -588
  107. package/dist/profile/manager.js +1 -1
  108. package/dist/project/closure.d.ts +4 -4
  109. package/dist/project/closure.js +11 -5
  110. package/dist/project/init.js +2 -2
  111. package/dist/project/loader.d.ts +0 -25
  112. package/dist/project/loader.js +1 -154
  113. package/dist/project/transactions.d.ts +12 -14
  114. package/dist/project/transactions.js +60 -836
  115. package/dist/registry/registry.js +6 -4
  116. package/dist/registry/resolve.js +1 -1
  117. package/dist/runtime/action-registry.d.ts +45 -0
  118. package/dist/runtime/action-registry.js +108 -0
  119. package/dist/runtime/clock.d.ts +5 -17
  120. package/dist/runtime/clock.js +5 -15
  121. package/dist/runtime/context.d.ts +3 -0
  122. package/dist/runtime/context.js +17 -56
  123. package/dist/runtime/index.d.ts +3 -0
  124. package/dist/runtime/index.js +3 -0
  125. package/dist/runtime/package-runner-factory.d.ts +82 -0
  126. package/dist/runtime/package-runner-factory.js +186 -0
  127. package/dist/runtime/run-persistence.d.ts +85 -0
  128. package/dist/runtime/run-persistence.js +111 -0
  129. package/dist/runtime/runner.d.ts +25 -45
  130. package/dist/runtime/runner.js +130 -354
  131. package/dist/runtime/standalone.d.ts +25 -2
  132. package/dist/runtime/standalone.js +305 -87
  133. package/dist/schema/index.d.ts +1 -0
  134. package/dist/schema/index.js +1 -0
  135. package/dist/schema/validator.d.ts +10 -0
  136. package/dist/schema/validator.js +48 -54
  137. package/dist/server/dispatcher.d.ts +8 -3
  138. package/dist/server/dispatcher.js +16 -40
  139. package/dist/server/routes/runs.js +6 -4
  140. package/dist/server/security.d.ts +1 -7
  141. package/dist/server/security.js +2 -13
  142. package/dist/storage/clock.d.ts +25 -0
  143. package/dist/storage/clock.js +23 -0
  144. package/dist/storage/data-dir-lock.d.ts +3 -36
  145. package/dist/storage/data-dir-lock.js +48 -881
  146. package/dist/storage/index.d.ts +8 -1
  147. package/dist/storage/index.js +8 -2
  148. package/dist/storage/lazy.js +1 -0
  149. package/dist/storage/lock-core.d.ts +162 -0
  150. package/dist/storage/lock-core.js +885 -0
  151. package/dist/storage/sqlite.d.ts +19 -6
  152. package/dist/storage/sqlite.js +81 -89
  153. package/dist/storage/types.d.ts +27 -5
  154. package/dist/storage/types.js +21 -0
  155. package/dist/storage/utils.d.ts +22 -0
  156. package/dist/storage/utils.js +34 -0
  157. package/dist/target/local.d.ts +1 -0
  158. package/dist/target/local.js +23 -17
  159. package/dist/target/remote-errors.d.ts +21 -0
  160. package/dist/target/remote-errors.js +51 -0
  161. package/dist/target/remote-polling.d.ts +35 -0
  162. package/dist/target/remote-polling.js +104 -0
  163. package/dist/target/remote.d.ts +4 -0
  164. package/dist/target/remote.js +108 -301
  165. package/dist/target/sse-parser.d.ts +35 -0
  166. package/dist/target/sse-parser.js +163 -0
  167. package/dist/target/target.js +2 -0
  168. package/dist/target/types.d.ts +6 -0
  169. package/dist/utils/index.d.ts +1 -0
  170. package/dist/utils/index.js +1 -0
  171. package/dist/utils/net.d.ts +10 -0
  172. package/dist/utils/net.js +16 -0
  173. package/dist/version.d.ts +1 -1
  174. package/dist/version.js +1 -1
  175. package/package.json +2 -2
@@ -18,75 +18,36 @@ const validatorCache = new WeakMap();
18
18
  /**
19
19
  * 递归检查数据中是否包含危险的原型污染属性键名(__proto__、constructor、prototype)。
20
20
  */
21
+ import { validateActionInputValue } from "../json/value-validator.js";
22
+ /**
23
+ * 递归检查数据中是否包含危险的原型污染属性键名(__proto__、constructor、prototype)。
24
+ * 内部委托 validateActionInputValue 执行迭代遍历,确保环路安全、有向无环图安全且无调用栈溢出风险。
25
+ */
21
26
  export function hasDangerousKeys(data) {
22
- if (data === null || typeof data !== "object") {
23
- return false;
24
- }
25
- if (Array.isArray(data)) {
26
- for (const item of data) {
27
- if (hasDangerousKeys(item))
28
- return true;
29
- }
30
- return false;
31
- }
32
- const obj = data;
33
- const keys = Object.getOwnPropertyNames(obj);
34
- for (const key of keys) {
35
- if (key === "__proto__" || key === "constructor" || key === "prototype") {
36
- return true;
37
- }
38
- if (hasDangerousKeys(obj[key])) {
39
- return true;
40
- }
41
- }
42
- return false;
27
+ const result = validateActionInputValue(data);
28
+ return !result.valid && result.kind === "input-policy";
43
29
  }
44
30
  /**
45
- * 校验指定数据是否符合给定的 JSON Schema 契约。
31
+ * 内部纯 Schema 校验器。
32
+ * 仅处理布尔 Schema 与 Ajv 规则校验,严禁执行 dangerous-key 遍历。
46
33
  *
47
- * 行为与策略约定:
48
- * 1. 严格契约校验:当 Schema 中定义 additionalProperties: false 时,包含未声明属性的输入将被直接拒绝(返回 valid: false),系统不会静默过滤或篡改用户输入。
49
- * 2. 危险原型键硬拦截:输入数据或其任意嵌套对象中若出现 __proto__、constructor 或 prototype 键名,即使 Schema 允许亦立即拒绝,彻底杜绝原型污染。
50
- * 3. 布尔 Schema:false 显式拒绝所有输入;true 或未定义时允许所有合法非污染数据。
51
- * 4. 无原型对象支持:完全支持 Object.create(null) 创建的无原型对象校验。
52
- *
53
- * @param schema 期望匹配的 JSON Schema 对象(未提供或为空对象/true 时默认通过;为 false 时拒绝所有数据)
34
+ * @param schema 期望匹配的 JSON Schema 对象
54
35
  * @param data 待校验的原始数据
55
36
  * @returns 包含 valid 状态与错误信息列表的 ValidationResult
56
37
  */
57
- export function validateSchema(schema, data) {
58
- // 1. 布尔 Schema 支持:false 拒绝一切输入;true/undefined 允许一切输入
38
+ export function validateSchemaOnly(schema, data) {
59
39
  if (schema === false) {
60
40
  return {
61
41
  valid: false,
62
42
  errors: ["Schema is false, rejecting all data"],
63
43
  };
64
44
  }
65
- if (schema === true || !schema) {
66
- if (hasDangerousKeys(data)) {
67
- return {
68
- valid: false,
69
- errors: ["Data contains prohibited prototype pollution keys (__proto__, constructor, or prototype)"],
70
- };
71
- }
45
+ if (schema === true ||
46
+ !schema ||
47
+ typeof schema !== "object" ||
48
+ Object.keys(schema).length === 0) {
72
49
  return { valid: true };
73
50
  }
74
- if (typeof schema !== "object" || Object.keys(schema).length === 0) {
75
- if (hasDangerousKeys(data)) {
76
- return {
77
- valid: false,
78
- errors: ["Data contains prohibited prototype pollution keys (__proto__, constructor, or prototype)"],
79
- };
80
- }
81
- return { valid: true };
82
- }
83
- // 2. 危险原型键拦截
84
- if (hasDangerousKeys(data)) {
85
- return {
86
- valid: false,
87
- errors: ["Data contains prohibited prototype pollution keys (__proto__, constructor, or prototype)"],
88
- };
89
- }
90
51
  try {
91
52
  let validate = validatorCache.get(schema);
92
53
  if (!validate) {
@@ -120,3 +81,36 @@ export function validateSchema(schema, data) {
120
81
  };
121
82
  }
122
83
  }
84
+ /**
85
+ * 校验指定数据是否符合给定的 JSON Schema 契约。
86
+ *
87
+ * 行为与策略约定:
88
+ * 1. 严格契约校验:当 Schema 中定义 additionalProperties: false 时,包含未声明属性的输入将被直接拒绝(返回 valid: false),系统不会静默过滤或篡改用户输入。
89
+ * 2. 危险原型键硬拦截:输入数据或其任意嵌套对象中若出现 __proto__、constructor 或 prototype 键名,即使 Schema 允许亦立即拒绝,彻底杜绝原型污染。
90
+ * 3. 布尔 Schema:false 显式拒绝所有输入;true 或未定义时允许所有合法非污染数据。
91
+ * 4. 无原型对象支持:完全支持 Object.create(null) 创建的无原型对象校验。
92
+ *
93
+ * @param schema 期望匹配的 JSON Schema 对象(未提供或为空对象/true 时默认通过;为 false 时拒绝所有数据)
94
+ * @param data 待校验的原始数据
95
+ * @returns 包含 valid 状态与错误信息列表的 ValidationResult
96
+ */
97
+ export function validateSchema(schema, data) {
98
+ // 1. 布尔 Schema 支持:false 拒绝一切输入
99
+ if (schema === false) {
100
+ return {
101
+ valid: false,
102
+ errors: ["Schema is false, rejecting all data"],
103
+ };
104
+ }
105
+ // 2. 危险原型键拦截(保持历史兼容,包含 schema 为 true / undefined / {} 的场景)
106
+ if (hasDangerousKeys(data)) {
107
+ return {
108
+ valid: false,
109
+ errors: [
110
+ "Data contains prohibited prototype pollution keys (__proto__, constructor, or prototype)",
111
+ ],
112
+ };
113
+ }
114
+ // 3. 委托纯 Schema 校验器处理其余规则校验
115
+ return validateSchemaOnly(schema, data);
116
+ }
@@ -4,6 +4,8 @@
4
4
  export type FetchDispatcherProvider = () => unknown;
5
5
  /**
6
6
  * 注册全局用于忽略服务端证书校验的调度器提供者。
7
+ * 唯一注册通道:平台适配层(如 @actiondock/runtime-node 的
8
+ * installInsecureDispatcher)显式调用完成注册,不再读取全局符号注入。
7
9
  */
8
10
  export declare function setInsecureDispatcherProvider(provider?: FetchDispatcherProvider): void;
9
11
  /**
@@ -13,12 +15,15 @@ export declare function getInsecureDispatcherProvider(): FetchDispatcherProvider
13
15
  /**
14
16
  * 获取用于忽略服务端证书校验的调度器实例。
15
17
  * 纯粹解耦设计:
16
- * 1. 优先调用显式注册的提供者(如由 @actiondock/runtime-node 注册);
17
- * 2. 检查全局符号注入的提供者;
18
- * 3. 若在 Node 环境下且前述未注入,安全动态加载兜底连接池,绝不让 core 包产生静态硬编译依赖。
18
+ * 1. 优先调用显式注册的提供者(如由 @actiondock/runtime-node 显式安装);
19
+ * 2. 若在 Node 环境下且无注册提供者,动态加载 @actiondock/runtime-node
20
+ * (其入口聚合点会显式完成安装),绝不让 core 包产生静态硬编译依赖;
21
+ * 3. 仍不可得时返回 undefined,由调用方回退默认 fetch,绝不在 core 内
22
+ * 直接探测加载 undici,避免与平台适配层形成层次倒置与隐藏依赖环。
19
23
  */
20
24
  export declare function getInsecureDispatcher(): unknown;
21
25
  /**
22
26
  * 显式强制重置并销毁调度器连接池(仅用于单元测试重置或进程退出)。
27
+ * core 自身不再持有任何兜底连接池,关闭语义完全委托已注册的平台提供者宿主。
23
28
  */
24
29
  export declare function closeInsecureDispatcher(): Promise<void>;
@@ -1,8 +1,9 @@
1
1
  import { createRequire } from "node:module";
2
2
  let insecureDispatcherProvider;
3
- let fallbackInsecureAgent;
4
3
  /**
5
4
  * 注册全局用于忽略服务端证书校验的调度器提供者。
5
+ * 唯一注册通道:平台适配层(如 @actiondock/runtime-node 的
6
+ * installInsecureDispatcher)显式调用完成注册,不再读取全局符号注入。
6
7
  */
7
8
  export function setInsecureDispatcherProvider(provider) {
8
9
  insecureDispatcherProvider = provider;
@@ -16,18 +17,16 @@ export function getInsecureDispatcherProvider() {
16
17
  /**
17
18
  * 获取用于忽略服务端证书校验的调度器实例。
18
19
  * 纯粹解耦设计:
19
- * 1. 优先调用显式注册的提供者(如由 @actiondock/runtime-node 注册);
20
- * 2. 检查全局符号注入的提供者;
21
- * 3. 若在 Node 环境下且前述未注入,安全动态加载兜底连接池,绝不让 core 包产生静态硬编译依赖。
20
+ * 1. 优先调用显式注册的提供者(如由 @actiondock/runtime-node 显式安装);
21
+ * 2. 若在 Node 环境下且无注册提供者,动态加载 @actiondock/runtime-node
22
+ * (其入口聚合点会显式完成安装),绝不让 core 包产生静态硬编译依赖;
23
+ * 3. 仍不可得时返回 undefined,由调用方回退默认 fetch,绝不在 core 内
24
+ * 直接探测加载 undici,避免与平台适配层形成层次倒置与隐藏依赖环。
22
25
  */
23
26
  export function getInsecureDispatcher() {
24
27
  if (insecureDispatcherProvider) {
25
28
  return insecureDispatcherProvider();
26
29
  }
27
- const globalProvider = globalThis[Symbol.for("actiondock.insecureDispatcherProvider")];
28
- if (typeof globalProvider === "function") {
29
- return globalProvider();
30
- }
31
30
  if (typeof process !== "undefined" && process.versions?.node) {
32
31
  try {
33
32
  const req = createRequire(import.meta.url);
@@ -39,48 +38,25 @@ export function getInsecureDispatcher() {
39
38
  catch {
40
39
  // 忽略未安装 @actiondock/runtime-node 的情况
41
40
  }
42
- if (!fallbackInsecureAgent || fallbackInsecureAgent.closed || fallbackInsecureAgent.destroyed) {
43
- try {
44
- const req = createRequire(import.meta.url);
45
- const undici = req("undici");
46
- if (undici?.Agent) {
47
- fallbackInsecureAgent = new undici.Agent({
48
- connect: {
49
- rejectUnauthorized: false,
50
- },
51
- bodyTimeout: 0,
52
- });
53
- }
54
- }
55
- catch {
56
- // 忽略非 Node 环境或未安装 undici 的异常
57
- }
58
- }
59
- return fallbackInsecureAgent;
60
41
  }
61
42
  return undefined;
62
43
  }
63
44
  /**
64
45
  * 显式强制重置并销毁调度器连接池(仅用于单元测试重置或进程退出)。
46
+ * core 自身不再持有任何兜底连接池,关闭语义完全委托已注册的平台提供者宿主。
65
47
  */
66
48
  export async function closeInsecureDispatcher() {
67
- const globalClose = globalThis[Symbol.for("actiondock.closeInsecureDispatcher")];
68
- if (typeof globalClose === "function") {
69
- try {
70
- await globalClose();
71
- }
72
- catch {
73
- // 忽略关闭异常
74
- }
75
- }
76
- if (fallbackInsecureAgent) {
77
- const agent = fallbackInsecureAgent;
78
- fallbackInsecureAgent = undefined;
49
+ if (insecureDispatcherProvider) {
50
+ // 提供者由平台层注册:优先委托提供者宿主的关闭语义(如可用)
79
51
  try {
80
- await agent.close();
52
+ const req = createRequire(import.meta.url);
53
+ const runtimeNode = req("@actiondock/runtime-node");
54
+ if (typeof runtimeNode?.closeInsecureDispatcher === "function") {
55
+ await runtimeNode.closeInsecureDispatcher();
56
+ }
81
57
  }
82
58
  catch {
83
- // 忽略关闭异常
59
+ // 忽略未安装 @actiondock/runtime-node 的情况
84
60
  }
85
61
  }
86
62
  }
@@ -49,12 +49,11 @@ export async function handleRunsRoutes(ctx) {
49
49
  }
50
50
  if (app.storage && typeof app.storage.listRuns === "function") {
51
51
  try {
52
- const records = app.storage.listRuns({ actionId, limit });
52
+ // 状态过滤下推到存储层 SQL,避免先分页后过滤导致某包前 N 条非目标状态时贡献 0
53
+ const records = app.storage.listRuns({ actionId, status, limit });
53
54
  for (const r of records) {
54
55
  if (!seenRunIds.has(r.id)) {
55
56
  seenRunIds.add(r.id);
56
- if (status && r.status !== status)
57
- continue;
58
57
  if (options.packageAllowlist &&
59
58
  options.packageAllowlist.length > 0 &&
60
59
  r.packageId &&
@@ -65,7 +64,10 @@ export async function handleRunsRoutes(ctx) {
65
64
  }
66
65
  }
67
66
  }
68
- catch { }
67
+ catch (err) {
68
+ // 单包查询失败不阻断整体列表响应,但必须可观测
69
+ console.warn(`[actiondock] listRuns failed for package '${app.packageId}': ${err instanceof Error ? err.message : String(err)}`);
70
+ }
69
71
  }
70
72
  }
71
73
  allRuns.sort((a, b) => (b.startedAt || "").localeCompare(a.startedAt || ""));
@@ -1,3 +1,4 @@
1
+ export { isLoopbackHost } from "../utils/net.js";
1
2
  /**
2
3
  * 恒定时间字符串比较(Constant-time comparison)。
3
4
  * 使用底层的 `crypto.timingSafeEqual` 防范时序攻击(Timing Attack)。
@@ -7,13 +8,6 @@
7
8
  * @returns 两个字符串内容是否完全一致
8
9
  */
9
10
  export declare function safeEqual(a: string, b: string): boolean;
10
- /**
11
- * 检查指定的主机地址是否为本地回环接口(Loopback Host)。
12
- * 支持 127.0.0.1, localhost, ::1 等形式。
13
- *
14
- * @param host 主机名或 IP 字符串
15
- */
16
- export declare function isLoopbackHost(host: string): boolean;
17
11
  /**
18
12
  * 校验 HTTP 请求中的鉴权令牌选项。
19
13
  */
@@ -1,4 +1,6 @@
1
1
  import { timingSafeEqual } from "node:crypto";
2
+ // isLoopbackHost 已上移至 utils/net.ts(通用谓词单一事实源),此处仅保留 re-export 兼容旧引用路径
3
+ export { isLoopbackHost } from "../utils/net.js";
2
4
  /**
3
5
  * 恒定时间字符串比较(Constant-time comparison)。
4
6
  * 使用底层的 `crypto.timingSafeEqual` 防范时序攻击(Timing Attack)。
@@ -15,19 +17,6 @@ export function safeEqual(a, b) {
15
17
  }
16
18
  return timingSafeEqual(aa, bb);
17
19
  }
18
- /**
19
- * 检查指定的主机地址是否为本地回环接口(Loopback Host)。
20
- * 支持 127.0.0.1, localhost, ::1 等形式。
21
- *
22
- * @param host 主机名或 IP 字符串
23
- */
24
- export function isLoopbackHost(host) {
25
- const trimmed = host.trim().toLowerCase().replace(/^\[|\]$/g, "");
26
- return (trimmed === "127.0.0.1" ||
27
- trimmed === "::1" ||
28
- trimmed === "localhost" ||
29
- trimmed === "0:0:0:0:0:0:0:1");
30
- }
31
20
  /**
32
21
  * 校验 HTTP 请求中的鉴权令牌是否有效。
33
22
  *
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 统一时间与时钟基础契约。
3
+ *
4
+ * 单一事实源:Clock 接口与 SystemClock 实现定义于此,runtime/clock.ts 与
5
+ * storage/types.ts 一律从本文件引用或转发,禁止重复实现。
6
+ */
7
+ /**
8
+ * 统一时间与时钟接口。
9
+ */
10
+ export interface Clock {
11
+ /** 获取当前系统墙上时间 */
12
+ now(): Date;
13
+ /** 获取单调递增时间戳(单位:毫秒) */
14
+ monotonic(): number;
15
+ /** 异步休眠指定毫秒 */
16
+ sleep(ms: number): Promise<void>;
17
+ }
18
+ /**
19
+ * 生产环境系统时钟实现。
20
+ */
21
+ export declare class SystemClock implements Clock {
22
+ now(): Date;
23
+ monotonic(): number;
24
+ sleep(ms: number): Promise<void>;
25
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 统一时间与时钟基础契约。
3
+ *
4
+ * 单一事实源:Clock 接口与 SystemClock 实现定义于此,runtime/clock.ts 与
5
+ * storage/types.ts 一律从本文件引用或转发,禁止重复实现。
6
+ */
7
+ /**
8
+ * 生产环境系统时钟实现。
9
+ */
10
+ export class SystemClock {
11
+ now() {
12
+ return new Date();
13
+ }
14
+ monotonic() {
15
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
16
+ return performance.now();
17
+ }
18
+ return Date.now();
19
+ }
20
+ sleep(ms) {
21
+ return new Promise((resolve) => setTimeout(resolve, ms));
22
+ }
23
+ }
@@ -1,3 +1,4 @@
1
+ import { cleanStaleQuarantines, isProcessAlive, parseQuarantineTimestamp, safeRemoveStaleReclaimGuard, safeRollbackLock } from "./lock-core.js";
1
2
  /**
2
3
  * 数据目录排他锁元数据契约。
3
4
  */
@@ -17,41 +18,7 @@ export interface DataDirLockInfo {
17
18
  /** 宿主会话标识 */
18
19
  hostSessionId?: string;
19
20
  }
20
- /**
21
- * 检查目标进程是否处于存活状态。
22
- *
23
- * @param pid 待检测的进程标识符
24
- */
25
- export declare function isProcessAlive(pid: number): boolean;
26
- /**
27
- * 安全清理陈旧接管守卫(reclaim guard)。
28
- * 采用原子重命名检疫并严格核对 guardToken 与持有者存活状态,确保仅删除目标陈旧 guard,严禁误删活跃守卫或并发新守卫。
29
- *
30
- * @param reclaimPath 目标守卫路径
31
- * @param expectedGuardToken 预期持有的守卫令牌(必填,拒绝未指定令牌的盲目清理)
32
- */
33
- export declare function safeRemoveStaleReclaimGuard(reclaimPath: string, expectedGuardToken: string): void;
34
- /**
35
- * 从隔离或回滚临时目录名称中解析操作者 PID 与隔离生成时间戳。
36
- * 命名规范:*.(quarantine|rollback|release).<pid>.<timestamp>.<uuid>
37
- */
38
- export declare function parseQuarantineTimestamp(entryName: string): {
39
- operatorPid?: number;
40
- timestamp?: number;
41
- };
42
- /**
43
- * 清理过期的隔离目录(GC 回收机制)。
44
- * 实施分流存活校验,防止误删活跃持锁者与回滚中目录,同时防止孤儿目录无限泄漏:
45
- * - 共同前置条件:存活年龄必须超过 maxAgeMs(默认 10000ms),未超期前严禁清理(保持宽限期)。
46
- * - 活跃隔离目录(quarantine / rollback / release):若 operatorPid 存活或内部 metadata.json 记录的 pid 与 childPids 存活,跳过不予清理。
47
- * - 孤儿脱离目录(orphan):内部旧 metadata.pid 不再作为续命依据;提取其内部 orphanToken,仅当当前规范主路径(主锁目录与 reclaim 目录)依然持有该凭证时保守跳过;规范路径不存在或已变更凭据时直接安全清理。
48
- */
49
- export declare function cleanStaleQuarantines(parentDir: string, basePrefix: string, maxAgeMs?: number, deadline?: number): void;
50
- /**
51
- * 安全回滚当前进程创建的主锁。
52
- * 优先核对 lockToken,回退核对 sessionToken,防止误删接管者或并发新锁。
53
- */
54
- export declare function safeRollbackLock(lockPath: string, expectedSessionToken: string, expectedLockToken?: string): void;
21
+ export { isProcessAlive, parseQuarantineTimestamp, cleanStaleQuarantines, safeRemoveStaleReclaimGuard, safeRollbackLock, };
55
22
  /**
56
23
  * 安全释放主锁。
57
24
  * 仅当锁目录中持有当前 lockToken / sessionToken 时才删除,杜绝删除他人新锁。
@@ -95,7 +62,7 @@ export declare class DataDirLock {
95
62
  /**
96
63
  * 尝试获取指定数据目录的排他锁。
97
64
  *
98
- * 仲裁规则:
65
+ * 仲裁规则由目录锁内核(lock-core)统一承载:
99
66
  * - 引入所有竞争者均遵守的原子 reclaim guard 机制(lockDirPath.reclaim),每个 guard 具备全局唯一 guardToken。
100
67
  * - 基于原子目录创建 mkdirSync(lockDirPath, { mode: 0o700 }) 确立所有权,并在其下存放 metadata.json。
101
68
  * - 兼容已有常规文件锁(如老版本或测试 mock 场景)。