@actiondock/cli 2.5.1 → 2.6.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 +44 -3
- package/dist/commands/action/create.js +2 -2
- package/dist/commands/add.js +5 -5
- package/dist/commands/build.js +4 -5
- package/dist/commands/config/delete.js +2 -2
- package/dist/commands/config/env.js +6 -15
- package/dist/commands/config/get.js +8 -39
- package/dist/commands/config/list.js +8 -55
- package/dist/commands/config/schema.js +4 -6
- package/dist/commands/config/set.js +2 -2
- package/dist/commands/describe.js +2 -4
- package/dist/commands/doctor.js +3 -10
- package/dist/commands/export.js +4 -7
- package/dist/commands/generate.js +2 -4
- package/dist/commands/index.js +2 -5
- package/dist/commands/info.js +7 -15
- package/dist/commands/list.js +3 -5
- package/dist/commands/mcp.js +10 -3
- package/dist/commands/pack.js +4 -5
- package/dist/commands/playbook.js +14 -36
- package/dist/commands/profile.js +4 -11
- package/dist/commands/remove.js +13 -9
- package/dist/commands/run.d.ts +8 -0
- package/dist/commands/run.js +118 -33
- package/dist/commands/runs.js +10 -25
- package/dist/commands/serve.js +9 -1
- package/dist/commands/state-list.js +2 -5
- package/dist/commands/state.js +29 -16
- package/dist/commands/test.js +6 -2
- package/dist/commands/validate.js +3 -5
- package/dist/errors.d.ts +14 -1
- package/dist/errors.js +20 -2
- package/dist/index.js +1 -2
- package/dist/renderer.d.ts +0 -2
- package/dist/renderer.js +3 -15
- package/dist/services/config-merge.d.ts +41 -0
- package/dist/services/config-merge.js +93 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/types.d.ts +0 -2
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/input.d.ts +46 -0
- package/dist/utils/input.js +89 -0
- package/dist/utils/spawn.d.ts +13 -0
- package/dist/utils/spawn.js +31 -0
- package/dist/utils/target.d.ts +21 -0
- package/dist/utils/target.js +29 -14
- package/dist/utils/utils.d.ts +2 -3
- package/dist/utils/utils.js +6 -6
- package/package.json +6 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { findProjectRoot, writeActionTypes } from "@actiondock/core";
|
|
2
|
-
import {
|
|
2
|
+
import { ExecutionError, notInProjectError } from "../errors.js";
|
|
3
3
|
import { renderResult } from "../renderer.js";
|
|
4
4
|
import { getEffectiveOptions } from "../utils/index.js";
|
|
5
5
|
/**
|
|
@@ -16,12 +16,11 @@ export function registerGenerateCommands(program, context) {
|
|
|
16
16
|
.command("types")
|
|
17
17
|
.description("Generate .actiondock/generated/actions.d.ts from actiondock.json schema")
|
|
18
18
|
.option("--json", "Output as JSON")
|
|
19
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
20
19
|
.action(async (rawOptions, cmd) => {
|
|
21
20
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
22
21
|
const root = findProjectRoot();
|
|
23
22
|
if (!root) {
|
|
24
|
-
throw
|
|
23
|
+
throw notInProjectError();
|
|
25
24
|
}
|
|
26
25
|
try {
|
|
27
26
|
const { filePath, digest } = writeActionTypes(root);
|
|
@@ -32,7 +31,6 @@ export function registerGenerateCommands(program, context) {
|
|
|
32
31
|
};
|
|
33
32
|
renderResult(result, {
|
|
34
33
|
json: options.json,
|
|
35
|
-
envelope: options.envelope,
|
|
36
34
|
humanFormatter: () => `[OK] Generated action types at ${filePath}\nManifest digest: ${digest}`,
|
|
37
35
|
context,
|
|
38
36
|
});
|
package/dist/commands/index.js
CHANGED
|
@@ -45,8 +45,8 @@ export const CLI_VERSION = readCliVersion();
|
|
|
45
45
|
/**
|
|
46
46
|
* 递归注入全局通用选项。
|
|
47
47
|
*
|
|
48
|
-
* 输出纪律契约:--json
|
|
49
|
-
*
|
|
48
|
+
* 输出纪律契约:--json 对所有命令统一注入;已实现 renderResult
|
|
49
|
+
* 数据输出的命令会消费它,未实现机器输出的命令(init、link、test、new、
|
|
50
50
|
* serve、mcp 等)将其作为无操作标志忽略,不影响人类输出行为。
|
|
51
51
|
* 错误信封由顶层 main 的 renderError 统一兜底,与命令实现解耦。
|
|
52
52
|
*/
|
|
@@ -55,9 +55,6 @@ function applyCommonOptions(cmd) {
|
|
|
55
55
|
if (!hasOpt("json")) {
|
|
56
56
|
cmd.option("--json", "Output as JSON");
|
|
57
57
|
}
|
|
58
|
-
if (!hasOpt("envelope")) {
|
|
59
|
-
cmd.option("--envelope", "Wrap JSON output in standard envelope");
|
|
60
|
-
}
|
|
61
58
|
if (!hasOpt("dataDir") && !hasOpt("data-dir")) {
|
|
62
59
|
cmd.option("--data-dir <path>", "Custom database storage directory");
|
|
63
60
|
}
|
package/dist/commands/info.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { fetchRemoteInfo, filterWithFallbackInfo, getRegistryStatus, resolvePackageRoot,
|
|
2
|
-
import {
|
|
1
|
+
import { fetchRemoteInfo, filterWithFallbackInfo, getRegistryStatus, resolvePackageRoot, } from "@actiondock/core";
|
|
2
|
+
import { ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, packageNotFoundError } from "../errors.js";
|
|
3
3
|
import { projectDetailToJson, renderAggregatedPackages, renderProjectDetail, renderRegistryTree, renderResult, } from "../renderer.js";
|
|
4
4
|
import { getProjectDetailInfo, scanLocalAggregatedPackages } from "../services/index.js";
|
|
5
|
-
import { applyTargetOptions, getEffectiveOptions, resolveFallbackStrategy, resolveIntent } from "../utils/index.js";
|
|
5
|
+
import { applyTargetOptions, getEffectiveOptions, resolveFallbackStrategy, resolveIntent, resolveTargetFromOptions, } from "../utils/index.js";
|
|
6
6
|
/**
|
|
7
7
|
* 将远端信息响应归一为本地工程详情视图。
|
|
8
8
|
*/
|
|
@@ -29,7 +29,6 @@ function remoteInfoToDetail(remoteInfo) {
|
|
|
29
29
|
function renderProjectDetailOutput(detail, options) {
|
|
30
30
|
renderResult(projectDetailToJson(detail), {
|
|
31
31
|
json: options.json,
|
|
32
|
-
envelope: options.envelope,
|
|
33
32
|
humanFormatter: () => renderProjectDetail(detail),
|
|
34
33
|
context: options.context,
|
|
35
34
|
});
|
|
@@ -51,20 +50,14 @@ export function registerInfoCommand(program, context) {
|
|
|
51
50
|
.option("--fallback", "Enable fallback to full list when no items match intent")
|
|
52
51
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
53
52
|
.option("--json", "Output information as JSON")
|
|
54
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
55
53
|
.option("--data-dir <path>", "Custom database storage directory")
|
|
56
54
|
.action(async (patterns = [], rawOptions, cmd) => {
|
|
57
55
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
58
56
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
59
57
|
const { isMachine, shouldFallback } = resolveFallbackStrategy(options);
|
|
60
|
-
const outOpts = { json: options.json,
|
|
58
|
+
const outOpts = { json: options.json, context };
|
|
61
59
|
// 远端服务目标分支
|
|
62
|
-
const target =
|
|
63
|
-
profile: options.profile,
|
|
64
|
-
server: options.server,
|
|
65
|
-
token: options.token,
|
|
66
|
-
insecure: options.insecure,
|
|
67
|
-
}, context?.customHome);
|
|
60
|
+
const target = resolveTargetFromOptions(options, context);
|
|
68
61
|
if (target.type === "remote") {
|
|
69
62
|
const remoteInfo = await fetchRemoteInfo(target.serverUrl, target.token, {
|
|
70
63
|
intent: effectiveIntent,
|
|
@@ -104,7 +97,6 @@ export function registerInfoCommand(program, context) {
|
|
|
104
97
|
const status = getRegistryStatus(context?.customHome);
|
|
105
98
|
renderResult(status, {
|
|
106
99
|
json: options.json,
|
|
107
|
-
envelope: options.envelope,
|
|
108
100
|
humanFormatter: () => renderRegistryTree(status),
|
|
109
101
|
context,
|
|
110
102
|
});
|
|
@@ -118,7 +110,7 @@ export function registerInfoCommand(program, context) {
|
|
|
118
110
|
renderProjectDetailOutput(detail, outOpts);
|
|
119
111
|
return;
|
|
120
112
|
}
|
|
121
|
-
throw
|
|
113
|
+
throw packageNotFoundError(options.package);
|
|
122
114
|
}
|
|
123
115
|
// 扫描本地候选包(当前工程根目录 + 全局已链接包)
|
|
124
116
|
const { currentRoot, aggregated } = scanLocalAggregatedPackages(context);
|
|
@@ -132,7 +124,7 @@ export function registerInfoCommand(program, context) {
|
|
|
132
124
|
if (aggregated.length === 0) {
|
|
133
125
|
renderResult({ linkedPackages: [] }, {
|
|
134
126
|
...outOpts,
|
|
135
|
-
humanFormatter: () =>
|
|
127
|
+
humanFormatter: () => `${NO_PROJECT_NO_LINKED_MESSAGE}\nRun 'ad link' inside an Action package to register it.`,
|
|
136
128
|
});
|
|
137
129
|
return;
|
|
138
130
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { filterWithFallbackInfo } from "@actiondock/core";
|
|
2
|
-
import {
|
|
2
|
+
import { packageNotFoundError } from "../errors.js";
|
|
3
3
|
import { renderActionList, renderResult } from "../renderer.js";
|
|
4
4
|
import { applyTargetOptions, getEffectiveOptions, resolveFallbackStrategy, resolveIntent, resolveLocalPackageRoot, withTarget, } from "../utils/index.js";
|
|
5
5
|
/**
|
|
@@ -18,7 +18,6 @@ export function attachListCommand(parent, context) {
|
|
|
18
18
|
.option("--fallback", "Enable fallback to full list when no items match intent")
|
|
19
19
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
20
20
|
.option("--json", "Output as JSON")
|
|
21
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
22
21
|
.option("--data-dir <path>", "Custom database storage directory")
|
|
23
22
|
.action(async (patterns = [], rawOptions, cmd) => {
|
|
24
23
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
@@ -27,7 +26,7 @@ export function attachListCommand(parent, context) {
|
|
|
27
26
|
// 目标拓扑解析(仅 local 分支需要包寻址)
|
|
28
27
|
const targetPackageRoot = resolveLocalPackageRoot(options.package);
|
|
29
28
|
if (options.package && !targetPackageRoot) {
|
|
30
|
-
throw
|
|
29
|
+
throw packageNotFoundError(options.package);
|
|
31
30
|
}
|
|
32
31
|
// 通过 Target 门面统一获取 Action 列表
|
|
33
32
|
await withTarget(options, context, async (target, resolved) => {
|
|
@@ -56,7 +55,7 @@ export function attachListCommand(parent, context) {
|
|
|
56
55
|
});
|
|
57
56
|
const filterRes = filterWithFallbackInfo(rawList, effectiveIntent, [(a) => a.id, (a) => a.description], shouldFallback);
|
|
58
57
|
if (filterRes.isFallback && isMachine) {
|
|
59
|
-
renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0 }, { json: options.json,
|
|
58
|
+
renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0 }, { json: options.json, context });
|
|
60
59
|
return;
|
|
61
60
|
}
|
|
62
61
|
const title = resolved.type === "remote"
|
|
@@ -66,7 +65,6 @@ export function attachListCommand(parent, context) {
|
|
|
66
65
|
: "Available Actions";
|
|
67
66
|
renderResult(filterRes.items, {
|
|
68
67
|
json: options.json,
|
|
69
|
-
envelope: options.envelope,
|
|
70
68
|
humanFormatter: () => renderActionList(filterRes.items, title, filterRes.isFallback, effectiveIntent),
|
|
71
69
|
context,
|
|
72
70
|
});
|
package/dist/commands/mcp.js
CHANGED
|
@@ -2,7 +2,7 @@ import { resolve } from "node:path";
|
|
|
2
2
|
import { findProjectRoot, formatHostForUrl, loadProjectConfig, parseDuration } from "@actiondock/core";
|
|
3
3
|
import { createNodePlatform } from "@actiondock/runtime-node";
|
|
4
4
|
import { ArgumentError, ExecutionError } from "../errors.js";
|
|
5
|
-
import { writeStdout } from "../renderer.js";
|
|
5
|
+
import { writeStderr, writeStdout } from "../renderer.js";
|
|
6
6
|
import { getEffectiveOptions, parseByteSize, parseListOption } from "../utils/index.js";
|
|
7
7
|
/**
|
|
8
8
|
* 注册 mcp 命令(STDIO 与 HTTP 传输服务模式)。
|
|
@@ -18,7 +18,6 @@ export function registerMcpCommands(program, context) {
|
|
|
18
18
|
.option("--package <package-id>", "Specific linked package ID(s) to serve (can be specified multiple times or comma-separated)", parseListOption, [])
|
|
19
19
|
.option("--all", "Serve all linked packages from global registry")
|
|
20
20
|
.option("--timeout <duration>", "Execution timeout (e.g. 30s, 5m, 500ms)")
|
|
21
|
-
.option("--allow-insecure-http", "Allow insecure HTTP connections with auth token (INSECURE)")
|
|
22
21
|
.option("--data-dir <path>", "Custom database storage directory")
|
|
23
22
|
.action(async (rawOptions, cmd) => {
|
|
24
23
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
@@ -174,7 +173,15 @@ export function registerMcpCommands(program, context) {
|
|
|
174
173
|
writeStdout(`Press Ctrl+C to terminate.\n`, context);
|
|
175
174
|
const stopSignalHandler = () => {
|
|
176
175
|
writeStdout("\nStopping MCP HTTP server...", context);
|
|
177
|
-
server.stop()
|
|
176
|
+
Promise.resolve(server.stop())
|
|
177
|
+
.catch((err) => {
|
|
178
|
+
// 停止失败必须可见:写入 stderr 一行并标记失败退出码
|
|
179
|
+
writeStderr(`[ERROR] Failed to stop MCP HTTP server gracefully: ${err instanceof Error ? err.message : String(err)}`, context);
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
})
|
|
182
|
+
.finally(() => {
|
|
183
|
+
process.exit(typeof process.exitCode === "number" ? process.exitCode : 0);
|
|
184
|
+
});
|
|
178
185
|
};
|
|
179
186
|
process.once("SIGINT", stopSignalHandler);
|
|
180
187
|
process.once("SIGTERM", stopSignalHandler);
|
package/dist/commands/pack.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { findProjectRoot, resolvePackageRoot } from "@actiondock/core";
|
|
2
|
-
import { ExecutionError } from "../errors.js";
|
|
2
|
+
import { ExecutionError, notInProjectError, packageNotFoundError } from "../errors.js";
|
|
3
3
|
import { renderResult, writeStdout } from "../renderer.js";
|
|
4
4
|
import { getEffectiveOptions } from "../utils/index.js";
|
|
5
5
|
export function registerPackCommand(program, context) {
|
|
@@ -17,11 +17,11 @@ export function registerPackCommand(program, context) {
|
|
|
17
17
|
: findProjectRoot();
|
|
18
18
|
if (!root) {
|
|
19
19
|
if (options.package) {
|
|
20
|
-
throw
|
|
20
|
+
throw packageNotFoundError(options.package);
|
|
21
21
|
}
|
|
22
|
-
throw
|
|
22
|
+
throw notInProjectError("Please specify -P, --package <id> or cd into a project directory.");
|
|
23
23
|
}
|
|
24
|
-
const isMachine = Boolean(options.json
|
|
24
|
+
const isMachine = Boolean(options.json);
|
|
25
25
|
if (!isMachine && !options.dryRun) {
|
|
26
26
|
writeStdout("Packing Action package...");
|
|
27
27
|
}
|
|
@@ -39,7 +39,6 @@ export function registerPackCommand(program, context) {
|
|
|
39
39
|
}
|
|
40
40
|
renderResult(result, {
|
|
41
41
|
json: isMachine,
|
|
42
|
-
envelope: options.envelope,
|
|
43
42
|
humanFormatter: () => {
|
|
44
43
|
const lines = [];
|
|
45
44
|
if (options.dryRun) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
3
|
-
import { assertPathWithinRoot, fetchRemotePlaybooks, fetchRemotePlaybookShow, filterWithFallbackInfo, findProjectRoot, listLinkedPackages, loadActions, loadManifest, loadPlaybooks, loadProjectConfig, resolveActionProject, resolvePackageRoot, resolvePlaybookProject,
|
|
4
|
-
import { ArgumentError, CliError, ExecutionError } from "../errors.js";
|
|
3
|
+
import { assertPathWithinRoot, fetchRemotePlaybooks, fetchRemotePlaybookShow, filterWithFallbackInfo, findProjectRoot, listLinkedPackages, loadActions, loadManifest, loadPlaybooks, loadProjectConfig, resolveActionProject, resolvePackageRoot, resolvePlaybookProject, saveManifest, } from "@actiondock/core";
|
|
4
|
+
import { ArgumentError, CliError, ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, notInProjectError, packageNotFoundError, } from "../errors.js";
|
|
5
5
|
import { renderPlaybookDetail, renderPlaybookList, renderResult, writeStdout, } from "../renderer.js";
|
|
6
|
-
import { applyTargetOptions, getEffectiveOptions, resolveIntent } from "../utils/index.js";
|
|
6
|
+
import { applyTargetOptions, getEffectiveOptions, resolveIntent, resolveTargetFromOptions, } from "../utils/index.js";
|
|
7
7
|
/**
|
|
8
8
|
* 注册 Playbook 命令集合(list, show, validate, create)。
|
|
9
9
|
*
|
|
@@ -22,20 +22,13 @@ export function registerPlaybookCommands(program, context) {
|
|
|
22
22
|
.option("-P, --package <id>", "Target package ID or path"))
|
|
23
23
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
24
24
|
.option("--json", "Output as JSON")
|
|
25
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
26
25
|
.action(async (patterns = [], rawOptions, cmd) => {
|
|
27
26
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
28
27
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
29
28
|
const shouldFallback = options.fallback !== false;
|
|
30
|
-
const isMachine = Boolean(options.json
|
|
29
|
+
const isMachine = Boolean(options.json);
|
|
31
30
|
// 1. 远端服务分支
|
|
32
|
-
const target =
|
|
33
|
-
profile: options.profile,
|
|
34
|
-
server: options.server,
|
|
35
|
-
token: options.token,
|
|
36
|
-
insecure: options.insecure,
|
|
37
|
-
allowInsecureHttp: options.allowInsecureHttp,
|
|
38
|
-
}, context?.customHome);
|
|
31
|
+
const target = resolveTargetFromOptions(options, context);
|
|
39
32
|
if (target.type === "remote") {
|
|
40
33
|
const remotePbs = await fetchRemotePlaybooks(target.serverUrl, target.token, {
|
|
41
34
|
intent: effectiveIntent,
|
|
@@ -45,7 +38,6 @@ export function registerPlaybookCommands(program, context) {
|
|
|
45
38
|
});
|
|
46
39
|
renderResult(remotePbs, {
|
|
47
40
|
json: options.json,
|
|
48
|
-
envelope: options.envelope,
|
|
49
41
|
humanFormatter: () => renderPlaybookList(remotePbs, `Playbooks on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""}`, false, effectiveIntent),
|
|
50
42
|
context,
|
|
51
43
|
});
|
|
@@ -56,7 +48,7 @@ export function registerPlaybookCommands(program, context) {
|
|
|
56
48
|
if (options.package) {
|
|
57
49
|
targetRoot = resolvePackageRoot(options.package);
|
|
58
50
|
if (!targetRoot) {
|
|
59
|
-
throw
|
|
51
|
+
throw packageNotFoundError(options.package);
|
|
60
52
|
}
|
|
61
53
|
}
|
|
62
54
|
else {
|
|
@@ -72,12 +64,11 @@ export function registerPlaybookCommands(program, context) {
|
|
|
72
64
|
}));
|
|
73
65
|
const filterRes = filterWithFallbackInfo(rawList, effectiveIntent, [(p) => p.id, (p) => p.description], shouldFallback);
|
|
74
66
|
if (filterRes.isFallback && isMachine) {
|
|
75
|
-
renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0 }, { json: options.json,
|
|
67
|
+
renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0 }, { json: options.json, context });
|
|
76
68
|
return;
|
|
77
69
|
}
|
|
78
70
|
renderResult(filterRes.items, {
|
|
79
71
|
json: options.json,
|
|
80
|
-
envelope: options.envelope,
|
|
81
72
|
humanFormatter: () => renderPlaybookList(filterRes.items, `Playbooks in ${config.id} (${targetRoot})`, filterRes.isFallback, effectiveIntent),
|
|
82
73
|
context,
|
|
83
74
|
});
|
|
@@ -88,8 +79,7 @@ export function registerPlaybookCommands(program, context) {
|
|
|
88
79
|
if (linkedList.length === 0) {
|
|
89
80
|
renderResult([], {
|
|
90
81
|
json: options.json,
|
|
91
|
-
|
|
92
|
-
humanFormatter: () => "No ActionDock project in current directory, and no packages linked.",
|
|
82
|
+
humanFormatter: () => NO_PROJECT_NO_LINKED_MESSAGE,
|
|
93
83
|
context,
|
|
94
84
|
});
|
|
95
85
|
return;
|
|
@@ -143,12 +133,11 @@ export function registerPlaybookCommands(program, context) {
|
|
|
143
133
|
}
|
|
144
134
|
}
|
|
145
135
|
if (isFallback && isMachine) {
|
|
146
|
-
renderResult({ packages: filteredPackages, isFallback: true, matchedCount: 0 }, { json: options.json,
|
|
136
|
+
renderResult({ packages: filteredPackages, isFallback: true, matchedCount: 0 }, { json: options.json, context });
|
|
147
137
|
return;
|
|
148
138
|
}
|
|
149
139
|
renderResult(filteredPackages, {
|
|
150
140
|
json: options.json,
|
|
151
|
-
envelope: options.envelope,
|
|
152
141
|
humanFormatter: () => {
|
|
153
142
|
const lines = ["Linked Playbooks:\n"];
|
|
154
143
|
for (const pkg of filteredPackages) {
|
|
@@ -168,20 +157,13 @@ export function registerPlaybookCommands(program, context) {
|
|
|
168
157
|
.description("Show playbook content and metadata")
|
|
169
158
|
.option("-P, --package <id>", "Target package ID or path"))
|
|
170
159
|
.option("--json", "Output as JSON")
|
|
171
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
172
160
|
.action(async (id, rawOptions, cmd) => {
|
|
173
161
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
174
162
|
if (!id) {
|
|
175
163
|
throw new ArgumentError("Playbook ID is required for show");
|
|
176
164
|
}
|
|
177
165
|
// 1. 远端服务分支
|
|
178
|
-
const target =
|
|
179
|
-
profile: options.profile,
|
|
180
|
-
server: options.server,
|
|
181
|
-
token: options.token,
|
|
182
|
-
insecure: options.insecure,
|
|
183
|
-
allowInsecureHttp: options.allowInsecureHttp,
|
|
184
|
-
}, context?.customHome);
|
|
166
|
+
const target = resolveTargetFromOptions(options, context);
|
|
185
167
|
if (target.type === "remote") {
|
|
186
168
|
const detail = await fetchRemotePlaybookShow(target.serverUrl, id, target.token, {
|
|
187
169
|
allowInsecureHttp: Boolean(options.allowInsecureHttp),
|
|
@@ -189,7 +171,6 @@ export function registerPlaybookCommands(program, context) {
|
|
|
189
171
|
});
|
|
190
172
|
renderResult(detail, {
|
|
191
173
|
json: options.json,
|
|
192
|
-
envelope: options.envelope,
|
|
193
174
|
humanFormatter: () => renderPlaybookDetail(detail),
|
|
194
175
|
context,
|
|
195
176
|
});
|
|
@@ -200,7 +181,7 @@ export function registerPlaybookCommands(program, context) {
|
|
|
200
181
|
if (options.package && !id.includes("/")) {
|
|
201
182
|
const pkgRoot = resolvePackageRoot(options.package);
|
|
202
183
|
if (!pkgRoot) {
|
|
203
|
-
throw
|
|
184
|
+
throw packageNotFoundError(options.package);
|
|
204
185
|
}
|
|
205
186
|
showTarget = `${options.package}/${id}`;
|
|
206
187
|
}
|
|
@@ -222,7 +203,6 @@ export function registerPlaybookCommands(program, context) {
|
|
|
222
203
|
};
|
|
223
204
|
renderResult(detail, {
|
|
224
205
|
json: options.json,
|
|
225
|
-
envelope: options.envelope,
|
|
226
206
|
humanFormatter: () => renderPlaybookDetail(detail),
|
|
227
207
|
context,
|
|
228
208
|
});
|
|
@@ -233,14 +213,13 @@ export function registerPlaybookCommands(program, context) {
|
|
|
233
213
|
.description("Validate playbook format and action references (in current project or linked packages)")
|
|
234
214
|
.option("-P, --package <id>", "Target package ID or path")
|
|
235
215
|
.option("--json", "Output as JSON")
|
|
236
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
237
216
|
.action(async (id, rawOptions, cmd) => {
|
|
238
217
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
239
218
|
let root = null;
|
|
240
219
|
if (options.package) {
|
|
241
220
|
root = resolvePackageRoot(options.package);
|
|
242
221
|
if (!root) {
|
|
243
|
-
throw
|
|
222
|
+
throw packageNotFoundError(options.package);
|
|
244
223
|
}
|
|
245
224
|
}
|
|
246
225
|
else {
|
|
@@ -278,7 +257,7 @@ export function registerPlaybookCommands(program, context) {
|
|
|
278
257
|
else {
|
|
279
258
|
const linkedList = listLinkedPackages(context?.customHome);
|
|
280
259
|
if (linkedList.length === 0) {
|
|
281
|
-
throw new ArgumentError(
|
|
260
|
+
throw new ArgumentError(`${NO_PROJECT_NO_LINKED_MESSAGE} Please run 'ad link' inside a package first.`);
|
|
282
261
|
}
|
|
283
262
|
for (const pkg of linkedList) {
|
|
284
263
|
if (!existsSync(pkg.path))
|
|
@@ -342,7 +321,6 @@ export function registerPlaybookCommands(program, context) {
|
|
|
342
321
|
const allValid = results.every((r) => r.valid);
|
|
343
322
|
renderResult({ valid: allValid, results }, {
|
|
344
323
|
json: options.json,
|
|
345
|
-
envelope: options.envelope,
|
|
346
324
|
humanFormatter: () => {
|
|
347
325
|
const lines = [];
|
|
348
326
|
for (const r of results) {
|
|
@@ -378,7 +356,7 @@ export function registerPlaybookCommands(program, context) {
|
|
|
378
356
|
export function handlePlaybookCreate(id, options, context) {
|
|
379
357
|
const root = findProjectRoot();
|
|
380
358
|
if (!root) {
|
|
381
|
-
throw
|
|
359
|
+
throw notInProjectError();
|
|
382
360
|
}
|
|
383
361
|
try {
|
|
384
362
|
const config = loadProjectConfig(root);
|
package/dist/commands/profile.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { addProfile, checkRemoteHealth, filterWithFallbackInfo, getProfile, listProfiles, loadProfiles, maskSecretValue, removeProfile, resolveProfileToken, resolveTarget, toSnakeUpperCase, updateProfile, useProfile, } from "@actiondock/core";
|
|
2
2
|
import { ArgumentError, CliError, ExecutionError, } from "../errors.js";
|
|
3
|
-
import { renderResult, writeStdout } from "../renderer.js";
|
|
3
|
+
import { renderResult, writeStderr, writeStdout } from "../renderer.js";
|
|
4
4
|
import { getEffectiveOptions, resolveFallbackStrategy, resolveIntent } from "../utils/index.js";
|
|
5
5
|
export function registerProfileCommands(program) {
|
|
6
6
|
const profileCmd = program
|
|
@@ -15,7 +15,6 @@ export function registerProfileCommands(program) {
|
|
|
15
15
|
.option("--fallback", "Enable fallback to full list when no items match intent")
|
|
16
16
|
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
17
17
|
.option("--json", "Output as JSON")
|
|
18
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
19
18
|
.action((patterns, rawOptions, cmd) => {
|
|
20
19
|
try {
|
|
21
20
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
@@ -39,12 +38,11 @@ export function registerProfileCommands(program) {
|
|
|
39
38
|
});
|
|
40
39
|
const filterRes = filterWithFallbackInfo(enriched, effectiveIntent, [(p) => p.name, (p) => p.serverUrl, (p) => p.description, (p) => p.tokenSource, (p) => (p.insecure ? "insecure" : "")], shouldFallback);
|
|
41
40
|
if (filterRes.isFallback && isMachine) {
|
|
42
|
-
renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0 }, { json: options.json
|
|
41
|
+
renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0 }, { json: options.json });
|
|
43
42
|
return;
|
|
44
43
|
}
|
|
45
44
|
renderResult(filterRes.items, {
|
|
46
45
|
json: options.json,
|
|
47
|
-
envelope: options.envelope,
|
|
48
46
|
humanFormatter: () => {
|
|
49
47
|
const lines = ["ActionDock Execution Profiles:\n"];
|
|
50
48
|
if (filterRes.isFallback && effectiveIntent) {
|
|
@@ -95,7 +93,7 @@ export function registerProfileCommands(program) {
|
|
|
95
93
|
.action((name, options) => {
|
|
96
94
|
try {
|
|
97
95
|
if (options.token) {
|
|
98
|
-
|
|
96
|
+
writeStderr("Warning: storing tokens directly in profiles.json is deprecated. Use --token-env or standard environment variables (e.g. ACTIONDOCK_<PROFILE>_TOKEN) instead.");
|
|
99
97
|
}
|
|
100
98
|
addProfile(name, {
|
|
101
99
|
serverUrl: options.server,
|
|
@@ -123,7 +121,7 @@ export function registerProfileCommands(program) {
|
|
|
123
121
|
.action((name, options) => {
|
|
124
122
|
try {
|
|
125
123
|
if (options.token) {
|
|
126
|
-
|
|
124
|
+
writeStderr("Warning: storing tokens directly in profiles.json is deprecated. Use --token-env or standard environment variables (e.g. ACTIONDOCK_<PROFILE>_TOKEN) instead.");
|
|
127
125
|
}
|
|
128
126
|
let insecure;
|
|
129
127
|
if (options.insecure === true) {
|
|
@@ -164,7 +162,6 @@ export function registerProfileCommands(program) {
|
|
|
164
162
|
.description("Display details of a profile (defaults to active profile)")
|
|
165
163
|
.option("--reveal, --show-secrets", "Reveal plain text values for tokens")
|
|
166
164
|
.option("--json", "Output as JSON")
|
|
167
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
168
165
|
.action((name, rawOptions, cmd) => {
|
|
169
166
|
try {
|
|
170
167
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
@@ -192,7 +189,6 @@ export function registerProfileCommands(program) {
|
|
|
192
189
|
};
|
|
193
190
|
renderResult(data, {
|
|
194
191
|
json: options.json,
|
|
195
|
-
envelope: options.envelope,
|
|
196
192
|
humanFormatter: () => {
|
|
197
193
|
const lines = [];
|
|
198
194
|
lines.push(`Profile: ${data.name}${data.isCurrent ? " (Active)" : ""}`);
|
|
@@ -261,7 +257,6 @@ export function registerProfileCommands(program) {
|
|
|
261
257
|
.option("-k, --insecure", "Allow insecure TLS connections (skip TLS certificate validation)")
|
|
262
258
|
.option("--allow-insecure-http", "Allow insecure HTTP connections with auth token")
|
|
263
259
|
.option("--json", "Output as JSON")
|
|
264
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
265
260
|
.action(async (name, rawOptions, cmd) => {
|
|
266
261
|
try {
|
|
267
262
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
@@ -273,7 +268,6 @@ export function registerProfileCommands(program) {
|
|
|
273
268
|
if (target.type === "local") {
|
|
274
269
|
renderResult({ ok: true, type: "local", message: "Local execution" }, {
|
|
275
270
|
json: options.json,
|
|
276
|
-
envelope: options.envelope,
|
|
277
271
|
humanFormatter: () => `Target profile '${target.profileName || "local"}' is local (runs in local runtime).`,
|
|
278
272
|
});
|
|
279
273
|
return;
|
|
@@ -284,7 +278,6 @@ export function registerProfileCommands(program) {
|
|
|
284
278
|
});
|
|
285
279
|
renderResult(health, {
|
|
286
280
|
json: options.json,
|
|
287
|
-
envelope: options.envelope,
|
|
288
281
|
humanFormatter: () => {
|
|
289
282
|
if (health.ok) {
|
|
290
283
|
return `[OK] Connected to ${target.serverUrl} (${health.latencyMs}ms) - Version: ${health.version}, Status: ${health.status}`;
|
package/dist/commands/remove.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { beginTransaction, findProjectRoot, getInstallCommand, loadLockfile, loadManifest, saveLockfile, saveManifest, } from "@actiondock/core";
|
|
3
|
-
import { ArgumentError, ExecutionError } from "../errors.js";
|
|
4
|
-
import { renderResult } from "../renderer.js";
|
|
5
|
-
import { getEffectiveOptions, spawnAsync } from "../utils/index.js";
|
|
3
|
+
import { ArgumentError, ExecutionError, notInProjectError } from "../errors.js";
|
|
4
|
+
import { renderResult, writeStderr } from "../renderer.js";
|
|
5
|
+
import { assertSafePackageSpec, getEffectiveOptions, spawnAsync } from "../utils/index.js";
|
|
6
6
|
/**
|
|
7
7
|
* 注册 ad remove 依赖移除命令。
|
|
8
8
|
* 遵循规范:移除依赖并更新锁文件,保留该包的历史存储数据,检查反向引用防止误删。
|
|
@@ -13,12 +13,11 @@ export function registerRemoveCommand(program, context) {
|
|
|
13
13
|
.description("Remove an Action package dependency and update actiondock.lock.json")
|
|
14
14
|
.option("-P, --package <path>", "Target project directory path")
|
|
15
15
|
.option("--json", "Output as JSON")
|
|
16
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
17
16
|
.action(async (packageIdentifier, rawOptions, cmd) => {
|
|
18
17
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
19
18
|
const root = options.package ? resolve(options.package) : findProjectRoot();
|
|
20
19
|
if (!root) {
|
|
21
|
-
throw
|
|
20
|
+
throw notInProjectError("Please specify -P, --package <path> or cd into a project directory.");
|
|
22
21
|
}
|
|
23
22
|
const manifest = loadManifest(root);
|
|
24
23
|
if (!manifest) {
|
|
@@ -86,7 +85,8 @@ export function registerRemoveCommand(program, context) {
|
|
|
86
85
|
delete lockfile.packages[targetPackageId];
|
|
87
86
|
saveLockfile(root, lockfile);
|
|
88
87
|
}
|
|
89
|
-
//
|
|
88
|
+
// 调用包管理器执行卸载(说明符白名单校验,消除 win32 shell 拼接注入面)
|
|
89
|
+
assertSafePackageSpec(npmPackageName);
|
|
90
90
|
const installCmd = getInstallCommand(root);
|
|
91
91
|
const pm = installCmd[0];
|
|
92
92
|
const args = pm === "bun" ? ["remove", npmPackageName] : ["uninstall", npmPackageName, "--ignore-scripts"];
|
|
@@ -94,18 +94,22 @@ export function registerRemoveCommand(program, context) {
|
|
|
94
94
|
cwd: root,
|
|
95
95
|
stdio: "pipe",
|
|
96
96
|
});
|
|
97
|
-
//
|
|
98
|
-
|
|
97
|
+
// 卸载失败不阻断事务提交,但必须可见:非零退出码输出 WARN 并在结果中标记
|
|
98
|
+
const uninstallFailed = uninstallProc.status !== 0;
|
|
99
|
+
if (uninstallFailed) {
|
|
100
|
+
const errMsg = uninstallProc.stderr || uninstallProc.stdout || "unknown error";
|
|
101
|
+
writeStderr(`[WARN] Package manager '${pm}' reported failure while uninstalling '${npmPackageName}' (exit code ${uninstallProc.status ?? "null"}): ${errMsg.trim().split("\n").pop() || ""}`, context);
|
|
102
|
+
}
|
|
99
103
|
// 提交事务
|
|
100
104
|
await tx.commit();
|
|
101
105
|
const resultPayload = {
|
|
102
106
|
packageId: targetPackageId,
|
|
103
107
|
npmPackage: npmPackageName,
|
|
104
108
|
retainedNamespace: targetPackageId,
|
|
109
|
+
...(uninstallFailed ? { uninstallFailed: true } : {}),
|
|
105
110
|
};
|
|
106
111
|
renderResult(resultPayload, {
|
|
107
112
|
json: options.json,
|
|
108
|
-
envelope: options.envelope,
|
|
109
113
|
humanFormatter: () => `Successfully removed '${resultPayload.packageId}'.\n[INFO] Retained configuration and state namespace for package '${resultPayload.packageId}'.`,
|
|
110
114
|
context,
|
|
111
115
|
});
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import type { ExecutionResult } from "@actiondock/sdk";
|
|
1
2
|
import { Command } from "commander";
|
|
2
3
|
import type { CliContext } from "../types.js";
|
|
4
|
+
/**
|
|
5
|
+
* 以原始纯文本形式渲染 Action 执行终态结果(默认纯文本模式)。
|
|
6
|
+
*
|
|
7
|
+
* 设计契约:
|
|
8
|
+
* 1. stdout: 仅承载业务有效载荷(payload)。保持原生排版、未转义多行与真实换行,供用户阅读或下游管道直接消费。
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderRawExecutionResult(targetRef: string, result: ExecutionResult, context?: CliContext): void;
|
|
3
11
|
/**
|
|
4
12
|
* 统一执行 Action 核心逻辑(使用 ActionDockTarget 门面)。
|
|
5
13
|
*/
|