@actiondock/cli 2.12.0 → 2.14.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 +10 -5
- package/bin/ad.js +0 -16
- package/dist/commands/config/delete.js +8 -19
- package/dist/commands/config/env.js +3 -2
- package/dist/commands/config/get.js +10 -17
- package/dist/commands/config/list.js +10 -16
- package/dist/commands/config/schema.js +7 -6
- package/dist/commands/config/set.js +12 -34
- package/dist/commands/describe.js +6 -9
- package/dist/commands/list.js +16 -16
- package/dist/commands/run.js +23 -21
- package/dist/commands/state.js +19 -2
- package/dist/commands/validate.js +13 -1
- package/dist/renderers/action.d.ts +5 -25
- package/dist/renderers/action.js +19 -97
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/utils/target.d.ts +57 -3
- package/dist/utils/target.js +202 -18
- package/package.json +5 -5
- package/dist/utils/warning.d.ts +0 -4
- package/dist/utils/warning.js +0 -4
package/README.md
CHANGED
|
@@ -59,6 +59,10 @@ ad run sample.greet -- name=Alice count:=1
|
|
|
59
59
|
# 复杂参数或对象从文件读取(与扁平参数互斥)
|
|
60
60
|
ad run sample.greet --input-file input.json
|
|
61
61
|
|
|
62
|
+
# 原始文本管道绑定(假定已注册 summarize 动作且 text 是字符串入参)
|
|
63
|
+
# 可与扁平赋值组合,与 --input / --input-file 互斥
|
|
64
|
+
printf '这是一段正文' | ad run summarize --stdin-field text -- style=brief
|
|
65
|
+
|
|
62
66
|
# 自动化与脚本通过标准输入传递
|
|
63
67
|
cat input.json | ad run sample.greet --input-file -
|
|
64
68
|
```
|
|
@@ -126,11 +130,12 @@ ad pack
|
|
|
126
130
|
- 路径冲突(叶节点与容器冲突、对象与数组冲突、重复赋值)严格拒绝(`INPUT_PATH_CONFLICT`)。
|
|
127
131
|
- 拦截原型污染敏感属性(`__proto__`、`constructor`、`prototype`)。
|
|
128
132
|
- 三种输入模式互斥:扁平参数、`--input` 与 `--input-file` 严格互斥,不可混用(`INPUT_CONFLICT`);未指定输入时默认为 `{}`。
|
|
133
|
+
- 原始文本管道绑定:`printf '正文' | ad run <action> --stdin-field <field> [-- <assignments...>]` 将 stdin 完整正文原样绑定为指定顶层字段的字符串值(不解析类型、不剥 BOM、不修剪空白、空输入为空字符串);可与扁平赋值组合,与 `--input` / `--input-file` 互斥;stdin 源字节与绑定后的最终入参序列化大小各自受默认 10MiB 上限约束,分别超限报 `INPUT_LIMIT_EXCEEDED` 与 `FLAT_INPUT_LIMIT_EXCEEDED`,JSON 转义放大也计入最终大小;严格 UTF-8 校验失败报读取错误。管道非事务,高风险写入先完整生成并校验再执行。
|
|
129
134
|
- 传统输入选项:
|
|
130
135
|
- 简单内联参数:使用 `--input <json>` 直接解析 JSON 字符串。
|
|
131
136
|
- 复杂对象文件:使用 `--input-file <path>` 读取文件并解析 JSON,避免各类终端的引号转义损坏。
|
|
132
137
|
- 标准输入管道:使用 `--input-file -` 从标准输入读取全部数据并解析 JSON。
|
|
133
|
-
-
|
|
138
|
+
- 统一解析:JSON 输入在解析前剔除 UTF-8 BOM,默认大小上限为 10MiB;改用文件不会解除限制。原始文本绑定保留正文 BOM。
|
|
134
139
|
|
|
135
140
|
### Windows 与多终端传参建议
|
|
136
141
|
|
|
@@ -156,7 +161,7 @@ $data | ConvertTo-Json -Depth 100 | ad run complex-action --input-file -
|
|
|
156
161
|
- **显式正文输出与默认注解**:
|
|
157
162
|
- 显式指定正文字段:通过 `ad run <id> --text-field <field> -- ...` 提取结果中特定顶层自有字符串字段输出至 stdout(保留真实换行且无 JSON 转义),其余字段以 JSON 对象形式输出至 stderr(元数据为空时不输出)。
|
|
158
163
|
- 声明式默认正文:Action 清单中声明 `annotations["actiondock.cli"] = { "textField": "<field>" }` 时,未指定 `--text-field` 时自动按声明提取正文与元数据;显式 `--text-field` 优先于默认注解。
|
|
159
|
-
-
|
|
164
|
+
- 严格校验与契约闭环:正文字段缺失、非字符串或返回数据非对象时,明确输出错误并以退出码 1 退出,不静默回退,不改写运行记录。契约通过 `ad describe` 呈现通道语义与覆盖指引,并通过 `ad validate` 进行纯静态元数据合规校验。
|
|
160
165
|
- 参数互斥:`--text-field` 与 `--json`、`--async` 严格互斥,在执行前校验并以退出码 2 退出。
|
|
161
166
|
- **机器模式**(`--json`):当需要以程序化方式消费、获取完整结构化数据或被外部系统集成时,传入 `--json`。此时始终输出完整执行信封(包含 `ok`、`runId`、`data` 或 `error`),忽略默认正文注解;若参数解析出错输出标准错误信封并以退出码 2 退出。
|
|
162
167
|
|
|
@@ -171,9 +176,9 @@ $data | ConvertTo-Json -Depth 100 | ad run complex-action --input-file -
|
|
|
171
176
|
| `ad remove <package>` | 卸载并更新锁定依赖,提供原子回滚保护 |
|
|
172
177
|
| `ad info [patterns...]` | 检索包元数据与能力清单,支持模式匹配与树形展示 |
|
|
173
178
|
| `ad list [patterns...]` | 列出包内所有已注册的 Action |
|
|
174
|
-
| `ad describe <id>` | 编码顾问:查看 Action 的详情、模式字段明细、Flat
|
|
175
|
-
| `ad run <id>` | 本地或远程执行指定 Action(规范语法 `ad run <id> [options] -- <assignments...>`,支持 `--text-field` 提取正文与 `--json` 输出标准信封) |
|
|
176
|
-
| `ad validate [id]` | 校验 Action
|
|
179
|
+
| `ad describe <id>` | 编码顾问:查看 Action 的详情、模式字段明细、Flat 编码指引、建议赋值与输出选择契约 |
|
|
180
|
+
| `ad run <id>` | 本地或远程执行指定 Action(规范语法 `ad run <id> [options] -- <assignments...>`,支持 `--stdin-field` 绑定原始文本、`--text-field` 提取正文与 `--json` 输出标准信封) |
|
|
181
|
+
| `ad validate [id]` | 校验 Action 规范、模式规范与输出选择契约 |
|
|
177
182
|
| `ad doctor` | 执行运行环境与项目结构健康诊断 |
|
|
178
183
|
| `ad action create <id>` | 创建新 Action 源码 |
|
|
179
184
|
| `ad playbook list` / `show` | 查看智能体操作规程 Playbook |
|
package/bin/ad.js
CHANGED
|
@@ -3,19 +3,6 @@ import { enableCompileCache } from "node:module";
|
|
|
3
3
|
try {
|
|
4
4
|
enableCompileCache?.();
|
|
5
5
|
} catch {}
|
|
6
|
-
try {
|
|
7
|
-
await import("@actiondock/core/warning");
|
|
8
|
-
} catch {
|
|
9
|
-
try {
|
|
10
|
-
const { resolve } = await import("node:path");
|
|
11
|
-
const { existsSync } = await import("node:fs");
|
|
12
|
-
const { pathToFileURL } = await import("node:url");
|
|
13
|
-
const coreWarningSrc = resolve(import.meta.dirname, "../../core/src/utils/warning.ts");
|
|
14
|
-
if (existsSync(coreWarningSrc)) {
|
|
15
|
-
await import(pathToFileURL(coreWarningSrc).href);
|
|
16
|
-
}
|
|
17
|
-
} catch {}
|
|
18
|
-
}
|
|
19
6
|
|
|
20
7
|
import { spawn } from "node:child_process";
|
|
21
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -238,9 +225,6 @@ if (needsTsx) {
|
|
|
238
225
|
|
|
239
226
|
if (tsxSpecifier) {
|
|
240
227
|
const extraFlags = [];
|
|
241
|
-
if (process.env.ACTIONDOCK_SILENCE_WARNINGS !== "0") {
|
|
242
|
-
extraFlags.push("--no-warnings=ExperimentalWarning");
|
|
243
|
-
}
|
|
244
228
|
if (nodeMajor >= 24) {
|
|
245
229
|
extraFlags.push("--no-strip-types");
|
|
246
230
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolvePackageRoot, } from "@actiondock/core/registry";
|
|
3
|
-
import { ArgumentError, packageNotFoundError } from "../../errors.js";
|
|
1
|
+
import { ArgumentError } from "../../errors.js";
|
|
4
2
|
import { writeStdout } from "../../renderer.js";
|
|
5
|
-
import { applyTargetOptions, getEffectiveOptions, withService } from "../../utils/index.js";
|
|
3
|
+
import { applyTargetOptions, getEffectiveOptions, resolveTargetPackageScope, withService, } from "../../utils/index.js";
|
|
6
4
|
/**
|
|
7
5
|
* 注册 config delete 子命令:删除配置项。
|
|
8
6
|
*
|
|
@@ -22,31 +20,22 @@ export function registerConfigDeleteCommand(configCmd, context) {
|
|
|
22
20
|
if (!key) {
|
|
23
21
|
throw new ArgumentError("Configuration key is required");
|
|
24
22
|
}
|
|
25
|
-
const
|
|
23
|
+
const scope = resolveTargetPackageScope(options, context);
|
|
26
24
|
await withService(options, context, async (service, resolved) => {
|
|
27
25
|
if (resolved.type === "remote") {
|
|
28
|
-
await service.management?.config.delete(
|
|
26
|
+
await service.management?.config.delete(scope.packageId, key);
|
|
29
27
|
writeStdout(`[OK] Configuration '${key}' deleted from remote server`, context);
|
|
30
28
|
return;
|
|
31
29
|
}
|
|
32
|
-
if (options.global) {
|
|
30
|
+
if (options.global || (!scope.root && !options.package)) {
|
|
33
31
|
await service.management?.config.delete("global", key);
|
|
34
32
|
writeStdout(`[OK] Global configuration '${key}' deleted`, context);
|
|
35
33
|
return;
|
|
36
34
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
throw packageNotFoundError(options.package);
|
|
40
|
-
}
|
|
41
|
-
await service.management?.config.delete("global", key);
|
|
42
|
-
writeStdout(`[OK] Global configuration '${key}' deleted`, context);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const projConfig = loadProjectConfig(root);
|
|
46
|
-
await service.management?.config.delete(projConfig.id, key);
|
|
47
|
-
writeStdout(`[OK] Configuration '${key}' deleted for package '${projConfig.id}'`, context);
|
|
35
|
+
await service.management?.config.delete(scope.packageId, key);
|
|
36
|
+
writeStdout(`[OK] Configuration '${key}' deleted for package '${scope.packageId}'`, context);
|
|
48
37
|
},
|
|
49
38
|
// 按实际查询范围打开存储:显式包寻址结果作为本地根目录传递
|
|
50
|
-
{ localRoot: root
|
|
39
|
+
{ localRoot: scope.root });
|
|
51
40
|
});
|
|
52
41
|
}
|
|
@@ -2,7 +2,7 @@ import { resolveEnvValue } from "@actiondock/core";
|
|
|
2
2
|
import { fetchRemoteConfigEnv } from "@actiondock/core/profile";
|
|
3
3
|
import { isSecretConfigKey, } from "@actiondock/core/project";
|
|
4
4
|
import { renderConfigEnv, renderResult } from "../../renderer.js";
|
|
5
|
-
import { applyTargetOptions, getEffectiveOptions, requirePackageRoot, resolveTargetFromOptions } from "../../utils/index.js";
|
|
5
|
+
import { applyTargetOptions, assertNotCompositeSkill, getEffectiveOptions, requirePackageRoot, resolveTargetFromOptions, } from "../../utils/index.js";
|
|
6
6
|
/**
|
|
7
7
|
* 注册 config env 子命令:诊断环境变量对声明配置的满足率。
|
|
8
8
|
*
|
|
@@ -18,7 +18,7 @@ export function registerConfigEnvCommand(configCmd, context) {
|
|
|
18
18
|
.action(async (identifier, rawOptions, cmd) => {
|
|
19
19
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
20
20
|
const targetPkg = identifier || options.package;
|
|
21
|
-
//
|
|
21
|
+
// 1. 目标拓扑解析(远端服务分支完全隔离本地文件系统)
|
|
22
22
|
const target = resolveTargetFromOptions(options, context);
|
|
23
23
|
if (target.type === "remote") {
|
|
24
24
|
const res = await fetchRemoteConfigEnv(target.serverUrl, target.token, targetPkg, {
|
|
@@ -37,6 +37,7 @@ export function registerConfigEnvCommand(configCmd, context) {
|
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
// 本地项目分支
|
|
40
|
+
assertNotCompositeSkill(targetPkg);
|
|
40
41
|
const { root, projConfig } = requirePackageRoot(targetPkg, {
|
|
41
42
|
loadConfig: true,
|
|
42
43
|
hint: "Usage: ad config env [package-id] or cd into a project directory.",
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { fetchRemoteConfig, } from "@actiondock/core/profile";
|
|
2
2
|
import { isSecretConfigKey, maskSecretValue, } from "@actiondock/core/project";
|
|
3
3
|
import { loadProjectConfig, } from "@actiondock/core";
|
|
4
|
-
import {
|
|
5
|
-
import { ArgumentError, packageNotFoundError } from "../../errors.js";
|
|
4
|
+
import { ArgumentError } from "../../errors.js";
|
|
6
5
|
import { renderResult } from "../../renderer.js";
|
|
7
6
|
import { resolveMergedConfigEntry } from "../../services/config-merge.js";
|
|
8
|
-
import { applyTargetOptions, getEffectiveOptions,
|
|
7
|
+
import { applyTargetOptions, getEffectiveOptions, resolveTargetPackageScope, withService, } from "../../utils/index.js";
|
|
9
8
|
/**
|
|
10
9
|
* 注册 config get 子命令:查询指定配置键的生效值。
|
|
11
10
|
*
|
|
@@ -27,12 +26,11 @@ export function registerConfigGetCommand(configCmd, context) {
|
|
|
27
26
|
throw new ArgumentError("Configuration key is required");
|
|
28
27
|
}
|
|
29
28
|
const reveal = Boolean(options.reveal || options.showSecrets);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const res = await fetchRemoteConfig(target.serverUrl, target.token, options.package, {
|
|
29
|
+
const scope = resolveTargetPackageScope(options, context);
|
|
30
|
+
if (scope.isRemote) {
|
|
31
|
+
const res = await fetchRemoteConfig(scope.resolved.serverUrl, scope.resolved.token, scope.packageId, {
|
|
34
32
|
allowInsecureHttp: Boolean(options.allowInsecureHttp),
|
|
35
|
-
insecure:
|
|
33
|
+
insecure: scope.resolved.insecure,
|
|
36
34
|
});
|
|
37
35
|
const val = res.values?.[key];
|
|
38
36
|
const isSecret = isSecretConfigKey(key, res.declared?.[key]);
|
|
@@ -50,13 +48,8 @@ export function registerConfigGetCommand(configCmd, context) {
|
|
|
50
48
|
});
|
|
51
49
|
return;
|
|
52
50
|
}
|
|
53
|
-
// 本地分支
|
|
54
|
-
const root = resolvePackageRoot(options.package);
|
|
55
51
|
await withService(options, context, async (service) => {
|
|
56
|
-
if (options.global || !root) {
|
|
57
|
-
if (!options.global && options.package && !root) {
|
|
58
|
-
throw packageNotFoundError(options.package);
|
|
59
|
-
}
|
|
52
|
+
if (options.global || !scope.root) {
|
|
60
53
|
const confView = await service.management?.config.get("global", key);
|
|
61
54
|
const val = confView?.value;
|
|
62
55
|
const isSecret = confView?.secret ?? isSecretConfigKey(key);
|
|
@@ -74,10 +67,10 @@ export function registerConfigGetCommand(configCmd, context) {
|
|
|
74
67
|
});
|
|
75
68
|
return;
|
|
76
69
|
}
|
|
77
|
-
const projConfig = loadProjectConfig(root);
|
|
70
|
+
const projConfig = scope.projConfig || loadProjectConfig(scope.root);
|
|
78
71
|
const declaredItem = projConfig.config?.[key];
|
|
79
72
|
// 合并优先级链单一事实源:项目包级 > 全局持久化 > 环境变量 > 声明默认值
|
|
80
|
-
const { value: displayValue, source, secret: isSecret } = await resolveMergedConfigEntry(key,
|
|
73
|
+
const { value: displayValue, source, secret: isSecret } = await resolveMergedConfigEntry(key, scope.packageId, declaredItem, service, reveal);
|
|
81
74
|
const payload = {
|
|
82
75
|
key,
|
|
83
76
|
value: displayValue,
|
|
@@ -91,6 +84,6 @@ export function registerConfigGetCommand(configCmd, context) {
|
|
|
91
84
|
});
|
|
92
85
|
},
|
|
93
86
|
// 按实际查询范围打开存储:显式包寻址结果作为本地根目录传递
|
|
94
|
-
{ localRoot: root
|
|
87
|
+
{ localRoot: scope.root });
|
|
95
88
|
});
|
|
96
89
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { fetchRemoteConfig, } from "@actiondock/core/profile";
|
|
2
2
|
import { loadProjectConfig, } from "@actiondock/core";
|
|
3
3
|
import { filterWithFallbackInfo, isSecretConfigKey, maskSecretValue, } from "@actiondock/core/project";
|
|
4
|
-
import { packageNotFoundError } from "../../errors.js";
|
|
5
4
|
import { renderConfigList, renderResult } from "../../renderer.js";
|
|
6
5
|
import { buildMergedConfigEntries } from "../../services/config-merge.js";
|
|
7
|
-
import { applyTargetOptions, getEffectiveOptions, remoteTargetLabel, resolveFallbackStrategy, resolveIntent,
|
|
6
|
+
import { applyTargetOptions, getEffectiveOptions, remoteTargetLabel, resolveFallbackStrategy, resolveIntent, resolveTargetPackageScope, withService, } from "../../utils/index.js";
|
|
8
7
|
/**
|
|
9
8
|
* 构造单条配置项的展示视图(含掩码决策,reveal 为假且为敏感键时打码)。
|
|
10
9
|
* 掩码决策收敛于命令层,renderer 仅负责值的字符串格式化。
|
|
@@ -42,29 +41,24 @@ export function registerConfigListCommand(configCmd, context) {
|
|
|
42
41
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
43
42
|
const { shouldFallback } = resolveFallbackStrategy(options);
|
|
44
43
|
const reveal = Boolean(options.reveal || options.showSecrets);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const res = await fetchRemoteConfig(remoteTargetInfo.serverUrl, remoteTargetInfo.token, options.package, {
|
|
44
|
+
const scope = resolveTargetPackageScope(options, context);
|
|
45
|
+
if (scope.isRemote) {
|
|
46
|
+
const res = await fetchRemoteConfig(scope.resolved.serverUrl, scope.resolved.token, scope.packageId, {
|
|
49
47
|
allowInsecureHttp: Boolean(options.allowInsecureHttp),
|
|
50
|
-
insecure:
|
|
48
|
+
insecure: scope.resolved.insecure,
|
|
51
49
|
});
|
|
52
50
|
const entries = Object.entries(res.values || {}).map(([k, v]) => toDisplayEntry(k, v, "remote", isSecretConfigKey(k, res.declared?.[k]), reveal, res.declared?.[k]?.description || ""));
|
|
53
51
|
renderResult(entries, {
|
|
54
52
|
json: options.json,
|
|
55
|
-
humanFormatter: () => renderConfigList(entries, remoteTargetLabel(
|
|
53
|
+
humanFormatter: () => renderConfigList(entries, remoteTargetLabel(scope.resolved), false, effectiveIntent),
|
|
56
54
|
context,
|
|
57
55
|
});
|
|
58
56
|
return;
|
|
59
57
|
}
|
|
60
58
|
// 本地分支(通过 Service 门面统一访问):显式包寻址结果作为本地根目录传递(仅 local 分支求值)
|
|
61
59
|
await withService(options, context, async (service) => {
|
|
62
|
-
const root = resolveLocalPackageRoot(options.package) || undefined;
|
|
63
60
|
// 全局作用域分支:显式 --global 或无工程回退时仅列举全局配置
|
|
64
|
-
if (options.global || !root) {
|
|
65
|
-
if (!options.global && options.package) {
|
|
66
|
-
throw packageNotFoundError(options.package);
|
|
67
|
-
}
|
|
61
|
+
if (options.global || !scope.root) {
|
|
68
62
|
const all = (await service.management?.config.list("global")) ?? [];
|
|
69
63
|
const entries = all.map((item) => toDisplayEntry(item.key, item.value, "global", item.secret || isSecretConfigKey(item.key), reveal));
|
|
70
64
|
const filterRes = filterWithFallbackInfo(entries, effectiveIntent, [(c) => c.key, (c) => c.value], shouldFallback);
|
|
@@ -75,9 +69,9 @@ export function registerConfigListCommand(configCmd, context) {
|
|
|
75
69
|
});
|
|
76
70
|
return;
|
|
77
71
|
}
|
|
78
|
-
const projConfig = loadProjectConfig(root);
|
|
72
|
+
const projConfig = scope.projConfig || loadProjectConfig(scope.root);
|
|
79
73
|
// 跨作用域合并视图单一事实源:项目包级 > 全局持久化 > 环境变量 > 声明默认值
|
|
80
|
-
const merged = await buildMergedConfigEntries(root, service, reveal);
|
|
74
|
+
const merged = await buildMergedConfigEntries(scope.root, service, reveal);
|
|
81
75
|
const filterRes = filterWithFallbackInfo(merged, effectiveIntent, [(c) => c.key, (c) => c.value, (c) => c.description, (c) => c.source], shouldFallback);
|
|
82
76
|
renderResult(filterRes.items, {
|
|
83
77
|
json: options.json,
|
|
@@ -85,7 +79,7 @@ export function registerConfigListCommand(configCmd, context) {
|
|
|
85
79
|
context,
|
|
86
80
|
});
|
|
87
81
|
}, {
|
|
88
|
-
localRoot:
|
|
82
|
+
localRoot: scope.root,
|
|
89
83
|
});
|
|
90
84
|
});
|
|
91
85
|
}
|
|
@@ -2,7 +2,7 @@ import { isSecretConfigKey, } from "@actiondock/core/project";
|
|
|
2
2
|
import { notInProjectError } from "../../errors.js";
|
|
3
3
|
import { renderConfigSchema, renderResult } from "../../renderer.js";
|
|
4
4
|
import { buildMergedConfigEntries } from "../../services/config-merge.js";
|
|
5
|
-
import { applyTargetOptions, getEffectiveOptions, requirePackageRoot, withService } from "../../utils/index.js";
|
|
5
|
+
import { applyTargetOptions, assertNotCompositeSkill, getEffectiveOptions, requirePackageRoot, withService, } from "../../utils/index.js";
|
|
6
6
|
/**
|
|
7
7
|
* 注册 config schema 子命令:检查声明配置的解析状态。
|
|
8
8
|
*
|
|
@@ -25,17 +25,18 @@ export function registerConfigSchemaCommand(configCmd, context) {
|
|
|
25
25
|
.option("--json", "Output as JSON")
|
|
26
26
|
.action(async (identifier, rawOptions, cmd) => {
|
|
27
27
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
28
|
+
// 远端目标显式拒绝:schema 状态推导依赖本地工程声明与环境变量解析,
|
|
29
|
+
// 远端作用域无法获得等价视图,静默落到本地会误导调用方
|
|
30
|
+
if (options.profile || options.server) {
|
|
31
|
+
throw notInProjectError("'ad config schema' only supports local projects. Remote targets are not supported for schema inspection.");
|
|
32
|
+
}
|
|
28
33
|
const targetPkg = identifier || options.package;
|
|
34
|
+
assertNotCompositeSkill(targetPkg);
|
|
29
35
|
const { root, projConfig } = requirePackageRoot(targetPkg, {
|
|
30
36
|
loadConfig: true,
|
|
31
37
|
hint: "Usage: ad config schema [package-id] or cd into a project directory.",
|
|
32
38
|
});
|
|
33
39
|
const declared = projConfig.config || {};
|
|
34
|
-
// 远端目标显式拒绝:schema 状态推导依赖本地工程声明与环境变量解析,
|
|
35
|
-
// 远端作用域无法获得等价视图,静默落到本地会误导调用方
|
|
36
|
-
if (options.profile || options.server) {
|
|
37
|
-
throw notInProjectError("'ad config schema' only supports local projects. Remote targets are not supported for schema inspection.");
|
|
38
|
-
}
|
|
39
40
|
// 跨作用域合并视图单一事实源:与 config list 共享同一条优先级链
|
|
40
41
|
await withService(options, context, async (service) => {
|
|
41
42
|
const merged = await buildMergedConfigEntries(root, service, true);
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { isSecretConfigKey, } from "@actiondock/core/project";
|
|
2
|
-
import {
|
|
3
|
-
import { resolvePackageRoot, } from "@actiondock/core/registry";
|
|
4
|
-
import { ArgumentError, packageNotFoundError } from "../../errors.js";
|
|
2
|
+
import { ArgumentError } from "../../errors.js";
|
|
5
3
|
import { writeStdout } from "../../renderer.js";
|
|
6
|
-
import { applyTargetOptions, getEffectiveOptions, withService } from "../../utils/index.js";
|
|
4
|
+
import { applyTargetOptions, getEffectiveOptions, resolveTargetPackageScope, withService, } from "../../utils/index.js";
|
|
7
5
|
import { resolveConfigValueInput } from "../../prompt.js";
|
|
8
6
|
/**
|
|
9
7
|
* 注册 config set 子命令:写入配置值(支持标准输入与安全提示)。
|
|
@@ -24,22 +22,8 @@ export function registerConfigSetCommand(configCmd, context) {
|
|
|
24
22
|
if (!key) {
|
|
25
23
|
throw new ArgumentError("Configuration key is required");
|
|
26
24
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let declaredDef;
|
|
30
|
-
if (!options.global && !options.profile && !options.server) {
|
|
31
|
-
root = resolvePackageRoot(options.package);
|
|
32
|
-
if (root) {
|
|
33
|
-
try {
|
|
34
|
-
projConfig = loadProjectConfig(root);
|
|
35
|
-
declaredDef = projConfig.config?.[key];
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
// 清单损坏时必须让错误可见:静默忽略会导致写入错误的作用域
|
|
39
|
-
throw err;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
25
|
+
const scope = resolveTargetPackageScope(options, context);
|
|
26
|
+
const declaredDef = scope.projConfig?.config?.[key];
|
|
43
27
|
const isSecret = isSecretConfigKey(key, declaredDef);
|
|
44
28
|
let effectiveValueStr = rawVal;
|
|
45
29
|
if (effectiveValueStr === undefined || options.stdin) {
|
|
@@ -60,27 +44,21 @@ export function registerConfigSetCommand(configCmd, context) {
|
|
|
60
44
|
// 目标解析与写入(远端分支直接写远端作用域,本地分支按全局/项目作用域写入)
|
|
61
45
|
await withService(options, context, async (service, resolved) => {
|
|
62
46
|
if (resolved.type === "remote") {
|
|
63
|
-
await service.management?.config.set(
|
|
47
|
+
await service.management?.config.set(scope.packageId, key, parsedVal);
|
|
64
48
|
writeStdout(`[OK] Configuration '${key}' updated on remote server`, context);
|
|
65
49
|
return;
|
|
66
50
|
}
|
|
67
|
-
if (options.global) {
|
|
68
|
-
await service.management?.config.set("global", key, parsedVal);
|
|
69
|
-
writeStdout(`[OK] Global configuration '${key}' updated`, context);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (!root) {
|
|
73
|
-
if (options.package) {
|
|
74
|
-
throw packageNotFoundError(options.package);
|
|
75
|
-
}
|
|
51
|
+
if (options.global || (!scope.root && !options.package)) {
|
|
76
52
|
await service.management?.config.set("global", key, parsedVal);
|
|
77
|
-
writeStdout(
|
|
53
|
+
writeStdout(!scope.root && !options.package
|
|
54
|
+
? `[OK] Global configuration '${key}' updated (no project in current directory)`
|
|
55
|
+
: `[OK] Global configuration '${key}' updated`, context);
|
|
78
56
|
return;
|
|
79
57
|
}
|
|
80
|
-
await service.management?.config.set(
|
|
81
|
-
writeStdout(`[OK] Configuration '${key}' updated for package '${
|
|
58
|
+
await service.management?.config.set(scope.packageId, key, parsedVal);
|
|
59
|
+
writeStdout(`[OK] Configuration '${key}' updated for package '${scope.packageId}'`, context);
|
|
82
60
|
},
|
|
83
61
|
// 按实际查询范围打开存储:显式包寻址结果作为本地根目录传递
|
|
84
|
-
{ localRoot: root
|
|
62
|
+
{ localRoot: scope.root });
|
|
85
63
|
});
|
|
86
64
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ACTION_NOT_FOUND, PACKAGE_NOT_FOUND, NOT_FOUND, } from "@actiondock/core";
|
|
2
2
|
import { buildActionDescribePayload, } from "@actiondock/core/project";
|
|
3
|
-
import { ArgumentError, ExecutionError
|
|
3
|
+
import { ArgumentError, ExecutionError } from "../errors.js";
|
|
4
4
|
import { formatActionDetail, renderResult } from "../renderer.js";
|
|
5
|
-
import { applyTargetOptions, getEffectiveOptions,
|
|
5
|
+
import { applyTargetOptions, getEffectiveOptions, resolveCommandTarget, withService, } from "../utils/index.js";
|
|
6
6
|
/**
|
|
7
7
|
* 挂载 describe 子命令至指定 Commander 节点。
|
|
8
8
|
*
|
|
@@ -22,12 +22,9 @@ export function attachDescribeCommand(parent, context) {
|
|
|
22
22
|
if (!id) {
|
|
23
23
|
throw new ArgumentError("Action ID is required for describe");
|
|
24
24
|
}
|
|
25
|
-
//
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
throw packageNotFoundError(options.package);
|
|
29
|
-
}
|
|
30
|
-
const targetRef = id;
|
|
25
|
+
// 目标拓扑与动作引用解析(远端目标完全隔离本地文件系统)
|
|
26
|
+
const target = resolveCommandTarget(options, context);
|
|
27
|
+
const targetRef = target.resolveActionRef(id);
|
|
31
28
|
// 通过 Service 门面统一查询 Action 规范
|
|
32
29
|
await withService(options, context, async (service) => {
|
|
33
30
|
let spec;
|
|
@@ -50,7 +47,7 @@ export function attachDescribeCommand(parent, context) {
|
|
|
50
47
|
humanFormatter: () => formatActionDetail(payload),
|
|
51
48
|
context,
|
|
52
49
|
});
|
|
53
|
-
}, { localRoot: targetPackageRoot
|
|
50
|
+
}, { localRoot: target.targetPackageRoot, scanLinkedPackages: !target.localSkill });
|
|
54
51
|
});
|
|
55
52
|
}
|
|
56
53
|
/**
|
package/dist/commands/list.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { filterWithFallbackInfo, } from "@actiondock/core/project";
|
|
2
|
-
import { packageNotFoundError } from "../errors.js";
|
|
3
2
|
import { renderActionList, renderResult } from "../renderer.js";
|
|
4
|
-
import { applyTargetOptions, getEffectiveOptions, remoteTargetSuffix, resolveFallbackStrategy, resolveIntent,
|
|
3
|
+
import { applyTargetOptions, getEffectiveOptions, remoteTargetSuffix, resolveCommandTarget, resolveFallbackStrategy, resolveIntent, withService, } from "../utils/index.js";
|
|
5
4
|
/**
|
|
6
5
|
* 挂载 list 子命令至指定 Commander 节点。
|
|
7
6
|
*
|
|
@@ -23,22 +22,20 @@ export function attachListCommand(parent, context) {
|
|
|
23
22
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
24
23
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
25
24
|
const { isMachine, shouldFallback } = resolveFallbackStrategy(options);
|
|
26
|
-
//
|
|
27
|
-
const
|
|
28
|
-
if (options.package && !targetPackageRoot) {
|
|
29
|
-
throw packageNotFoundError(options.package);
|
|
30
|
-
}
|
|
25
|
+
// 目标拓扑与动作引用解析(远端目标完全隔离本地文件系统)
|
|
26
|
+
const target = resolveCommandTarget(options, context);
|
|
31
27
|
// 通过 Service 门面统一获取 Action 列表
|
|
32
28
|
await withService(options, context, async (service, resolved) => {
|
|
33
29
|
let rawSummaries = await service.discovery.listActions();
|
|
34
|
-
//
|
|
30
|
+
// 若指定了包 ID(非本地技能目录),则按包归属精确过滤(本地与远端统一生效):
|
|
35
31
|
// 仅保留全限定名命中目标包前缀、或 packageId 归属目标包的条目;
|
|
36
32
|
// 无法归属到目标包的无前缀短 id 条目不放行,避免他包短 id 混入
|
|
37
|
-
if (options.package && !
|
|
33
|
+
if (options.package && !target.localSkill) {
|
|
38
34
|
const targetPkg = options.package;
|
|
39
35
|
rawSummaries = rawSummaries.filter((s) => {
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const pkgId = s.packageId;
|
|
37
|
+
if (pkgId) {
|
|
38
|
+
return (pkgId === targetPkg ||
|
|
42
39
|
s.id === targetPkg ||
|
|
43
40
|
s.id.startsWith(`${targetPkg}/`));
|
|
44
41
|
}
|
|
@@ -46,14 +43,17 @@ export function attachListCommand(parent, context) {
|
|
|
46
43
|
return s.id === targetPkg || s.id.startsWith(`${targetPkg}/`);
|
|
47
44
|
});
|
|
48
45
|
}
|
|
46
|
+
const isCompositeSkill = target.localSkill?.type === "composite";
|
|
49
47
|
const rawList = rawSummaries.map((s) => {
|
|
50
48
|
let id = s.id;
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
// 复合技能保持完全限定 ID,不剥离包名前缀
|
|
50
|
+
if (!isCompositeSkill && (target.targetPackageRoot || options.package)) {
|
|
51
|
+
const targetPkg = target.localSkill ? target.localSkill.packages[0].packageId : options.package;
|
|
52
|
+
const pkgId = targetPkg || s.packageId;
|
|
53
53
|
if (pkgId && id.startsWith(`${pkgId}/`)) {
|
|
54
54
|
id = id.slice(pkgId.length + 1);
|
|
55
55
|
}
|
|
56
|
-
else if (targetPackageRoot && id.includes("/")) {
|
|
56
|
+
else if (target.targetPackageRoot && id.includes("/")) {
|
|
57
57
|
id = id.slice(id.indexOf("/") + 1);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -80,7 +80,7 @@ export function attachListCommand(parent, context) {
|
|
|
80
80
|
}
|
|
81
81
|
const title = resolved.type === "remote"
|
|
82
82
|
? `Actions ${remoteTargetSuffix(resolved)}`
|
|
83
|
-
: targetPackageRoot
|
|
83
|
+
: target.targetPackageRoot
|
|
84
84
|
? `Actions in ${options.package || "current project"}`
|
|
85
85
|
: "Available Actions";
|
|
86
86
|
renderResult(filterRes.items, {
|
|
@@ -88,7 +88,7 @@ export function attachListCommand(parent, context) {
|
|
|
88
88
|
humanFormatter: () => renderActionList(filterRes.items, title, filterRes.isFallback, effectiveIntent),
|
|
89
89
|
context,
|
|
90
90
|
});
|
|
91
|
-
}, { localRoot: targetPackageRoot
|
|
91
|
+
}, { localRoot: target.targetPackageRoot, scanLinkedPackages: !target.localSkill });
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
/**
|
package/dist/commands/run.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { validateActionInputValue, mapInputValidationFailure, } from "@actiondock/core/project";
|
|
2
|
-
import { resolveTarget, } from "@actiondock/core/profile";
|
|
3
2
|
import { ACTION_NOT_FOUND, NOT_FOUND, resolveExecutionHint, } from "@actiondock/core";
|
|
4
|
-
import { ArgumentError, ExecutionError, SigintError
|
|
3
|
+
import { ArgumentError, ExecutionError, SigintError } from "../errors.js";
|
|
5
4
|
import { extractTextFieldPayload, resolveActionDefaultTextField, writeStderr, writeStdout, } from "../renderer.js";
|
|
6
|
-
import { applyTargetOptions, getEffectiveOptions, resolveActionInput,
|
|
5
|
+
import { applyTargetOptions, getEffectiveOptions, resolveActionInput, resolveCommandTarget, resolveTimeoutMs, withService, } from "../utils/index.js";
|
|
7
6
|
/**
|
|
8
7
|
* 以原始纯文本形式渲染 Action 执行终态结果(默认纯文本模式)。
|
|
9
8
|
*
|
|
@@ -78,6 +77,13 @@ export async function executeAction(id, options, context, flatArgs, control) {
|
|
|
78
77
|
throw new ArgumentError("Cannot specify both --text-field and --async.");
|
|
79
78
|
}
|
|
80
79
|
}
|
|
80
|
+
// --stdin-field 与 --text-field 分属输入绑定与输出选择,可同时使用;
|
|
81
|
+
// 与完整 JSON 输入来源的互斥由共享 resolver 统一裁决(INPUT_CONFLICT)
|
|
82
|
+
if (options.stdinField !== undefined) {
|
|
83
|
+
if (typeof options.stdinField !== "string") {
|
|
84
|
+
throw new ArgumentError("The --stdin-field option requires a field name.");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
81
87
|
const effectiveControl = control ?? context?.control;
|
|
82
88
|
const effectiveSignal = effectiveControl?.signal;
|
|
83
89
|
const effectiveFlatArgs = flatArgs ?? options.flatArgs;
|
|
@@ -85,7 +91,9 @@ export async function executeAction(id, options, context, flatArgs, control) {
|
|
|
85
91
|
input: options.input,
|
|
86
92
|
inputFile: options.inputFile,
|
|
87
93
|
flatArgs: effectiveFlatArgs && effectiveFlatArgs.length > 0 ? effectiveFlatArgs : undefined,
|
|
94
|
+
stdinField: options.stdinField,
|
|
88
95
|
stdin: context?.stdin,
|
|
96
|
+
signal: effectiveSignal,
|
|
89
97
|
});
|
|
90
98
|
const check = validateActionInputValue(input);
|
|
91
99
|
if (!check.valid) {
|
|
@@ -102,25 +110,13 @@ export async function executeAction(id, options, context, flatArgs, control) {
|
|
|
102
110
|
}
|
|
103
111
|
}
|
|
104
112
|
try {
|
|
113
|
+
// 目标拓扑与动作引用解析(远端目标完全隔离本地文件系统)
|
|
114
|
+
const target = resolveCommandTarget(options, context);
|
|
105
115
|
// 异步执行仅支持远端目标,本地模式直接拒绝
|
|
106
|
-
|
|
107
|
-
profile: options.profile,
|
|
108
|
-
server: options.server,
|
|
109
|
-
token: options.token,
|
|
110
|
-
insecure: options.insecure,
|
|
111
|
-
}, context?.customHome);
|
|
112
|
-
if (resolvedTopology.type === "local" && options.async) {
|
|
116
|
+
if (!target.isRemote && options.async) {
|
|
113
117
|
throw new ExecutionError("Async execution requires a long-running ActionDock server.\nUse --profile, --server, or start 'ad serve'.");
|
|
114
118
|
}
|
|
115
|
-
|
|
116
|
-
const targetPackageRoot = resolveLocalPackageRoot(options.package);
|
|
117
|
-
if (options.package && !targetPackageRoot) {
|
|
118
|
-
throw packageNotFoundError(options.package);
|
|
119
|
-
}
|
|
120
|
-
let targetRef = id;
|
|
121
|
-
if (options.package && !id.includes("/")) {
|
|
122
|
-
targetRef = `${options.package}/${id}`;
|
|
123
|
-
}
|
|
119
|
+
const targetRef = target.resolveActionRef(id);
|
|
124
120
|
// 通过 Service 统一执行
|
|
125
121
|
await withService(options, context, async (service) => {
|
|
126
122
|
const isMachine = Boolean(options.json);
|
|
@@ -196,7 +192,7 @@ export async function executeAction(id, options, context, flatArgs, control) {
|
|
|
196
192
|
return;
|
|
197
193
|
}
|
|
198
194
|
}
|
|
199
|
-
}, { localRoot: targetPackageRoot || undefined, scanLinkedPackages:
|
|
195
|
+
}, { localRoot: target.targetPackageRoot || undefined, scanLinkedPackages: !target.localSkill, ownDataDir: true });
|
|
200
196
|
}
|
|
201
197
|
catch (err) {
|
|
202
198
|
// sigint 取消源、中断链路特征错误码或 SigintError 本身,均统一归并为 SigintError
|
|
@@ -242,6 +238,11 @@ Flat Input Syntax & Examples:
|
|
|
242
238
|
# Extract specific text field to stdout (remaining fields as JSON to stderr)
|
|
243
239
|
ad run <id> --text-field content -- path="README.md"
|
|
244
240
|
|
|
241
|
+
Raw Text Input from stdin:
|
|
242
|
+
# Bind entire stdin content as a string to the given input field
|
|
243
|
+
printf 'body text' | ad run <id> --stdin-field text -- style=brief
|
|
244
|
+
cat report.md | ad run <id> --stdin-field content
|
|
245
|
+
|
|
245
246
|
Complex or multiline inputs:
|
|
246
247
|
# Pass via JSON file with --input-file to avoid shell escaping issues
|
|
247
248
|
ad run <id> --input-file input.json`;
|
|
@@ -258,7 +259,8 @@ export function attachRunCommand(parent, context) {
|
|
|
258
259
|
.option("-P, --package <id>", "Target package ID or path")
|
|
259
260
|
.option("-i, --input <json>", "Action input as inline JSON")
|
|
260
261
|
.option("-f, --input-file <path>", "Action input from JSON file, or '-' for stdin")
|
|
261
|
-
.option("-c, --config <key=value...>", "Temporary config override (repeatable)")
|
|
262
|
+
.option("-c, --config <key=value...>", "Temporary config override (repeatable)")
|
|
263
|
+
.option("--stdin-field <field>", "Bind raw stdin text to a top-level action input field");
|
|
262
264
|
cmd.unknownOption = function (flag) {
|
|
263
265
|
this.error(`unknown option '${flag}'\nHint: Separate action inputs from CLI options using '--', e.g.: ad run <id> [options] -- <param>=<val> or <param>:=<json>.`, { code: "commander.unknownOption" });
|
|
264
266
|
};
|