@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/dist/commands/state.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { decodeStateKey, } from "@actiondock/sdk";
|
|
2
|
-
import { loadProjectConfig, } from "@actiondock/core";
|
|
2
|
+
import { loadProjectConfig, inspectLocalPackageDirectory, } from "@actiondock/core";
|
|
3
3
|
import { filterWithFallbackInfo, } from "@actiondock/core/project";
|
|
4
4
|
import { listLinkedPackages, } from "@actiondock/core/registry";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { ArgumentError, ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE } from "../errors.js";
|
|
7
7
|
import { renderResult, renderStateList, writeStderr, writeStdout } from "../renderer.js";
|
|
8
|
-
import { applyTargetOptions, getEffectiveOptions, getTargetRoot, remoteTargetLabel, requirePackageRoot, resolveFallbackStrategy, resolveIntent, resolveLocalPackageRoot, resolveTargetFromOptions, withService, } from "../utils/index.js";
|
|
8
|
+
import { applyTargetOptions, assertNotCompositeSkill, getEffectiveOptions, getTargetRoot, remoteTargetLabel, requirePackageRoot, resolveFallbackStrategy, resolveIntent, resolveLocalPackageRoot, resolveTargetFromOptions, withService, } from "../utils/index.js";
|
|
9
9
|
/**
|
|
10
10
|
* 统一预解码状态键寻址(get、set、delete 共享的单一事实源)。
|
|
11
11
|
*
|
|
@@ -57,6 +57,22 @@ function resolveStateScope(resolved, options, rawKey) {
|
|
|
57
57
|
localRoot: undefined,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
+
assertNotCompositeSkill(options.package);
|
|
61
|
+
// 本地单包技能目录优先处理
|
|
62
|
+
if (options.package) {
|
|
63
|
+
const localSkill = inspectLocalPackageDirectory(options.package);
|
|
64
|
+
if (localSkill?.type === "single") {
|
|
65
|
+
const pkg = localSkill.packages[0];
|
|
66
|
+
return {
|
|
67
|
+
packageId: pkg.packageId,
|
|
68
|
+
scopeSuffix: `in package '${pkg.packageId}'`,
|
|
69
|
+
deleteSuffix: `from package '${pkg.packageId}'`,
|
|
70
|
+
effectiveKey: rawKey || "",
|
|
71
|
+
projConfig: pkg.projectConfig,
|
|
72
|
+
localRoot: pkg.packageRoot,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
60
76
|
// 本地项目模式:getTargetRoot 会从键中剥离 pkg/ 前缀,寻址基于解耦后的裸键
|
|
61
77
|
const { root, key: effectiveKey } = getTargetRoot(options.package, rawKey);
|
|
62
78
|
const projConfig = loadProjectConfig(root);
|
|
@@ -175,6 +191,7 @@ export function registerStateCommands(program, context) {
|
|
|
175
191
|
return;
|
|
176
192
|
}
|
|
177
193
|
// 本地工程与链接包模式:按目标根目录决策输出范围
|
|
194
|
+
assertNotCompositeSkill(options.package);
|
|
178
195
|
const targetRoot = options.package
|
|
179
196
|
? requirePackageRoot(options.package).root
|
|
180
197
|
: resolveLocalPackageRoot();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { checkGeneratedTypes, GENERATED_TYPES_OUTDATED_CODE, loadActions, } from "@actiondock/core/project";
|
|
1
|
+
import { checkGeneratedTypes, GENERATED_TYPES_OUTDATED_CODE, loadActions, analyzeActionOutputContract, } from "@actiondock/core/project";
|
|
2
2
|
import { ArgumentError, ExecutionError } from "../errors.js";
|
|
3
3
|
import { renderActionValidation, renderResult } from "../renderer.js";
|
|
4
4
|
import { getEffectiveOptions, requirePackageRoot } from "../utils/index.js";
|
|
@@ -49,10 +49,22 @@ export function attachValidateCommand(parent, context) {
|
|
|
49
49
|
if (outSchema && typeof outSchema !== "object") {
|
|
50
50
|
errors.push("Invalid outputSchema object");
|
|
51
51
|
}
|
|
52
|
+
const annotations = item.spec?.annotations ?? item.act?.annotations;
|
|
53
|
+
const contractAnalysis = analyzeActionOutputContract({
|
|
54
|
+
annotations,
|
|
55
|
+
outputSchema: outSchema,
|
|
56
|
+
});
|
|
57
|
+
if (contractAnalysis.errors.length > 0) {
|
|
58
|
+
for (const err of contractAnalysis.errors) {
|
|
59
|
+
errors.push(err);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const warnings = contractAnalysis.warnings.slice();
|
|
52
63
|
results.push({
|
|
53
64
|
id: item.id,
|
|
54
65
|
valid: errors.length === 0,
|
|
55
66
|
errors,
|
|
67
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
56
68
|
});
|
|
57
69
|
}
|
|
58
70
|
const allValid = results.every((r) => r.valid);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
1
|
+
import { formatActionDetail, type FormatActionDetailOptions } from "@actiondock/core/project";
|
|
2
|
+
export { formatActionDetail, type FormatActionDetailOptions, };
|
|
3
3
|
/**
|
|
4
4
|
* 解析 Action 注解中的默认正文输出字段声明。
|
|
5
5
|
*
|
|
@@ -7,7 +7,7 @@ import { buildActionDescribePayload } from "@actiondock/core/project";
|
|
|
7
7
|
* - 注解键名固定为 actiondock.cli,值必须为非空非数组对象。
|
|
8
8
|
* - 正文字段键名为 textField,值必须为非空字符串。
|
|
9
9
|
* - 无相关注解或未声明 textField 时返回 undefined。
|
|
10
|
-
* - 注解存在但类型非法时抛出 ExecutionError。
|
|
10
|
+
* - 注解存在但类型非法时抛出 ExecutionError (INVALID_ANNOTATION)。
|
|
11
11
|
*/
|
|
12
12
|
export declare function resolveActionDefaultTextField(annotations?: Record<string, unknown>): string | undefined;
|
|
13
13
|
/**
|
|
@@ -35,30 +35,9 @@ export declare function renderActionList(items: Array<{
|
|
|
35
35
|
description: string;
|
|
36
36
|
packageId?: string;
|
|
37
37
|
}>, title?: string, isFallback?: boolean, intent?: string): string;
|
|
38
|
-
/**
|
|
39
|
-
* 格式化渲染 Action 详情人类可读文本。
|
|
40
|
-
*
|
|
41
|
-
* @param input Action 描述载荷或 Action 规范
|
|
42
|
-
* @returns 格式化后的说明文本
|
|
43
|
-
*/
|
|
44
|
-
export declare function formatActionDetail(input: ReturnType<typeof buildActionDescribePayload> | (Partial<ActionSpec> & {
|
|
45
|
-
id: string;
|
|
46
|
-
}) | {
|
|
47
|
-
id: string;
|
|
48
|
-
packageId?: string;
|
|
49
|
-
projectRoot?: string;
|
|
50
|
-
description?: string;
|
|
51
|
-
inputSchema?: unknown;
|
|
52
|
-
outputSchema?: unknown;
|
|
53
|
-
tags?: string[];
|
|
54
|
-
annotations?: Record<string, unknown>;
|
|
55
|
-
uses?: string[];
|
|
56
|
-
entry?: string;
|
|
57
|
-
[key: string]: any;
|
|
58
|
-
}): string;
|
|
59
38
|
/**
|
|
60
39
|
* 格式化渲染 Action 详情(编码顾问 Encoding Advisor)。
|
|
61
|
-
*
|
|
40
|
+
* 统一复用核心 formatActionDetail 实现。
|
|
62
41
|
*/
|
|
63
42
|
export declare function renderActionDetail(action: {
|
|
64
43
|
id: string;
|
|
@@ -75,4 +54,5 @@ export declare function renderActionValidation(results: Array<{
|
|
|
75
54
|
id: string;
|
|
76
55
|
valid: boolean;
|
|
77
56
|
errors: string[];
|
|
57
|
+
warnings?: string[];
|
|
78
58
|
}>): string;
|
package/dist/renderers/action.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inspectActionTextFieldAnnotation, formatActionDetail, } from "@actiondock/core/project";
|
|
2
|
+
export { formatActionDetail, };
|
|
2
3
|
import { ExecutionError } from "../errors.js";
|
|
3
4
|
/**
|
|
4
5
|
* 解析 Action 注解中的默认正文输出字段声明。
|
|
@@ -7,33 +8,14 @@ import { ExecutionError } from "../errors.js";
|
|
|
7
8
|
* - 注解键名固定为 actiondock.cli,值必须为非空非数组对象。
|
|
8
9
|
* - 正文字段键名为 textField,值必须为非空字符串。
|
|
9
10
|
* - 无相关注解或未声明 textField 时返回 undefined。
|
|
10
|
-
* - 注解存在但类型非法时抛出 ExecutionError。
|
|
11
|
+
* - 注解存在但类型非法时抛出 ExecutionError (INVALID_ANNOTATION)。
|
|
11
12
|
*/
|
|
12
13
|
export function resolveActionDefaultTextField(annotations) {
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
const inspection = inspectActionTextFieldAnnotation(annotations);
|
|
15
|
+
if (!inspection.valid) {
|
|
16
|
+
throw new ExecutionError(inspection.error, undefined, "INVALID_ANNOTATION");
|
|
15
17
|
}
|
|
16
|
-
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
19
|
-
const cli = annotations["actiondock.cli"];
|
|
20
|
-
if (cli === undefined) {
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
if (typeof cli !== "object" || cli === null || Array.isArray(cli)) {
|
|
24
|
-
throw new ExecutionError(`Invalid 'actiondock.cli' annotation: expected an object, but received ${cli === null ? "null" : Array.isArray(cli) ? "an array" : typeof cli}.`, undefined, "INVALID_ANNOTATION");
|
|
25
|
-
}
|
|
26
|
-
if (!Object.hasOwn(cli, "textField") || cli.textField === undefined) {
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
const textField = cli.textField;
|
|
30
|
-
if (typeof textField !== "string") {
|
|
31
|
-
throw new ExecutionError(`Invalid 'actiondock.cli.textField' annotation: expected a string, but received ${textField === null ? "null" : Array.isArray(textField) ? "an array" : typeof textField}.`, undefined, "INVALID_ANNOTATION");
|
|
32
|
-
}
|
|
33
|
-
if (textField.trim() === "") {
|
|
34
|
-
throw new ExecutionError("Invalid 'actiondock.cli.textField' annotation: cannot be an empty string.", undefined, "INVALID_ANNOTATION");
|
|
35
|
-
}
|
|
36
|
-
return textField;
|
|
18
|
+
return inspection.textField;
|
|
37
19
|
}
|
|
38
20
|
/**
|
|
39
21
|
* 从 Action 执行数据中抽取指定自有顶层正文字段及剩余元数据。
|
|
@@ -94,78 +76,9 @@ export function renderActionList(items, title = "Actions", isFallback = false, i
|
|
|
94
76
|
lines.push("\nTip: For composite or multi-step tasks, check 'ad playbook list' for standard operating procedures.");
|
|
95
77
|
return lines.join("\n");
|
|
96
78
|
}
|
|
97
|
-
/**
|
|
98
|
-
* 格式化渲染 Action 详情人类可读文本。
|
|
99
|
-
*
|
|
100
|
-
* @param input Action 描述载荷或 Action 规范
|
|
101
|
-
* @returns 格式化后的说明文本
|
|
102
|
-
*/
|
|
103
|
-
export function formatActionDetail(input) {
|
|
104
|
-
const payload = "inputAdvice" in input && input.inputAdvice
|
|
105
|
-
? input
|
|
106
|
-
: buildActionDescribePayload(input);
|
|
107
|
-
const lines = [];
|
|
108
|
-
lines.push(`Action: ${payload.id}`);
|
|
109
|
-
if (payload.packageId) {
|
|
110
|
-
lines.push(`Package: ${payload.packageId}`);
|
|
111
|
-
}
|
|
112
|
-
if (payload.description) {
|
|
113
|
-
lines.push(`Description: ${payload.description}`);
|
|
114
|
-
}
|
|
115
|
-
if (payload.tags && payload.tags.length > 0) {
|
|
116
|
-
lines.push(`Tags: ${payload.tags.join(", ")}`);
|
|
117
|
-
}
|
|
118
|
-
if (payload.uses && payload.uses.length > 0) {
|
|
119
|
-
lines.push(`Uses: ${payload.uses.join(", ")}`);
|
|
120
|
-
}
|
|
121
|
-
if (payload.entry) {
|
|
122
|
-
lines.push(`Entry: ${payload.entry}`);
|
|
123
|
-
}
|
|
124
|
-
if (payload.inputSchema !== undefined) {
|
|
125
|
-
lines.push("");
|
|
126
|
-
lines.push("Input Schema:");
|
|
127
|
-
lines.push(typeof payload.inputSchema === "string"
|
|
128
|
-
? payload.inputSchema
|
|
129
|
-
: JSON.stringify(payload.inputSchema, null, 2));
|
|
130
|
-
}
|
|
131
|
-
if (payload.outputSchema !== undefined) {
|
|
132
|
-
lines.push("");
|
|
133
|
-
lines.push("Output Schema:");
|
|
134
|
-
lines.push(typeof payload.outputSchema === "string"
|
|
135
|
-
? payload.outputSchema
|
|
136
|
-
: JSON.stringify(payload.outputSchema, null, 2));
|
|
137
|
-
}
|
|
138
|
-
lines.push("");
|
|
139
|
-
lines.push(`Recommended Input: ${payload.inputAdvice.recommendedMode}`);
|
|
140
|
-
if (payload.inputAdvice.reason) {
|
|
141
|
-
lines.push(`Reason: ${payload.inputAdvice.reason}`);
|
|
142
|
-
}
|
|
143
|
-
const hasAssignments = Boolean(payload.inputAdvice.assignments &&
|
|
144
|
-
Object.keys(payload.inputAdvice.assignments).length > 0);
|
|
145
|
-
if (hasAssignments) {
|
|
146
|
-
lines.push("");
|
|
147
|
-
lines.push("Assignments:");
|
|
148
|
-
for (const [key, op] of Object.entries(payload.inputAdvice.assignments)) {
|
|
149
|
-
lines.push(` ${key}${op}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (payload.inputAdvice.recommendedMode === "flat" || hasAssignments) {
|
|
153
|
-
lines.push("");
|
|
154
|
-
lines.push("Syntax Reference:");
|
|
155
|
-
lines.push(...(payload.syntaxReference || ACTION_DESCRIBE_SYNTAX_REFERENCE));
|
|
156
|
-
}
|
|
157
|
-
if (payload.inputAdvice.issues && payload.inputAdvice.issues.length > 0) {
|
|
158
|
-
lines.push("");
|
|
159
|
-
lines.push("Issues:");
|
|
160
|
-
for (const issue of payload.inputAdvice.issues) {
|
|
161
|
-
lines.push(` - ${issue.path}: ${issue.code}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return lines.join("\n");
|
|
165
|
-
}
|
|
166
79
|
/**
|
|
167
80
|
* 格式化渲染 Action 详情(编码顾问 Encoding Advisor)。
|
|
168
|
-
*
|
|
81
|
+
* 统一复用核心 formatActionDetail 实现。
|
|
169
82
|
*/
|
|
170
83
|
export function renderActionDetail(action) {
|
|
171
84
|
return formatActionDetail(action);
|
|
@@ -177,10 +90,19 @@ export function renderActionValidation(results) {
|
|
|
177
90
|
const lines = [];
|
|
178
91
|
for (const r of results) {
|
|
179
92
|
if (r.valid) {
|
|
180
|
-
|
|
93
|
+
if (r.warnings && r.warnings.length > 0) {
|
|
94
|
+
lines.push(`[OK] ${r.id}: Valid (warnings: ${r.warnings.join("; ")})`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
lines.push(`[OK] ${r.id}: Valid`);
|
|
98
|
+
}
|
|
181
99
|
}
|
|
182
100
|
else {
|
|
183
|
-
|
|
101
|
+
let msg = `[FAIL] ${r.id}: ${r.errors.join(", ")}`;
|
|
102
|
+
if (r.warnings && r.warnings.length > 0) {
|
|
103
|
+
msg += ` (warnings: ${r.warnings.join("; ")})`;
|
|
104
|
+
}
|
|
105
|
+
lines.push(msg);
|
|
184
106
|
}
|
|
185
107
|
}
|
|
186
108
|
return lines.join("\n");
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
package/dist/utils/target.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ActionDockService } from "@actiondock/core";
|
|
1
|
+
import { loadProjectConfig, type ActionDockService, type LocalSkillDirectory } from "@actiondock/core";
|
|
2
2
|
import { type ResolvedTarget } from "@actiondock/core/profile";
|
|
3
3
|
import type { Command } from "commander";
|
|
4
4
|
import type { CliContext } from "../types.js";
|
|
@@ -33,14 +33,68 @@ export interface TargetResolutionOptions {
|
|
|
33
33
|
/**
|
|
34
34
|
* 目标拓扑解析单一入口:从命令选项视图解析远端/本地目标拓扑信息。
|
|
35
35
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* 显式路径与包标识符分类规则:
|
|
37
|
+
* - 显式路径形态(以 .、/、~ 开头或包含非 scoped 分隔符):
|
|
38
|
+
* 严格作为本地技能或包目录解析;禁止与显式远端选项(--server 或非 local 的 --profile)组合,违规抛出 ArgumentError;
|
|
39
|
+
* 不受隐式 Profile 或 ACTIONDOCK_SERVER_URL 环境变量改变目标,本地执行优先;
|
|
40
|
+
* 若本地目录不存在或不合法,抛出 packageNotFoundError;
|
|
41
|
+
* - 纯词法包标识符(如 pkg.a、@scope/pkg):
|
|
42
|
+
* 若存在远端目标(显式 --server、非 local profile 或默认远端环境),严格作为远端包标识解析,绝不因本地 cwd 存在同名目录而劫持为本地执行;
|
|
43
|
+
* 若无远端目标,正常按本地目标解析。
|
|
38
44
|
*
|
|
39
45
|
* @param options 命令选项视图(支持直接传入 Commander 解析后的 options 对象)
|
|
40
46
|
* @param context CLI 上下文
|
|
41
47
|
* @returns 解析后的目标拓扑信息(仅信息解析,不创建 Service 实例)
|
|
42
48
|
*/
|
|
43
49
|
export declare function resolveTargetFromOptions(options: TargetResolutionOptions, context?: CliContext): ResolvedTarget;
|
|
50
|
+
/**
|
|
51
|
+
* 命令目标与动作引用解析结果。
|
|
52
|
+
*/
|
|
53
|
+
export interface CommandTargetResolution {
|
|
54
|
+
/** 目标拓扑信息 */
|
|
55
|
+
resolved: ResolvedTarget;
|
|
56
|
+
/** 是否为远端目标 */
|
|
57
|
+
isRemote: boolean;
|
|
58
|
+
/** 本地封闭技能目录视图(非本地技能目录时为 null) */
|
|
59
|
+
localSkill: LocalSkillDirectory | null;
|
|
60
|
+
/** 本地包根目录路径(远端分支为 undefined,未找到为 undefined) */
|
|
61
|
+
targetPackageRoot?: string;
|
|
62
|
+
/** 统一计算目标 Action 引用的辅助函数 */
|
|
63
|
+
resolveActionRef: (actionId: string) => string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 统一解析 CLI 命令的目标拓扑与包作用域(run, describe, list 等命令的单一事实源)。
|
|
67
|
+
*
|
|
68
|
+
* 保证:
|
|
69
|
+
* - 优先判定目标拓扑;
|
|
70
|
+
* - 远端目标(remote)完全隔离:不执行任何本地目录探测、清单加载或包根寻址;
|
|
71
|
+
* - 远端目标动作引用保持原始词法包 ID 拼接(${options.package}/${actionId}),绝不被本地同名目录或清单 ID 篡改;
|
|
72
|
+
* - 本地目标按本地封闭技能目录或工程包严格解析,不存在时透明抛出 packageNotFoundError。
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveCommandTarget(options: TargetResolutionOptions, context?: CliContext): CommandTargetResolution;
|
|
75
|
+
/**
|
|
76
|
+
* 配置与状态管理目标作用域。
|
|
77
|
+
*/
|
|
78
|
+
export interface TargetPackageScope {
|
|
79
|
+
/** 解析后的目标拓扑 */
|
|
80
|
+
resolved: ResolvedTarget;
|
|
81
|
+
/** 是否为远端目标 */
|
|
82
|
+
isRemote: boolean;
|
|
83
|
+
/** 目标包标识(远端为 options.package 原值,本地为工程配置 id 或 "global") */
|
|
84
|
+
packageId: string;
|
|
85
|
+
/** 本地工程根目录(远端分支为 undefined) */
|
|
86
|
+
root?: string;
|
|
87
|
+
/** 本地工程配置(远端分支为 undefined) */
|
|
88
|
+
projConfig?: ReturnType<typeof loadProjectConfig>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 校验并拒绝复合技能目录用于配置或状态管理命令(单一事实源)。
|
|
92
|
+
*/
|
|
93
|
+
export declare function assertNotCompositeSkill(packageOption?: string): void;
|
|
94
|
+
/**
|
|
95
|
+
* 统一解析配置与状态命令的目标作用域(支持单包技能目录、本地工程、远端服务与 --profile local)。
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveTargetPackageScope(options: TargetResolutionOptions, context?: CliContext): TargetPackageScope;
|
|
44
98
|
/**
|
|
45
99
|
* 高阶服务执行辅助:统一封装「目标解析 → Service 创建 → 业务回调 → try/finally close」样板。
|
|
46
100
|
*
|
package/dist/utils/target.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { connectActionDock, createActionDock, createNodePlatform, findProjectRoot, } from "@actiondock/core";
|
|
1
|
+
import { connectActionDock, createActionDock, createNodePlatform, findProjectRoot, inspectLocalPackageDirectory, isExplicitPath, loadProjectConfig, } from "@actiondock/core";
|
|
2
2
|
import { resolvePackageRoot, } from "@actiondock/core/registry";
|
|
3
3
|
import { resolveTarget, } from "@actiondock/core/profile";
|
|
4
|
-
import { ArgumentError } from "../errors.js";
|
|
4
|
+
import { ArgumentError, packageNotFoundError } from "../errors.js";
|
|
5
5
|
/**
|
|
6
6
|
* 统一为 Commander 命令节点挂载目标环境解析相关选项(单一事实源)。
|
|
7
7
|
* 包括:-p/--profile, -s/--server, -t/--token, -k/--insecure, --allow-insecure-http。
|
|
@@ -20,14 +20,36 @@ export function applyTargetOptions(command) {
|
|
|
20
20
|
/**
|
|
21
21
|
* 目标拓扑解析单一入口:从命令选项视图解析远端/本地目标拓扑信息。
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* 显式路径与包标识符分类规则:
|
|
24
|
+
* - 显式路径形态(以 .、/、~ 开头或包含非 scoped 分隔符):
|
|
25
|
+
* 严格作为本地技能或包目录解析;禁止与显式远端选项(--server 或非 local 的 --profile)组合,违规抛出 ArgumentError;
|
|
26
|
+
* 不受隐式 Profile 或 ACTIONDOCK_SERVER_URL 环境变量改变目标,本地执行优先;
|
|
27
|
+
* 若本地目录不存在或不合法,抛出 packageNotFoundError;
|
|
28
|
+
* - 纯词法包标识符(如 pkg.a、@scope/pkg):
|
|
29
|
+
* 若存在远端目标(显式 --server、非 local profile 或默认远端环境),严格作为远端包标识解析,绝不因本地 cwd 存在同名目录而劫持为本地执行;
|
|
30
|
+
* 若无远端目标,正常按本地目标解析。
|
|
25
31
|
*
|
|
26
32
|
* @param options 命令选项视图(支持直接传入 Commander 解析后的 options 对象)
|
|
27
33
|
* @param context CLI 上下文
|
|
28
34
|
* @returns 解析后的目标拓扑信息(仅信息解析,不创建 Service 实例)
|
|
29
35
|
*/
|
|
30
36
|
export function resolveTargetFromOptions(options, context) {
|
|
37
|
+
const isExplicit = options.package ? isExplicitPath(options.package) : false;
|
|
38
|
+
if (isExplicit) {
|
|
39
|
+
const hasExplicitServer = Boolean(options.server && options.server.trim());
|
|
40
|
+
const hasExplicitProfile = Boolean(options.profile && options.profile.trim() && options.profile.trim() !== "local");
|
|
41
|
+
if (hasExplicitServer || hasExplicitProfile) {
|
|
42
|
+
throw new ArgumentError("Cannot combine local package/skill directory with remote target options (--server or --profile). Specify a package ID for remote targets, or remove remote target options for local directory execution.");
|
|
43
|
+
}
|
|
44
|
+
const localSkill = inspectLocalPackageDirectory(options.package);
|
|
45
|
+
if (!localSkill) {
|
|
46
|
+
throw packageNotFoundError(options.package);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
type: "local",
|
|
50
|
+
profileName: "local",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
31
53
|
return resolveTarget({
|
|
32
54
|
profile: options.profile,
|
|
33
55
|
server: options.server,
|
|
@@ -36,6 +58,143 @@ export function resolveTargetFromOptions(options, context) {
|
|
|
36
58
|
allowInsecureHttp: options.allowInsecureHttp,
|
|
37
59
|
}, context?.customHome);
|
|
38
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* 统一解析 CLI 命令的目标拓扑与包作用域(run, describe, list 等命令的单一事实源)。
|
|
63
|
+
*
|
|
64
|
+
* 保证:
|
|
65
|
+
* - 优先判定目标拓扑;
|
|
66
|
+
* - 远端目标(remote)完全隔离:不执行任何本地目录探测、清单加载或包根寻址;
|
|
67
|
+
* - 远端目标动作引用保持原始词法包 ID 拼接(${options.package}/${actionId}),绝不被本地同名目录或清单 ID 篡改;
|
|
68
|
+
* - 本地目标按本地封闭技能目录或工程包严格解析,不存在时透明抛出 packageNotFoundError。
|
|
69
|
+
*/
|
|
70
|
+
export function resolveCommandTarget(options, context) {
|
|
71
|
+
const resolved = resolveTargetFromOptions(options, context);
|
|
72
|
+
if (resolved.type === "remote") {
|
|
73
|
+
return {
|
|
74
|
+
resolved,
|
|
75
|
+
isRemote: true,
|
|
76
|
+
localSkill: null,
|
|
77
|
+
targetPackageRoot: undefined,
|
|
78
|
+
resolveActionRef: (actionId) => {
|
|
79
|
+
if (options.package && !actionId.includes("/")) {
|
|
80
|
+
return `${options.package}/${actionId}`;
|
|
81
|
+
}
|
|
82
|
+
return actionId;
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// 本地分支
|
|
87
|
+
const localSkill = options.package ? inspectLocalPackageDirectory(options.package) : null;
|
|
88
|
+
const targetPackageRoot = localSkill
|
|
89
|
+
? localSkill.root
|
|
90
|
+
: options.package
|
|
91
|
+
? resolveLocalPackageRoot(options.package)
|
|
92
|
+
: findProjectRoot();
|
|
93
|
+
if (options.package && !targetPackageRoot) {
|
|
94
|
+
throw packageNotFoundError(options.package);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
resolved,
|
|
98
|
+
isRemote: false,
|
|
99
|
+
localSkill,
|
|
100
|
+
targetPackageRoot: targetPackageRoot || undefined,
|
|
101
|
+
resolveActionRef: (actionId) => {
|
|
102
|
+
if (!options.package)
|
|
103
|
+
return actionId;
|
|
104
|
+
if (localSkill) {
|
|
105
|
+
if (localSkill.type === "single" && !actionId.includes("/")) {
|
|
106
|
+
return `${localSkill.packages[0].packageId}/${actionId}`;
|
|
107
|
+
}
|
|
108
|
+
// 复合技能短名不拼目录名,保持 actionId 供 Host resolveAction 裁决唯一匹配或多包歧义
|
|
109
|
+
return actionId;
|
|
110
|
+
}
|
|
111
|
+
if (!actionId.includes("/")) {
|
|
112
|
+
return `${options.package}/${actionId}`;
|
|
113
|
+
}
|
|
114
|
+
return actionId;
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 校验并拒绝复合技能目录用于配置或状态管理命令(单一事实源)。
|
|
120
|
+
*/
|
|
121
|
+
export function assertNotCompositeSkill(packageOption) {
|
|
122
|
+
if (!packageOption)
|
|
123
|
+
return;
|
|
124
|
+
const localSkill = inspectLocalPackageDirectory(packageOption);
|
|
125
|
+
if (localSkill?.type === "composite") {
|
|
126
|
+
throw new ArgumentError("Multi-package entries do not support config/state management; configure actions using --config and --data-dir.");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 统一解析配置与状态命令的目标作用域(支持单包技能目录、本地工程、远端服务与 --profile local)。
|
|
131
|
+
*/
|
|
132
|
+
export function resolveTargetPackageScope(options, context) {
|
|
133
|
+
// 1. 目标拓扑解析
|
|
134
|
+
const resolved = resolveTargetFromOptions(options, context);
|
|
135
|
+
// 2. 远端分支:直接消费 options.package,不触碰本地文件系统
|
|
136
|
+
if (resolved.type === "remote") {
|
|
137
|
+
return {
|
|
138
|
+
resolved,
|
|
139
|
+
isRemote: true,
|
|
140
|
+
packageId: options.package || "",
|
|
141
|
+
root: undefined,
|
|
142
|
+
projConfig: undefined,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// 3. 复合技能目录守卫(单一事实源)仅在本地分支生效
|
|
146
|
+
assertNotCompositeSkill(options.package);
|
|
147
|
+
// 4. 本地分支:统一解析单包目录、工程包或全局作用域
|
|
148
|
+
if (options.package) {
|
|
149
|
+
const localSkill = inspectLocalPackageDirectory(options.package);
|
|
150
|
+
if (localSkill?.type === "single") {
|
|
151
|
+
const pkg = localSkill.packages[0];
|
|
152
|
+
return {
|
|
153
|
+
resolved,
|
|
154
|
+
isRemote: false,
|
|
155
|
+
packageId: pkg.packageId,
|
|
156
|
+
root: pkg.packageRoot,
|
|
157
|
+
projConfig: pkg.projectConfig,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const root = resolveLocalPackageRoot(options.package);
|
|
161
|
+
if (!root) {
|
|
162
|
+
throw packageNotFoundError(options.package);
|
|
163
|
+
}
|
|
164
|
+
const projConfig = loadProjectConfig(root);
|
|
165
|
+
return {
|
|
166
|
+
resolved,
|
|
167
|
+
isRemote: false,
|
|
168
|
+
packageId: projConfig.id,
|
|
169
|
+
root,
|
|
170
|
+
projConfig,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// 未指定 -P:当前目录工程探测
|
|
174
|
+
const root = findProjectRoot();
|
|
175
|
+
if (root) {
|
|
176
|
+
try {
|
|
177
|
+
const projConfig = loadProjectConfig(root);
|
|
178
|
+
return {
|
|
179
|
+
resolved,
|
|
180
|
+
isRemote: false,
|
|
181
|
+
packageId: projConfig.id,
|
|
182
|
+
root,
|
|
183
|
+
projConfig,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// 当前目录配置损坏时仍允许全局操作
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
resolved,
|
|
192
|
+
isRemote: false,
|
|
193
|
+
packageId: "global",
|
|
194
|
+
root: undefined,
|
|
195
|
+
projConfig: undefined,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
39
198
|
/**
|
|
40
199
|
* 高阶服务执行辅助:统一封装「目标解析 → Service 创建 → 业务回调 → try/finally close」样板。
|
|
41
200
|
*
|
|
@@ -56,10 +215,13 @@ export async function withService(options, context, fn, localOptions) {
|
|
|
56
215
|
if (localOptions?.requireRemote && resolved.type === "local") {
|
|
57
216
|
throw new ArgumentError("'ad runs cancel' is only supported for remote execution targets. Use --profile <name> or --server <url>.");
|
|
58
217
|
}
|
|
218
|
+
const localSkill = resolved.type === "local" && options.package
|
|
219
|
+
? inspectLocalPackageDirectory(options.package)
|
|
220
|
+
: null;
|
|
59
221
|
// localRoot 仅在 local 分支求值,避免远端模式下触发包寻址副作用
|
|
60
|
-
const localRoot = resolved.type === "local"
|
|
222
|
+
const localRoot = resolved.type === "local" && !localSkill
|
|
61
223
|
? (typeof localOptions?.localRoot === "function" ? localOptions.localRoot() : localOptions?.localRoot) ??
|
|
62
|
-
findProjectRoot() ??
|
|
224
|
+
(options.package ? resolveLocalPackageRoot(options.package) : findProjectRoot()) ??
|
|
63
225
|
undefined
|
|
64
226
|
: undefined;
|
|
65
227
|
const recoverOrphans = localOptions?.recoverOrphans ?? (localOptions?.ownDataDir === true);
|
|
@@ -70,20 +232,38 @@ export async function withService(options, context, fn, localOptions) {
|
|
|
70
232
|
insecure: resolved.insecure,
|
|
71
233
|
allowInsecureHttp: resolved.allowInsecureHttp ?? options.allowInsecureHttp,
|
|
72
234
|
})
|
|
73
|
-
:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
235
|
+
: localSkill
|
|
236
|
+
? await createActionDock({
|
|
237
|
+
type: "local",
|
|
238
|
+
packages: localSkill.packages.map((pkg) => ({
|
|
239
|
+
packageRoot: pkg.packageRoot,
|
|
240
|
+
projectConfig: pkg.projectConfig,
|
|
241
|
+
dataDir: options.dataDir || context?.dataDir,
|
|
242
|
+
})),
|
|
243
|
+
customHome: context?.customHome,
|
|
244
|
+
dataDir: options.dataDir || context?.dataDir,
|
|
245
|
+
platform: createNodePlatform({
|
|
246
|
+
customHome: context?.customHome,
|
|
247
|
+
dataDir: options.dataDir || context?.dataDir,
|
|
248
|
+
}),
|
|
249
|
+
autoLoadCurrentProject: false,
|
|
250
|
+
scanLinkedPackages: false,
|
|
251
|
+
recoverOrphans,
|
|
252
|
+
})
|
|
253
|
+
: await createActionDock({
|
|
254
|
+
type: "local",
|
|
255
|
+
projectRoot: localRoot,
|
|
79
256
|
customHome: context?.customHome,
|
|
80
257
|
dataDir: options.dataDir || context?.dataDir,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
258
|
+
platform: createNodePlatform({
|
|
259
|
+
customHome: context?.customHome,
|
|
260
|
+
dataDir: options.dataDir || context?.dataDir,
|
|
261
|
+
}),
|
|
262
|
+
...(localOptions?.scanLinkedPackages !== undefined
|
|
263
|
+
? { scanLinkedPackages: localOptions.scanLinkedPackages }
|
|
264
|
+
: undefined),
|
|
265
|
+
recoverOrphans,
|
|
266
|
+
});
|
|
87
267
|
try {
|
|
88
268
|
await fn(service, resolved);
|
|
89
269
|
}
|
|
@@ -97,6 +277,10 @@ export async function withService(options, context, fn, localOptions) {
|
|
|
97
277
|
*/
|
|
98
278
|
export function resolveLocalPackageRoot(pkgId) {
|
|
99
279
|
if (pkgId) {
|
|
280
|
+
const localSkill = inspectLocalPackageDirectory(pkgId);
|
|
281
|
+
if (localSkill) {
|
|
282
|
+
return localSkill.root;
|
|
283
|
+
}
|
|
100
284
|
return resolvePackageRoot(pkgId);
|
|
101
285
|
}
|
|
102
286
|
return findProjectRoot();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actiondock/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "ActionDock CLI facade for developing, testing, building, and exporting standalone Agent Actions and Skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"test": "node ../../scripts/run-tests.ts"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@actiondock/builder": "^2.
|
|
38
|
-
"@actiondock/core": "^2.
|
|
39
|
-
"@actiondock/mcp": "^2.
|
|
40
|
-
"@actiondock/sdk": "^2.
|
|
37
|
+
"@actiondock/builder": "^2.14.0",
|
|
38
|
+
"@actiondock/core": "^2.14.0",
|
|
39
|
+
"@actiondock/mcp": "^2.14.0",
|
|
40
|
+
"@actiondock/sdk": "^2.14.0",
|
|
41
41
|
"commander": "^13.1.0",
|
|
42
42
|
"selfsigned": "^5.5.0"
|
|
43
43
|
},
|
package/dist/utils/warning.d.ts
DELETED
package/dist/utils/warning.js
DELETED