@actiondock/core 2.5.1 → 2.7.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/dist/app/app.d.ts +14 -1
- package/dist/app/app.js +88 -222
- package/dist/app/static-index.d.ts +39 -0
- package/dist/app/static-index.js +164 -0
- package/dist/app/types.d.ts +5 -0
- package/dist/catalog/action-index.js +6 -4
- package/dist/catalog/package-catalog.js +2 -1
- package/dist/doctor/checks/index.d.ts +5 -0
- package/dist/doctor/checks/index.js +5 -0
- package/dist/doctor/checks/project-locate.d.ts +6 -0
- package/dist/doctor/checks/project-locate.js +36 -0
- package/dist/doctor/checks/project.d.ts +6 -0
- package/dist/doctor/checks/project.js +383 -0
- package/dist/doctor/checks/registry.d.ts +13 -0
- package/dist/doctor/checks/registry.js +207 -0
- package/dist/doctor/checks/runtime.d.ts +13 -0
- package/dist/doctor/checks/runtime.js +90 -0
- package/dist/doctor/checks/storage.d.ts +5 -0
- package/dist/doctor/checks/storage.js +34 -0
- package/dist/doctor/context.d.ts +35 -0
- package/dist/doctor/context.js +11 -0
- package/dist/doctor/doctor.d.ts +5 -0
- package/dist/doctor/doctor.js +33 -602
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +22 -0
- package/dist/execution/service.js +14 -9
- package/dist/export/templates.js +42 -30
- package/dist/host/host.d.ts +33 -0
- package/dist/host/host.js +222 -362
- package/dist/host/routing.d.ts +92 -0
- package/dist/host/routing.js +238 -0
- package/dist/host/types.d.ts +8 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/input/advice.d.ts +100 -0
- package/dist/input/advice.js +767 -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 +72 -0
- package/dist/input/flat-errors.js +87 -0
- package/dist/input/flat-materializer.d.ts +29 -0
- package/dist/input/flat-materializer.js +179 -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/ipc/host.js +38 -0
- 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/platform/types.d.ts +7 -0
- package/dist/process/control-arbiter.d.ts +107 -0
- package/dist/process/control-arbiter.js +251 -0
- package/dist/process/diagnostics-sink.d.ts +26 -0
- package/dist/process/diagnostics-sink.js +35 -0
- package/dist/process/index.d.ts +7 -0
- package/dist/process/index.js +7 -0
- package/dist/process/input-dispatcher.d.ts +88 -0
- package/dist/process/input-dispatcher.js +240 -0
- package/dist/process/managed-record.d.ts +78 -0
- package/dist/process/managed-record.js +1 -0
- package/dist/process/process-manager.d.ts +15 -56
- package/dist/process/process-manager.js +119 -707
- package/dist/process/reservation-table.d.ts +39 -0
- package/dist/process/reservation-table.js +80 -0
- package/dist/process/run-executor.d.ts +16 -0
- package/dist/process/run-executor.js +155 -0
- package/dist/process/terminal-output-cache.d.ts +66 -0
- package/dist/process/terminal-output-cache.js +132 -0
- package/dist/profile/client-actions.d.ts +56 -0
- package/dist/profile/client-actions.js +215 -0
- package/dist/profile/client-config.d.ts +20 -0
- package/dist/profile/client-config.js +51 -0
- package/dist/profile/client-health.d.ts +14 -0
- package/dist/profile/client-health.js +69 -0
- package/dist/profile/client-playbooks.d.ts +19 -0
- package/dist/profile/client-playbooks.js +36 -0
- package/dist/profile/client-query.d.ts +30 -0
- package/dist/profile/client-query.js +70 -0
- package/dist/profile/client-runs.d.ts +42 -0
- package/dist/profile/client-runs.js +58 -0
- package/dist/profile/client-state.d.ts +56 -0
- package/dist/profile/client-state.js +83 -0
- package/dist/profile/client-transport.d.ts +61 -0
- package/dist/profile/client-transport.js +156 -0
- package/dist/profile/client.d.ts +23 -142
- package/dist/profile/client.js +28 -588
- package/dist/profile/manager.js +1 -1
- package/dist/project/closure.d.ts +4 -4
- package/dist/project/closure.js +11 -5
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +0 -25
- package/dist/project/loader.js +1 -154
- package/dist/project/transactions.d.ts +12 -14
- package/dist/project/transactions.js +60 -836
- package/dist/registry/registry.js +6 -4
- package/dist/registry/resolve.js +1 -1
- package/dist/runtime/action-registry.d.ts +45 -0
- package/dist/runtime/action-registry.js +108 -0
- package/dist/runtime/clock.d.ts +5 -17
- package/dist/runtime/clock.js +5 -15
- package/dist/runtime/context.d.ts +3 -0
- package/dist/runtime/context.js +17 -56
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/package-runner-factory.d.ts +82 -0
- package/dist/runtime/package-runner-factory.js +186 -0
- package/dist/runtime/run-persistence.d.ts +85 -0
- package/dist/runtime/run-persistence.js +111 -0
- package/dist/runtime/runner.d.ts +25 -45
- package/dist/runtime/runner.js +130 -354
- package/dist/runtime/standalone.d.ts +25 -2
- package/dist/runtime/standalone.js +305 -87
- 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/dispatcher.d.ts +8 -3
- package/dist/server/dispatcher.js +16 -40
- package/dist/server/routes/runs.js +6 -4
- package/dist/server/security.d.ts +1 -7
- package/dist/server/security.js +2 -13
- package/dist/storage/clock.d.ts +25 -0
- package/dist/storage/clock.js +23 -0
- package/dist/storage/data-dir-lock.d.ts +3 -36
- package/dist/storage/data-dir-lock.js +48 -881
- package/dist/storage/index.d.ts +8 -1
- package/dist/storage/index.js +8 -2
- package/dist/storage/lazy.js +1 -0
- package/dist/storage/lock-core.d.ts +162 -0
- package/dist/storage/lock-core.js +885 -0
- package/dist/storage/sqlite.d.ts +19 -6
- package/dist/storage/sqlite.js +81 -89
- package/dist/storage/types.d.ts +27 -5
- package/dist/storage/types.js +21 -0
- package/dist/storage/utils.d.ts +22 -0
- package/dist/storage/utils.js +34 -0
- package/dist/target/local.d.ts +1 -0
- package/dist/target/local.js +23 -17
- package/dist/target/remote-errors.d.ts +21 -0
- package/dist/target/remote-errors.js +51 -0
- package/dist/target/remote-polling.d.ts +35 -0
- package/dist/target/remote-polling.js +104 -0
- package/dist/target/remote.d.ts +4 -0
- package/dist/target/remote.js +108 -301
- package/dist/target/sse-parser.d.ts +35 -0
- package/dist/target/sse-parser.js +163 -0
- package/dist/target/target.js +2 -0
- package/dist/target/types.d.ts +6 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/net.d.ts +10 -0
- package/dist/utils/net.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ActionDefinition } from "@actiondock/sdk";
|
|
2
2
|
import type { ActionSpec } from "../app/types.js";
|
|
3
3
|
import type { ConfigItemDefinition } from "../project/types.js";
|
|
4
4
|
import type { ActionDockTarget } from "../target/types.js";
|
|
@@ -11,6 +11,13 @@ export declare const ExitCode: {
|
|
|
11
11
|
readonly INVALID_ARGUMENT: 2;
|
|
12
12
|
readonly SIGINT: 130;
|
|
13
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* 调用控制选项契约。
|
|
16
|
+
*/
|
|
17
|
+
export interface InvocationControl {
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
cancellationSource?: "external" | "sigint";
|
|
20
|
+
}
|
|
14
21
|
/**
|
|
15
22
|
* 独立二进制运行分发器配置选项。
|
|
16
23
|
*/
|
|
@@ -63,14 +70,25 @@ export declare class StandaloneDispatcher {
|
|
|
63
70
|
* 解析命令行参数并分发执行对应子命令。
|
|
64
71
|
*
|
|
65
72
|
* @param argv 命令行参数数组(如 process.argv.slice(2))
|
|
73
|
+
* @param control 可选的中断与取消控制契约
|
|
66
74
|
* @returns 退出状态码(0: 成功, 1: 失败, 2: 参数错误, 130: 中断)
|
|
67
75
|
*/
|
|
68
|
-
dispatch(argv: string[]): Promise<number>;
|
|
76
|
+
dispatch(argv: string[], control?: InvocationControl): Promise<number>;
|
|
69
77
|
private handleList;
|
|
70
78
|
private handleDescribe;
|
|
71
79
|
private handleRun;
|
|
72
80
|
private handleConfig;
|
|
73
81
|
private handleState;
|
|
82
|
+
/**
|
|
83
|
+
* 判定配置读取异常是否为目标层能力缺失(键未配置语义)而非内部故障。
|
|
84
|
+
*/
|
|
85
|
+
private isTargetCapabilityError;
|
|
86
|
+
/**
|
|
87
|
+
* 解析携带命名空间前缀的状态键参数:委托 sdk 单一事实源 decodeStateKey。
|
|
88
|
+
*
|
|
89
|
+
* 解析失败(如存在多个未转义冒号的歧义键)时按纯键处理,交由存储层兜底。
|
|
90
|
+
*/
|
|
91
|
+
private decodeStateKeyArg;
|
|
74
92
|
private printHelp;
|
|
75
93
|
}
|
|
76
94
|
/**
|
|
@@ -87,3 +105,8 @@ export declare class StandaloneRuntime {
|
|
|
87
105
|
* 工厂函数:创建独立运行时实例。
|
|
88
106
|
*/
|
|
89
107
|
export declare function createStandaloneRuntime(options: StandaloneRuntimeOptions): StandaloneRuntime;
|
|
108
|
+
/**
|
|
109
|
+
* 独立二进制进程入口适配器。
|
|
110
|
+
* 仅在命令行可执行入口调用,负责全局 SIGINT 监听与退出状态码写入。
|
|
111
|
+
*/
|
|
112
|
+
export declare function runStandaloneProcess(argv: string[], options: StandaloneDispatcherOptions): Promise<void>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decodeStateKey } from "@actiondock/sdk";
|
|
2
2
|
import { filterWithFallbackInfo } from "../filter/index.js";
|
|
3
3
|
import { createActionDockTarget } from "../target/target.js";
|
|
4
4
|
import { STANDALONE_ASYNC_UNSUPPORTED } from "../errors.js";
|
|
5
|
+
import { resolveActionInput, formatActionDetail, buildActionDescribePayload, mapInputValidationFailure, } from "../input/index.js";
|
|
6
|
+
import { validateActionInputValue } from "../json/value-validator.js";
|
|
5
7
|
import { normalizeActionCollection } from "./action-collection.js";
|
|
6
8
|
import { parseDuration } from "../utils/index.js";
|
|
7
9
|
/**
|
|
@@ -83,24 +85,27 @@ export class StandaloneDispatcher {
|
|
|
83
85
|
* 解析命令行参数并分发执行对应子命令。
|
|
84
86
|
*
|
|
85
87
|
* @param argv 命令行参数数组(如 process.argv.slice(2))
|
|
88
|
+
* @param control 可选的中断与取消控制契约
|
|
86
89
|
* @returns 退出状态码(0: 成功, 1: 失败, 2: 参数错误, 130: 中断)
|
|
87
90
|
*/
|
|
88
|
-
async dispatch(argv) {
|
|
89
|
-
const
|
|
91
|
+
async dispatch(argv, control) {
|
|
92
|
+
const separator = argv.indexOf("--");
|
|
93
|
+
const controlArgs = separator >= 0 ? argv.slice(0, separator) : argv;
|
|
94
|
+
const actionArgs = separator >= 0 ? argv.slice(separator + 1) : [];
|
|
90
95
|
let dataDir;
|
|
91
96
|
const configOverrides = {};
|
|
92
|
-
// 1. 提取全局参数(--data-dir, --config
|
|
97
|
+
// 1. 提取全局参数(--data-dir, --config)仅从 controlArgs 中解析
|
|
93
98
|
const filteredArgs = [];
|
|
94
|
-
for (let i = 0; i <
|
|
95
|
-
const arg =
|
|
96
|
-
if (arg === "--data-dir" && i + 1 <
|
|
97
|
-
dataDir =
|
|
99
|
+
for (let i = 0; i < controlArgs.length; i++) {
|
|
100
|
+
const arg = controlArgs[i];
|
|
101
|
+
if (arg === "--data-dir" && i + 1 < controlArgs.length) {
|
|
102
|
+
dataDir = controlArgs[++i];
|
|
98
103
|
}
|
|
99
104
|
else if (arg.startsWith("--data-dir=")) {
|
|
100
105
|
dataDir = arg.slice(11);
|
|
101
106
|
}
|
|
102
|
-
else if (arg === "--config" && i + 1 <
|
|
103
|
-
const pair =
|
|
107
|
+
else if (arg === "--config" && i + 1 < controlArgs.length) {
|
|
108
|
+
const pair = controlArgs[++i];
|
|
104
109
|
const [k, ...v] = pair.split("=");
|
|
105
110
|
if (k)
|
|
106
111
|
configOverrides[k] = v.join("=");
|
|
@@ -118,8 +123,8 @@ export class StandaloneDispatcher {
|
|
|
118
123
|
const command = filteredArgs[0] || "help";
|
|
119
124
|
const subArgs = filteredArgs.slice(1);
|
|
120
125
|
// 2. 独立入口拒绝异步启动语义
|
|
121
|
-
if (
|
|
122
|
-
const isJson =
|
|
126
|
+
if (controlArgs.includes("--async")) {
|
|
127
|
+
const isJson = controlArgs.includes("--json");
|
|
123
128
|
if (isJson) {
|
|
124
129
|
this.writeOut(JSON.stringify({
|
|
125
130
|
ok: false,
|
|
@@ -154,47 +159,51 @@ export class StandaloneDispatcher {
|
|
|
154
159
|
this.writeErr(`Error initializing standalone target: ${err?.message || err}`);
|
|
155
160
|
return ExitCode.FAILURE;
|
|
156
161
|
}
|
|
157
|
-
const controller = new AbortController();
|
|
158
|
-
const sigintHandler = () => {
|
|
159
|
-
controller.abort(new Error("Interrupted by SIGINT"));
|
|
160
|
-
};
|
|
161
|
-
process.once("SIGINT", sigintHandler);
|
|
162
162
|
try {
|
|
163
|
+
let code;
|
|
163
164
|
switch (command) {
|
|
164
165
|
case "list":
|
|
165
|
-
|
|
166
|
+
code = await this.handleList(target, subArgs);
|
|
167
|
+
break;
|
|
166
168
|
case "describe":
|
|
167
169
|
case "show":
|
|
168
|
-
|
|
170
|
+
code = await this.handleDescribe(target, subArgs);
|
|
171
|
+
break;
|
|
169
172
|
case "run":
|
|
170
|
-
|
|
173
|
+
code = await this.handleRun(target, subArgs, actionArgs, control?.signal);
|
|
174
|
+
break;
|
|
171
175
|
case "config":
|
|
172
|
-
|
|
176
|
+
code = await this.handleConfig(target, subArgs);
|
|
177
|
+
break;
|
|
173
178
|
case "state":
|
|
174
|
-
|
|
179
|
+
code = await this.handleState(target, subArgs);
|
|
180
|
+
break;
|
|
175
181
|
default:
|
|
176
182
|
this.writeErr(`Unknown command: '${command}'`);
|
|
177
183
|
this.printHelp();
|
|
178
184
|
return ExitCode.INVALID_ARGUMENT;
|
|
179
185
|
}
|
|
186
|
+
if (control?.cancellationSource === "sigint" && control?.signal?.aborted) {
|
|
187
|
+
return ExitCode.SIGINT;
|
|
188
|
+
}
|
|
189
|
+
return code;
|
|
180
190
|
}
|
|
181
191
|
catch (err) {
|
|
182
|
-
if (
|
|
192
|
+
if (control?.cancellationSource === "sigint" &&
|
|
193
|
+
(control?.signal?.aborted || err?.name === "AbortError" || err?.message?.includes("SIGINT"))) {
|
|
183
194
|
return ExitCode.SIGINT;
|
|
184
195
|
}
|
|
185
196
|
this.writeErr(`Error: ${err?.message || err}`);
|
|
186
197
|
return ExitCode.FAILURE;
|
|
187
198
|
}
|
|
188
199
|
finally {
|
|
189
|
-
process.removeListener("SIGINT", sigintHandler);
|
|
190
200
|
if (ownsTarget) {
|
|
191
201
|
await target.close();
|
|
192
202
|
}
|
|
193
203
|
}
|
|
194
204
|
}
|
|
195
205
|
async handleList(target, subArgs) {
|
|
196
|
-
const isJson = subArgs.includes("--json")
|
|
197
|
-
const useEnvelope = subArgs.includes("--envelope");
|
|
206
|
+
const isJson = subArgs.includes("--json");
|
|
198
207
|
const noFallback = subArgs.includes("--no-fallback");
|
|
199
208
|
let intent;
|
|
200
209
|
const positionalPatterns = [];
|
|
@@ -222,10 +231,7 @@ export class StandaloneDispatcher {
|
|
|
222
231
|
}));
|
|
223
232
|
const filterRes = filterWithFallbackInfo(list, effectiveIntent, [(a) => a.id, (a) => a.description], !noFallback);
|
|
224
233
|
if (isJson) {
|
|
225
|
-
|
|
226
|
-
? { ok: true, data: filterRes.items }
|
|
227
|
-
: filterRes.items;
|
|
228
|
-
this.writeOut(JSON.stringify(payload, null, 2));
|
|
234
|
+
this.writeOut(JSON.stringify(filterRes.items, null, 2));
|
|
229
235
|
}
|
|
230
236
|
else {
|
|
231
237
|
let text = `Actions in ${this.options.packageId} (v${this.options.version}):\n\n`;
|
|
@@ -238,10 +244,20 @@ export class StandaloneDispatcher {
|
|
|
238
244
|
}
|
|
239
245
|
async handleDescribe(target, subArgs) {
|
|
240
246
|
const id = subArgs.find((a) => !a.startsWith("-"));
|
|
241
|
-
const isJson = subArgs.includes("--json")
|
|
242
|
-
const useEnvelope = subArgs.includes("--envelope");
|
|
247
|
+
const isJson = subArgs.includes("--json");
|
|
243
248
|
if (!id) {
|
|
244
|
-
|
|
249
|
+
if (isJson) {
|
|
250
|
+
this.writeOut(JSON.stringify({
|
|
251
|
+
ok: false,
|
|
252
|
+
error: {
|
|
253
|
+
code: "INVALID_ARGUMENT",
|
|
254
|
+
message: "Action ID is required for describe",
|
|
255
|
+
},
|
|
256
|
+
}, null, 2));
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
this.writeErr("Error: Action ID is required for describe");
|
|
260
|
+
}
|
|
245
261
|
return ExitCode.INVALID_ARGUMENT;
|
|
246
262
|
}
|
|
247
263
|
let action;
|
|
@@ -249,83 +265,223 @@ export class StandaloneDispatcher {
|
|
|
249
265
|
action = await target.describeAction(id);
|
|
250
266
|
}
|
|
251
267
|
catch {
|
|
252
|
-
|
|
268
|
+
if (isJson) {
|
|
269
|
+
this.writeOut(JSON.stringify({
|
|
270
|
+
ok: false,
|
|
271
|
+
error: {
|
|
272
|
+
code: "INVALID_ARGUMENT",
|
|
273
|
+
message: `Action '${id}' not found`,
|
|
274
|
+
},
|
|
275
|
+
}, null, 2));
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
this.writeErr(`Error: Action '${id}' not found`);
|
|
279
|
+
}
|
|
253
280
|
return ExitCode.INVALID_ARGUMENT;
|
|
254
281
|
}
|
|
255
|
-
const
|
|
256
|
-
id: action.id,
|
|
282
|
+
const payload = buildActionDescribePayload(action, {
|
|
257
283
|
packageId: this.options.packageId,
|
|
258
|
-
|
|
259
|
-
inputSchema: action.inputSchema,
|
|
260
|
-
outputSchema: action.outputSchema,
|
|
261
|
-
};
|
|
284
|
+
});
|
|
262
285
|
if (isJson) {
|
|
263
|
-
const payload = useEnvelope ? { ok: true, data: detail } : detail;
|
|
264
286
|
this.writeOut(JSON.stringify(payload, null, 2));
|
|
265
287
|
}
|
|
266
288
|
else {
|
|
267
|
-
|
|
268
|
-
if (action.description)
|
|
269
|
-
text += `Description: ${action.description}\n`;
|
|
270
|
-
if (action.inputSchema) {
|
|
271
|
-
text += `\nInput Schema:\n${JSON.stringify(action.inputSchema, null, 2)}\n`;
|
|
272
|
-
}
|
|
273
|
-
if (action.outputSchema) {
|
|
274
|
-
text += `\nOutput Schema:\n${JSON.stringify(action.outputSchema, null, 2)}\n`;
|
|
275
|
-
}
|
|
276
|
-
this.writeOut(text.trimEnd());
|
|
289
|
+
this.writeOut(formatActionDetail(payload));
|
|
277
290
|
}
|
|
278
291
|
return ExitCode.SUCCESS;
|
|
279
292
|
}
|
|
280
|
-
async handleRun(target, subArgs, signal) {
|
|
293
|
+
async handleRun(target, subArgs, actionArgs, signal) {
|
|
294
|
+
const isJson = subArgs.includes("--json");
|
|
281
295
|
const id = subArgs.find((a) => !a.startsWith("-"));
|
|
282
296
|
if (!id) {
|
|
283
|
-
|
|
297
|
+
if (isJson) {
|
|
298
|
+
this.writeOut(JSON.stringify({
|
|
299
|
+
ok: false,
|
|
300
|
+
error: {
|
|
301
|
+
code: "INVALID_ARGUMENT",
|
|
302
|
+
message: "Error: Action ID is required for run",
|
|
303
|
+
},
|
|
304
|
+
}, null, 2));
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
this.writeErr("Error: Action ID is required for run");
|
|
308
|
+
}
|
|
284
309
|
return ExitCode.INVALID_ARGUMENT;
|
|
285
310
|
}
|
|
286
|
-
let
|
|
311
|
+
let inputStr;
|
|
312
|
+
let inputFile;
|
|
287
313
|
let timeoutMs;
|
|
288
314
|
for (let i = 0; i < subArgs.length; i++) {
|
|
289
315
|
const arg = subArgs[i];
|
|
290
316
|
if (arg === "--timeout" && i + 1 < subArgs.length) {
|
|
291
|
-
timeoutMs = parseDuration(subArgs[++i]);
|
|
292
|
-
}
|
|
293
|
-
else if (arg.startsWith("--timeout=")) {
|
|
294
|
-
timeoutMs = parseDuration(arg.slice(10));
|
|
295
|
-
}
|
|
296
|
-
else if (arg === "--input" && i + 1 < subArgs.length) {
|
|
297
317
|
try {
|
|
298
|
-
|
|
318
|
+
timeoutMs = parseDuration(subArgs[++i]);
|
|
299
319
|
}
|
|
300
|
-
catch (
|
|
301
|
-
|
|
320
|
+
catch (err) {
|
|
321
|
+
if (isJson) {
|
|
322
|
+
this.writeOut(JSON.stringify({
|
|
323
|
+
ok: false,
|
|
324
|
+
error: {
|
|
325
|
+
code: "INVALID_ARGUMENT",
|
|
326
|
+
message: `Invalid timeout format: ${err?.message || err}`,
|
|
327
|
+
},
|
|
328
|
+
}, null, 2));
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
this.writeErr(`Error: Invalid timeout format: ${err?.message || err}`);
|
|
332
|
+
}
|
|
302
333
|
return ExitCode.INVALID_ARGUMENT;
|
|
303
334
|
}
|
|
304
335
|
}
|
|
305
|
-
else if (arg.startsWith("--
|
|
336
|
+
else if (arg.startsWith("--timeout=")) {
|
|
306
337
|
try {
|
|
307
|
-
|
|
338
|
+
timeoutMs = parseDuration(arg.slice(10));
|
|
308
339
|
}
|
|
309
|
-
catch (
|
|
310
|
-
|
|
340
|
+
catch (err) {
|
|
341
|
+
if (isJson) {
|
|
342
|
+
this.writeOut(JSON.stringify({
|
|
343
|
+
ok: false,
|
|
344
|
+
error: {
|
|
345
|
+
code: "INVALID_ARGUMENT",
|
|
346
|
+
message: `Invalid timeout format: ${err?.message || err}`,
|
|
347
|
+
},
|
|
348
|
+
}, null, 2));
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
this.writeErr(`Error: Invalid timeout format: ${err?.message || err}`);
|
|
352
|
+
}
|
|
311
353
|
return ExitCode.INVALID_ARGUMENT;
|
|
312
354
|
}
|
|
313
355
|
}
|
|
356
|
+
else if (arg === "--input" && i + 1 < subArgs.length) {
|
|
357
|
+
inputStr = subArgs[++i];
|
|
358
|
+
}
|
|
359
|
+
else if (arg.startsWith("--input=")) {
|
|
360
|
+
inputStr = arg.slice(8);
|
|
361
|
+
}
|
|
314
362
|
else if (arg === "--input-file" && i + 1 < subArgs.length) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
this.writeErr(`Error reading --input-file: ${e.message}`);
|
|
320
|
-
return ExitCode.INVALID_ARGUMENT;
|
|
321
|
-
}
|
|
363
|
+
inputFile = subArgs[++i];
|
|
364
|
+
}
|
|
365
|
+
else if (arg.startsWith("--input-file=")) {
|
|
366
|
+
inputFile = arg.slice(13);
|
|
322
367
|
}
|
|
323
368
|
}
|
|
369
|
+
let input;
|
|
370
|
+
try {
|
|
371
|
+
input = await resolveActionInput({
|
|
372
|
+
input: inputStr,
|
|
373
|
+
inputFile,
|
|
374
|
+
flatArgs: actionArgs.length > 0 ? actionArgs : undefined,
|
|
375
|
+
stdin: process.stdin,
|
|
376
|
+
});
|
|
377
|
+
const check = validateActionInputValue(input);
|
|
378
|
+
if (!check.valid) {
|
|
379
|
+
throw mapInputValidationFailure("cli-pre-target", check);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch (err) {
|
|
383
|
+
if (isJson) {
|
|
384
|
+
this.writeOut(JSON.stringify({
|
|
385
|
+
ok: false,
|
|
386
|
+
error: {
|
|
387
|
+
code: err?.code || "INVALID_ARGUMENT",
|
|
388
|
+
message: err?.message || String(err),
|
|
389
|
+
...(err?.details !== undefined ? { details: err.details } : {}),
|
|
390
|
+
},
|
|
391
|
+
}, null, 2));
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
this.writeErr(`Error: ${err?.message || String(err)}`);
|
|
395
|
+
}
|
|
396
|
+
return ExitCode.INVALID_ARGUMENT;
|
|
397
|
+
}
|
|
324
398
|
const result = await target.runAction(id, input, {
|
|
325
399
|
signal,
|
|
326
400
|
timeoutMs,
|
|
327
401
|
});
|
|
328
|
-
|
|
402
|
+
if (isJson) {
|
|
403
|
+
this.writeOut(JSON.stringify(result, null, 2));
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
if (result.ok) {
|
|
407
|
+
const data = result.data;
|
|
408
|
+
let rawText;
|
|
409
|
+
let metaInfo;
|
|
410
|
+
if (typeof data === "string") {
|
|
411
|
+
rawText = data;
|
|
412
|
+
}
|
|
413
|
+
else if (data !== null && typeof data === "object") {
|
|
414
|
+
if ("content" in data && data.content !== undefined) {
|
|
415
|
+
rawText =
|
|
416
|
+
typeof data.content === "object" && data.content !== null
|
|
417
|
+
? JSON.stringify(data.content, null, 2)
|
|
418
|
+
: String(data.content);
|
|
419
|
+
const { content, ...rest } = data;
|
|
420
|
+
if (Object.keys(rest).length > 0) {
|
|
421
|
+
metaInfo = rest;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
else if ("text" in data && typeof data.text === "string") {
|
|
425
|
+
rawText = data.text;
|
|
426
|
+
const { text, ...rest } = data;
|
|
427
|
+
if (Object.keys(rest).length > 0) {
|
|
428
|
+
metaInfo = rest;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else if ("message" in data && typeof data.message === "string") {
|
|
432
|
+
rawText = data.message;
|
|
433
|
+
const { message, ...rest } = data;
|
|
434
|
+
if (Object.keys(rest).length > 0) {
|
|
435
|
+
metaInfo = rest;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
rawText = JSON.stringify(data, null, 2);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
else if (data !== undefined) {
|
|
443
|
+
rawText = String(data);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
rawText = "";
|
|
447
|
+
}
|
|
448
|
+
if (metaInfo) {
|
|
449
|
+
const parts = [];
|
|
450
|
+
const title = metaInfo.path ? String(metaInfo.path) : id;
|
|
451
|
+
parts.push(title);
|
|
452
|
+
if (metaInfo.startLine !== undefined && metaInfo.endLine !== undefined) {
|
|
453
|
+
parts.push(`lines ${metaInfo.startLine}-${metaInfo.endLine}`);
|
|
454
|
+
}
|
|
455
|
+
else if (metaInfo.line !== undefined) {
|
|
456
|
+
parts.push(`line ${metaInfo.line}`);
|
|
457
|
+
}
|
|
458
|
+
if (metaInfo.hasMore !== undefined) {
|
|
459
|
+
parts.push(`hasMore: ${metaInfo.hasMore}`);
|
|
460
|
+
}
|
|
461
|
+
if (metaInfo.truncated) {
|
|
462
|
+
parts.push("truncated: true");
|
|
463
|
+
}
|
|
464
|
+
const handled = new Set(["path", "startLine", "endLine", "line", "hasMore", "truncated"]);
|
|
465
|
+
for (const [k, v] of Object.entries(metaInfo)) {
|
|
466
|
+
if (!handled.has(k) && v !== undefined) {
|
|
467
|
+
parts.push(`${k}: ${typeof v === "object" ? JSON.stringify(v) : v}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (parts.length > 0) {
|
|
471
|
+
this.writeErr(`[${parts.join(" | ")}]`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
this.writeOut(rawText);
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
this.writeErr(`Error [${result.error.code}]: ${result.error.message}`);
|
|
478
|
+
if (result.error.details) {
|
|
479
|
+
this.writeErr(typeof result.error.details === "string"
|
|
480
|
+
? result.error.details
|
|
481
|
+
: JSON.stringify(result.error.details, null, 2));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
329
485
|
return result.ok ? ExitCode.SUCCESS : ExitCode.FAILURE;
|
|
330
486
|
}
|
|
331
487
|
async handleConfig(target, subArgs) {
|
|
@@ -352,9 +508,15 @@ export class StandaloneDispatcher {
|
|
|
352
508
|
this.writeOut(item.configured ? JSON.stringify(item.value) : "undefined");
|
|
353
509
|
return ExitCode.SUCCESS;
|
|
354
510
|
}
|
|
355
|
-
catch {
|
|
356
|
-
|
|
357
|
-
|
|
511
|
+
catch (err) {
|
|
512
|
+
// 目标层能力缺失(如远端未开启管理接口)视为键未配置,输出 undefined
|
|
513
|
+
if (this.isTargetCapabilityError(err)) {
|
|
514
|
+
this.writeOut("undefined");
|
|
515
|
+
return ExitCode.SUCCESS;
|
|
516
|
+
}
|
|
517
|
+
// 内部异常严禁吞没:透传 stderr 并以 FAILURE 退出码暴露
|
|
518
|
+
this.writeErr(`Error reading config '${key}': ${err?.message || String(err)}`);
|
|
519
|
+
return ExitCode.FAILURE;
|
|
358
520
|
}
|
|
359
521
|
}
|
|
360
522
|
if (sub === "set") {
|
|
@@ -425,10 +587,10 @@ export class StandaloneDispatcher {
|
|
|
425
587
|
}
|
|
426
588
|
let ns = namespace;
|
|
427
589
|
let actualKey = key;
|
|
428
|
-
if (ns === undefined
|
|
429
|
-
const
|
|
430
|
-
ns =
|
|
431
|
-
actualKey = key
|
|
590
|
+
if (ns === undefined) {
|
|
591
|
+
const decoded = this.decodeStateKeyArg(key);
|
|
592
|
+
ns = decoded.namespace || undefined;
|
|
593
|
+
actualKey = decoded.key;
|
|
432
594
|
}
|
|
433
595
|
const val = await target.getState(this.options.packageId, "", actualKey, { namespace: ns });
|
|
434
596
|
if (isJson) {
|
|
@@ -448,10 +610,10 @@ export class StandaloneDispatcher {
|
|
|
448
610
|
}
|
|
449
611
|
let ns = namespace || "";
|
|
450
612
|
let actualKey = key;
|
|
451
|
-
if (namespace === undefined
|
|
452
|
-
const
|
|
453
|
-
ns =
|
|
454
|
-
actualKey = key
|
|
613
|
+
if (namespace === undefined) {
|
|
614
|
+
const decoded = this.decodeStateKeyArg(key);
|
|
615
|
+
ns = decoded.namespace;
|
|
616
|
+
actualKey = decoded.key;
|
|
455
617
|
}
|
|
456
618
|
let parsed = rawVal;
|
|
457
619
|
try {
|
|
@@ -501,6 +663,30 @@ export class StandaloneDispatcher {
|
|
|
501
663
|
this.writeErr(`Unknown state subcommand: '${sub}'`);
|
|
502
664
|
return ExitCode.INVALID_ARGUMENT;
|
|
503
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* 判定配置读取异常是否为目标层能力缺失(键未配置语义)而非内部故障。
|
|
668
|
+
*/
|
|
669
|
+
isTargetCapabilityError(err) {
|
|
670
|
+
const code = String(err?.code || "");
|
|
671
|
+
const msg = String(err?.message || "");
|
|
672
|
+
return (code === "TARGET_CAPABILITY_UNAVAILABLE" ||
|
|
673
|
+
code === "CAPABILITY_UNAVAILABLE" ||
|
|
674
|
+
msg.includes("TARGET_CAPABILITY_UNAVAILABLE") ||
|
|
675
|
+
msg.includes("Management APIs are not enabled"));
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* 解析携带命名空间前缀的状态键参数:委托 sdk 单一事实源 decodeStateKey。
|
|
679
|
+
*
|
|
680
|
+
* 解析失败(如存在多个未转义冒号的歧义键)时按纯键处理,交由存储层兜底。
|
|
681
|
+
*/
|
|
682
|
+
decodeStateKeyArg(key) {
|
|
683
|
+
try {
|
|
684
|
+
return decodeStateKey(key);
|
|
685
|
+
}
|
|
686
|
+
catch {
|
|
687
|
+
return { namespace: "", key };
|
|
688
|
+
}
|
|
689
|
+
}
|
|
504
690
|
printHelp() {
|
|
505
691
|
this.writeOut(`${this.options.packageId} (v${this.options.version})`);
|
|
506
692
|
if (this.options.description)
|
|
@@ -508,7 +694,8 @@ export class StandaloneDispatcher {
|
|
|
508
694
|
this.writeOut("Usage:");
|
|
509
695
|
this.writeOut(" <cmd> list [--json] List available actions");
|
|
510
696
|
this.writeOut(" <cmd> describe <id> [--json] Show action details and schemas");
|
|
511
|
-
this.writeOut(" <cmd> run <id> [--input '<json>'] Execute action
|
|
697
|
+
this.writeOut(" <cmd> run <id> [--input '<json>'] Execute action (raw text output by default)");
|
|
698
|
+
this.writeOut(" <cmd> run <id> [--json] Output standard execution result in JSON format");
|
|
512
699
|
this.writeOut(" <cmd> config list/get/set/delete Manage package configuration");
|
|
513
700
|
this.writeOut(" <cmd> state list/get/set/delete Manage shared state store");
|
|
514
701
|
this.writeOut("\nGlobal options:");
|
|
@@ -534,3 +721,34 @@ export class StandaloneRuntime {
|
|
|
534
721
|
export function createStandaloneRuntime(options) {
|
|
535
722
|
return new StandaloneRuntime(options);
|
|
536
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* 独立二进制进程入口适配器。
|
|
726
|
+
* 仅在命令行可执行入口调用,负责全局 SIGINT 监听与退出状态码写入。
|
|
727
|
+
*/
|
|
728
|
+
export async function runStandaloneProcess(argv, options) {
|
|
729
|
+
const controller = new AbortController();
|
|
730
|
+
const control = {
|
|
731
|
+
signal: controller.signal,
|
|
732
|
+
};
|
|
733
|
+
let sigintCount = 0;
|
|
734
|
+
const sigintHandler = () => {
|
|
735
|
+
sigintCount++;
|
|
736
|
+
if (sigintCount === 1) {
|
|
737
|
+
control.cancellationSource = "sigint";
|
|
738
|
+
controller.abort(new Error("Interrupted by SIGINT"));
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
process.exitCode = 130;
|
|
742
|
+
process.exit(130);
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
process.on("SIGINT", sigintHandler);
|
|
746
|
+
try {
|
|
747
|
+
const dispatcher = new StandaloneDispatcher(options);
|
|
748
|
+
const exitCode = await dispatcher.dispatch(argv, control);
|
|
749
|
+
process.exitCode = exitCode;
|
|
750
|
+
}
|
|
751
|
+
finally {
|
|
752
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./validator.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./validator.js";
|
|
@@ -10,8 +10,18 @@ export interface ValidationResult {
|
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* 递归检查数据中是否包含危险的原型污染属性键名(__proto__、constructor、prototype)。
|
|
13
|
+
* 内部委托 validateActionInputValue 执行迭代遍历,确保环路安全、有向无环图安全且无调用栈溢出风险。
|
|
13
14
|
*/
|
|
14
15
|
export declare function hasDangerousKeys(data: unknown): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 内部纯 Schema 校验器。
|
|
18
|
+
* 仅处理布尔 Schema 与 Ajv 规则校验,严禁执行 dangerous-key 遍历。
|
|
19
|
+
*
|
|
20
|
+
* @param schema 期望匹配的 JSON Schema 对象
|
|
21
|
+
* @param data 待校验的原始数据
|
|
22
|
+
* @returns 包含 valid 状态与错误信息列表的 ValidationResult
|
|
23
|
+
*/
|
|
24
|
+
export declare function validateSchemaOnly(schema: JsonSchema | undefined, data: unknown): ValidationResult;
|
|
15
25
|
/**
|
|
16
26
|
* 校验指定数据是否符合给定的 JSON Schema 契约。
|
|
17
27
|
*
|