@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.
- package/README.md +61 -68
- package/dist/catalog/action-catalog.d.ts +56 -0
- package/dist/catalog/action-catalog.js +202 -0
- package/dist/catalog/discovery.d.ts +60 -0
- package/dist/catalog/discovery.js +119 -0
- package/dist/catalog/graph.d.ts +190 -0
- package/dist/catalog/graph.js +589 -0
- package/dist/catalog/index.d.ts +5 -5
- package/dist/catalog/index.js +5 -5
- package/dist/catalog/resolve-action.d.ts +43 -0
- package/dist/catalog/resolve-action.js +119 -0
- package/dist/catalog/resolve-playbook.d.ts +26 -0
- package/dist/catalog/resolve-playbook.js +114 -0
- package/dist/doctor/checks/project.js +27 -8
- package/dist/doctor/checks/registry.js +2 -2
- package/dist/doctor/checks/runtime.js +34 -1
- package/dist/errors.d.ts +122 -10
- package/dist/errors.js +123 -10
- package/dist/execution/service.d.ts +18 -16
- package/dist/execution/service.js +183 -252
- package/dist/execution/types.d.ts +25 -69
- package/dist/execution/types.js +2 -1
- package/dist/filter/intent.d.ts +8 -9
- package/dist/filter/intent.js +49 -10
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.js +4 -0
- package/dist/host/host.d.ts +74 -32
- package/dist/host/host.js +585 -336
- package/dist/host/routing.d.ts +15 -48
- package/dist/host/routing.js +90 -153
- package/dist/host/types.d.ts +50 -17
- package/dist/index.d.ts +21 -22
- package/dist/index.js +19 -21
- package/dist/input/advice.d.ts +104 -0
- package/dist/input/advice.js +716 -0
- package/dist/input/describe.d.ts +106 -0
- package/dist/input/describe.js +176 -0
- package/dist/input/file-input.d.ts +54 -0
- package/dist/input/file-input.js +124 -0
- package/dist/input/flat-decode.d.ts +11 -0
- package/dist/input/flat-decode.js +13 -0
- package/dist/input/flat-errors.d.ts +71 -0
- package/dist/input/flat-errors.js +87 -0
- package/dist/input/flat-materializer.d.ts +29 -0
- package/dist/input/flat-materializer.js +177 -0
- package/dist/input/flat-parser.d.ts +50 -0
- package/dist/input/flat-parser.js +169 -0
- package/dist/input/flat-predicates.d.ts +34 -0
- package/dist/input/flat-predicates.js +50 -0
- package/dist/input/index.d.ts +14 -0
- package/dist/input/index.js +14 -0
- package/dist/input/input-resolver.d.ts +63 -0
- package/dist/input/input-resolver.js +254 -0
- package/dist/input/json-depth-scanner.d.ts +17 -0
- package/dist/input/json-depth-scanner.js +49 -0
- package/dist/input/metadata.d.ts +99 -0
- package/dist/input/metadata.js +99 -0
- package/dist/input/stdin-input.d.ts +26 -0
- package/dist/input/stdin-input.js +125 -0
- package/dist/input/utf8.d.ts +19 -0
- package/dist/input/utf8.js +36 -0
- package/dist/input/validation-mapper.d.ts +27 -0
- package/dist/input/validation-mapper.js +144 -0
- package/dist/invocation/index.d.ts +2 -0
- package/dist/invocation/index.js +2 -0
- package/dist/invocation/policy.d.ts +157 -0
- package/dist/invocation/policy.js +231 -0
- package/dist/invocation/types.d.ts +172 -0
- package/dist/invocation/types.js +97 -0
- package/dist/ipc/diagnostic.d.ts +0 -12
- package/dist/ipc/diagnostic.js +0 -21
- package/dist/ipc/host.d.ts +4 -4
- package/dist/ipc/host.js +44 -47
- package/dist/ipc/index.d.ts +1 -1
- package/dist/ipc/index.js +1 -1
- package/dist/ipc/service.d.ts +40 -0
- package/dist/ipc/service.js +349 -0
- package/dist/ipc/types.d.ts +21 -15
- package/dist/json/index.d.ts +1 -0
- package/dist/json/index.js +1 -0
- package/dist/json/value-validator.d.ts +88 -0
- package/dist/json/value-validator.js +330 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/module-loader.d.ts +63 -0
- package/dist/node/module-loader.js +112 -0
- package/dist/package/index.d.ts +23 -0
- package/dist/package/index.js +18 -0
- package/dist/{app/app.d.ts → package/runtime.d.ts} +41 -20
- package/dist/{app/app.js → package/runtime.js} +140 -95
- package/dist/{app → package}/static-index.js +58 -41
- package/dist/package/storage-view.d.ts +13 -0
- package/dist/package/storage-view.js +22 -0
- package/dist/{app → package}/types.d.ts +76 -57
- package/dist/package/types.js +22 -0
- package/dist/platform/index.d.ts +1 -1
- package/dist/platform/index.js +1 -1
- package/dist/platform/node.d.ts +48 -0
- package/dist/platform/node.js +96 -0
- package/dist/platform/types.d.ts +0 -9
- package/dist/process/control-arbiter.d.ts +2 -1
- package/dist/process/control-arbiter.js +1 -2
- package/dist/process/driver.d.ts +9 -8
- package/dist/process/driver.js +11 -22
- package/dist/process/index.d.ts +2 -0
- package/dist/process/index.js +2 -0
- package/dist/process/managed-record.d.ts +2 -2
- package/dist/process/process-driver.d.ts +109 -0
- package/dist/process/process-driver.js +650 -0
- package/dist/process/process-executor.d.ts +47 -0
- package/dist/process/process-executor.js +296 -0
- package/dist/process/process-manager.js +48 -26
- package/dist/process/run-executor.js +12 -12
- package/dist/profile/client-actions.js +2 -2
- package/dist/profile/client-health.js +3 -2
- package/dist/profile/client-query.js +5 -6
- package/dist/profile/client-transport.d.ts +19 -17
- package/dist/profile/client-transport.js +48 -80
- package/dist/profile/client.d.ts +2 -2
- package/dist/profile/client.js +1 -1
- package/dist/profile/index.d.ts +1 -0
- package/dist/profile/index.js +1 -0
- package/dist/profile/manager.d.ts +5 -0
- package/dist/profile/manager.js +41 -6
- package/dist/project/closure.js +2 -2
- package/dist/project/index.d.ts +8 -1
- package/dist/project/index.js +10 -1
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +1 -1
- package/dist/project/loader.js +26 -29
- package/dist/project/transactions.js +5 -10
- package/dist/registry/index.d.ts +3 -1
- package/dist/registry/index.js +2 -1
- package/dist/registry/lock.d.ts +28 -7
- package/dist/registry/lock.js +136 -16
- package/dist/registry/registry.d.ts +1 -5
- package/dist/registry/registry.js +1 -53
- package/dist/registry/scan.d.ts +0 -15
- package/dist/registry/scan.js +0 -59
- package/dist/registry/store.d.ts +42 -0
- package/dist/registry/store.js +43 -0
- package/dist/registry/types.d.ts +0 -24
- package/dist/runtime/action-collection.d.ts +1 -1
- package/dist/runtime/action-registry.d.ts +0 -4
- package/dist/runtime/action-registry.js +0 -7
- package/dist/runtime/context.js +5 -11
- package/dist/runtime/events.js +3 -6
- package/dist/runtime/identity.d.ts +22 -0
- package/dist/runtime/identity.js +10 -0
- package/dist/runtime/index.d.ts +1 -2
- package/dist/runtime/index.js +1 -2
- package/dist/runtime/module-loader.d.ts +2 -34
- package/dist/runtime/module-loader.js +1 -16
- package/dist/runtime/process.d.ts +6 -24
- package/dist/runtime/process.js +1 -118
- package/dist/runtime/run-persistence.js +2 -5
- package/dist/runtime/runner.d.ts +33 -103
- package/dist/runtime/runner.js +142 -404
- package/dist/runtime/standalone.d.ts +37 -9
- package/dist/runtime/standalone.js +195 -136
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/validator.d.ts +10 -0
- package/dist/schema/validator.js +48 -54
- package/dist/server/body.d.ts +16 -8
- package/dist/server/body.js +33 -16
- package/dist/server/dispatcher.d.ts +6 -10
- package/dist/server/dispatcher.js +24 -32
- package/dist/server/http-server.d.ts +87 -0
- package/dist/server/http-server.js +304 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.js +10 -0
- package/dist/server/mcp-endpoint.d.ts +61 -0
- package/dist/server/mcp-endpoint.js +118 -0
- package/dist/server/routes/actions.js +34 -28
- package/dist/server/routes/common.d.ts +10 -14
- package/dist/server/routes/common.js +32 -56
- package/dist/server/routes/config.js +54 -64
- package/dist/server/routes/doctor.d.ts +1 -1
- package/dist/server/routes/doctor.js +10 -18
- package/dist/server/routes/health.d.ts +1 -1
- package/dist/server/routes/health.js +2 -3
- package/dist/server/routes/info.d.ts +2 -2
- package/dist/server/routes/info.js +12 -13
- package/dist/server/routes/playbooks.js +16 -29
- package/dist/server/routes/runs.js +95 -117
- package/dist/server/routes/state.js +63 -62
- package/dist/server/server.d.ts +2 -2
- package/dist/server/server.js +43 -227
- package/dist/server/types.d.ts +8 -19
- package/dist/service/factory.d.ts +12 -0
- package/dist/service/factory.js +85 -0
- package/dist/{target → service}/index.d.ts +1 -1
- package/dist/{target → service}/index.js +1 -1
- package/dist/service/local.d.ts +27 -0
- package/dist/service/local.js +138 -0
- package/dist/{target → service}/remote-errors.d.ts +3 -6
- package/dist/service/remote-errors.js +38 -0
- package/dist/{target → service}/remote-polling.d.ts +2 -2
- package/dist/{target → service}/remote-polling.js +8 -8
- package/dist/service/remote.d.ts +34 -0
- package/dist/service/remote.js +634 -0
- package/dist/service/sse-stream.d.ts +24 -0
- package/dist/service/sse-stream.js +139 -0
- package/dist/service/types.d.ts +272 -0
- package/dist/service/types.js +32 -0
- package/dist/storage/data-dir-lock.js +10 -5
- package/dist/storage/driver.d.ts +2 -1
- package/dist/storage/driver.js +3 -52
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/lock-core.d.ts +2 -6
- package/dist/storage/lock-core.js +4 -17
- package/dist/storage/params.d.ts +2 -8
- package/dist/storage/params.js +2 -8
- package/dist/storage/sqlite-driver.d.ts +40 -0
- package/dist/storage/sqlite-driver.js +116 -0
- package/dist/storage/sqlite.js +21 -6
- package/dist/storage/types.d.ts +4 -4
- package/dist/storage/types.js +2 -2
- package/dist/utils/index.d.ts +53 -0
- package/dist/utils/index.js +99 -4
- package/dist/utils/process.d.ts +10 -0
- package/dist/utils/process.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +41 -3
- package/dist/app/index.d.ts +0 -2
- package/dist/app/index.js +0 -2
- package/dist/app/types.js +0 -1
- package/dist/catalog/action-index.d.ts +0 -9
- package/dist/catalog/action-index.js +0 -95
- package/dist/catalog/action-resolver.d.ts +0 -20
- package/dist/catalog/action-resolver.js +0 -95
- package/dist/catalog/location-registry.d.ts +0 -15
- package/dist/catalog/location-registry.js +0 -138
- package/dist/catalog/package-catalog.d.ts +0 -7
- package/dist/catalog/package-catalog.js +0 -77
- package/dist/catalog/types.d.ts +0 -68
- package/dist/catalog/types.js +0 -1
- package/dist/export/index.d.ts +0 -1
- package/dist/export/index.js +0 -1
- package/dist/export/templates.d.ts +0 -71
- package/dist/export/templates.js +0 -623
- package/dist/ipc/target.d.ts +0 -74
- package/dist/ipc/target.js +0 -365
- package/dist/platform/default.d.ts +0 -38
- package/dist/platform/default.js +0 -78
- package/dist/project/resolver.d.ts +0 -116
- package/dist/project/resolver.js +0 -376
- package/dist/registry/resolve.d.ts +0 -62
- package/dist/registry/resolve.js +0 -195
- package/dist/runtime/execution-manager.d.ts +0 -48
- package/dist/runtime/execution-manager.js +0 -66
- package/dist/runtime/package-runner-factory.d.ts +0 -82
- package/dist/runtime/package-runner-factory.js +0 -186
- package/dist/target/local.d.ts +0 -53
- package/dist/target/local.js +0 -349
- package/dist/target/remote-errors.js +0 -51
- package/dist/target/remote.d.ts +0 -86
- package/dist/target/remote.js +0 -818
- package/dist/target/target.d.ts +0 -6
- package/dist/target/target.js +0 -37
- package/dist/target/types.d.ts +0 -261
- package/dist/target/types.js +0 -35
- /package/dist/{app → package}/static-index.d.ts +0 -0
- /package/dist/{target → service}/sse-parser.d.ts +0 -0
- /package/dist/{target → service}/sse-parser.js +0 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
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,
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
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:
|
|
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 =
|
|
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
|
|
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:
|
|
157
|
+
runId: "",
|
|
159
158
|
error: {
|
|
160
|
-
code:
|
|
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:
|
|
177
|
-
error: { code:
|
|
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:
|
|
184
|
-
error: { code:
|
|
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:
|
|
190
|
-
error: { code:
|
|
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
|
|
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:
|
|
245
|
+
runId: "",
|
|
240
246
|
error: {
|
|
241
|
-
code:
|
|
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
|
|
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:
|
|
295
|
+
runId: "",
|
|
290
296
|
error: {
|
|
291
|
-
code:
|
|
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 {
|
|
2
|
-
import type {
|
|
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
|
-
|
|
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/
|
|
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
|
|
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
|
|
41
|
-
packageAllowlist?: string[];
|
|
42
|
-
}): void;
|
|
38
|
+
export declare function assertPackageAllowed(packageId: string, options?: ServerOptions): void;
|
|
43
39
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
40
|
+
* 解析并确定生效的目标 packageId。
|
|
41
|
+
* 遵循严格的解析规则与白名单检查。
|
|
46
42
|
*/
|
|
47
|
-
export declare function
|
|
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/
|
|
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
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
43
|
+
options.packageAllowlist.length > 0 &&
|
|
44
|
+
!options.packageAllowlist.includes(packageId)) {
|
|
45
|
+
throw new PackageNotAllowedError(packageId);
|
|
49
46
|
}
|
|
50
47
|
}
|
|
51
48
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* 解析并确定生效的目标 packageId。
|
|
50
|
+
* 遵循严格的解析规则与白名单检查。
|
|
54
51
|
*/
|
|
55
|
-
export function
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
63
|
+
return matched.id;
|
|
74
64
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
88
|
-
|
|
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
|
-
|
|
101
|
-
|
|
75
|
+
else {
|
|
76
|
+
throw new ActionDockError(PACKAGE_NOT_FOUND, "No registered package found in service");
|
|
102
77
|
}
|
|
103
|
-
|
|
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,
|
|
3
|
+
import { isSecretConfigKey, sanitizeConfigDefinitions } from "../../storage/index.js";
|
|
4
4
|
import { readJsonBody } from "../body.js";
|
|
5
|
-
import {
|
|
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,
|
|
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
|
-
|
|
34
|
-
|
|
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,
|
|
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:
|
|
58
|
+
return jsonResponse({ ok: true, packageId: targetPackageId, envChecks }, 200, corsHeaders);
|
|
53
59
|
}
|
|
54
60
|
catch (err) {
|
|
55
|
-
if (err.code ===
|
|
61
|
+
if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
|
|
56
62
|
return jsonResponse({
|
|
57
63
|
ok: false,
|
|
58
64
|
error: {
|
|
59
|
-
code:
|
|
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
|
|
65
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
|
84
|
-
if (
|
|
85
|
-
maskedValues[
|
|
85
|
+
for (const v of views) {
|
|
86
|
+
if (v.secret) {
|
|
87
|
+
maskedValues[v.key] = "********";
|
|
86
88
|
}
|
|
87
89
|
else {
|
|
88
|
-
maskedValues[
|
|
90
|
+
maskedValues[v.key] = v.value;
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
|
-
return jsonResponse({ ok: true, packageId:
|
|
93
|
+
return jsonResponse({ ok: true, packageId: targetPackageId, declared, values: maskedValues }, 200, corsHeaders);
|
|
92
94
|
}
|
|
93
95
|
catch (err) {
|
|
94
|
-
if (err.code ===
|
|
96
|
+
if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
|
|
95
97
|
return jsonResponse({
|
|
96
98
|
ok: false,
|
|
97
99
|
error: {
|
|
98
|
-
code:
|
|
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
|
|
104
|
-
|
|
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
|
-
|
|
115
|
-
|
|
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:
|
|
118
|
+
return jsonResponse({ ok: false, error: { code: INVALID_ARGUMENT, message: "Config 'key' is required" } }, 400, corsHeaders);
|
|
122
119
|
}
|
|
123
|
-
await
|
|
124
|
-
return jsonResponse({ ok: true, packageId:
|
|
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 ===
|
|
124
|
+
if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
|
|
128
125
|
return jsonResponse({
|
|
129
126
|
ok: false,
|
|
130
127
|
error: {
|
|
131
|
-
code:
|
|
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
|
|
137
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 ===
|
|
149
|
+
if (err.code === PACKAGE_NOT_ALLOWED || err.status === 403) {
|
|
158
150
|
return jsonResponse({
|
|
159
151
|
ok: false,
|
|
160
152
|
error: {
|
|
161
|
-
code:
|
|
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
|
|
167
|
-
|
|
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;
|