@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,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, isManagementAllowedByPolicy, 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
- * 在未显式开启管理功能 (options.enableManagement !== true) 时返回 403 拒绝。
16
+ * 在未显式开启管理功能时返回 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, activePolicy: policy } = 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 (!isManagementAllowedByPolicy(policy) || !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, policy);
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, policy);
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, policy);
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, policy);
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>;
@@ -1,63 +1,55 @@
1
+ import { DOCTOR_ERROR, PACKAGE_NOT_ALLOWED } from "../../errors.js";
1
2
  import { runDoctorChecks } from "../../doctor/doctor.js";
2
3
  import { assertPackageAllowed, getSubPath, jsonResponse } from "./common.js";
3
4
  /**
4
- * 处理环境与依赖诊断接口(GET /api/v2/doctor、/doctor 与兼容别名 /api/v1/doctor)。
5
+ * 处理环境与依赖诊断接口(GET /api/v2/doctor 与 /doctor)。
5
6
  */
6
7
  export async function handleDoctorRoute(ctx) {
7
- const { req, url, pathname, corsHeaders, projectRoot, customHome, host, target, options } = ctx;
8
+ const { req, url, pathname, corsHeaders, projectRoot, customHome, service, options, activePolicy: policy } = ctx;
8
9
  const subpath = getSubPath(pathname);
9
10
  if (subpath !== "/doctor" || req.method !== "GET") {
10
11
  return null;
11
12
  }
12
13
  try {
13
14
  const targetPkg = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
14
- if (options.packageAllowlist && options.packageAllowlist.length > 0) {
15
+ if (policy.packageAllowlist && policy.packageAllowlist.length > 0) {
15
16
  if (!targetPkg) {
16
17
  return jsonResponse({
17
18
  ok: false,
18
19
  error: {
19
- code: "PACKAGE_NOT_ALLOWED",
20
+ code: PACKAGE_NOT_ALLOWED,
20
21
  message: "Global doctor check is forbidden when package allowlist is active. Please specify an allowed package.",
21
22
  },
22
23
  }, 403, corsHeaders);
23
24
  }
24
25
  }
25
26
  if (targetPkg) {
26
- assertPackageAllowed(targetPkg, options);
27
+ assertPackageAllowed(targetPkg, policy);
27
28
  }
28
29
  let packageRoot;
29
30
  if (targetPkg) {
30
- if (host) {
31
- packageRoot = host.getApp(targetPkg)?.packageRoot;
32
- }
33
- if (!packageRoot && target?.unwrap) {
34
- const inner = target.unwrap();
35
- if (inner && "getApp" in inner && typeof inner.getApp === "function") {
36
- packageRoot = inner.getApp(targetPkg)?.packageRoot;
37
- }
38
- else if (inner && "packageId" in inner && inner.packageId === targetPkg) {
39
- packageRoot = inner.packageRoot;
40
- }
41
- }
31
+ const pkgs = await service.discovery.listPackages();
32
+ const matched = pkgs.find((p) => p.id === targetPkg || p.packageRoot === targetPkg);
33
+ packageRoot = matched?.packageRoot;
42
34
  }
43
35
  const report = await runDoctorChecks({
44
36
  cwd: packageRoot || projectRoot || process.cwd(),
45
37
  packageIdOrPath: packageRoot || targetPkg,
46
38
  customHome,
47
- packageAllowlist: options.packageAllowlist,
39
+ packageAllowlist: policy.packageAllowlist,
48
40
  });
49
41
  return jsonResponse({ ok: true, report }, 200, corsHeaders);
50
42
  }
51
43
  catch (err) {
52
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
44
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
53
45
  return jsonResponse({
54
46
  ok: false,
55
47
  error: {
56
- code: "PACKAGE_NOT_ALLOWED",
48
+ code: PACKAGE_NOT_ALLOWED,
57
49
  message: err.message || "Package is not in the allowed package list",
58
50
  },
59
51
  }, 403, corsHeaders);
60
52
  }
61
- return jsonResponse({ ok: false, error: { code: "DOCTOR_ERROR", message: err.message } }, 500, corsHeaders);
53
+ return jsonResponse({ ok: false, error: { code: DOCTOR_ERROR, message: err.message } }, 500, corsHeaders);
62
54
  }
63
55
  }
@@ -1,5 +1,5 @@
1
1
  import { type RouteContext } from "./common.js";
2
2
  /**
3
- * 处理健康检查与就绪状态接口(支持 /api/v2/health、/health 与兼容别名 /api/v1/health)。
3
+ * 处理健康检查与就绪状态接口(支持 /api/v2/health 与 /health)。
4
4
  */
5
5
  export declare function handleHealthRoute(ctx: RouteContext): Promise<Response | null>;
@@ -3,16 +3,15 @@ import { UNAUTHORIZED } from "../../errors.js";
3
3
  import { verifyBearerToken } from "../security.js";
4
4
  import { jsonResponse } from "./common.js";
5
5
  /**
6
- * 处理健康检查与就绪状态接口(支持 /api/v2/health、/health 与兼容别名 /api/v1/health)。
6
+ * 处理健康检查与就绪状态接口(支持 /api/v2/health 与 /health)。
7
7
  */
8
8
  export async function handleHealthRoute(ctx) {
9
- const { req, pathname, corsHeaders, options, projectRoot } = ctx;
9
+ const { req, pathname, corsHeaders, options, projectRoot, activePolicy: policy } = ctx;
10
10
  if (pathname !== "/api/v2/health" &&
11
- pathname !== "/health" &&
12
- pathname !== "/api/v1/health") {
11
+ pathname !== "/health") {
13
12
  return null;
14
13
  }
15
- if (!verifyBearerToken(req, options.token, options)) {
14
+ if (!verifyBearerToken(req, policy.token, options)) {
16
15
  return jsonResponse({
17
16
  ok: false,
18
17
  error: {
@@ -1,7 +1,7 @@
1
1
  import { type RouteContext } from "./common.js";
2
2
  /**
3
3
  * 处理系统自省与包信息接口:
4
- * - GET /api/v2/info 与 GET /info -> 委托 target.info()
5
- * - GET /api/v2/packages 与 GET /packages -> 委托 host.info() 或 target.info()
4
+ * - GET /api/v2/info 与 GET /info -> 委托 service.info()
5
+ * - GET /api/v2/packages 与 GET /packages -> 委托 service.discovery.listPackages()
6
6
  */
7
7
  export declare function handleInfoRoute(ctx: RouteContext): Promise<Response | null>;
@@ -1,8 +1,8 @@
1
1
  import { filterWithFallbackInfo } from "../../filter/index.js";
2
- import { PACKAGE_NOT_FOUND } from "../../errors.js";
2
+ import { INFO_ERROR, PACKAGE_NOT_ALLOWED, PACKAGE_NOT_FOUND, PACKAGES_INFO_ERROR } from "../../errors.js";
3
3
  import { ACTIONDOCK_VERSION } from "../../version.js";
4
4
  import { sanitizeConfigDefinitions } from "../../storage/index.js";
5
- import { assertPackageAllowed, getSubPath, jsonResponse } from "./common.js";
5
+ import { assertPackageAllowed, filterPackagesByPolicy, getSubPath, jsonResponse } from "./common.js";
6
6
  function sanitizePackage(pkg, exposeDebugInfo) {
7
7
  if (!pkg)
8
8
  return pkg;
@@ -18,42 +18,39 @@ function sanitizePackage(pkg, exposeDebugInfo) {
18
18
  }
19
19
  /**
20
20
  * 处理系统自省与包信息接口:
21
- * - GET /api/v2/info 与 GET /info -> 委托 target.info()
22
- * - GET /api/v2/packages 与 GET /packages -> 委托 host.info() 或 target.info()
21
+ * - GET /api/v2/info 与 GET /info -> 委托 service.info()
22
+ * - GET /api/v2/packages 与 GET /packages -> 委托 service.discovery.listPackages()
23
23
  */
24
24
  export async function handleInfoRoute(ctx) {
25
- const { req, url, pathname, corsHeaders, options, target, host, projectRoot } = ctx;
25
+ const { req, url, pathname, corsHeaders, options, service, projectRoot, activePolicy: policy } = ctx;
26
26
  const subpath = getSubPath(pathname);
27
27
  // 1. Packages List: GET /api/v2/packages, /packages
28
28
  if ((subpath === "/packages" || pathname === "/api/v2/packages" || pathname === "/packages") && req.method === "GET") {
29
29
  try {
30
30
  const targetPkg = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
31
31
  if (targetPkg) {
32
- assertPackageAllowed(targetPkg, options);
33
- }
34
- const rawPackages = target ? await target.listPackages() : (host ? await host.info() : []);
35
- let packages = rawPackages.map((p) => sanitizePackage(p, options.exposeDebugInfo));
36
- if (options.packageAllowlist && Array.isArray(options.packageAllowlist) && options.packageAllowlist.length > 0) {
37
- packages = packages.filter((p) => p.id && options.packageAllowlist.includes(p.id));
32
+ assertPackageAllowed(targetPkg, policy);
38
33
  }
34
+ const rawPackages = await service.discovery.listPackages();
35
+ const packages = filterPackagesByPolicy(rawPackages.map((p) => sanitizePackage(p, options.exposeDebugInfo)), policy);
39
36
  return jsonResponse({
40
37
  ok: true,
41
38
  packages,
42
39
  }, 200, corsHeaders);
43
40
  }
44
41
  catch (err) {
45
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
42
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
46
43
  return jsonResponse({
47
44
  ok: false,
48
45
  error: {
49
- code: "PACKAGE_NOT_ALLOWED",
46
+ code: PACKAGE_NOT_ALLOWED,
50
47
  message: err.message || "Package is not in the allowed package list",
51
48
  },
52
49
  }, 403, corsHeaders);
53
50
  }
54
51
  return jsonResponse({
55
52
  ok: false,
56
- error: { code: "PACKAGES_INFO_ERROR", message: err.message },
53
+ error: { code: PACKAGES_INFO_ERROR, message: err.message },
57
54
  }, 500, corsHeaders);
58
55
  }
59
56
  }
@@ -64,14 +61,10 @@ export async function handleInfoRoute(ctx) {
64
61
  const intent = url.searchParams.get("intent") || undefined;
65
62
  const targetPkg = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
66
63
  if (targetPkg) {
67
- assertPackageAllowed(targetPkg, options);
68
- }
69
- const targetInfo = await target.info();
70
- const rawPackages = targetInfo.packages || [];
71
- let packages = rawPackages.map((p) => sanitizePackage(p, options.exposeDebugInfo));
72
- if (options.packageAllowlist && Array.isArray(options.packageAllowlist) && options.packageAllowlist.length > 0) {
73
- packages = packages.filter((p) => p.id && options.packageAllowlist.includes(p.id));
64
+ assertPackageAllowed(targetPkg, policy);
74
65
  }
66
+ const rawPackages = await service.info();
67
+ const packages = filterPackagesByPolicy(rawPackages.map((p) => sanitizePackage(p, options.exposeDebugInfo)), policy);
75
68
  if (isTree) {
76
69
  return jsonResponse({ ok: true, type: "tree", packages }, 200, corsHeaders);
77
70
  }
@@ -139,18 +132,18 @@ export async function handleInfoRoute(ctx) {
139
132
  }, 200, corsHeaders);
140
133
  }
141
134
  catch (err) {
142
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
135
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
143
136
  return jsonResponse({
144
137
  ok: false,
145
138
  error: {
146
- code: "PACKAGE_NOT_ALLOWED",
139
+ code: PACKAGE_NOT_ALLOWED,
147
140
  message: err.message || "Package is not in the allowed package list",
148
141
  },
149
142
  }, 403, corsHeaders);
150
143
  }
151
144
  return jsonResponse({
152
145
  ok: false,
153
- error: { code: "INFO_ERROR", message: err.message },
146
+ error: { code: INFO_ERROR, message: err.message },
154
147
  }, 500, corsHeaders);
155
148
  }
156
149
  }
@@ -1,10 +1,11 @@
1
1
  import { filterByIntent } from "../../filter/index.js";
2
- import { assertPackageAllowed, getSubPath, jsonResponse } from "./common.js";
2
+ import { PACKAGE_NOT_ALLOWED, PLAYBOOK_NOT_FOUND, PLAYBOOKS_LIST_ERROR } from "../../errors.js";
3
+ import { assertPackageAllowed, getSubPath, isPackageAllowedByPolicy, jsonResponse } from "./common.js";
3
4
  /**
4
5
  * 处理 Playbook 规程相关的 HTTP 路由(列表与 SOP 详情查询)。
5
6
  */
6
7
  export async function handlePlaybooksRoutes(ctx) {
7
- const { req, url, pathname, corsHeaders, target, host, options } = ctx;
8
+ const { req, url, pathname, corsHeaders, service, options, activePolicy: policy } = ctx;
8
9
  const subpath = getSubPath(pathname);
9
10
  // 1. Playbooks List: GET /api/v2/playbooks, GET /playbooks
10
11
  if (subpath === "/playbooks" && req.method === "GET") {
@@ -12,14 +13,14 @@ export async function handlePlaybooksRoutes(ctx) {
12
13
  const intent = url.searchParams.get("intent") || undefined;
13
14
  const targetPkg = url.searchParams.get("package") || url.searchParams.get("packageId") || undefined;
14
15
  if (targetPkg) {
15
- assertPackageAllowed(targetPkg, options);
16
+ assertPackageAllowed(targetPkg, policy);
16
17
  }
17
- let pbs = await target.listPlaybooks({ intent, package: targetPkg });
18
+ let pbs = await service.discovery.listPlaybooks({ intent, package: targetPkg });
18
19
  if (targetPkg) {
19
20
  pbs = pbs.filter((p) => p.packageId === targetPkg);
20
21
  }
21
- if (options.packageAllowlist && Array.isArray(options.packageAllowlist) && options.packageAllowlist.length > 0) {
22
- pbs = pbs.filter((p) => p.packageId && options.packageAllowlist.includes(p.packageId));
22
+ if (policy.packageAllowlist && Array.isArray(policy.packageAllowlist) && policy.packageAllowlist.length > 0) {
23
+ pbs = pbs.filter((p) => isPackageAllowedByPolicy(p.packageId, policy));
23
24
  }
24
25
  if (intent) {
25
26
  pbs = filterByIntent(pbs, intent, [(p) => p.id, (p) => p.description || "", (p) => p.packageId || "", (p) => (p.actions || []).join(" ")], false);
@@ -27,16 +28,16 @@ export async function handlePlaybooksRoutes(ctx) {
27
28
  return jsonResponse(pbs, 200, corsHeaders);
28
29
  }
29
30
  catch (err) {
30
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
31
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
31
32
  return jsonResponse({
32
33
  ok: false,
33
34
  error: {
34
- code: "PACKAGE_NOT_ALLOWED",
35
+ code: PACKAGE_NOT_ALLOWED,
35
36
  message: err.message || "Package is not in the allowed package list",
36
37
  },
37
38
  }, 403, corsHeaders);
38
39
  }
39
- return jsonResponse({ ok: false, error: { code: "PLAYBOOKS_LIST_ERROR", message: err.message } }, 500, corsHeaders);
40
+ return jsonResponse({ ok: false, error: { code: PLAYBOOKS_LIST_ERROR, message: err.message } }, 500, corsHeaders);
40
41
  }
41
42
  }
42
43
  // 2. Multi-package Playbook Show: GET /api/v2/packages/:packageId/playbooks/:playbookId
@@ -45,27 +46,27 @@ export async function handlePlaybooksRoutes(ctx) {
45
46
  const packageId = decodeURIComponent(pkgPlaybookMatch[1]);
46
47
  const playbookId = decodeURIComponent(pkgPlaybookMatch[2]);
47
48
  try {
48
- assertPackageAllowed(packageId, options);
49
+ assertPackageAllowed(packageId, policy);
49
50
  }
50
51
  catch (err) {
51
52
  return jsonResponse({
52
53
  ok: false,
53
54
  error: {
54
- code: "PACKAGE_NOT_ALLOWED",
55
+ code: PACKAGE_NOT_ALLOWED,
55
56
  message: err.message || `Package '${packageId}' is not in the allowed package list`,
56
57
  },
57
58
  }, 403, corsHeaders);
58
59
  }
59
60
  const ref = `${packageId}/${playbookId}`;
60
61
  try {
61
- const pb = await target.describePlaybook(ref);
62
+ const pb = await service.discovery.describePlaybook(ref);
62
63
  return jsonResponse(pb, 200, corsHeaders);
63
64
  }
64
65
  catch (err) {
65
66
  return jsonResponse({
66
67
  ok: false,
67
68
  error: {
68
- code: "PLAYBOOK_NOT_FOUND",
69
+ code: PLAYBOOK_NOT_FOUND,
69
70
  message: err.message || `Playbook '${playbookId}' not found in package '${packageId}'`,
70
71
  },
71
72
  }, 404, corsHeaders);
@@ -78,56 +79,42 @@ export async function handlePlaybooksRoutes(ctx) {
78
79
  if (playbookId.includes("/")) {
79
80
  const pkgFromRef = playbookId.slice(0, playbookId.lastIndexOf("/"));
80
81
  try {
81
- assertPackageAllowed(pkgFromRef, options);
82
+ assertPackageAllowed(pkgFromRef, policy);
82
83
  }
83
84
  catch (err) {
84
85
  return jsonResponse({
85
86
  ok: false,
86
87
  error: {
87
- code: "PACKAGE_NOT_ALLOWED",
88
+ code: PACKAGE_NOT_ALLOWED,
88
89
  message: err.message || `Package '${pkgFromRef}' is not in the allowed package list`,
89
90
  },
90
91
  }, 403, corsHeaders);
91
92
  }
92
93
  }
93
94
  try {
94
- const pb = await target.describePlaybook(playbookId);
95
- const unwrapped = target.unwrap?.();
96
- let pkgId = pb.packageId || (unwrapped && "packageId" in unwrapped ? unwrapped.packageId : undefined);
97
- if (!pkgId && host) {
98
- for (const app of host.listApps()) {
99
- try {
100
- const map = app.getStaticPlaybookMap?.();
101
- const cleanId = playbookId.replace(/\.md$/, "");
102
- if (map?.has(cleanId) || map?.has(playbookId)) {
103
- pkgId = app.packageId;
104
- break;
105
- }
106
- }
107
- catch { }
108
- }
109
- }
95
+ const pb = await service.discovery.describePlaybook(playbookId);
96
+ let pkgId = pb.packageId;
110
97
  if (!pkgId) {
111
- const allPbs = await target.listPlaybooks().catch(() => []);
98
+ const allPbs = await service.discovery.listPlaybooks().catch(() => []);
112
99
  const matched = allPbs.find((p) => p.id === playbookId || p.id.endsWith(`/${playbookId}`));
113
100
  if (matched?.packageId) {
114
101
  pkgId = matched.packageId;
115
102
  }
116
103
  }
117
- assertPackageAllowed(pkgId, options);
104
+ assertPackageAllowed(pkgId, policy);
118
105
  return jsonResponse(pb, 200, corsHeaders);
119
106
  }
120
107
  catch (err) {
121
- if (err.code === "PACKAGE_NOT_ALLOWED" || err.status === 403) {
108
+ if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
122
109
  return jsonResponse({
123
110
  ok: false,
124
111
  error: {
125
- code: "PACKAGE_NOT_ALLOWED",
112
+ code: PACKAGE_NOT_ALLOWED,
126
113
  message: err.message || "Package is not in the allowed package list",
127
114
  },
128
115
  }, 403, corsHeaders);
129
116
  }
130
- return jsonResponse({ ok: false, error: { code: "PLAYBOOK_NOT_FOUND", message: err.message } }, 404, corsHeaders);
117
+ return jsonResponse({ ok: false, error: { code: PLAYBOOK_NOT_FOUND, message: err.message } }, 404, corsHeaders);
131
118
  }
132
119
  }
133
120
  return null;