@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
@@ -10,8 +10,18 @@ export interface ValidationResult {
10
10
  }
11
11
  /**
12
12
  * 递归检查数据中是否包含危险的原型污染属性键名(__proto__、constructor、prototype)。
13
+ * 内部委托 validateActionInputValue 执行迭代遍历,确保环路安全、有向无环图安全且无调用栈溢出风险。
13
14
  */
14
15
  export declare function hasDangerousKeys(data: unknown): boolean;
16
+ /**
17
+ * 内部纯 Schema 校验器。
18
+ * 仅处理布尔 Schema 与 Ajv 规则校验,严禁执行 dangerous-key 遍历。
19
+ *
20
+ * @param schema 期望匹配的 JSON Schema 对象
21
+ * @param data 待校验的原始数据
22
+ * @returns 包含 valid 状态与错误信息列表的 ValidationResult
23
+ */
24
+ export declare function validateSchemaOnly(schema: JsonSchema | undefined, data: unknown): ValidationResult;
15
25
  /**
16
26
  * 校验指定数据是否符合给定的 JSON Schema 契约。
17
27
  *
@@ -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
+ }
@@ -1,3 +1,4 @@
1
+ import { ActionDockError } from "../errors.js";
1
2
  /**
2
3
  * 默认单次 HTTP 请求体最大字节上限(1 MiB)。
3
4
  */
@@ -5,15 +6,13 @@ export declare const DEFAULT_MAX_BODY_BYTES: number;
5
6
  /**
6
7
  * 请求体超出最大允许体积时抛出的异常(对应 HTTP 413 Payload Too Large)。
7
8
  */
8
- export declare class RequestTooLargeError extends Error {
9
- code: string;
9
+ export declare class RequestTooLargeError extends ActionDockError {
10
10
  constructor(message?: string);
11
11
  }
12
12
  /**
13
13
  * 请求体非合法 JSON 格式时抛出的异常(对应 HTTP 400 Bad Request)。
14
14
  */
15
- export declare class InvalidJsonError extends Error {
16
- code: string;
15
+ export declare class InvalidJsonError extends ActionDockError {
17
16
  constructor(message?: string);
18
17
  }
19
18
  /**
@@ -24,12 +23,21 @@ export interface ReadJsonBodyOptions {
24
23
  maxBytes?: number;
25
24
  }
26
25
  /**
27
- * 流式安全地读取 HTTP 请求体并解析为 JSON 对象。
26
+ * 流式安全地读取 HTTP 请求体为原始字节数组(有界读取单一事实源)。
28
27
  *
29
28
  * 防护机制:
30
- * 1. 快速拒绝(Fast-path):若请求头中存在 `Content-Length` 且超过 `maxBytes`,立即抛出 RequestTooLargeError,不进行内存分配。
31
- * 2. 流式计数器(Chunk Streaming):在读取 ReadableStream 过程中实时累计字节数,中途超出上限即刻截断并释放流锁,防止大文件 DoS 与内存耗尽(OOM)。
32
- * 3. 安全解码:处理空请求体与 UTF-8 字符集解码。
29
+ * 1. 快速拒绝(Fast-path):若请求头中存在 Content-Length 且超过 maxBytes,立即抛出 RequestTooLargeError,不进行内存分配。
30
+ * 2. 流式计数器(Chunk Streaming):读取 ReadableStream 过程中实时累计字节数,中途超出上限即刻截断并释放流锁,防止大文件 DoS 与内存耗尽(OOM)。
31
+ *
32
+ * @param req 传入的 Request 对象
33
+ * @param options 读取配置选项
34
+ * @returns 原始字节数组(空请求体返回空数组)
35
+ * @throws {RequestTooLargeError} 若请求体体积超限
36
+ */
37
+ export declare function readBodyWithLimit(req: Request, options?: ReadJsonBodyOptions): Promise<Uint8Array>;
38
+ /**
39
+ * 流式安全地读取 HTTP 请求体并解析为 JSON 对象。
40
+ * 防护机制见 readBodyWithLimit;此外处理空请求体与 UTF-8 字符集解码。
33
41
  *
34
42
  * @param req 传入的 Request 对象
35
43
  * @param options 读取配置选项
@@ -1,3 +1,4 @@
1
+ import { ActionDockError } from "../errors.js";
1
2
  /**
2
3
  * 默认单次 HTTP 请求体最大字节上限(1 MiB)。
3
4
  */
@@ -5,38 +6,36 @@ export const DEFAULT_MAX_BODY_BYTES = 1024 * 1024; // 1 MiB
5
6
  /**
6
7
  * 请求体超出最大允许体积时抛出的异常(对应 HTTP 413 Payload Too Large)。
7
8
  */
8
- export class RequestTooLargeError extends Error {
9
- code = "REQUEST_TOO_LARGE";
9
+ export class RequestTooLargeError extends ActionDockError {
10
10
  constructor(message = "Request body exceeds maximum allowed size") {
11
- super(message);
11
+ super("REQUEST_TOO_LARGE", message);
12
12
  this.name = "RequestTooLargeError";
13
+ Object.setPrototypeOf(this, RequestTooLargeError.prototype);
13
14
  }
14
15
  }
15
16
  /**
16
17
  * 请求体非合法 JSON 格式时抛出的异常(对应 HTTP 400 Bad Request)。
17
18
  */
18
- export class InvalidJsonError extends Error {
19
- code = "INVALID_JSON";
19
+ export class InvalidJsonError extends ActionDockError {
20
20
  constructor(message = "Failed to parse request body as JSON") {
21
- super(message);
21
+ super("INVALID_JSON", message);
22
22
  this.name = "InvalidJsonError";
23
+ Object.setPrototypeOf(this, InvalidJsonError.prototype);
23
24
  }
24
25
  }
25
26
  /**
26
- * 流式安全地读取 HTTP 请求体并解析为 JSON 对象。
27
+ * 流式安全地读取 HTTP 请求体为原始字节数组(有界读取单一事实源)。
27
28
  *
28
29
  * 防护机制:
29
- * 1. 快速拒绝(Fast-path):若请求头中存在 `Content-Length` 且超过 `maxBytes`,立即抛出 RequestTooLargeError,不进行内存分配。
30
- * 2. 流式计数器(Chunk Streaming):在读取 ReadableStream 过程中实时累计字节数,中途超出上限即刻截断并释放流锁,防止大文件 DoS 与内存耗尽(OOM)。
31
- * 3. 安全解码:处理空请求体与 UTF-8 字符集解码。
30
+ * 1. 快速拒绝(Fast-path):若请求头中存在 Content-Length 且超过 maxBytes,立即抛出 RequestTooLargeError,不进行内存分配。
31
+ * 2. 流式计数器(Chunk Streaming):读取 ReadableStream 过程中实时累计字节数,中途超出上限即刻截断并释放流锁,防止大文件 DoS 与内存耗尽(OOM)。
32
32
  *
33
33
  * @param req 传入的 Request 对象
34
34
  * @param options 读取配置选项
35
- * @returns 解析后的 JSON 对象
35
+ * @returns 原始字节数组(空请求体返回空数组)
36
36
  * @throws {RequestTooLargeError} 若请求体体积超限
37
- * @throws {InvalidJsonError} 若请求体非合法 JSON
38
37
  */
39
- export async function readJsonBody(req, options = {}) {
38
+ export async function readBodyWithLimit(req, options = {}) {
40
39
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BODY_BYTES;
41
40
  // 1. 快速检查 Content-Length 头部
42
41
  const contentLengthHeader = req.headers.get("content-length");
@@ -48,7 +47,7 @@ export async function readJsonBody(req, options = {}) {
48
47
  }
49
48
  // 2. 流式读取并实时统计字节数
50
49
  if (!req.body) {
51
- return {};
50
+ return new Uint8Array(0);
52
51
  }
53
52
  const reader = req.body.getReader();
54
53
  const chunks = [];
@@ -61,6 +60,7 @@ export async function readJsonBody(req, options = {}) {
61
60
  if (value) {
62
61
  totalBytes += value.byteLength;
63
62
  if (totalBytes > maxBytes) {
63
+ await reader.cancel();
64
64
  throw new RequestTooLargeError();
65
65
  }
66
66
  chunks.push(value);
@@ -71,15 +71,32 @@ export async function readJsonBody(req, options = {}) {
71
71
  reader.releaseLock();
72
72
  }
73
73
  if (chunks.length === 0 || totalBytes === 0) {
74
- return {};
74
+ return new Uint8Array(0);
75
75
  }
76
- // 3. 拼接字节数组并进行 UTF-8 解码
76
+ // 3. 拼接字节数组
77
77
  const merged = new Uint8Array(totalBytes);
78
78
  let offset = 0;
79
79
  for (const chunk of chunks) {
80
80
  merged.set(chunk, offset);
81
81
  offset += chunk.byteLength;
82
82
  }
83
+ return merged;
84
+ }
85
+ /**
86
+ * 流式安全地读取 HTTP 请求体并解析为 JSON 对象。
87
+ * 防护机制见 readBodyWithLimit;此外处理空请求体与 UTF-8 字符集解码。
88
+ *
89
+ * @param req 传入的 Request 对象
90
+ * @param options 读取配置选项
91
+ * @returns 解析后的 JSON 对象
92
+ * @throws {RequestTooLargeError} 若请求体体积超限
93
+ * @throws {InvalidJsonError} 若请求体非合法 JSON
94
+ */
95
+ export async function readJsonBody(req, options = {}) {
96
+ const merged = await readBodyWithLimit(req, options);
97
+ if (merged.byteLength === 0) {
98
+ return {};
99
+ }
83
100
  const text = new TextDecoder("utf-8").decode(merged);
84
101
  if (!text.trim()) {
85
102
  return {};
@@ -4,8 +4,6 @@
4
4
  export type FetchDispatcherProvider = () => unknown;
5
5
  /**
6
6
  * 注册全局用于忽略服务端证书校验的调度器提供者。
7
- * 唯一注册通道:平台适配层(如 @actiondock/runtime-node 的
8
- * installInsecureDispatcher)显式调用完成注册,不再读取全局符号注入。
9
7
  */
10
8
  export declare function setInsecureDispatcherProvider(provider?: FetchDispatcherProvider): void;
11
9
  /**
@@ -13,17 +11,15 @@ export declare function setInsecureDispatcherProvider(provider?: FetchDispatcher
13
11
  */
14
12
  export declare function getInsecureDispatcherProvider(): FetchDispatcherProvider | undefined;
15
13
  /**
16
- * 获取用于忽略服务端证书校验的调度器实例。
17
- * 纯粹解耦设计:
18
- * 1. 优先调用显式注册的提供者(如由 @actiondock/runtime-node 显式安装);
19
- * 2. 若在 Node 环境下且无注册提供者,动态加载 @actiondock/runtime-node
20
- * (其入口聚合点会显式完成安装),绝不让 core 包产生静态硬编译依赖;
21
- * 3. 仍不可得时返回 undefined,由调用方回退默认 fetch,绝不在 core 内
22
- * 直接探测加载 undici,避免与平台适配层形成层次倒置与隐藏依赖环。
14
+ * 获取用于忽略服务端证书校验的全局单例 Undici Agent 调度器。
15
+ * 显式配置 bodyTimeout: 0 以防止 SSE 长连接与流式事件读取出现 UND_ERR_BODY_TIMEOUT 空闲超时断流。
23
16
  */
24
17
  export declare function getInsecureDispatcher(): unknown;
18
+ /**
19
+ * 显式安装平台调度器:向 core 注册本模块的提供者函数。
20
+ */
21
+ export declare function installInsecureDispatcher(): void;
25
22
  /**
26
23
  * 显式强制重置并销毁调度器连接池(仅用于单元测试重置或进程退出)。
27
- * core 自身不再持有任何兜底连接池,关闭语义完全委托已注册的平台提供者宿主。
28
24
  */
29
25
  export declare function closeInsecureDispatcher(): Promise<void>;
@@ -1,9 +1,8 @@
1
- import { createRequire } from "node:module";
1
+ import { Agent } from "undici";
2
2
  let insecureDispatcherProvider;
3
+ let globalInsecureAgent;
3
4
  /**
4
5
  * 注册全局用于忽略服务端证书校验的调度器提供者。
5
- * 唯一注册通道:平台适配层(如 @actiondock/runtime-node 的
6
- * installInsecureDispatcher)显式调用完成注册,不再读取全局符号注入。
7
6
  */
8
7
  export function setInsecureDispatcherProvider(provider) {
9
8
  insecureDispatcherProvider = provider;
@@ -15,48 +14,41 @@ export function getInsecureDispatcherProvider() {
15
14
  return insecureDispatcherProvider;
16
15
  }
17
16
  /**
18
- * 获取用于忽略服务端证书校验的调度器实例。
19
- * 纯粹解耦设计:
20
- * 1. 优先调用显式注册的提供者(如由 @actiondock/runtime-node 显式安装);
21
- * 2. 若在 Node 环境下且无注册提供者,动态加载 @actiondock/runtime-node
22
- * (其入口聚合点会显式完成安装),绝不让 core 包产生静态硬编译依赖;
23
- * 3. 仍不可得时返回 undefined,由调用方回退默认 fetch,绝不在 core 内
24
- * 直接探测加载 undici,避免与平台适配层形成层次倒置与隐藏依赖环。
17
+ * 获取用于忽略服务端证书校验的全局单例 Undici Agent 调度器。
18
+ * 显式配置 bodyTimeout: 0 以防止 SSE 长连接与流式事件读取出现 UND_ERR_BODY_TIMEOUT 空闲超时断流。
25
19
  */
26
20
  export function getInsecureDispatcher() {
27
21
  if (insecureDispatcherProvider) {
28
22
  return insecureDispatcherProvider();
29
23
  }
30
- if (typeof process !== "undefined" && process.versions?.node) {
31
- try {
32
- const req = createRequire(import.meta.url);
33
- const runtimeNode = req("@actiondock/runtime-node");
34
- if (typeof runtimeNode?.getInsecureDispatcher === "function") {
35
- return runtimeNode.getInsecureDispatcher();
36
- }
37
- }
38
- catch {
39
- // 忽略未安装 @actiondock/runtime-node 的情况
40
- }
24
+ if (!globalInsecureAgent || globalInsecureAgent.closed || globalInsecureAgent.destroyed) {
25
+ globalInsecureAgent = new Agent({
26
+ connect: {
27
+ rejectUnauthorized: false,
28
+ },
29
+ bodyTimeout: 0,
30
+ });
41
31
  }
42
- return undefined;
32
+ return globalInsecureAgent;
33
+ }
34
+ /**
35
+ * 显式安装平台调度器:向 core 注册本模块的提供者函数。
36
+ */
37
+ export function installInsecureDispatcher() {
38
+ setInsecureDispatcherProvider(getInsecureDispatcher);
43
39
  }
44
40
  /**
45
41
  * 显式强制重置并销毁调度器连接池(仅用于单元测试重置或进程退出)。
46
- * core 自身不再持有任何兜底连接池,关闭语义完全委托已注册的平台提供者宿主。
47
42
  */
48
43
  export async function closeInsecureDispatcher() {
49
- if (insecureDispatcherProvider) {
50
- // 提供者由平台层注册:优先委托提供者宿主的关闭语义(如可用)
44
+ if (globalInsecureAgent) {
45
+ const agent = globalInsecureAgent;
46
+ globalInsecureAgent = undefined;
51
47
  try {
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
- }
48
+ await agent.close();
57
49
  }
58
- catch {
59
- // 忽略未安装 @actiondock/runtime-node 的情况
50
+ catch (err) {
51
+ console.warn("[actiondock] closeInsecureDispatcher failed to close agent:", err);
60
52
  }
61
53
  }
62
54
  }
@@ -0,0 +1,87 @@
1
+ import { type IncomingMessage, type Server, type ServerResponse } from "node:http";
2
+ import { type Server as HttpsServer } from "node:https";
3
+ import type { ServerTlsOptions } from "./types.js";
4
+ /**
5
+ * Web 标准 Request 处理函数。
6
+ */
7
+ export type WebRequestHandler = (request: Request) => Promise<Response> | Response;
8
+ /**
9
+ * Node HTTP 服务端配置选项。
10
+ */
11
+ export interface NodeHttpServerOptions {
12
+ port?: number;
13
+ host?: string;
14
+ fetch: WebRequestHandler;
15
+ baseOrigin?: string;
16
+ tls?: ServerTlsOptions;
17
+ }
18
+ /**
19
+ * 将 Node.js IncomingMessage 请求转化为标准的 Web Request 对象。
20
+ */
21
+ export declare function createWebRequest(req: IncomingMessage, options?: {
22
+ baseOrigin?: string;
23
+ res?: ServerResponse;
24
+ } | ServerResponse): Request;
25
+ /**
26
+ * 将标准的 Web Response 响应流式写回 Node.js ServerResponse 客户端输出。
27
+ */
28
+ export declare function sendWebResponse(webResponse: Response, res: ServerResponse): Promise<void>;
29
+ /**
30
+ * 创建适用于 Node.js 原生 http.createServer 的请求监听函数。
31
+ */
32
+ export declare function createRequestListener(handler: WebRequestHandler, options?: {
33
+ baseOrigin?: string;
34
+ }): (req: IncomingMessage, res: ServerResponse) => void;
35
+ /**
36
+ * 基于 Node.js node:http 的轻量级 HTTP 服务端封装。
37
+ * 接收 Node HTTP 请求并转化为标准 Web Request,支持 Web Response 流式输出。
38
+ */
39
+ export declare class NodeHttpServer {
40
+ private server;
41
+ private listening;
42
+ private portNumber;
43
+ private hostAddress;
44
+ private tlsOptions?;
45
+ private persistentErrorHandler?;
46
+ constructor(optionsOrHandler: WebRequestHandler | NodeHttpServerOptions);
47
+ /**
48
+ * 底层原生 Node.js http.Server / https.Server 实例。
49
+ */
50
+ get rawServer(): Server | HttpsServer;
51
+ /**
52
+ * 绑定的监听端口号。
53
+ */
54
+ get port(): number;
55
+ /**
56
+ * 绑定的主机地址。
57
+ */
58
+ get host(): string;
59
+ /**
60
+ * 完整的 HTTP 访问基础路径。
61
+ */
62
+ get url(): string;
63
+ /**
64
+ * 服务端当前是否正在监听连接。
65
+ */
66
+ get isListening(): boolean;
67
+ /**
68
+ * 启动监听。若未显式传入端口,则默认使用实例配置或随机可用端口(0)。
69
+ */
70
+ listen(port?: number, host?: string): Promise<{
71
+ port: number;
72
+ host: string;
73
+ url: string;
74
+ }>;
75
+ /**
76
+ * 优雅关闭服务端并释放端口与活动连接。
77
+ */
78
+ close(): Promise<void>;
79
+ /**
80
+ * 停止服务端监听(兼容 stop 契约)。
81
+ */
82
+ stop(): Promise<void>;
83
+ /**
84
+ * 便捷工厂方法:创建并启动监听。
85
+ */
86
+ static start(optionsOrHandler: WebRequestHandler | NodeHttpServerOptions, port?: number, host?: string): Promise<NodeHttpServer>;
87
+ }