@actiondock/core 2.7.0 → 2.9.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 (243) hide show
  1. package/README.md +77 -64
  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 +106 -10
  18. package/dist/errors.js +105 -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/project/types.d.ts +5 -0
  105. package/dist/registry/index.d.ts +3 -1
  106. package/dist/registry/index.js +2 -1
  107. package/dist/registry/lock.d.ts +28 -7
  108. package/dist/registry/lock.js +136 -16
  109. package/dist/registry/registry.d.ts +1 -5
  110. package/dist/registry/registry.js +1 -53
  111. package/dist/registry/scan.d.ts +0 -15
  112. package/dist/registry/scan.js +0 -59
  113. package/dist/registry/store.d.ts +42 -0
  114. package/dist/registry/store.js +43 -0
  115. package/dist/registry/types.d.ts +0 -24
  116. package/dist/runtime/action-collection.d.ts +1 -1
  117. package/dist/runtime/action-registry.d.ts +0 -4
  118. package/dist/runtime/action-registry.js +0 -7
  119. package/dist/runtime/context.js +5 -11
  120. package/dist/runtime/events.js +3 -6
  121. package/dist/runtime/identity.d.ts +22 -0
  122. package/dist/runtime/identity.js +10 -0
  123. package/dist/runtime/index.d.ts +1 -2
  124. package/dist/runtime/index.js +1 -2
  125. package/dist/runtime/module-loader.d.ts +2 -34
  126. package/dist/runtime/module-loader.js +1 -16
  127. package/dist/runtime/process.d.ts +6 -24
  128. package/dist/runtime/process.js +1 -118
  129. package/dist/runtime/run-persistence.js +2 -5
  130. package/dist/runtime/runner.d.ts +33 -93
  131. package/dist/runtime/runner.js +132 -364
  132. package/dist/runtime/standalone.d.ts +23 -8
  133. package/dist/runtime/standalone.js +100 -147
  134. package/dist/server/body.d.ts +16 -8
  135. package/dist/server/body.js +33 -16
  136. package/dist/server/dispatcher.d.ts +6 -10
  137. package/dist/server/dispatcher.js +24 -32
  138. package/dist/server/http-server.d.ts +87 -0
  139. package/dist/server/http-server.js +304 -0
  140. package/dist/server/index.d.ts +12 -0
  141. package/dist/server/index.js +11 -0
  142. package/dist/server/mcp-endpoint.d.ts +61 -0
  143. package/dist/server/mcp-endpoint.js +118 -0
  144. package/dist/server/policy.d.ts +138 -0
  145. package/dist/server/policy.js +368 -0
  146. package/dist/server/routes/actions.js +114 -64
  147. package/dist/server/routes/common.d.ts +18 -33
  148. package/dist/server/routes/common.js +45 -65
  149. package/dist/server/routes/config.d.ts +1 -1
  150. package/dist/server/routes/config.js +55 -65
  151. package/dist/server/routes/doctor.d.ts +1 -1
  152. package/dist/server/routes/doctor.js +13 -21
  153. package/dist/server/routes/health.d.ts +1 -1
  154. package/dist/server/routes/health.js +4 -5
  155. package/dist/server/routes/info.d.ts +2 -2
  156. package/dist/server/routes/info.js +17 -24
  157. package/dist/server/routes/playbooks.js +23 -36
  158. package/dist/server/routes/runs.js +151 -141
  159. package/dist/server/routes/state.d.ts +1 -1
  160. package/dist/server/routes/state.js +64 -63
  161. package/dist/server/security.d.ts +11 -0
  162. package/dist/server/security.js +36 -19
  163. package/dist/server/server.d.ts +2 -2
  164. package/dist/server/server.js +224 -238
  165. package/dist/server/types.d.ts +68 -21
  166. package/dist/service/factory.d.ts +12 -0
  167. package/dist/service/factory.js +85 -0
  168. package/dist/{target → service}/index.d.ts +1 -1
  169. package/dist/{target → service}/index.js +1 -1
  170. package/dist/service/local.d.ts +27 -0
  171. package/dist/service/local.js +138 -0
  172. package/dist/{target → service}/remote-errors.d.ts +3 -6
  173. package/dist/service/remote-errors.js +38 -0
  174. package/dist/{target → service}/remote-polling.d.ts +2 -2
  175. package/dist/{target → service}/remote-polling.js +8 -8
  176. package/dist/service/remote.d.ts +34 -0
  177. package/dist/service/remote.js +634 -0
  178. package/dist/service/sse-stream.d.ts +24 -0
  179. package/dist/service/sse-stream.js +139 -0
  180. package/dist/service/types.d.ts +272 -0
  181. package/dist/service/types.js +32 -0
  182. package/dist/storage/data-dir-lock.js +10 -5
  183. package/dist/storage/driver.d.ts +2 -1
  184. package/dist/storage/driver.js +3 -52
  185. package/dist/storage/index.d.ts +1 -0
  186. package/dist/storage/index.js +1 -0
  187. package/dist/storage/lock-core.d.ts +2 -6
  188. package/dist/storage/lock-core.js +4 -17
  189. package/dist/storage/params.d.ts +2 -8
  190. package/dist/storage/params.js +2 -8
  191. package/dist/storage/sqlite-driver.d.ts +40 -0
  192. package/dist/storage/sqlite-driver.js +116 -0
  193. package/dist/storage/sqlite.js +21 -6
  194. package/dist/storage/types.d.ts +4 -4
  195. package/dist/storage/types.js +2 -2
  196. package/dist/utils/index.d.ts +53 -0
  197. package/dist/utils/index.js +99 -4
  198. package/dist/utils/process.d.ts +10 -0
  199. package/dist/utils/process.js +21 -0
  200. package/dist/version.d.ts +1 -1
  201. package/dist/version.js +1 -1
  202. package/package.json +41 -3
  203. package/dist/app/index.d.ts +0 -2
  204. package/dist/app/index.js +0 -2
  205. package/dist/app/types.js +0 -1
  206. package/dist/catalog/action-index.d.ts +0 -9
  207. package/dist/catalog/action-index.js +0 -95
  208. package/dist/catalog/action-resolver.d.ts +0 -20
  209. package/dist/catalog/action-resolver.js +0 -95
  210. package/dist/catalog/location-registry.d.ts +0 -15
  211. package/dist/catalog/location-registry.js +0 -138
  212. package/dist/catalog/package-catalog.d.ts +0 -7
  213. package/dist/catalog/package-catalog.js +0 -77
  214. package/dist/catalog/types.d.ts +0 -68
  215. package/dist/catalog/types.js +0 -1
  216. package/dist/export/index.d.ts +0 -1
  217. package/dist/export/index.js +0 -1
  218. package/dist/export/templates.d.ts +0 -71
  219. package/dist/export/templates.js +0 -635
  220. package/dist/ipc/target.d.ts +0 -74
  221. package/dist/ipc/target.js +0 -365
  222. package/dist/platform/default.d.ts +0 -38
  223. package/dist/platform/default.js +0 -78
  224. package/dist/project/resolver.d.ts +0 -116
  225. package/dist/project/resolver.js +0 -376
  226. package/dist/registry/resolve.d.ts +0 -62
  227. package/dist/registry/resolve.js +0 -195
  228. package/dist/runtime/execution-manager.d.ts +0 -48
  229. package/dist/runtime/execution-manager.js +0 -66
  230. package/dist/runtime/package-runner-factory.d.ts +0 -82
  231. package/dist/runtime/package-runner-factory.js +0 -186
  232. package/dist/target/local.d.ts +0 -53
  233. package/dist/target/local.js +0 -349
  234. package/dist/target/remote-errors.js +0 -51
  235. package/dist/target/remote.d.ts +0 -86
  236. package/dist/target/remote.js +0 -818
  237. package/dist/target/target.d.ts +0 -6
  238. package/dist/target/target.js +0 -37
  239. package/dist/target/types.d.ts +0 -261
  240. package/dist/target/types.js +0 -35
  241. /package/dist/{app → package}/static-index.d.ts +0 -0
  242. /package/dist/{target → service}/sse-parser.d.ts +0 -0
  243. /package/dist/{target → service}/sse-parser.js +0 -0
@@ -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
+ }
@@ -0,0 +1,304 @@
1
+ import { createServer } from "node:http";
2
+ import { createServer as createHttpsServer } from "node:https";
3
+ import { readFileSync } from "node:fs";
4
+ import { Readable } from "node:stream";
5
+ import { pipeline } from "node:stream/promises";
6
+ import { BAD_REQUEST, SERVER_ERROR } from "../errors.js";
7
+ import { formatHostForUrl } from "./server.js";
8
+ /**
9
+ * 将 Node.js IncomingMessage 请求转化为标准的 Web Request 对象。
10
+ */
11
+ export function createWebRequest(req, options) {
12
+ const protocol = req.socket?.encrypted ? "https" : "http";
13
+ const host = req.headers.host || "127.0.0.1";
14
+ let baseOrigin;
15
+ let res;
16
+ if (options) {
17
+ if ("writableFinished" in options || typeof options.writeHead === "function") {
18
+ res = options;
19
+ }
20
+ else {
21
+ baseOrigin = options.baseOrigin;
22
+ res = options.res;
23
+ }
24
+ }
25
+ if (!res && req.res) {
26
+ res = req.res;
27
+ }
28
+ baseOrigin = baseOrigin || `${protocol}://${host}`;
29
+ const url = new URL(req.url || "/", baseOrigin).href;
30
+ const headers = new Headers();
31
+ for (const [key, val] of Object.entries(req.headers)) {
32
+ if (val === undefined)
33
+ continue;
34
+ if (Array.isArray(val)) {
35
+ for (const item of val) {
36
+ headers.append(key, item);
37
+ }
38
+ }
39
+ else {
40
+ headers.set(key, val);
41
+ }
42
+ }
43
+ const method = (req.method || "GET").toUpperCase();
44
+ const hasBody = method !== "GET" && method !== "HEAD";
45
+ const ac = new AbortController();
46
+ req.on("close", () => {
47
+ if (!req.complete) {
48
+ ac.abort(new Error("Request aborted by client"));
49
+ }
50
+ });
51
+ if (res) {
52
+ res.on("close", () => {
53
+ if (!res.writableFinished) {
54
+ ac.abort(new Error("Request aborted by client"));
55
+ }
56
+ });
57
+ }
58
+ const init = {
59
+ method,
60
+ headers,
61
+ signal: ac.signal,
62
+ };
63
+ if (hasBody) {
64
+ const webStream = Readable.toWeb(req);
65
+ init.body = webStream;
66
+ init.duplex = "half";
67
+ }
68
+ return new Request(url, init);
69
+ }
70
+ /**
71
+ * 将标准的 Web Response 响应流式写回 Node.js ServerResponse 客户端输出。
72
+ */
73
+ export async function sendWebResponse(webResponse, res) {
74
+ res.statusCode = webResponse.status;
75
+ if (webResponse.statusText) {
76
+ res.statusMessage = webResponse.statusText;
77
+ }
78
+ webResponse.headers.forEach((val, key) => {
79
+ if (key.toLowerCase() === "set-cookie") {
80
+ if (typeof webResponse.headers.getSetCookie === "function") {
81
+ res.setHeader("set-cookie", webResponse.headers.getSetCookie());
82
+ }
83
+ else {
84
+ res.appendHeader("set-cookie", val);
85
+ }
86
+ }
87
+ else {
88
+ res.setHeader(key, val);
89
+ }
90
+ });
91
+ if (!webResponse.body) {
92
+ res.end();
93
+ return;
94
+ }
95
+ const nodeStream = Readable.fromWeb(webResponse.body);
96
+ try {
97
+ await pipeline(nodeStream, res);
98
+ }
99
+ catch (err) {
100
+ if (err?.code !== "ERR_STREAM_PREMATURE_CLOSE") {
101
+ throw err;
102
+ }
103
+ }
104
+ }
105
+ /**
106
+ * 创建适用于 Node.js 原生 http.createServer 的请求监听函数。
107
+ */
108
+ export function createRequestListener(handler, options) {
109
+ return (req, res) => {
110
+ let webReq;
111
+ try {
112
+ webReq = createWebRequest(req, { ...options, res });
113
+ }
114
+ catch (err) {
115
+ if (!res.headersSent) {
116
+ res.statusCode = 400;
117
+ res.setHeader("Content-Type", "application/json");
118
+ res.end(JSON.stringify({
119
+ ok: false,
120
+ error: {
121
+ code: BAD_REQUEST,
122
+ message: err?.message || String(err),
123
+ },
124
+ }));
125
+ }
126
+ else {
127
+ res.destroy(err);
128
+ }
129
+ return;
130
+ }
131
+ Promise.resolve()
132
+ .then(() => handler(webReq))
133
+ .then((webRes) => sendWebResponse(webRes, res))
134
+ .catch((err) => {
135
+ if (!res.headersSent) {
136
+ res.statusCode = 500;
137
+ res.setHeader("Content-Type", "application/json");
138
+ res.end(JSON.stringify({
139
+ ok: false,
140
+ error: {
141
+ code: SERVER_ERROR,
142
+ message: err?.message || String(err),
143
+ },
144
+ }));
145
+ }
146
+ else {
147
+ res.destroy(err);
148
+ }
149
+ });
150
+ };
151
+ }
152
+ /**
153
+ * 基于 Node.js node:http 的轻量级 HTTP 服务端封装。
154
+ * 接收 Node HTTP 请求并转化为标准 Web Request,支持 Web Response 流式输出。
155
+ */
156
+ export class NodeHttpServer {
157
+ server;
158
+ listening = false;
159
+ portNumber = 0;
160
+ hostAddress = "127.0.0.1";
161
+ tlsOptions;
162
+ persistentErrorHandler;
163
+ constructor(optionsOrHandler) {
164
+ const handler = typeof optionsOrHandler === "function"
165
+ ? optionsOrHandler
166
+ : optionsOrHandler.fetch;
167
+ const baseOrigin = typeof optionsOrHandler === "object"
168
+ ? optionsOrHandler.baseOrigin
169
+ : undefined;
170
+ const tls = typeof optionsOrHandler === "object"
171
+ ? optionsOrHandler.tls
172
+ : undefined;
173
+ this.tlsOptions = tls;
174
+ const requestListener = createRequestListener(handler, { baseOrigin });
175
+ if (tls) {
176
+ let cert = tls.cert;
177
+ if (!cert && tls.certPath) {
178
+ cert = readFileSync(tls.certPath);
179
+ }
180
+ let key = tls.key;
181
+ if (!key && tls.keyPath) {
182
+ key = readFileSync(tls.keyPath);
183
+ }
184
+ const httpsOptions = { cert, key };
185
+ if (tls.ca)
186
+ httpsOptions.ca = tls.ca;
187
+ if (tls.passphrase)
188
+ httpsOptions.passphrase = tls.passphrase;
189
+ this.server = createHttpsServer(httpsOptions, requestListener);
190
+ }
191
+ else {
192
+ this.server = createServer(requestListener);
193
+ }
194
+ if (typeof optionsOrHandler === "object") {
195
+ if (optionsOrHandler.port !== undefined) {
196
+ this.portNumber = optionsOrHandler.port;
197
+ }
198
+ if (optionsOrHandler.host !== undefined) {
199
+ this.hostAddress = optionsOrHandler.host;
200
+ }
201
+ }
202
+ }
203
+ /**
204
+ * 底层原生 Node.js http.Server / https.Server 实例。
205
+ */
206
+ get rawServer() {
207
+ return this.server;
208
+ }
209
+ /**
210
+ * 绑定的监听端口号。
211
+ */
212
+ get port() {
213
+ return this.portNumber;
214
+ }
215
+ /**
216
+ * 绑定的主机地址。
217
+ */
218
+ get host() {
219
+ return this.hostAddress;
220
+ }
221
+ /**
222
+ * 完整的 HTTP 访问基础路径。
223
+ */
224
+ get url() {
225
+ const protocol = this.tlsOptions ? "https" : "http";
226
+ return `${protocol}://${formatHostForUrl(this.hostAddress)}:${this.portNumber}`;
227
+ }
228
+ /**
229
+ * 服务端当前是否正在监听连接。
230
+ */
231
+ get isListening() {
232
+ return this.listening;
233
+ }
234
+ /**
235
+ * 启动监听。若未显式传入端口,则默认使用实例配置或随机可用端口(0)。
236
+ */
237
+ async listen(port, host) {
238
+ const targetPort = port ?? this.portNumber ?? 0;
239
+ const targetHost = host ?? this.hostAddress ?? "127.0.0.1";
240
+ if (!this.persistentErrorHandler) {
241
+ this.persistentErrorHandler = (err) => {
242
+ console.error("[NodeHttpServer] unexpected server error:", err);
243
+ };
244
+ this.server.on("error", this.persistentErrorHandler);
245
+ }
246
+ return new Promise((resolve, reject) => {
247
+ const onListenError = (err) => {
248
+ this.server.removeListener("error", onListenError);
249
+ reject(err);
250
+ };
251
+ this.server.once("error", onListenError);
252
+ this.server.listen(targetPort, targetHost, () => {
253
+ this.server.removeListener("error", onListenError);
254
+ const addr = this.server.address();
255
+ if (addr && typeof addr === "object") {
256
+ this.portNumber = addr.port;
257
+ this.hostAddress = addr.address === "::" ? "127.0.0.1" : addr.address;
258
+ }
259
+ this.listening = true;
260
+ resolve({
261
+ port: this.portNumber,
262
+ host: this.hostAddress,
263
+ url: this.url,
264
+ });
265
+ });
266
+ });
267
+ }
268
+ /**
269
+ * 优雅关闭服务端并释放端口与活动连接。
270
+ */
271
+ async close() {
272
+ if (!this.listening) {
273
+ return;
274
+ }
275
+ if (this.persistentErrorHandler) {
276
+ this.server.removeListener("error", this.persistentErrorHandler);
277
+ this.persistentErrorHandler = undefined;
278
+ }
279
+ return new Promise((resolve, reject) => {
280
+ this.server.close((err) => {
281
+ this.listening = false;
282
+ if (err)
283
+ reject(err);
284
+ else
285
+ resolve();
286
+ });
287
+ this.server.closeAllConnections?.();
288
+ });
289
+ }
290
+ /**
291
+ * 停止服务端监听(兼容 stop 契约)。
292
+ */
293
+ async stop() {
294
+ return this.close();
295
+ }
296
+ /**
297
+ * 便捷工厂方法:创建并启动监听。
298
+ */
299
+ static async start(optionsOrHandler, port, host) {
300
+ const server = new NodeHttpServer(optionsOrHandler);
301
+ await server.listen(port, host);
302
+ return server;
303
+ }
304
+ }
@@ -1,6 +1,18 @@
1
1
  export * from "./types.js";
2
2
  export * from "./security.js";
3
+ export * from "./policy.js";
3
4
  export * from "./body.js";
4
5
  export * from "./routes/index.js";
5
6
  export * from "./server.js";
7
+ export * from "./mcp-endpoint.js";
6
8
  export * from "./dispatcher.js";
9
+ export * from "./http-server.js";
10
+ export { serveParentIpc } from "../ipc/host.js";
11
+ export { IpcActionDockService } from "../ipc/service.js";
12
+ export type { IpcServiceOptions } from "../ipc/types.js";
13
+ export { parseDuration } from "../utils/index.js";
14
+ export type { ActionDockHost, ActionDockHostOptions, } from "../host/types.js";
15
+ export { createActionDockHost, DefaultActionDockHost, } from "../host/host.js";
16
+ export { LocalActionDockService, } from "../service/local.js";
17
+ export { RemoteActionDockService, } from "../service/remote.js";
18
+ export { ExitCode, StandaloneDispatcher, type StandaloneDispatcherOptions, type InvocationControl, runStandaloneProcess, } from "../runtime/standalone.js";
@@ -1,6 +1,17 @@
1
1
  export * from "./types.js";
2
2
  export * from "./security.js";
3
+ export * from "./policy.js";
3
4
  export * from "./body.js";
4
5
  export * from "./routes/index.js";
5
6
  export * from "./server.js";
7
+ export * from "./mcp-endpoint.js";
6
8
  export * from "./dispatcher.js";
9
+ export * from "./http-server.js";
10
+ export { serveParentIpc } from "../ipc/host.js";
11
+ export { IpcActionDockService } from "../ipc/service.js";
12
+ export { parseDuration } from "../utils/index.js";
13
+ export { createActionDockHost, DefaultActionDockHost, } from "../host/host.js";
14
+ export { LocalActionDockService, } from "../service/local.js";
15
+ export { RemoteActionDockService, } from "../service/remote.js";
16
+ // 独立进程分发器与退出控制
17
+ export { ExitCode, StandaloneDispatcher, runStandaloneProcess, } from "../runtime/standalone.js";