@actiondock/core 2.0.11 → 2.2.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 (258) hide show
  1. package/README.md +61 -30
  2. package/dist/app/app.d.ts +73 -0
  3. package/dist/app/app.js +700 -0
  4. package/dist/app/index.d.ts +2 -0
  5. package/dist/app/index.js +2 -0
  6. package/dist/app/types.d.ts +283 -0
  7. package/dist/app/types.js +1 -0
  8. package/dist/catalog/action-index.d.ts +9 -0
  9. package/dist/catalog/action-index.js +93 -0
  10. package/dist/catalog/action-resolver.d.ts +20 -0
  11. package/dist/catalog/action-resolver.js +95 -0
  12. package/dist/catalog/index.d.ts +5 -0
  13. package/dist/catalog/index.js +5 -0
  14. package/dist/catalog/location-registry.d.ts +15 -0
  15. package/dist/catalog/location-registry.js +138 -0
  16. package/dist/catalog/package-catalog.d.ts +7 -0
  17. package/dist/catalog/package-catalog.js +76 -0
  18. package/dist/catalog/types.d.ts +68 -0
  19. package/dist/catalog/types.js +1 -0
  20. package/dist/doctor/doctor.d.ts +7 -0
  21. package/dist/doctor/doctor.js +613 -0
  22. package/dist/doctor/index.d.ts +2 -0
  23. package/dist/doctor/index.js +2 -0
  24. package/dist/doctor/types.d.ts +23 -0
  25. package/dist/doctor/types.js +1 -0
  26. package/dist/errors.d.ts +122 -0
  27. package/dist/errors.js +123 -0
  28. package/dist/execution/index.d.ts +2 -0
  29. package/dist/execution/index.js +2 -0
  30. package/dist/execution/service.d.ts +79 -0
  31. package/dist/execution/service.js +633 -0
  32. package/dist/execution/types.d.ts +131 -0
  33. package/dist/execution/types.js +1 -0
  34. package/dist/export/index.d.ts +1 -0
  35. package/dist/export/index.js +1 -0
  36. package/dist/export/templates.d.ts +71 -0
  37. package/dist/export/templates.js +621 -0
  38. package/dist/filter/index.d.ts +1 -0
  39. package/dist/filter/index.js +1 -0
  40. package/dist/filter/intent.d.ts +33 -0
  41. package/dist/filter/intent.js +123 -0
  42. package/dist/host/host.d.ts +55 -0
  43. package/dist/host/host.js +740 -0
  44. package/dist/host/index.d.ts +2 -0
  45. package/dist/host/index.js +2 -0
  46. package/dist/host/types.d.ts +81 -0
  47. package/dist/host/types.js +1 -0
  48. package/dist/index.d.ts +21 -0
  49. package/dist/index.js +20 -0
  50. package/dist/ipc/diagnostic.d.ts +65 -0
  51. package/dist/ipc/diagnostic.js +129 -0
  52. package/dist/ipc/host.d.ts +7 -0
  53. package/dist/ipc/host.js +93 -0
  54. package/dist/ipc/index.d.ts +4 -0
  55. package/dist/ipc/index.js +4 -0
  56. package/dist/ipc/target.d.ts +56 -0
  57. package/dist/ipc/target.js +297 -0
  58. package/dist/ipc/types.d.ts +42 -0
  59. package/dist/ipc/types.js +1 -0
  60. package/dist/platform/default.d.ts +32 -0
  61. package/dist/platform/default.js +40 -0
  62. package/dist/platform/index.d.ts +3 -0
  63. package/dist/platform/index.js +3 -0
  64. package/dist/platform/node-fs.d.ts +37 -0
  65. package/dist/platform/node-fs.js +81 -0
  66. package/dist/platform/types.d.ts +104 -0
  67. package/dist/platform/types.js +1 -0
  68. package/dist/profile/client.d.ts +162 -0
  69. package/dist/profile/client.js +467 -0
  70. package/dist/profile/index.d.ts +3 -0
  71. package/dist/profile/index.js +3 -0
  72. package/dist/profile/manager.d.ts +47 -0
  73. package/dist/profile/manager.js +294 -0
  74. package/dist/profile/types.d.ts +61 -0
  75. package/dist/profile/types.js +1 -0
  76. package/dist/project/closure.d.ts +25 -0
  77. package/dist/project/closure.js +68 -0
  78. package/dist/project/digest.d.ts +29 -0
  79. package/dist/project/digest.js +324 -0
  80. package/dist/project/index.d.ts +10 -0
  81. package/dist/project/index.js +10 -0
  82. package/dist/project/init.d.ts +26 -0
  83. package/dist/project/init.js +175 -0
  84. package/dist/project/loader.d.ts +99 -0
  85. package/dist/project/loader.js +484 -0
  86. package/dist/project/lockfile.d.ts +55 -0
  87. package/dist/project/lockfile.js +175 -0
  88. package/dist/project/manifest.d.ts +22 -0
  89. package/dist/project/manifest.js +220 -0
  90. package/dist/project/resolver.d.ts +116 -0
  91. package/dist/project/resolver.js +376 -0
  92. package/dist/project/transactions.d.ts +103 -0
  93. package/dist/project/transactions.js +1203 -0
  94. package/dist/project/types-generator.d.ts +33 -0
  95. package/dist/project/types-generator.js +168 -0
  96. package/dist/project/types.d.ts +107 -0
  97. package/dist/project/types.js +1 -0
  98. package/dist/registry/index.d.ts +5 -0
  99. package/dist/registry/index.js +5 -0
  100. package/dist/registry/lock.d.ts +27 -0
  101. package/dist/registry/lock.js +93 -0
  102. package/dist/registry/registry.d.ts +45 -0
  103. package/dist/registry/registry.js +611 -0
  104. package/dist/registry/resolve.d.ts +62 -0
  105. package/dist/registry/resolve.js +195 -0
  106. package/dist/registry/scan.d.ts +38 -0
  107. package/dist/registry/scan.js +135 -0
  108. package/dist/registry/types.d.ts +117 -0
  109. package/dist/registry/types.js +1 -0
  110. package/dist/runtime/action-collection.d.ts +27 -0
  111. package/dist/runtime/action-collection.js +57 -0
  112. package/dist/runtime/clock.d.ts +19 -0
  113. package/dist/runtime/clock.js +17 -0
  114. package/dist/runtime/context.d.ts +86 -0
  115. package/dist/runtime/context.js +242 -0
  116. package/dist/runtime/env.d.ts +56 -0
  117. package/dist/runtime/env.js +159 -0
  118. package/dist/runtime/events.d.ts +93 -0
  119. package/dist/runtime/events.js +604 -0
  120. package/dist/runtime/execution-manager.d.ts +48 -0
  121. package/dist/runtime/execution-manager.js +66 -0
  122. package/dist/runtime/index.d.ts +10 -0
  123. package/dist/runtime/index.js +10 -0
  124. package/dist/runtime/module-loader.d.ts +34 -0
  125. package/dist/runtime/module-loader.js +16 -0
  126. package/dist/runtime/process.d.ts +9 -0
  127. package/dist/runtime/process.js +146 -0
  128. package/dist/runtime/runner.d.ts +293 -0
  129. package/dist/runtime/runner.js +984 -0
  130. package/dist/runtime/standalone.d.ts +89 -0
  131. package/dist/runtime/standalone.js +536 -0
  132. package/dist/schema/validator.d.ts +28 -0
  133. package/dist/schema/validator.js +116 -0
  134. package/dist/server/body.d.ts +40 -0
  135. package/dist/server/body.js +93 -0
  136. package/dist/server/index.d.ts +5 -0
  137. package/dist/server/index.js +5 -0
  138. package/dist/server/routes/actions.d.ts +5 -0
  139. package/dist/server/routes/actions.js +298 -0
  140. package/dist/server/routes/common.d.ts +47 -0
  141. package/dist/server/routes/common.js +104 -0
  142. package/dist/server/routes/config.d.ts +6 -0
  143. package/dist/server/routes/config.js +173 -0
  144. package/dist/server/routes/doctor.d.ts +5 -0
  145. package/dist/server/routes/doctor.js +63 -0
  146. package/dist/server/routes/health.d.ts +5 -0
  147. package/dist/server/routes/health.js +35 -0
  148. package/dist/server/routes/index.d.ts +9 -0
  149. package/dist/server/routes/index.js +9 -0
  150. package/dist/server/routes/info.d.ts +7 -0
  151. package/dist/server/routes/info.js +158 -0
  152. package/dist/server/routes/playbooks.d.ts +5 -0
  153. package/dist/server/routes/playbooks.js +134 -0
  154. package/dist/server/routes/runs.d.ts +5 -0
  155. package/dist/server/routes/runs.js +343 -0
  156. package/dist/server/routes/state.d.ts +6 -0
  157. package/dist/server/routes/state.js +169 -0
  158. package/dist/server/security.d.ts +44 -0
  159. package/dist/server/security.js +101 -0
  160. package/dist/server/server.d.ts +15 -0
  161. package/dist/server/server.js +355 -0
  162. package/dist/server/types.d.ts +78 -0
  163. package/dist/server/types.js +1 -0
  164. package/dist/storage/data-dir-lock.d.ts +119 -0
  165. package/dist/storage/data-dir-lock.js +1016 -0
  166. package/dist/storage/driver.d.ts +9 -0
  167. package/dist/storage/driver.js +59 -0
  168. package/dist/storage/index.d.ts +64 -0
  169. package/dist/storage/index.js +98 -0
  170. package/dist/storage/lazy.d.ts +8 -0
  171. package/dist/storage/lazy.js +44 -0
  172. package/dist/storage/mask.d.ts +26 -0
  173. package/dist/storage/mask.js +48 -0
  174. package/dist/storage/params.d.ts +13 -0
  175. package/dist/storage/params.js +32 -0
  176. package/dist/storage/sqlite.d.ts +79 -0
  177. package/dist/storage/sqlite.js +689 -0
  178. package/dist/storage/types.d.ts +172 -0
  179. package/dist/storage/types.js +14 -0
  180. package/dist/target/index.d.ts +4 -0
  181. package/dist/target/index.js +4 -0
  182. package/dist/target/local.d.ts +52 -0
  183. package/dist/target/local.js +343 -0
  184. package/dist/target/remote.d.ts +76 -0
  185. package/dist/target/remote.js +839 -0
  186. package/dist/target/target.d.ts +6 -0
  187. package/dist/target/target.js +35 -0
  188. package/dist/target/types.d.ts +246 -0
  189. package/dist/target/types.js +34 -0
  190. package/dist/utils/index.d.ts +49 -0
  191. package/dist/utils/index.js +180 -0
  192. package/dist/version.d.ts +4 -0
  193. package/dist/version.js +4 -0
  194. package/package.json +13 -13
  195. package/src/build/index.ts +0 -1
  196. package/src/build/templates.ts +0 -59
  197. package/src/catalog/action-index.ts +0 -92
  198. package/src/catalog/action-resolver.ts +0 -120
  199. package/src/catalog/index.ts +0 -5
  200. package/src/catalog/location-registry.ts +0 -134
  201. package/src/catalog/package-catalog.ts +0 -87
  202. package/src/catalog/types.ts +0 -75
  203. package/src/doctor/doctor.ts +0 -419
  204. package/src/doctor/index.ts +0 -2
  205. package/src/doctor/types.ts +0 -25
  206. package/src/execution/index.ts +0 -2
  207. package/src/execution/service.ts +0 -322
  208. package/src/execution/types.ts +0 -73
  209. package/src/export/index.ts +0 -1
  210. package/src/export/templates.ts +0 -590
  211. package/src/filter/index.ts +0 -1
  212. package/src/filter/intent.ts +0 -154
  213. package/src/index.ts +0 -16
  214. package/src/profile/client.ts +0 -552
  215. package/src/profile/index.ts +0 -3
  216. package/src/profile/manager.ts +0 -341
  217. package/src/profile/types.ts +0 -71
  218. package/src/project/index.ts +0 -4
  219. package/src/project/init.ts +0 -242
  220. package/src/project/loader.ts +0 -479
  221. package/src/project/manifest.ts +0 -299
  222. package/src/project/types.ts +0 -141
  223. package/src/registry/index.ts +0 -2
  224. package/src/registry/registry.ts +0 -940
  225. package/src/registry/types.ts +0 -127
  226. package/src/runtime/clock.ts +0 -41
  227. package/src/runtime/context.ts +0 -259
  228. package/src/runtime/env.ts +0 -172
  229. package/src/runtime/events.ts +0 -142
  230. package/src/runtime/execution-manager.ts +0 -74
  231. package/src/runtime/index.ts +0 -10
  232. package/src/runtime/module-loader.ts +0 -67
  233. package/src/runtime/process.ts +0 -245
  234. package/src/runtime/runner.ts +0 -527
  235. package/src/runtime/standalone.ts +0 -429
  236. package/src/schema/validator.ts +0 -61
  237. package/src/server/body.ts +0 -112
  238. package/src/server/index.ts +0 -7
  239. package/src/server/routes/actions.ts +0 -262
  240. package/src/server/routes/common.ts +0 -126
  241. package/src/server/routes/config.ts +0 -147
  242. package/src/server/routes/doctor.ts +0 -29
  243. package/src/server/routes/health.ts +0 -40
  244. package/src/server/routes/index.ts +0 -9
  245. package/src/server/routes/info.ts +0 -221
  246. package/src/server/routes/playbooks.ts +0 -109
  247. package/src/server/routes/runs.ts +0 -301
  248. package/src/server/routes/state.ts +0 -133
  249. package/src/server/runtime-registry.ts +0 -115
  250. package/src/server/security.ts +0 -115
  251. package/src/server/server.ts +0 -251
  252. package/src/server/types.ts +0 -57
  253. package/src/storage/driver.ts +0 -126
  254. package/src/storage/index.ts +0 -81
  255. package/src/storage/mask.ts +0 -34
  256. package/src/storage/sqlite.ts +0 -714
  257. package/src/storage/types.ts +0 -139
  258. package/src/utils/index.ts +0 -62
@@ -1,133 +0,0 @@
1
- import { readJsonBody } from "../body";
2
- import { type RouteContext, jsonResponse, resolveStorageForPackage } from "./common";
3
-
4
- /**
5
- * 处理状态键名列表、读取、写入、删除及清空接口。
6
- */
7
- export async function handleStateRoutes(ctx: RouteContext): Promise<Response | null> {
8
- const { req, url, pathname, corsHeaders, projectRoot, customHome, runtimeRegistry, options } = ctx;
9
-
10
- // 1. State List: GET /api/v1/state
11
- if (pathname === "/api/v1/state" && req.method === "GET") {
12
- try {
13
- const pkgParam = url.searchParams.get("package") || undefined;
14
- const nsParam = url.searchParams.get("namespace") ?? undefined;
15
- const prefix = url.searchParams.get("prefix") || "";
16
-
17
- const { packageId, storage } = resolveStorageForPackage(
18
- pkgParam,
19
- runtimeRegistry,
20
- projectRoot,
21
- customHome
22
- );
23
- const keys = await storage.listStateKeys(nsParam !== undefined ? nsParam : null, prefix);
24
- return jsonResponse({ ok: true, packageId, keys }, 200, corsHeaders);
25
- } catch (err: any) {
26
- return jsonResponse(
27
- { ok: false, error: { code: "STATE_LIST_ERROR", message: err.message } },
28
- 500,
29
- corsHeaders
30
- );
31
- }
32
- }
33
-
34
- // 2. State Clear: POST /api/v1/state/clear
35
- if (pathname === "/api/v1/state/clear" && req.method === "POST") {
36
- try {
37
- const body = await readJsonBody(req, { maxBytes: options.maxBodyBytes }).catch(() => ({}));
38
- const pkgParam = url.searchParams.get("package") || body.package || undefined;
39
- const { packageId, storage } = resolveStorageForPackage(
40
- pkgParam,
41
- runtimeRegistry,
42
- projectRoot,
43
- customHome
44
- );
45
- const clearedCount = await storage.clearState({
46
- namespace: body.namespace ?? (url.searchParams.get("namespace") || undefined),
47
- all: Boolean(body.all ?? url.searchParams.get("all") === "true"),
48
- prefix: body.prefix ?? (url.searchParams.get("prefix") || undefined),
49
- });
50
- return jsonResponse({ ok: true, packageId, clearedCount }, 200, corsHeaders);
51
- } catch (err: any) {
52
- return jsonResponse(
53
- { ok: false, error: { code: "STATE_CLEAR_ERROR", message: err.message } },
54
- 500,
55
- corsHeaders
56
- );
57
- }
58
- }
59
-
60
- // 3. State Key CRUD: GET / PUT / POST / DELETE /api/v1/state/:key
61
- const stateKeyMatch = pathname.match(/^\/api\/v1\/state\/([^/]+)$/);
62
- if (stateKeyMatch) {
63
- const key = decodeURIComponent(stateKeyMatch[1]);
64
- const pkgParam = url.searchParams.get("package") || undefined;
65
- const nsParam = url.searchParams.get("namespace") || undefined;
66
-
67
- const { packageId, storage } = resolveStorageForPackage(
68
- pkgParam,
69
- runtimeRegistry,
70
- projectRoot,
71
- customHome
72
- );
73
-
74
- if (req.method === "GET") {
75
- const entry = await storage.findState(key, nsParam);
76
- if (!entry || entry.value === undefined) {
77
- return jsonResponse(
78
- { ok: false, error: { code: "STATE_KEY_NOT_FOUND", message: `State key '${key}' not found` } },
79
- 404,
80
- corsHeaders
81
- );
82
- }
83
- return jsonResponse(
84
- {
85
- ok: true,
86
- packageId,
87
- key: entry.key,
88
- namespace: entry.namespace,
89
- value: entry.value,
90
- expiresAt: entry.expiresAt,
91
- },
92
- 200,
93
- corsHeaders
94
- );
95
- }
96
-
97
- if (req.method === "PUT" || req.method === "POST") {
98
- const body = await readJsonBody(req, { maxBytes: options.maxBodyBytes });
99
- const val = body.value !== undefined ? body.value : body;
100
- const ttl = typeof body.ttl === "number" ? body.ttl : undefined;
101
- const namespace = body.namespace || nsParam || "";
102
-
103
- let actualKey = key;
104
- let ns = namespace;
105
- if (!ns && key.includes(":")) {
106
- const idx = key.indexOf(":");
107
- ns = key.slice(0, idx);
108
- actualKey = key.slice(idx + 1);
109
- }
110
-
111
- await storage.setState(ns, actualKey, val, ttl);
112
- return jsonResponse(
113
- { ok: true, packageId, key: actualKey, namespace: ns, message: "updated" },
114
- 200,
115
- corsHeaders
116
- );
117
- }
118
-
119
- if (req.method === "DELETE") {
120
- const deleted = await storage.deleteStateSmart(key, nsParam);
121
- if (!deleted) {
122
- return jsonResponse(
123
- { ok: false, error: { code: "STATE_KEY_NOT_FOUND", message: `State key '${key}' not found` } },
124
- 404,
125
- corsHeaders
126
- );
127
- }
128
- return jsonResponse({ ok: true, packageId, key, deleted: true }, 200, corsHeaders);
129
- }
130
- }
131
-
132
- return null;
133
- }
@@ -1,115 +0,0 @@
1
- import { ExecutionManager } from "../runtime/execution-manager";
2
- import { createStorage } from "../storage";
3
- import type { RuntimeStorage } from "../storage/types";
4
-
5
- /**
6
- * 服务端运行时注册表(ServerRuntimeRegistry)。
7
- *
8
- * 职责:
9
- * 1. 在长期运行的 HTTP 服务端(`ad serve` / `ad mcp serve`)中,跨请求缓存并池化管理 SQLite 数据库存储连接。
10
- * 2. 集中维护活跃的在途任务执行句柄(ExecutionManager)。
11
- * 3. 服务端停止或优雅关机(Graceful Shutdown)时,统一中断在途任务并安全关闭所有数据库连接。
12
- */
13
- export class ServerRuntimeRegistry {
14
- private storages = new Map<string, RuntimeStorage>();
15
- private listeners = new Map<string, Set<(event: { type: string; data: any }) => void>>();
16
- public executionManager: ExecutionManager;
17
-
18
- constructor() {
19
- this.executionManager = new ExecutionManager();
20
- }
21
-
22
- /**
23
- * 订阅指定 runId 的事件(用于 SSE 流式推送)。
24
- */
25
- public subscribe(runId: string, listener: (event: { type: string; data: any }) => void): () => void {
26
- let set = this.listeners.get(runId);
27
- if (!set) {
28
- set = new Set();
29
- this.listeners.set(runId, set);
30
- }
31
- set.add(listener);
32
- return () => {
33
- set?.delete(listener);
34
- if (set && set.size === 0) {
35
- this.listeners.delete(runId);
36
- }
37
- };
38
- }
39
-
40
- /**
41
- * 向指定 runId 的订阅者广播事件。
42
- */
43
- public emit(runId: string, event: { type: string; data: any }): void {
44
- const set = this.listeners.get(runId);
45
- if (set) {
46
- for (const listener of set) {
47
- try {
48
- listener(event);
49
- } catch {
50
- // 忽略单个监听器错误
51
- }
52
- }
53
- }
54
- }
55
-
56
- /**
57
- * 获取或懒加载指定 Package 和 projectRoot 的缓存 RuntimeStorage 实例。
58
- *
59
- * @param packageId 所属 Package ID
60
- * @param projectRoot 项目根目录(可选)
61
- * @returns 缓存或新创建的 RuntimeStorage 实例
62
- */
63
- public getStorage(packageId: string, projectRoot?: string): RuntimeStorage {
64
- const key = `${projectRoot || ""}:${packageId}`;
65
- let storage = this.storages.get(key);
66
- if (!storage) {
67
- storage = createStorage(packageId, { projectRoot });
68
- this.storages.set(key, storage);
69
- }
70
- return storage;
71
- }
72
-
73
- /**
74
- * 获取当前缓存的所有活跃 RuntimeStorage 实例列表。
75
- */
76
- public getAllStorages(): RuntimeStorage[] {
77
- return Array.from(this.storages.values());
78
- }
79
-
80
- /**
81
- * 跨所有已建立的存储连接全局查找指定 runId 的运行记录。
82
- *
83
- * @param runId 目标运行 ID
84
- */
85
- public findRun(runId: string) {
86
- for (const storage of this.storages.values()) {
87
- const run = storage.getRun(runId);
88
- if (run) {
89
- return { storage, run };
90
- }
91
- }
92
- return undefined;
93
- }
94
-
95
- /**
96
- * 优雅关机:中断所有在途异步任务并安全关闭所有 SQLite 存储连接。
97
- */
98
- public close(): void {
99
- // 1. 批量向所有在途任务发送取消信号
100
- for (const handle of this.executionManager.list()) {
101
- handle.cancel("Server shutting down");
102
- }
103
- this.executionManager.clear();
104
-
105
- // 2. 依次安全关闭所有 SQLite 数据库连接
106
- for (const storage of this.storages.values()) {
107
- try {
108
- storage.close();
109
- } catch {
110
- // 忽略关机过程中的单个存储关闭异常
111
- }
112
- }
113
- this.storages.clear();
114
- }
115
- }
@@ -1,115 +0,0 @@
1
- import { timingSafeEqual } from "node:crypto";
2
-
3
- /**
4
- * 恒定时间字符串比较(Constant-time comparison)。
5
- * 使用底层的 `crypto.timingSafeEqual` 防范时序攻击(Timing Attack)。
6
- *
7
- * @param a 字符串 A
8
- * @param b 字符串 B
9
- * @returns 两个字符串内容是否完全一致
10
- */
11
- export function safeEqual(a: string, b: string): boolean {
12
- const aa = Buffer.from(a);
13
- const bb = Buffer.from(b);
14
-
15
- if (aa.length !== bb.length) {
16
- return false;
17
- }
18
-
19
- return timingSafeEqual(aa, bb);
20
- }
21
-
22
- /**
23
- * 检查指定的主机地址是否为本地回环接口(Loopback Host)。
24
- * 支持 127.0.0.1, localhost, ::1 等形式。
25
- *
26
- * @param host 主机名或 IP 字符串
27
- */
28
- export function isLoopbackHost(host: string): boolean {
29
- const trimmed = host.trim().toLowerCase();
30
- return (
31
- trimmed === "127.0.0.1" ||
32
- trimmed === "::1" ||
33
- trimmed === "localhost" ||
34
- trimmed === "0:0:0:0:0:0:0:1"
35
- );
36
- }
37
-
38
- /**
39
- * 校验 HTTP 请求中的鉴权令牌是否有效。
40
- *
41
- * 支持两种凭证携带模式(均通过恒定时间对比):
42
- * 1. HTTP 请求头: `Authorization: Bearer <token>`
43
- * 2. URL 查询参数: `?token=<token>`(便于浏览器/Web 调试)
44
- *
45
- * @param req 传入的 HTTP Request 对象
46
- * @param expectedToken 服务端预期的正确 Token(若未配置 Token 则默认放行)
47
- * @returns 是否鉴权成功
48
- */
49
- export function verifyBearerToken(req: Request, expectedToken?: string): boolean {
50
- if (!expectedToken || !expectedToken.trim()) {
51
- return true;
52
- }
53
-
54
- const trimmedExpected = expectedToken.trim();
55
-
56
- // 1. 请求头 Authorization: Bearer <token>
57
- const authHeader = req.headers.get("authorization");
58
- if (authHeader && authHeader.startsWith("Bearer ")) {
59
- const token = authHeader.slice(7).trim();
60
- if (safeEqual(token, trimmedExpected)) {
61
- return true;
62
- }
63
- }
64
-
65
- // 2. URL 查询参数: ?token=<token>
66
- try {
67
- const url = new URL(req.url);
68
- const tokenParam = url.searchParams.get("token");
69
- if (tokenParam && safeEqual(tokenParam.trim(), trimmedExpected)) {
70
- return true;
71
- }
72
- } catch {
73
- // 畸形 URL 视作鉴权失败
74
- }
75
-
76
- return false;
77
- }
78
-
79
- /**
80
- * 根据配置的 CORS 白名单解析并返回跨域响应头。
81
- * 默认情况下若未配置 corsOrigins,返回空对象(即默认关闭跨域访问,防范 CSRF)。
82
- *
83
- * @param origin 请求头中的 Origin 字段
84
- * @param allowedOrigins 允许跨域的白名单来源数组
85
- * @returns 包含 Access-Control-* 的响应头对象
86
- */
87
- export function resolveCorsHeaders(
88
- origin: string | null,
89
- allowedOrigins?: string[]
90
- ): Record<string, string> {
91
- if (!allowedOrigins || allowedOrigins.length === 0) {
92
- return {};
93
- }
94
-
95
- const hasWildcard = allowedOrigins.includes("*");
96
- if (hasWildcard) {
97
- return {
98
- "Access-Control-Allow-Origin": "*",
99
- "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
100
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
101
- Vary: "Origin",
102
- };
103
- }
104
-
105
- if (origin && allowedOrigins.includes(origin)) {
106
- return {
107
- "Access-Control-Allow-Origin": origin,
108
- "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
109
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
110
- Vary: "Origin",
111
- };
112
- }
113
-
114
- return {};
115
- }
@@ -1,251 +0,0 @@
1
- import { createServer as createNodeHttpServer, type IncomingMessage, type ServerResponse } from "node:http";
2
- import { resolve } from "node:path";
3
- import { Readable } from "node:stream";
4
- import { pipeline } from "node:stream/promises";
5
- import { findProjectRoot } from "../project/loader";
6
- import {
7
- handleActionsRoutes,
8
- handleConfigRoutes,
9
- handleDoctorRoute,
10
- handleHealthRoute,
11
- handleInfoRoute,
12
- handlePlaybooksRoutes,
13
- handleRunsRoutes,
14
- handleStateRoutes,
15
- jsonResponse,
16
- type RouteContext,
17
- } from "./routes";
18
- import { ServerRuntimeRegistry } from "./runtime-registry";
19
- import { isLoopbackHost, resolveCorsHeaders, verifyBearerToken } from "./security";
20
- import type { ActionDockServerInstance, CoreHttpServerFactory, CoreHttpServerInstance, ServerOptions } from "./types";
21
-
22
- let customHttpServerFactory: CoreHttpServerFactory | undefined;
23
-
24
- /**
25
- * 注册自定义 HTTP 服务端工厂(用于 Node.js / Bun 运行时环境适配)。
26
- */
27
- export function setHttpServerFactory(factory: CoreHttpServerFactory): void {
28
- customHttpServerFactory = factory;
29
- }
30
-
31
- /**
32
- * 根据当前运行时环境启动标准 Web Request/Response 兼容的 HTTP 服务。
33
- */
34
- export function launchHttpServer(
35
- port: number,
36
- host: string,
37
- fetchHandler: (req: Request) => Promise<Response>
38
- ): CoreHttpServerInstance {
39
- if (customHttpServerFactory) {
40
- return customHttpServerFactory({ port, host, fetch: fetchHandler }) as CoreHttpServerInstance;
41
- }
42
-
43
- // 若处于原生 Bun 运行时
44
- if (typeof (globalThis as any).Bun !== "undefined" && typeof (globalThis as any).Bun.serve === "function") {
45
- const bunServer = (globalThis as any).Bun.serve({
46
- port,
47
- hostname: host,
48
- fetch: fetchHandler,
49
- });
50
- return {
51
- port: bunServer.port,
52
- stop: (closeActive?: boolean) => bunServer.stop(closeActive),
53
- };
54
- }
55
-
56
- // Node.js 原生 node:http 兜底实现
57
- const srv = createNodeHttpServer(async (req: IncomingMessage, res: ServerResponse) => {
58
- try {
59
- const protocol = (req.socket as any)?.encrypted ? "https" : "http";
60
- const hostHeader = req.headers.host || "127.0.0.1";
61
- const url = new URL(req.url || "/", `${protocol}://${hostHeader}`).href;
62
-
63
- const headers = new Headers();
64
- for (const [k, v] of Object.entries(req.headers)) {
65
- if (v === undefined) continue;
66
- if (Array.isArray(v)) {
67
- for (const item of v) headers.append(k, item);
68
- } else {
69
- headers.set(k, v);
70
- }
71
- }
72
-
73
- const method = (req.method || "GET").toUpperCase();
74
- const hasBody = method !== "GET" && method !== "HEAD";
75
- const init: RequestInit = { method, headers };
76
- if (hasBody) {
77
- (init as any).body = Readable.toWeb(req);
78
- (init as any).duplex = "half";
79
- }
80
-
81
- const webReq = new Request(url, init);
82
- const webRes = await fetchHandler(webReq);
83
-
84
- res.statusCode = webRes.status;
85
- if (webRes.statusText) res.statusMessage = webRes.statusText;
86
- webRes.headers.forEach((v, k) => res.setHeader(k, v));
87
-
88
- if (!webRes.body) {
89
- res.end();
90
- return;
91
- }
92
- await pipeline(Readable.fromWeb(webRes.body as any), res);
93
- } catch (err: any) {
94
- if (!res.headersSent) {
95
- res.statusCode = 500;
96
- res.end(JSON.stringify({ error: err?.message || String(err) }));
97
- } else {
98
- res.destroy(err);
99
- }
100
- }
101
- });
102
-
103
- srv.listen(port, host);
104
- const addr = srv.address();
105
- const actualPort = typeof addr === "object" && addr ? addr.port : port;
106
-
107
- return {
108
- port: actualPort,
109
- stop: () => {
110
- srv.close();
111
- (srv as any).closeAllConnections?.();
112
- },
113
- };
114
- }
115
-
116
- /**
117
- * 启动 ActionDock 2.0 原生轻量级 HTTP 服务装配中枢。
118
- *
119
- * 仅负责中间件流转、认证拦截、路由分发与服务生命周期管理。
120
- */
121
- export function startActionDockServer(
122
- options: ServerOptions = {}
123
- ): ActionDockServerInstance {
124
- const port = options.port ?? 5177;
125
- const host = options.host ?? "127.0.0.1";
126
- const token = options.token;
127
- const customHome = options.customHome;
128
- const projectRoot = options.projectRoot
129
- ? resolve(options.projectRoot)
130
- : findProjectRoot(process.cwd());
131
-
132
- // 非回环地址强制要求配置 Token 鉴权(防裸奔)
133
- if (!isLoopbackHost(host) && !token && !options.allowInsecureNoAuth) {
134
- throw new Error(
135
- "Authentication token is required when binding to a non-loopback address. Use --allow-insecure-no-auth to override."
136
- );
137
- }
138
-
139
- const runtimeRegistry = new ServerRuntimeRegistry();
140
-
141
- const server = launchHttpServer(port, host, async (req) => {
142
- const origin = req.headers.get("origin");
143
- const corsHeaders = resolveCorsHeaders(origin, options.corsOrigins);
144
-
145
- if (req.method === "OPTIONS") {
146
- return new Response(null, {
147
- status: 204,
148
- headers: corsHeaders,
149
- });
150
- }
151
-
152
- const url = new URL(req.url);
153
- const pathname = url.pathname;
154
-
155
- const ctx: RouteContext = {
156
- req,
157
- url,
158
- pathname,
159
- corsHeaders,
160
- projectRoot,
161
- customHome,
162
- runtimeRegistry,
163
- options,
164
- };
165
-
166
- // 1. 健康检查路由(内部处理独立鉴权逻辑)
167
- const healthResponse = await handleHealthRoute(ctx);
168
- if (healthResponse) {
169
- return healthResponse;
170
- }
171
-
172
- // 2. MCP 统一网关端点
173
- if (
174
- options.enableMcp !== false &&
175
- options.mcpHandler &&
176
- (pathname === "/mcp" || pathname.startsWith("/mcp/"))
177
- ) {
178
- if (!verifyBearerToken(req, token)) {
179
- return jsonResponse(
180
- {
181
- ok: false,
182
- error: {
183
- code: "UNAUTHORIZED",
184
- message: "Invalid or missing Bearer token",
185
- },
186
- },
187
- 401,
188
- corsHeaders
189
- );
190
- }
191
- const mcpRes = await options.mcpHandler(req);
192
- if (mcpRes) return mcpRes;
193
- }
194
-
195
- // 3. 全局 API 认证鉴权拦截
196
- if (!verifyBearerToken(req, token)) {
197
- return jsonResponse(
198
- {
199
- ok: false,
200
- error: {
201
- code: "UNAUTHORIZED",
202
- message: "Invalid or missing Bearer token",
203
- },
204
- },
205
- 401,
206
- corsHeaders
207
- );
208
- }
209
-
210
- // 4. 业务领域路由分发
211
- const routeResponse =
212
- (await handleInfoRoute(ctx)) ||
213
- (await handleDoctorRoute(ctx)) ||
214
- (await handleActionsRoutes(ctx)) ||
215
- (await handlePlaybooksRoutes(ctx)) ||
216
- (await handleRunsRoutes(ctx)) ||
217
- (await handleStateRoutes(ctx)) ||
218
- (await handleConfigRoutes(ctx));
219
-
220
- if (routeResponse) {
221
- return routeResponse;
222
- }
223
-
224
- // 5. 404 路由兜底
225
- return jsonResponse(
226
- {
227
- ok: false,
228
- error: {
229
- code: "NOT_FOUND",
230
- message: `Route not found: ${req.method} ${pathname}`,
231
- },
232
- },
233
- 404,
234
- corsHeaders
235
- );
236
- });
237
-
238
- const actualHost = host === "0.0.0.0" ? "127.0.0.1" : host;
239
- const url = `http://${actualHost}:${server.port}`;
240
-
241
- return {
242
- port: server.port ?? port,
243
- host,
244
- url,
245
- runtimeRegistry,
246
- stop: () => {
247
- runtimeRegistry.close();
248
- server.stop(true);
249
- },
250
- };
251
- }
@@ -1,57 +0,0 @@
1
- import type { ServerRuntimeRegistry } from "./runtime-registry";
2
-
3
- export interface CoreHttpServerInstance {
4
- port: number;
5
- stop: (closeActiveConnections?: boolean) => void | Promise<void>;
6
- }
7
-
8
- export type CoreHttpServerFactory = (options: {
9
- port: number;
10
- host: string;
11
- fetch: (req: Request) => Promise<Response>;
12
- }) => CoreHttpServerInstance | Promise<CoreHttpServerInstance>;
13
-
14
- /**
15
- * 启动 ActionDock HTTP Runner 服务端的配置选项。
16
- */
17
- export interface ServerOptions {
18
- /** 监听端口号(默认 5177) */
19
- port?: number;
20
- /** 绑定监听的主机地址(默认 "127.0.0.1") */
21
- host?: string;
22
- /** 用于 HTTP Bearer Token 鉴权的密钥令牌 */
23
- token?: string;
24
- /** 服务的项目根目录(可选,若未指定则自动向上查找或进入全局 Registry 模式) */
25
- projectRoot?: string;
26
- /** 自定义 ActionDock 用户家目录 */
27
- customHome?: string;
28
- /** 是否允许非回环地址(如 0.0.0.0)在未配置 Token 的情况下启动(不安全) */
29
- allowInsecureNoAuth?: boolean;
30
- /** 允许跨域请求的 CORS Origin 白名单列表 */
31
- corsOrigins?: string[];
32
- /** 最大允许的请求体字节限制(默认 1MB,防 DoS) */
33
- maxBodyBytes?: number;
34
- /** 是否在 health 和 info 接口中透传本地 projectRoot 等调试路径 */
35
- exposeDebugInfo?: boolean;
36
- /** 是否启用一体化 MCP 协议支持(默认开启) */
37
- enableMcp?: boolean;
38
- /** 自定义 MCP 请求处理器钩子(若挂载则 /mcp 路由交由其处理) */
39
- mcpHandler?: (req: Request) => Promise<Response | null | undefined> | Response | null | undefined;
40
- }
41
-
42
- /**
43
- * 已启动的 ActionDock HTTP Runner 实例句柄。
44
- */
45
- export interface ActionDockServerInstance {
46
- /** 实际监听的端口号 */
47
- port: number;
48
- /** 实际绑定的主机地址 */
49
- host: string;
50
- /** 服务端可访问的基础 URL(如 "http://127.0.0.1:5177") */
51
- url: string;
52
- /** 关联的 ServerRuntimeRegistry 运行时注册表 */
53
- runtimeRegistry?: ServerRuntimeRegistry;
54
- /** 优雅关闭服务端并释放所有存储连接 */
55
- stop: () => Promise<void> | void;
56
- }
57
-