@actiondock/cli 2.13.0 → 2.15.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/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/info.js +97 -18
- package/dist/commands/list.js +16 -16
- package/dist/commands/playbook.js +148 -105
- package/dist/commands/run.js +7 -20
- package/dist/commands/state.js +19 -2
- package/dist/renderers/project.d.ts +4 -0
- package/dist/renderers/project.js +16 -0
- package/dist/services/inspect.d.ts +14 -1
- package/dist/services/inspect.js +54 -12
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/utils/target.d.ts +94 -4
- package/dist/utils/target.js +304 -26
- package/package.json +5 -5
- package/dist/utils/warning.d.ts +0 -4
- package/dist/utils/warning.js +0 -4
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/info.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isExplicitPath, } from "@actiondock/core";
|
|
2
2
|
import { filterWithFallbackInfo, } from "@actiondock/core/project";
|
|
3
|
+
import { fetchRemoteInfo, } from "@actiondock/core/profile";
|
|
3
4
|
import { getRegistryStatus, resolvePackageRoot, } from "@actiondock/core/registry";
|
|
4
|
-
import { ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, packageNotFoundError } from "../errors.js";
|
|
5
|
-
import { projectDetailToJson, renderAggregatedPackages, renderProjectDetail, renderRegistryTree, renderResult, } from "../renderer.js";
|
|
6
|
-
import { getProjectDetailInfo, scanLocalAggregatedPackages } from "../services/index.js";
|
|
7
|
-
import { applyTargetOptions, getEffectiveOptions, resolveFallbackStrategy, resolveIntent,
|
|
5
|
+
import { ArgumentError, ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, packageNotFoundError } from "../errors.js";
|
|
6
|
+
import { projectDetailToJson, renderAggregatedPackages, renderCompositeSkillDetail, renderProjectDetail, renderRegistryTree, renderResult, } from "../renderer.js";
|
|
7
|
+
import { buildCompositeDetails, filterProjectDetails, getProjectDetailInfo, scanLocalAggregatedPackages, } from "../services/index.js";
|
|
8
|
+
import { applyTargetOptions, getEffectiveOptions, resolveCommandTarget, resolveFallbackStrategy, resolveIntent, } from "../utils/index.js";
|
|
8
9
|
/**
|
|
9
10
|
* 将远端信息响应归一为本地工程详情视图。
|
|
10
11
|
*/
|
|
@@ -51,9 +52,49 @@ function remoteInfoToDetail(remoteInfo) {
|
|
|
51
52
|
* 渲染工程详情输出(机器模式输出视图转换结果,人类模式渲染详情)。
|
|
52
53
|
*/
|
|
53
54
|
function renderProjectDetailOutput(detail, options) {
|
|
54
|
-
|
|
55
|
+
const { matchedCount, isFallback } = filterProjectDetails([detail], options.effectiveIntent, Boolean(options.shouldFallback));
|
|
56
|
+
if (matchedCount === 0 && !isFallback) {
|
|
57
|
+
if (options.isMachine) {
|
|
58
|
+
renderResult({ root: detail.projectRoot, packages: [], matchedCount: 0, isFallback: false }, options);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
throw new ExecutionError(`No packages matched intent '${options.effectiveIntent}'`);
|
|
62
|
+
}
|
|
63
|
+
renderResult({
|
|
64
|
+
...projectDetailToJson(detail),
|
|
65
|
+
...(options.effectiveIntent ? { matchedCount, isFallback } : {}),
|
|
66
|
+
}, {
|
|
67
|
+
json: options.json,
|
|
68
|
+
humanFormatter: () => (isFallback ? `(No packages matched intent '${options.effectiveIntent}', showing selected package)\n\n` : "") +
|
|
69
|
+
renderProjectDetail(detail),
|
|
70
|
+
context: options.context,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 渲染复合技能整体输出(机器模式输出聚合结构,人类模式渲染各子包卡片)。
|
|
75
|
+
*/
|
|
76
|
+
function renderCompositeSkillOutput(root, details, options) {
|
|
77
|
+
const outOpts = { json: options.json, context: options.context };
|
|
78
|
+
const { items: filteredDetails, isFallback, matchedCount } = filterProjectDetails(details, options.effectiveIntent, Boolean(options.shouldFallback));
|
|
79
|
+
if (options.effectiveIntent && matchedCount === 0 && !isFallback) {
|
|
80
|
+
if (options.isMachine) {
|
|
81
|
+
renderResult({ root, packages: [], matchedCount: 0, isFallback: false }, outOpts);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
throw new ExecutionError(`No packages matched intent '${options.effectiveIntent}' in skill directory`);
|
|
85
|
+
}
|
|
86
|
+
if (options.isMachine) {
|
|
87
|
+
const machineResult = {
|
|
88
|
+
root,
|
|
89
|
+
packages: filteredDetails.map(projectDetailToJson),
|
|
90
|
+
...(options.effectiveIntent ? { matchedCount, isFallback } : {}),
|
|
91
|
+
};
|
|
92
|
+
renderResult(machineResult, outOpts);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
renderResult(filteredDetails, {
|
|
55
96
|
json: options.json,
|
|
56
|
-
humanFormatter: () =>
|
|
97
|
+
humanFormatter: () => renderCompositeSkillDetail(root, filteredDetails, isFallback, options.effectiveIntent),
|
|
57
98
|
context: options.context,
|
|
58
99
|
});
|
|
59
100
|
}
|
|
@@ -77,18 +118,25 @@ export function registerInfoCommand(program, context) {
|
|
|
77
118
|
.option("--data-dir <path>", "Custom database storage directory")
|
|
78
119
|
.action(async (patterns = [], rawOptions, cmd) => {
|
|
79
120
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
121
|
+
// 唯一显式路径位置参数属于寻址目标,必须先于远端与树状图分支裁决。
|
|
122
|
+
if (!options.package && !options.intent && patterns.length === 1 && isExplicitPath(patterns[0])) {
|
|
123
|
+
options.package = patterns[0];
|
|
124
|
+
patterns = [];
|
|
125
|
+
}
|
|
80
126
|
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
81
127
|
const { isMachine, shouldFallback } = resolveFallbackStrategy(options);
|
|
82
128
|
const outOpts = { json: options.json, context };
|
|
129
|
+
const detailOpts = { ...outOpts, effectiveIntent, shouldFallback, isMachine };
|
|
130
|
+
// info 仅查询单包清单,复合目录仍保留封闭集合校验。
|
|
131
|
+
const target = resolveCommandTarget(options, context, { metadataOnly: true });
|
|
83
132
|
// 远端服务目标分支
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const remoteInfo = await fetchRemoteInfo(target.serverUrl, target.token, {
|
|
133
|
+
if (target.isRemote) {
|
|
134
|
+
const remoteInfo = await fetchRemoteInfo(target.resolved.serverUrl, target.resolved.token, {
|
|
87
135
|
intent: effectiveIntent,
|
|
88
136
|
package: options.package,
|
|
89
137
|
tree: Boolean(options.tree),
|
|
90
138
|
allowInsecureHttp: Boolean(options.allowInsecureHttp),
|
|
91
|
-
insecure: target.insecure,
|
|
139
|
+
insecure: target.resolved.insecure,
|
|
92
140
|
});
|
|
93
141
|
if (isMachine) {
|
|
94
142
|
renderResult(remoteInfo, outOpts);
|
|
@@ -116,8 +164,11 @@ export function registerInfoCommand(program, context) {
|
|
|
116
164
|
});
|
|
117
165
|
return;
|
|
118
166
|
}
|
|
119
|
-
//
|
|
167
|
+
// 本地注册表树状图分支(若与显式技能或包组合,给出明确 ArgumentError)
|
|
120
168
|
if (options.tree) {
|
|
169
|
+
if (options.package || target.localSkill) {
|
|
170
|
+
throw new ArgumentError("Tree view (--tree) is not supported with explicit skill directories or packages; use 'ad info -P <path>' without --tree.");
|
|
171
|
+
}
|
|
121
172
|
const status = getRegistryStatus(context?.customHome);
|
|
122
173
|
renderResult(status, {
|
|
123
174
|
json: options.json,
|
|
@@ -126,12 +177,30 @@ export function registerInfoCommand(program, context) {
|
|
|
126
177
|
});
|
|
127
178
|
return;
|
|
128
179
|
}
|
|
129
|
-
//
|
|
180
|
+
// 显式复合技能分支
|
|
181
|
+
if (target.localSkill?.type === "composite") {
|
|
182
|
+
const details = await buildCompositeDetails(target.localSkill);
|
|
183
|
+
renderCompositeSkillOutput(target.localSkill.root, details, {
|
|
184
|
+
json: options.json,
|
|
185
|
+
context,
|
|
186
|
+
effectiveIntent,
|
|
187
|
+
shouldFallback,
|
|
188
|
+
isMachine,
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// 显式单包技能分支
|
|
193
|
+
if (target.localSkill?.type === "single") {
|
|
194
|
+
const pkg = target.localSkill.packages[0];
|
|
195
|
+
const detail = await getProjectDetailInfo(pkg.packageRoot);
|
|
196
|
+
renderProjectDetailOutput(detail, detailOpts);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// 显式指定 Package 参数分支(普通包 ID 或路径)
|
|
130
200
|
if (options.package) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
renderProjectDetailOutput(detail, outOpts);
|
|
201
|
+
if (target.targetPackageRoot) {
|
|
202
|
+
const detail = await getProjectDetailInfo(target.targetPackageRoot);
|
|
203
|
+
renderProjectDetailOutput(detail, detailOpts);
|
|
135
204
|
return;
|
|
136
205
|
}
|
|
137
206
|
throw packageNotFoundError(options.package);
|
|
@@ -163,8 +232,18 @@ export function registerInfoCommand(program, context) {
|
|
|
163
232
|
}
|
|
164
233
|
// 有关键字过滤场景:单一位置参数且非显式 --intent 时优先尝试包寻址
|
|
165
234
|
if (patterns.length === 1 && !options.intent) {
|
|
166
|
-
const directRoot = resolvePackageRoot(patterns[0]);
|
|
235
|
+
const directRoot = resolvePackageRoot(patterns[0], undefined, context?.customHome);
|
|
167
236
|
if (directRoot) {
|
|
237
|
+
const directTarget = resolveCommandTarget({ ...options, package: directRoot }, context, { metadataOnly: true });
|
|
238
|
+
if (directTarget.localSkill?.type === "composite") {
|
|
239
|
+
const details = await buildCompositeDetails(directTarget.localSkill);
|
|
240
|
+
renderCompositeSkillOutput(directTarget.localSkill.root, details, {
|
|
241
|
+
...outOpts,
|
|
242
|
+
shouldFallback,
|
|
243
|
+
isMachine,
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
168
247
|
const detail = await getProjectDetailInfo(directRoot);
|
|
169
248
|
renderProjectDetailOutput(detail, outOpts);
|
|
170
249
|
return;
|
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
|
/**
|