@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
@@ -1,14 +1,13 @@
1
- import { randomUUID } from "node:crypto";
2
- import { ActionResolver } from "../../catalog/action-resolver.js";
1
+ import { parseActionRef } from "../../catalog/resolve-action.js";
3
2
  import { filterByIntent } from "../../filter/index.js";
4
- import { ACTION_NOT_FOUND, ACTION_TIMEOUT, IDEMPOTENCY_CONFLICT, INPUT_VALIDATION_FAILED, PACKAGE_NOT_FOUND } from "../../errors.js";
3
+ import { ACTION_EXECUTION_ERROR, ACTION_NOT_FOUND, ACTION_START_FAILED, ACTION_TIMEOUT, ACTIONS_LIST_ERROR, IDEMPOTENCY_CONFLICT, INPUT_VALIDATION_FAILED, INVALID_JSON, PACKAGE_FORBIDDEN, PACKAGE_NOT_FOUND, REQUEST_TOO_LARGE, } from "../../errors.js";
5
4
  import { InvalidJsonError, readJsonBody, RequestTooLargeError } from "../body.js";
6
5
  import { getSubPath, jsonResponse } from "./common.js";
7
6
  /**
8
7
  * 处理 Action 相关的 HTTP 路由(列表、规范查询、同步执行与异步启动)。
9
8
  */
10
9
  export async function handleActionsRoutes(ctx) {
11
- const { req, url, pathname, corsHeaders, options, target } = ctx;
10
+ const { req, url, pathname, corsHeaders, options, service } = ctx;
12
11
  const subpath = getSubPath(pathname);
13
12
  // 1. Actions List: GET /api/v2/actions, GET /actions
14
13
  if (subpath === "/actions" && req.method === "GET") {
@@ -18,7 +17,7 @@ export async function handleActionsRoutes(ctx) {
18
17
  const targetPkg = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
19
18
  const prefix = url.searchParams.get("prefix") || undefined;
20
19
  const tags = url.searchParams.getAll("tag");
21
- let actions = await target.listActions({
20
+ let actions = await service.discovery.listActions({
22
21
  query,
23
22
  prefix,
24
23
  tags: tags.length > 0 ? tags : undefined,
@@ -37,7 +36,7 @@ export async function handleActionsRoutes(ctx) {
37
36
  catch (err) {
38
37
  return jsonResponse({
39
38
  ok: false,
40
- error: { code: "ACTIONS_LIST_ERROR", message: err.message },
39
+ error: { code: ACTIONS_LIST_ERROR, message: err.message },
41
40
  }, 500, corsHeaders);
42
41
  }
43
42
  }
@@ -51,7 +50,7 @@ export async function handleActionsRoutes(ctx) {
51
50
  return jsonResponse({
52
51
  ok: false,
53
52
  error: {
54
- code: "PACKAGE_FORBIDDEN",
53
+ code: PACKAGE_FORBIDDEN,
55
54
  message: `Package '${packageId}' is not in the allowed package list`,
56
55
  },
57
56
  }, 403, corsHeaders);
@@ -59,7 +58,7 @@ export async function handleActionsRoutes(ctx) {
59
58
  }
60
59
  const ref = `${packageId}/${actionId}`;
61
60
  try {
62
- const spec = await target.describeAction(ref);
61
+ const spec = await service.discovery.describeAction(ref);
63
62
  return jsonResponse(spec, 200, corsHeaders);
64
63
  }
65
64
  catch (err) {
@@ -77,13 +76,13 @@ export async function handleActionsRoutes(ctx) {
77
76
  if (actionShowMatch && req.method === "GET") {
78
77
  const actionId = decodeURIComponent(actionShowMatch[1]);
79
78
  try {
80
- const parsed = ActionResolver.parseRef(actionId);
79
+ const parsed = parseActionRef(actionId);
81
80
  if (parsed.packageId && options.packageAllowlist && options.packageAllowlist.length > 0) {
82
81
  if (!options.packageAllowlist.includes(parsed.packageId)) {
83
82
  return jsonResponse({
84
83
  ok: false,
85
84
  error: {
86
- code: "PACKAGE_FORBIDDEN",
85
+ code: PACKAGE_FORBIDDEN,
87
86
  message: `Package '${parsed.packageId}' is not in the allowed package list`,
88
87
  },
89
88
  }, 403, corsHeaders);
@@ -92,14 +91,14 @@ export async function handleActionsRoutes(ctx) {
92
91
  }
93
92
  catch { }
94
93
  try {
95
- const spec = await target.describeAction(actionId);
94
+ const spec = await service.discovery.describeAction(actionId);
96
95
  if (options.packageAllowlist &&
97
96
  options.packageAllowlist.length > 0 &&
98
97
  (!spec.packageId || !options.packageAllowlist.includes(spec.packageId))) {
99
98
  return jsonResponse({
100
99
  ok: false,
101
100
  error: {
102
- code: "PACKAGE_FORBIDDEN",
101
+ code: PACKAGE_FORBIDDEN,
103
102
  message: `Package '${spec.packageId}' is not in the allowed package list`,
104
103
  },
105
104
  }, 403, corsHeaders);
@@ -138,7 +137,7 @@ export async function handleActionsRoutes(ctx) {
138
137
  actionRef = decodeURIComponent(shortRunMatch[1]);
139
138
  endpointMode = shortRunMatch[2];
140
139
  try {
141
- const parsed = ActionResolver.parseRef(actionRef);
140
+ const parsed = parseActionRef(actionRef);
142
141
  pkgId = parsed.packageId;
143
142
  }
144
143
  catch { }
@@ -147,7 +146,7 @@ export async function handleActionsRoutes(ctx) {
147
146
  if (options.packageAllowlist && options.packageAllowlist.length > 0) {
148
147
  if (!pkgId) {
149
148
  try {
150
- const spec = await target.describeAction(actionRef);
149
+ const spec = await service.discovery.describeAction(actionRef);
151
150
  pkgId = spec?.packageId;
152
151
  }
153
152
  catch { }
@@ -155,9 +154,9 @@ export async function handleActionsRoutes(ctx) {
155
154
  if (!pkgId || !options.packageAllowlist.includes(pkgId)) {
156
155
  return jsonResponse({
157
156
  ok: false,
158
- runId: randomUUID(),
157
+ runId: "",
159
158
  error: {
160
- code: "PACKAGE_FORBIDDEN",
159
+ code: PACKAGE_FORBIDDEN,
161
160
  message: pkgId
162
161
  ? `Package '${pkgId}' is not in the allowed package list`
163
162
  : `Action '${actionRef}' does not belong to any allowed package`,
@@ -173,21 +172,21 @@ export async function handleActionsRoutes(ctx) {
173
172
  if (err instanceof RequestTooLargeError) {
174
173
  return jsonResponse({
175
174
  ok: false,
176
- runId: randomUUID(),
177
- error: { code: "REQUEST_TOO_LARGE", message: err.message },
175
+ runId: "",
176
+ error: { code: REQUEST_TOO_LARGE, message: err.message },
178
177
  }, 413, corsHeaders);
179
178
  }
180
179
  if (err instanceof InvalidJsonError) {
181
180
  return jsonResponse({
182
181
  ok: false,
183
- runId: randomUUID(),
184
- error: { code: "INVALID_JSON", message: err.message },
182
+ runId: "",
183
+ error: { code: INVALID_JSON, message: err.message },
185
184
  }, 400, corsHeaders);
186
185
  }
187
186
  return jsonResponse({
188
187
  ok: false,
189
- runId: randomUUID(),
190
- error: { code: "INVALID_JSON", message: `Failed to parse request body: ${err.message}` },
188
+ runId: "",
189
+ error: { code: INVALID_JSON, message: `Failed to parse request body: ${err.message}` },
191
190
  }, 400, corsHeaders);
192
191
  }
193
192
  const isAsync = endpointMode === "start" ||
@@ -205,9 +204,16 @@ export async function handleActionsRoutes(ctx) {
205
204
  body?.requestId ||
206
205
  body?.execution?.requestId ||
207
206
  undefined;
207
+ let actionRefObj;
208
+ try {
209
+ actionRefObj = parseActionRef(actionRef);
210
+ }
211
+ catch {
212
+ actionRefObj = { actionId: actionRef };
213
+ }
208
214
  if (isAsync) {
209
215
  try {
210
- const ticket = await target.startAction(actionRef, input, {
216
+ const ticket = await service.execution.start(actionRefObj, input, {
211
217
  timeoutMs,
212
218
  config: configOverrides,
213
219
  requestId,
@@ -236,9 +242,9 @@ export async function handleActionsRoutes(ctx) {
236
242
  }
237
243
  return jsonResponse({
238
244
  ok: false,
239
- runId: randomUUID(),
245
+ runId: "",
240
246
  error: {
241
- code: "ACTION_START_FAILED",
247
+ code: ACTION_START_FAILED,
242
248
  message: err.message || String(err),
243
249
  },
244
250
  }, 500, corsHeaders);
@@ -246,7 +252,7 @@ export async function handleActionsRoutes(ctx) {
246
252
  }
247
253
  // 同步执行模式
248
254
  try {
249
- const result = await target.runAction(actionRef, input, {
255
+ const result = await service.execution.run(actionRefObj, input, {
250
256
  signal: req.signal,
251
257
  timeoutMs,
252
258
  config: configOverrides,
@@ -286,9 +292,9 @@ export async function handleActionsRoutes(ctx) {
286
292
  }
287
293
  return jsonResponse({
288
294
  ok: false,
289
- runId: randomUUID(),
295
+ runId: "",
290
296
  error: {
291
- code: "ACTION_EXECUTION_ERROR",
297
+ code: ACTION_EXECUTION_ERROR,
292
298
  message: err.message || String(err),
293
299
  },
294
300
  }, 500, corsHeaders);
@@ -1,6 +1,5 @@
1
- import type { ActionDockApp } from "../../app/types.js";
2
- import type { ActionDockHost } from "../../host/types.js";
3
- import type { ActionDockTarget } from "../../target/types.js";
1
+ import type { PackageInfo } from "../../package/types.js";
2
+ import type { ActionDockService } from "../../service/types.js";
4
3
  import type { ServerOptions } from "../types.js";
5
4
  /**
6
5
  * 路由处理统一上下文对象。
@@ -12,8 +11,7 @@ export interface RouteContext {
12
11
  corsHeaders: Record<string, string>;
13
12
  projectRoot: string | null;
14
13
  customHome?: string;
15
- host?: ActionDockHost;
16
- target: ActionDockTarget;
14
+ service: ActionDockService;
17
15
  options: ServerOptions;
18
16
  }
19
17
  /**
@@ -21,27 +19,25 @@ export interface RouteContext {
21
19
  */
22
20
  export declare function jsonResponse(data: unknown, status?: number, corsHeaders?: Record<string, string>): Response;
23
21
  /**
24
- * 剥离标准版本前缀 (/api/v2/ 与兼容的 /api/v1/),获取相对路由子路径。
22
+ * 剥离标准版本前缀 (/api/v2/),获取相对路由子路径。
25
23
  */
26
24
  export declare function getSubPath(pathname: string): string;
25
+ import { ActionDockError } from "../../errors.js";
27
26
  /**
28
27
  * 包未列入白名单拒绝访问异常。
29
28
  */
30
- export declare class PackageNotAllowedError extends Error {
29
+ export declare class PackageNotAllowedError extends ActionDockError {
31
30
  status: number;
32
31
  statusCode: number;
33
- code: string;
34
32
  constructor(packageId: string);
35
33
  }
36
34
  /**
37
35
  * 校验目标 packageId 是否属于允许包白名单。
38
36
  * 当 options.packageAllowlist 存在且目标 packageId 不在白名单中时抛出 403 PACKAGE_NOT_ALLOWED 异常。
39
37
  */
40
- export declare function assertPackageAllowed(packageId: string | undefined | null, options?: {
41
- packageAllowlist?: string[];
42
- }): void;
38
+ export declare function assertPackageAllowed(packageId: string, options?: ServerOptions): void;
43
39
  /**
44
- * 从 Host 或 Target 中依据 packageId 解析对应的 ActionDockApp 实例。
45
- * 若提供了 options,过滤或优先选择白名单内的 app。
40
+ * 解析并确定生效的目标 packageId。
41
+ * 遵循严格的解析规则与白名单检查。
46
42
  */
47
- export declare function resolveAppForPackage(packageIdOrPath: string | undefined, host?: ActionDockHost, target?: ActionDockTarget, options?: ServerOptions): ActionDockApp;
43
+ export declare function resolveTargetPackageId(packages: PackageInfo[], requestedPackageId?: string, options?: ServerOptions): string;
@@ -12,27 +12,25 @@ export function jsonResponse(data, status = 200, corsHeaders = {}) {
12
12
  });
13
13
  }
14
14
  /**
15
- * 剥离标准版本前缀 (/api/v2/ 与兼容的 /api/v1/),获取相对路由子路径。
15
+ * 剥离标准版本前缀 (/api/v2/),获取相对路由子路径。
16
16
  */
17
17
  export function getSubPath(pathname) {
18
18
  if (pathname.startsWith("/api/v2/")) {
19
19
  return pathname.slice(7);
20
20
  }
21
- if (pathname.startsWith("/api/v1/")) {
22
- return pathname.slice(7);
23
- }
24
21
  return pathname;
25
22
  }
23
+ import { ActionDockError, PACKAGE_NOT_FOUND, PACKAGE_NOT_ALLOWED } from "../../errors.js";
26
24
  /**
27
25
  * 包未列入白名单拒绝访问异常。
28
26
  */
29
- export class PackageNotAllowedError extends Error {
27
+ export class PackageNotAllowedError extends ActionDockError {
30
28
  status = 403;
31
29
  statusCode = 403;
32
- code = "PACKAGE_NOT_ALLOWED";
33
30
  constructor(packageId) {
34
- super(`Package '${packageId}' is not in the allowed package list`);
31
+ super(PACKAGE_NOT_ALLOWED, `Package '${packageId}' is not in the allowed package list`);
35
32
  this.name = "PackageNotAllowedError";
33
+ Object.setPrototypeOf(this, PackageNotAllowedError.prototype);
36
34
  }
37
35
  }
38
36
  /**
@@ -42,63 +40,41 @@ export class PackageNotAllowedError extends Error {
42
40
  export function assertPackageAllowed(packageId, options) {
43
41
  if (options?.packageAllowlist &&
44
42
  Array.isArray(options.packageAllowlist) &&
45
- options.packageAllowlist.length > 0) {
46
- if (!packageId || !options.packageAllowlist.includes(packageId)) {
47
- throw new PackageNotAllowedError(packageId || "");
48
- }
43
+ options.packageAllowlist.length > 0 &&
44
+ !options.packageAllowlist.includes(packageId)) {
45
+ throw new PackageNotAllowedError(packageId);
49
46
  }
50
47
  }
51
48
  /**
52
- * 从 Host 或 Target 中依据 packageId 解析对应的 ActionDockApp 实例。
53
- * 若提供了 options,过滤或优先选择白名单内的 app。
49
+ * 解析并确定生效的目标 packageId。
50
+ * 遵循严格的解析规则与白名单检查。
54
51
  */
55
- export function resolveAppForPackage(packageIdOrPath, host, target, options) {
56
- if (packageIdOrPath) {
57
- assertPackageAllowed(packageIdOrPath, options);
58
- assertValidPackageId(packageIdOrPath);
59
- if (host) {
60
- const app = host.getApp(packageIdOrPath);
61
- if (app)
62
- return app;
63
- }
64
- const innerTarget = target?.unwrap?.();
65
- if (innerTarget && "packageId" in innerTarget && innerTarget.packageId === packageIdOrPath) {
66
- return innerTarget;
52
+ export function resolveTargetPackageId(packages, requestedPackageId, options) {
53
+ if (requestedPackageId) {
54
+ if (requestedPackageId === "global") {
55
+ return "global";
67
56
  }
68
- if (innerTarget && "getApp" in innerTarget) {
69
- const app = innerTarget.getApp(packageIdOrPath);
70
- if (app)
71
- return app;
57
+ assertValidPackageId(requestedPackageId);
58
+ assertPackageAllowed(requestedPackageId, options);
59
+ const matched = packages.find((p) => p.id === requestedPackageId || p.packageRoot === requestedPackageId);
60
+ if (!matched) {
61
+ throw new ActionDockError(PACKAGE_NOT_FOUND, `Unknown or unregistered package: '${requestedPackageId}'`);
72
62
  }
73
- throw new Error(`Unknown or unregistered package: '${packageIdOrPath}'`);
63
+ return matched.id;
74
64
  }
75
- if (host) {
76
- const apps = host.listApps();
77
- if (options?.packageAllowlist &&
78
- Array.isArray(options.packageAllowlist) &&
79
- options.packageAllowlist.length > 0) {
80
- const allowedApp = apps.find((a) => options.packageAllowlist.includes(a.packageId));
81
- if (allowedApp)
82
- return allowedApp;
83
- }
84
- if (apps.length > 0)
85
- return apps[0];
65
+ let targetId;
66
+ if (options?.packageAllowlist &&
67
+ Array.isArray(options.packageAllowlist) &&
68
+ options.packageAllowlist.length > 0) {
69
+ const allowed = packages.find((p) => options.packageAllowlist.includes(p.id));
70
+ targetId = allowed ? allowed.id : (packages[0]?.id || "");
86
71
  }
87
- const innerTarget = target?.unwrap?.();
88
- if (innerTarget && "listApps" in innerTarget) {
89
- const apps = innerTarget.listApps();
90
- if (options?.packageAllowlist &&
91
- Array.isArray(options.packageAllowlist) &&
92
- options.packageAllowlist.length > 0) {
93
- const allowedApp = apps.find((a) => options.packageAllowlist.includes(a.packageId));
94
- if (allowedApp)
95
- return allowedApp;
96
- }
97
- if (apps.length > 0)
98
- return apps[0];
72
+ else if (packages.length > 0) {
73
+ targetId = packages[0].id;
99
74
  }
100
- if (innerTarget && "packageId" in innerTarget) {
101
- return innerTarget;
75
+ else {
76
+ throw new ActionDockError(PACKAGE_NOT_FOUND, "No registered package found in service");
102
77
  }
103
- throw new Error("No registered package found in host or target");
78
+ assertPackageAllowed(targetId, options);
79
+ return targetId;
104
80
  }
@@ -1,14 +1,22 @@
1
- import { CAPABILITY_UNAVAILABLE } from "../../errors.js";
1
+ import { CAPABILITY_UNAVAILABLE, CONFIG_DELETE_ERROR, CONFIG_ENV_ERROR, CONFIG_LIST_ERROR, CONFIG_SET_ERROR, INVALID_ARGUMENT, INVALID_PACKAGE_ID, PACKAGE_NOT_ALLOWED, PACKAGE_NOT_FOUND, PATH_TRAVERSAL, } from "../../errors.js";
2
2
  import { resolveEnvValue } from "../../runtime/index.js";
3
- import { isSecretConfigKey, maskSecretValue, sanitizeConfigDefinitions } from "../../storage/index.js";
3
+ import { isSecretConfigKey, sanitizeConfigDefinitions } from "../../storage/index.js";
4
4
  import { readJsonBody } from "../body.js";
5
- import { assertPackageAllowed, getSubPath, jsonResponse, resolveAppForPackage } from "./common.js";
5
+ import { getSubPath, jsonResponse, resolveTargetPackageId } from "./common.js";
6
+ function isClientConfigError(err) {
7
+ return (err?.code === PACKAGE_NOT_FOUND ||
8
+ err?.code === INVALID_PACKAGE_ID ||
9
+ err?.code === PATH_TRAVERSAL ||
10
+ err?.code === INVALID_ARGUMENT ||
11
+ err?.status === 400 ||
12
+ err?.status === 404);
13
+ }
6
14
  /**
7
15
  * 处理配置元数据与当前值读取、更新及删除接口。
8
16
  * 在未显式开启管理功能 (options.enableManagement !== true) 时返回 403 拒绝。
9
17
  */
10
18
  export async function handleConfigRoutes(ctx) {
11
- const { req, url, pathname, corsHeaders, options, host, target } = ctx;
19
+ const { req, url, pathname, corsHeaders, options, service } = ctx;
12
20
  const subpath = getSubPath(pathname);
13
21
  const isConfigRoute = subpath === "/config" ||
14
22
  subpath.startsWith("/config/") ||
@@ -17,7 +25,7 @@ export async function handleConfigRoutes(ctx) {
17
25
  return null;
18
26
  }
19
27
  // 校验管理能力开关
20
- if (options.enableManagement !== true) {
28
+ if (options.enableManagement !== true || !service.management?.config) {
21
29
  return jsonResponse({
22
30
  ok: false,
23
31
  error: {
@@ -29,16 +37,14 @@ export async function handleConfigRoutes(ctx) {
29
37
  // 1. Config Env Check: GET /config/env
30
38
  if (subpath === "/config/env" && req.method === "GET") {
31
39
  try {
40
+ const pkgs = await service.discovery.listPackages();
32
41
  const pkgParam = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
33
- if (pkgParam) {
34
- assertPackageAllowed(pkgParam, options);
35
- }
36
- const app = resolveAppForPackage(pkgParam, host, target, options);
37
- assertPackageAllowed(app.packageId, options);
38
- const declared = app.projectConfig?.config || {};
42
+ const targetPackageId = resolveTargetPackageId(pkgs, pkgParam, options);
43
+ const pkg = pkgs.find((p) => p.id === targetPackageId);
44
+ const declared = pkg?.config || {};
39
45
  const envChecks = [];
40
46
  for (const [k, def] of Object.entries(declared)) {
41
- const resolved = resolveEnvValue(k, def, app.packageId);
47
+ const resolved = resolveEnvValue(k, def, targetPackageId);
42
48
  const matchedEnv = resolved?.envKey || null;
43
49
  envChecks.push({
44
50
  key: k,
@@ -49,61 +55,55 @@ export async function handleConfigRoutes(ctx) {
49
55
  secret: isSecretConfigKey(k, def),
50
56
  });
51
57
  }
52
- return jsonResponse({ ok: true, packageId: app.packageId, envChecks }, 200, corsHeaders);
58
+ return jsonResponse({ ok: true, packageId: targetPackageId, envChecks }, 200, corsHeaders);
53
59
  }
54
60
  catch (err) {
55
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
61
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
56
62
  return jsonResponse({
57
63
  ok: false,
58
64
  error: {
59
- code: "PACKAGE_NOT_ALLOWED",
65
+ code: PACKAGE_NOT_ALLOWED,
60
66
  message: err.message || "Package is not in the allowed package list",
61
67
  },
62
68
  }, 403, corsHeaders);
63
69
  }
64
- const isClient = err.message?.includes("Unknown or unregistered package") ||
65
- err.message?.includes("Invalid packageId") ||
66
- err.message?.includes("escapes boundary");
67
- return jsonResponse({ ok: false, error: { code: isClient ? "INVALID_ARGUMENT" : "CONFIG_ENV_ERROR", message: err.message } }, isClient ? 400 : 500, corsHeaders);
70
+ const isClient = isClientConfigError(err);
71
+ return jsonResponse({ ok: false, error: { code: isClient ? INVALID_ARGUMENT : CONFIG_ENV_ERROR, message: err.message } }, isClient ? 400 : 500, corsHeaders);
68
72
  }
69
73
  }
70
74
  // 2. Config Query: GET /config
71
75
  if (subpath === "/config" && req.method === "GET") {
72
76
  try {
77
+ const pkgs = await service.discovery.listPackages();
73
78
  const pkgParam = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
74
- if (pkgParam) {
75
- assertPackageAllowed(pkgParam, options);
76
- }
77
- const app = resolveAppForPackage(pkgParam, host, target, options);
78
- assertPackageAllowed(app.packageId, options);
79
- const stored = app.storage.listConfig();
80
- const rawDeclared = app.projectConfig?.config || {};
79
+ const targetPackageId = resolveTargetPackageId(pkgs, pkgParam, options);
80
+ const pkg = pkgs.find((p) => p.id === targetPackageId);
81
+ const rawDeclared = pkg?.config || {};
81
82
  const declared = sanitizeConfigDefinitions(rawDeclared) || {};
83
+ const views = await service.management.config.list(targetPackageId);
82
84
  const maskedValues = {};
83
- for (const [k, v] of Object.entries(stored)) {
84
- if (isSecretConfigKey(k, rawDeclared[k])) {
85
- maskedValues[k] = maskSecretValue(v);
85
+ for (const v of views) {
86
+ if (v.secret) {
87
+ maskedValues[v.key] = "********";
86
88
  }
87
89
  else {
88
- maskedValues[k] = v;
90
+ maskedValues[v.key] = v.value;
89
91
  }
90
92
  }
91
- return jsonResponse({ ok: true, packageId: app.packageId, declared, values: maskedValues }, 200, corsHeaders);
93
+ return jsonResponse({ ok: true, packageId: targetPackageId, declared, values: maskedValues }, 200, corsHeaders);
92
94
  }
93
95
  catch (err) {
94
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
96
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
95
97
  return jsonResponse({
96
98
  ok: false,
97
99
  error: {
98
- code: "PACKAGE_NOT_ALLOWED",
100
+ code: PACKAGE_NOT_ALLOWED,
99
101
  message: err.message || "Package is not in the allowed package list",
100
102
  },
101
103
  }, 403, corsHeaders);
102
104
  }
103
- const isClient = err.message?.includes("Unknown or unregistered package") ||
104
- err.message?.includes("Invalid packageId") ||
105
- err.message?.includes("escapes boundary");
106
- return jsonResponse({ ok: false, error: { code: isClient ? "INVALID_ARGUMENT" : "CONFIG_LIST_ERROR", message: err.message } }, isClient ? 400 : 500, corsHeaders);
105
+ const isClient = isClientConfigError(err);
106
+ return jsonResponse({ ok: false, error: { code: isClient ? INVALID_ARGUMENT : CONFIG_LIST_ERROR, message: err.message } }, isClient ? 400 : 500, corsHeaders);
107
107
  }
108
108
  }
109
109
  // 3. Config Update: PUT / POST /config
@@ -111,32 +111,27 @@ export async function handleConfigRoutes(ctx) {
111
111
  try {
112
112
  const body = await readJsonBody(req, { maxBytes: options.maxBodyBytes });
113
113
  const pkgParam = url.searchParams.get("package") || url.searchParams.get("packageId") || body.package || undefined;
114
- if (pkgParam) {
115
- assertPackageAllowed(pkgParam, options);
116
- }
117
- const app = resolveAppForPackage(pkgParam, host, target, options);
118
- assertPackageAllowed(app.packageId, options);
114
+ const pkgs = await service.discovery.listPackages();
115
+ const targetPackageId = resolveTargetPackageId(pkgs, pkgParam, options);
119
116
  const key = body.key;
120
117
  if (!key) {
121
- return jsonResponse({ ok: false, error: { code: "INVALID_ARGUMENT", message: "Config 'key' is required" } }, 400, corsHeaders);
118
+ return jsonResponse({ ok: false, error: { code: INVALID_ARGUMENT, message: "Config 'key' is required" } }, 400, corsHeaders);
122
119
  }
123
- await app.setConfig(key, body.value);
124
- return jsonResponse({ ok: true, packageId: app.packageId, key, message: "updated" }, 200, corsHeaders);
120
+ await service.management.config.set(targetPackageId, key, body.value);
121
+ return jsonResponse({ ok: true, packageId: targetPackageId, key, message: "updated" }, 200, corsHeaders);
125
122
  }
126
123
  catch (err) {
127
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
124
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
128
125
  return jsonResponse({
129
126
  ok: false,
130
127
  error: {
131
- code: "PACKAGE_NOT_ALLOWED",
128
+ code: PACKAGE_NOT_ALLOWED,
132
129
  message: err.message || "Package is not in the allowed package list",
133
130
  },
134
131
  }, 403, corsHeaders);
135
132
  }
136
- const isClient = err.message?.includes("Unknown or unregistered package") ||
137
- err.message?.includes("Invalid packageId") ||
138
- err.message?.includes("escapes boundary");
139
- return jsonResponse({ ok: false, error: { code: isClient ? "INVALID_ARGUMENT" : "CONFIG_SET_ERROR", message: err.message } }, isClient ? 400 : 500, corsHeaders);
133
+ const isClient = isClientConfigError(err);
134
+ return jsonResponse({ ok: false, error: { code: isClient ? INVALID_ARGUMENT : CONFIG_SET_ERROR, message: err.message } }, isClient ? 400 : 500, corsHeaders);
140
135
  }
141
136
  }
142
137
  // 4. Config Delete: DELETE /config/:key
@@ -145,28 +140,23 @@ export async function handleConfigRoutes(ctx) {
145
140
  try {
146
141
  const key = decodeURIComponent(configKeyMatch[1]);
147
142
  const pkgParam = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
148
- if (pkgParam) {
149
- assertPackageAllowed(pkgParam, options);
150
- }
151
- const app = resolveAppForPackage(pkgParam, host, target, options);
152
- assertPackageAllowed(app.packageId, options);
153
- const deleted = app.storage.deleteConfig(key);
154
- return jsonResponse({ ok: true, packageId: app.packageId, key, deleted }, 200, corsHeaders);
143
+ const pkgs = await service.discovery.listPackages();
144
+ const targetPackageId = resolveTargetPackageId(pkgs, pkgParam, options);
145
+ const deleted = await service.management.config.delete(targetPackageId, key);
146
+ return jsonResponse({ ok: true, packageId: targetPackageId, key, deleted }, 200, corsHeaders);
155
147
  }
156
148
  catch (err) {
157
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
149
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
158
150
  return jsonResponse({
159
151
  ok: false,
160
152
  error: {
161
- code: "PACKAGE_NOT_ALLOWED",
153
+ code: PACKAGE_NOT_ALLOWED,
162
154
  message: err.message || "Package is not in the allowed package list",
163
155
  },
164
156
  }, 403, corsHeaders);
165
157
  }
166
- const isClient = err.message?.includes("Unknown or unregistered package") ||
167
- err.message?.includes("Invalid packageId") ||
168
- err.message?.includes("escapes boundary");
169
- return jsonResponse({ ok: false, error: { code: isClient ? "INVALID_ARGUMENT" : "CONFIG_DELETE_ERROR", message: err.message } }, isClient ? 400 : 500, corsHeaders);
158
+ const isClient = isClientConfigError(err);
159
+ return jsonResponse({ ok: false, error: { code: isClient ? INVALID_ARGUMENT : CONFIG_DELETE_ERROR, message: err.message } }, isClient ? 400 : 500, corsHeaders);
170
160
  }
171
161
  }
172
162
  return null;
@@ -1,5 +1,5 @@
1
1
  import { type RouteContext } from "./common.js";
2
2
  /**
3
- * 处理环境与依赖诊断接口(GET /api/v2/doctor、/doctor 与兼容别名 /api/v1/doctor)。
3
+ * 处理环境与依赖诊断接口(GET /api/v2/doctor 与 /doctor)。
4
4
  */
5
5
  export declare function handleDoctorRoute(ctx: RouteContext): Promise<Response | null>;