@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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
3
|
-
import { assertPathWithinRoot,
|
|
4
|
-
import { DefaultActionCatalog,
|
|
3
|
+
import { assertPathWithinRoot, loadManifest, loadPlaybooks, saveManifest, filterWithFallbackInfo, } from "@actiondock/core/project";
|
|
4
|
+
import { DefaultActionCatalog, resolveAction, resolvePlaybook, } from "@actiondock/core/graph";
|
|
5
5
|
import { fetchRemotePlaybooks, fetchRemotePlaybookShow, } from "@actiondock/core/profile";
|
|
6
6
|
import { loadProjectConfig, } from "@actiondock/core";
|
|
7
7
|
import { listLinkedPackages, } from "@actiondock/core/registry";
|
|
8
8
|
import { ArgumentError, CliError, ExecutionError, NO_PROJECT_NO_LINKED_MESSAGE, wrapAsExecutionError, } from "../errors.js";
|
|
9
|
-
import { renderPlaybookDetail, renderPlaybookList, renderResult, writeStdout, } from "../renderer.js";
|
|
10
|
-
import { applyTargetOptions, getEffectiveOptions, remoteTargetSuffix, requirePackageRoot, resolveFallbackStrategy, resolveIntent,
|
|
9
|
+
import { renderPlaybookDetail, renderPlaybookList, renderResult, writeStderr, writeStdout, } from "../renderer.js";
|
|
10
|
+
import { applyTargetOptions, buildScopedPackageGraph, getEffectiveOptions, remoteTargetSuffix, requirePackageRoot, resolveCommandTarget, resolveFallbackStrategy, resolveIntent, } from "../utils/index.js";
|
|
11
11
|
/**
|
|
12
12
|
* 注册 Playbook 命令集合(list, show, validate, create)。
|
|
13
13
|
*
|
|
@@ -34,14 +34,15 @@ export function registerPlaybookCommands(program, context) {
|
|
|
34
34
|
const hints = [
|
|
35
35
|
"Tip: Run 'ad playbook show <id>' to inspect procedure steps before execution.",
|
|
36
36
|
];
|
|
37
|
-
// 1.
|
|
38
|
-
const target =
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
// 1. 目标拓扑统一解析
|
|
38
|
+
const target = resolveCommandTarget(options, context);
|
|
39
|
+
// 2. 远端服务分支
|
|
40
|
+
if (target.isRemote) {
|
|
41
|
+
const remotePbs = await fetchRemotePlaybooks(target.resolved.serverUrl, target.resolved.token, {
|
|
41
42
|
intent: effectiveIntent,
|
|
42
43
|
package: options.package,
|
|
43
44
|
allowInsecureHttp: Boolean(options.allowInsecureHttp),
|
|
44
|
-
insecure: target.insecure,
|
|
45
|
+
insecure: target.resolved.insecure,
|
|
45
46
|
});
|
|
46
47
|
if (isMachine) {
|
|
47
48
|
renderResult({ items: remotePbs, hints }, { json: options.json, context });
|
|
@@ -49,24 +50,44 @@ export function registerPlaybookCommands(program, context) {
|
|
|
49
50
|
}
|
|
50
51
|
renderResult(remotePbs, {
|
|
51
52
|
json: options.json,
|
|
52
|
-
humanFormatter: () => renderPlaybookList(remotePbs, `Playbooks ${remoteTargetSuffix(target)}`, false, effectiveIntent),
|
|
53
|
+
humanFormatter: () => renderPlaybookList(remotePbs, `Playbooks ${remoteTargetSuffix(target.resolved)}`, false, effectiveIntent),
|
|
53
54
|
context,
|
|
54
55
|
});
|
|
55
56
|
return;
|
|
56
57
|
}
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
// 3. 本地模式(单包技能、复合技能或本地工程)
|
|
59
|
+
if (target.localSkill || target.targetPackageRoot) {
|
|
60
|
+
const rawList = [];
|
|
61
|
+
let title;
|
|
62
|
+
if (target.localSkill) {
|
|
63
|
+
const isComposite = target.localSkill.type === "composite";
|
|
64
|
+
for (const pkg of target.localSkill.packages) {
|
|
65
|
+
const playbooks = loadPlaybooks(pkg.packageRoot, pkg.projectConfig.playbooksDir, pkg.projectConfig);
|
|
66
|
+
for (const pb of playbooks.values()) {
|
|
67
|
+
rawList.push({
|
|
68
|
+
id: isComposite ? `${pkg.packageId}/${pb.id}` : pb.id,
|
|
69
|
+
description: pb.description,
|
|
70
|
+
packageId: pkg.packageId,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
title = isComposite
|
|
75
|
+
? `Playbooks in ${options.package || target.localSkill.root}`
|
|
76
|
+
: `Playbooks in ${target.localSkill.packages[0].packageId} (${target.localSkill.packages[0].packageRoot})`;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const config = loadProjectConfig(target.targetPackageRoot);
|
|
80
|
+
const playbooks = loadPlaybooks(target.targetPackageRoot, config.playbooksDir, config);
|
|
81
|
+
for (const pb of playbooks.values()) {
|
|
82
|
+
rawList.push({
|
|
83
|
+
id: pb.id,
|
|
84
|
+
description: pb.description,
|
|
85
|
+
packageId: config.id,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
title = `Playbooks in ${config.id} (${target.targetPackageRoot})`;
|
|
89
|
+
}
|
|
90
|
+
const filterRes = filterWithFallbackInfo(rawList, effectiveIntent, [(p) => p.id, (p) => p.description || ""], shouldFallback);
|
|
70
91
|
if (isMachine) {
|
|
71
92
|
if (filterRes.isFallback) {
|
|
72
93
|
renderResult({ items: filterRes.items, isFallback: true, matchedCount: 0, hints }, { json: options.json, context });
|
|
@@ -81,7 +102,7 @@ export function registerPlaybookCommands(program, context) {
|
|
|
81
102
|
}
|
|
82
103
|
renderResult(filterRes.items, {
|
|
83
104
|
json: options.json,
|
|
84
|
-
humanFormatter: () => renderPlaybookList(filterRes.items,
|
|
105
|
+
humanFormatter: () => renderPlaybookList(filterRes.items, title, filterRes.isFallback, effectiveIntent),
|
|
85
106
|
context,
|
|
86
107
|
});
|
|
87
108
|
return;
|
|
@@ -102,25 +123,22 @@ export function registerPlaybookCommands(program, context) {
|
|
|
102
123
|
}
|
|
103
124
|
const aggregated = [];
|
|
104
125
|
for (const pkg of linkedList) {
|
|
105
|
-
if (!existsSync(pkg.path))
|
|
126
|
+
if (!existsSync(pkg.path)) {
|
|
127
|
+
writeStderr(`Skipping inaccessible linked package '${pkg.id}' at '${pkg.path}'`, context);
|
|
106
128
|
continue;
|
|
107
|
-
try {
|
|
108
|
-
const config = loadProjectConfig(pkg.path);
|
|
109
|
-
const pbs = loadPlaybooks(pkg.path, config.playbooksDir);
|
|
110
|
-
const list = Array.from(pbs.values()).map((p) => ({
|
|
111
|
-
id: p.id,
|
|
112
|
-
description: p.description,
|
|
113
|
-
}));
|
|
114
|
-
aggregated.push({
|
|
115
|
-
packageId: pkg.id,
|
|
116
|
-
packageName: pkg.name,
|
|
117
|
-
path: pkg.path,
|
|
118
|
-
playbooks: list,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// 单个链接包清单损坏或 Playbook 加载失败时跳过,继续聚合其余包
|
|
123
129
|
}
|
|
130
|
+
const config = loadProjectConfig(pkg.path);
|
|
131
|
+
const pbs = loadPlaybooks(pkg.path, config.playbooksDir, config);
|
|
132
|
+
const list = Array.from(pbs.values()).map((p) => ({
|
|
133
|
+
id: p.id,
|
|
134
|
+
description: p.description,
|
|
135
|
+
}));
|
|
136
|
+
aggregated.push({
|
|
137
|
+
packageId: pkg.id,
|
|
138
|
+
packageName: pkg.name,
|
|
139
|
+
path: pkg.path,
|
|
140
|
+
playbooks: list,
|
|
141
|
+
});
|
|
124
142
|
}
|
|
125
143
|
let filteredPackages = [];
|
|
126
144
|
let isFallback = false;
|
|
@@ -183,13 +201,14 @@ export function registerPlaybookCommands(program, context) {
|
|
|
183
201
|
if (!id) {
|
|
184
202
|
throw new ArgumentError("Playbook ID is required for show");
|
|
185
203
|
}
|
|
186
|
-
// 1.
|
|
187
|
-
const target =
|
|
188
|
-
|
|
204
|
+
// 1. 目标拓扑统一解析
|
|
205
|
+
const target = resolveCommandTarget(options, context);
|
|
206
|
+
// 2. 远端服务分支
|
|
207
|
+
if (target.isRemote) {
|
|
189
208
|
try {
|
|
190
|
-
const detail = await fetchRemotePlaybookShow(target.serverUrl, id, target.token, {
|
|
209
|
+
const detail = await fetchRemotePlaybookShow(target.resolved.serverUrl, target.resolvePlaybookRef(id), target.resolved.token, {
|
|
191
210
|
allowInsecureHttp: Boolean(options.allowInsecureHttp),
|
|
192
|
-
insecure: target.insecure,
|
|
211
|
+
insecure: target.resolved.insecure,
|
|
193
212
|
});
|
|
194
213
|
renderResult(detail, {
|
|
195
214
|
json: options.json,
|
|
@@ -203,20 +222,15 @@ export function registerPlaybookCommands(program, context) {
|
|
|
203
222
|
throw new ArgumentError(`${err.message}\n${hint}`, err?.details, err?.code, hint);
|
|
204
223
|
}
|
|
205
224
|
}
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
if (options.package && !id.includes("/")) {
|
|
209
|
-
requirePackageRoot(options.package);
|
|
210
|
-
showTarget = `${options.package}/${id}`;
|
|
211
|
-
}
|
|
225
|
+
// 3. 本地分支(单包技能、复合技能、本地工程或已链接包)
|
|
226
|
+
const showTarget = target.resolvePlaybookRef(id);
|
|
212
227
|
let resolved;
|
|
213
228
|
try {
|
|
214
|
-
const graph =
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
})
|
|
219
|
-
resolved = resolvePlaybook(showTarget, { graph });
|
|
229
|
+
const graph = buildScopedPackageGraph(target, context);
|
|
230
|
+
const caller = target.localSkill
|
|
231
|
+
? (target.localSkill.type === "single" ? target.localSkill.packages[0].packageId : undefined)
|
|
232
|
+
: (target.targetPackageRoot ? loadProjectConfig(target.targetPackageRoot).id : undefined);
|
|
233
|
+
resolved = resolvePlaybook(showTarget, { graph, caller });
|
|
220
234
|
}
|
|
221
235
|
catch (err) {
|
|
222
236
|
const hint = "Tip: Run 'ad playbook list' to discover available playbooks.";
|
|
@@ -249,36 +263,73 @@ export function registerPlaybookCommands(program, context) {
|
|
|
249
263
|
.option("--json", "Output as JSON")
|
|
250
264
|
.action(async (id, rawOptions, cmd) => {
|
|
251
265
|
const options = getEffectiveOptions(rawOptions, cmd);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
266
|
+
// 保持本地校验定位,profile 始终为 local
|
|
267
|
+
const target = resolveCommandTarget({ ...options, profile: "local" }, context);
|
|
268
|
+
const graph = buildScopedPackageGraph(target, context);
|
|
269
|
+
const catalog = new DefaultActionCatalog(graph);
|
|
255
270
|
const targets = [];
|
|
256
|
-
if (
|
|
271
|
+
if (target.localSkill) {
|
|
272
|
+
if (id) {
|
|
273
|
+
const showTarget = target.resolvePlaybookRef(id);
|
|
274
|
+
const caller = target.localSkill.type === "single" ? target.localSkill.packages[0].packageId : undefined;
|
|
275
|
+
let resolved;
|
|
276
|
+
try {
|
|
277
|
+
resolved = resolvePlaybook(showTarget, { graph, caller });
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
throw new ArgumentError(err.message, err?.details, err?.code);
|
|
281
|
+
}
|
|
282
|
+
targets.push({
|
|
283
|
+
root: resolved.projectRoot,
|
|
284
|
+
packageId: resolved.packageId,
|
|
285
|
+
playbooks: [resolved.playbook],
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
for (const pkg of target.localSkill.packages) {
|
|
290
|
+
const playbooks = loadPlaybooks(pkg.packageRoot, pkg.projectConfig.playbooksDir);
|
|
291
|
+
targets.push({
|
|
292
|
+
root: pkg.packageRoot,
|
|
293
|
+
packageId: pkg.packageId,
|
|
294
|
+
playbooks: Array.from(playbooks.values()),
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (target.targetPackageRoot) {
|
|
300
|
+
const root = target.targetPackageRoot;
|
|
257
301
|
const config = loadProjectConfig(root);
|
|
258
|
-
const playbooks = loadPlaybooks(root, config.playbooksDir);
|
|
259
302
|
if (id) {
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
303
|
+
const showTarget = target.resolvePlaybookRef(id);
|
|
304
|
+
let resolved;
|
|
305
|
+
try {
|
|
306
|
+
resolved = resolvePlaybook(showTarget, { graph, caller: config.id });
|
|
263
307
|
}
|
|
264
|
-
|
|
308
|
+
catch (err) {
|
|
309
|
+
throw new ArgumentError(err.message, err?.details, err?.code);
|
|
310
|
+
}
|
|
311
|
+
targets.push({
|
|
312
|
+
root: resolved.projectRoot,
|
|
313
|
+
packageId: resolved.packageId,
|
|
314
|
+
playbooks: [resolved.playbook],
|
|
315
|
+
});
|
|
265
316
|
}
|
|
266
317
|
else {
|
|
267
|
-
|
|
318
|
+
const playbooks = loadPlaybooks(root, config.playbooksDir);
|
|
319
|
+
targets.push({
|
|
320
|
+
root,
|
|
321
|
+
packageId: config.id,
|
|
322
|
+
playbooks: Array.from(playbooks.values()),
|
|
323
|
+
});
|
|
268
324
|
}
|
|
269
325
|
}
|
|
270
326
|
else if (id) {
|
|
271
327
|
let resolved;
|
|
272
328
|
try {
|
|
273
|
-
const graph = new PackageGraphBuilder({
|
|
274
|
-
root: resolveLocalPackageRoot() || undefined,
|
|
275
|
-
customHome: context?.customHome,
|
|
276
|
-
allowDevLinks: true,
|
|
277
|
-
}).buildSync();
|
|
278
329
|
resolved = resolvePlaybook(id, { graph });
|
|
279
330
|
}
|
|
280
331
|
catch (err) {
|
|
281
|
-
throw new ArgumentError(err.message);
|
|
332
|
+
throw new ArgumentError(err.message, err?.details, err?.code);
|
|
282
333
|
}
|
|
283
334
|
targets.push({
|
|
284
335
|
root: resolved.projectRoot,
|
|
@@ -292,34 +343,22 @@ export function registerPlaybookCommands(program, context) {
|
|
|
292
343
|
throw new ArgumentError(`${NO_PROJECT_NO_LINKED_MESSAGE} Please run 'ad link' inside a package first.`);
|
|
293
344
|
}
|
|
294
345
|
for (const pkg of linkedList) {
|
|
295
|
-
if (!existsSync(pkg.path))
|
|
346
|
+
if (!existsSync(pkg.path)) {
|
|
347
|
+
writeStderr(`Skipping inaccessible linked package '${pkg.id}' at '${pkg.path}'`, context);
|
|
296
348
|
continue;
|
|
297
|
-
try {
|
|
298
|
-
const config = loadProjectConfig(pkg.path);
|
|
299
|
-
const playbooks = loadPlaybooks(pkg.path, config.playbooksDir);
|
|
300
|
-
targets.push({
|
|
301
|
-
root: pkg.path,
|
|
302
|
-
packageId: pkg.id,
|
|
303
|
-
playbooks: Array.from(playbooks.values()),
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
catch {
|
|
307
|
-
// 单个链接包清单损坏或 Playbook 加载失败时跳过,不阻断整体校验
|
|
308
349
|
}
|
|
350
|
+
const config = loadProjectConfig(pkg.path);
|
|
351
|
+
const playbooks = loadPlaybooks(pkg.path, config.playbooksDir, config);
|
|
352
|
+
targets.push({
|
|
353
|
+
root: pkg.path,
|
|
354
|
+
packageId: pkg.id,
|
|
355
|
+
playbooks: Array.from(playbooks.values()),
|
|
356
|
+
});
|
|
309
357
|
}
|
|
310
358
|
}
|
|
311
359
|
const results = [];
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
customHome: context?.customHome,
|
|
315
|
-
allowDevLinks: true,
|
|
316
|
-
}).buildSync();
|
|
317
|
-
const catalog = new DefaultActionCatalog(graph);
|
|
318
|
-
for (const target of targets) {
|
|
319
|
-
const config = loadProjectConfig(target.root);
|
|
320
|
-
const manifest = loadManifest(target.root);
|
|
321
|
-
const actions = await loadActions(target.root, config.actionsDir);
|
|
322
|
-
for (const pb of target.playbooks) {
|
|
360
|
+
for (const targetItem of targets) {
|
|
361
|
+
for (const pb of targetItem.playbooks) {
|
|
323
362
|
const warnings = [];
|
|
324
363
|
const errors = [];
|
|
325
364
|
if (!pb.id) {
|
|
@@ -330,26 +369,30 @@ export function registerPlaybookCommands(program, context) {
|
|
|
330
369
|
}
|
|
331
370
|
if (pb.actions && Array.isArray(pb.actions)) {
|
|
332
371
|
for (const actRef of pb.actions) {
|
|
333
|
-
if (actRef.
|
|
372
|
+
if (typeof actRef !== "string" || !actRef.trim()) {
|
|
373
|
+
errors.push(`Referenced action must be a non-empty string, received ${JSON.stringify(actRef)}`);
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const trimmed = actRef.trim();
|
|
377
|
+
if (trimmed.includes("/")) {
|
|
334
378
|
try {
|
|
335
|
-
resolveAction(
|
|
379
|
+
resolveAction(trimmed, { graph, catalog, caller: targetItem.packageId });
|
|
336
380
|
}
|
|
337
381
|
catch (e) {
|
|
338
|
-
errors.push(`Referenced cross-package action '${
|
|
382
|
+
errors.push(`Referenced cross-package action '${trimmed}' not resolvable: ${e.message}. Hint: Action '${trimmed}' not found. Run 'ad action create ${trimmed}' to create local action, or 'ad add <pkg>' to install external dependency.`);
|
|
339
383
|
}
|
|
340
384
|
}
|
|
341
385
|
else {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
errors.push(`Referenced action '${actRef}' not found in project actions. Hint: Action '${actRef}' not found. Run 'ad action create ${actRef}' to create local action, or 'ad add <pkg>' to install external dependency.`);
|
|
386
|
+
// 短引用只接受所属包清单登记的 Action,不能由源码文件或其他包补位。
|
|
387
|
+
if (!catalog.get(targetItem.packageId, trimmed)) {
|
|
388
|
+
errors.push(`Referenced action '${trimmed}' not found in project actions. Hint: Action '${trimmed}' not found. Run 'ad action create ${trimmed}' to create local action, or 'ad add <pkg>' to install external dependency.`);
|
|
346
389
|
}
|
|
347
390
|
}
|
|
348
391
|
}
|
|
349
392
|
}
|
|
350
393
|
results.push({
|
|
351
394
|
id: pb.id,
|
|
352
|
-
packageId:
|
|
395
|
+
packageId: targetItem.packageId,
|
|
353
396
|
valid: errors.length === 0,
|
|
354
397
|
warnings,
|
|
355
398
|
errors,
|
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
|
*
|
|
@@ -111,25 +110,13 @@ export async function executeAction(id, options, context, flatArgs, control) {
|
|
|
111
110
|
}
|
|
112
111
|
}
|
|
113
112
|
try {
|
|
113
|
+
// 目标拓扑与动作引用解析(远端目标完全隔离本地文件系统)
|
|
114
|
+
const target = resolveCommandTarget(options, context);
|
|
114
115
|
// 异步执行仅支持远端目标,本地模式直接拒绝
|
|
115
|
-
|
|
116
|
-
profile: options.profile,
|
|
117
|
-
server: options.server,
|
|
118
|
-
token: options.token,
|
|
119
|
-
insecure: options.insecure,
|
|
120
|
-
}, context?.customHome);
|
|
121
|
-
if (resolvedTopology.type === "local" && options.async) {
|
|
116
|
+
if (!target.isRemote && options.async) {
|
|
122
117
|
throw new ExecutionError("Async execution requires a long-running ActionDock server.\nUse --profile, --server, or start 'ad serve'.");
|
|
123
118
|
}
|
|
124
|
-
|
|
125
|
-
const targetPackageRoot = resolveLocalPackageRoot(options.package);
|
|
126
|
-
if (options.package && !targetPackageRoot) {
|
|
127
|
-
throw packageNotFoundError(options.package);
|
|
128
|
-
}
|
|
129
|
-
let targetRef = id;
|
|
130
|
-
if (options.package && !id.includes("/")) {
|
|
131
|
-
targetRef = `${options.package}/${id}`;
|
|
132
|
-
}
|
|
119
|
+
const targetRef = target.resolveActionRef(id);
|
|
133
120
|
// 通过 Service 统一执行
|
|
134
121
|
await withService(options, context, async (service) => {
|
|
135
122
|
const isMachine = Boolean(options.json);
|
|
@@ -205,7 +192,7 @@ export async function executeAction(id, options, context, flatArgs, control) {
|
|
|
205
192
|
return;
|
|
206
193
|
}
|
|
207
194
|
}
|
|
208
|
-
}, { localRoot: targetPackageRoot || undefined, scanLinkedPackages:
|
|
195
|
+
}, { localRoot: target.targetPackageRoot || undefined, scanLinkedPackages: !target.localSkill, ownDataDir: true });
|
|
209
196
|
}
|
|
210
197
|
catch (err) {
|
|
211
198
|
// sigint 取消源、中断链路特征错误码或 SigintError 本身,均统一归并为 SigintError
|
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();
|
|
@@ -43,3 +43,7 @@ export type RegistryTreeView = RegistryStatusReport | (Record<string, unknown> &
|
|
|
43
43
|
* 格式化渲染注册表层级树结构。
|
|
44
44
|
*/
|
|
45
45
|
export declare function renderRegistryTree(status: RegistryTreeView): string;
|
|
46
|
+
/**
|
|
47
|
+
* 格式化渲染复合技能各子包的自省卡片列表。
|
|
48
|
+
*/
|
|
49
|
+
export declare function renderCompositeSkillDetail(root: string, filteredDetails: ProjectDetailInfo[], isFallback?: boolean, effectiveIntent?: string): string;
|
|
@@ -194,3 +194,19 @@ export function renderRegistryTree(status) {
|
|
|
194
194
|
}
|
|
195
195
|
return lines.join("\n");
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* 格式化渲染复合技能各子包的自省卡片列表。
|
|
199
|
+
*/
|
|
200
|
+
export function renderCompositeSkillDetail(root, filteredDetails, isFallback, effectiveIntent) {
|
|
201
|
+
const lines = [];
|
|
202
|
+
if (isFallback) {
|
|
203
|
+
lines.push(`(No packages in skill matched intent '${effectiveIntent}', showing all packages)\n`);
|
|
204
|
+
}
|
|
205
|
+
lines.push(`ActionDock Composite Skill: ${root}`);
|
|
206
|
+
lines.push(`Packages (${filteredDetails.length}):\n`);
|
|
207
|
+
for (const d of filteredDetails) {
|
|
208
|
+
lines.push(renderProjectDetail(d));
|
|
209
|
+
lines.push("");
|
|
210
|
+
}
|
|
211
|
+
return lines.join("\n").trimEnd();
|
|
212
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LocalSkillDirectory } from "@actiondock/core/project";
|
|
1
2
|
import type { AggregatedPackage, CliContext, ProjectDetailInfo } from "../types.js";
|
|
2
3
|
/**
|
|
3
4
|
* 装配单个工程的详情自省信息(清单、动作映射与 Playbook 映射)。
|
|
@@ -5,6 +6,18 @@ import type { AggregatedPackage, CliContext, ProjectDetailInfo } from "../types.
|
|
|
5
6
|
* @param root 工程根目录
|
|
6
7
|
*/
|
|
7
8
|
export declare function getProjectDetailInfo(root: string): Promise<ProjectDetailInfo>;
|
|
9
|
+
/**
|
|
10
|
+
* 装配复合技能各子包的自省详情,将 Action 与 Playbook 的调用索引提升为完全限定 ID。
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildCompositeDetails(localSkill: LocalSkillDirectory): Promise<ProjectDetailInfo[]>;
|
|
13
|
+
/**
|
|
14
|
+
* 单包与复合技能共享相同的详情过滤规则(单一事实源)。
|
|
15
|
+
*/
|
|
16
|
+
export declare function filterProjectDetails(details: ProjectDetailInfo[], effectiveIntent?: string, shouldFallback?: boolean): {
|
|
17
|
+
items: ProjectDetailInfo[];
|
|
18
|
+
matchedCount: number;
|
|
19
|
+
isFallback: boolean;
|
|
20
|
+
};
|
|
8
21
|
/**
|
|
9
22
|
* 装配单个包的摘要聚合信息(不含动作与 Playbook 明细映射)。
|
|
10
23
|
*
|
|
@@ -13,7 +26,7 @@ export declare function getProjectDetailInfo(root: string): Promise<ProjectDetai
|
|
|
13
26
|
export declare function getPackageSummary(root: string): AggregatedPackage;
|
|
14
27
|
/**
|
|
15
28
|
* 扫描本地候选包集合(当前工程根目录优先,叠加全局已链接包,按物理路径去重)。
|
|
16
|
-
*
|
|
29
|
+
* 失效或损坏链接给出明确诊断记录,不静默吞没也不中断其余条目聚合。
|
|
17
30
|
*
|
|
18
31
|
* @param context CLI 上下文(提供 customHome 定位全局注册表)
|
|
19
32
|
*/
|
package/dist/services/inspect.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { findProjectRoot, loadProjectConfig, } from "@actiondock/core";
|
|
3
3
|
import { listLinkedPackages, } from "@actiondock/core/registry";
|
|
4
|
-
import { loadManifest, loadPlaybooks, } from "@actiondock/core/project";
|
|
4
|
+
import { filterWithFallbackInfo, loadManifest, loadPlaybooks, } from "@actiondock/core/project";
|
|
5
5
|
/**
|
|
6
6
|
* 装配单个工程的详情自省信息(清单、动作映射与 Playbook 映射)。
|
|
7
7
|
*
|
|
@@ -38,6 +38,51 @@ export async function getProjectDetailInfo(root) {
|
|
|
38
38
|
playbooksMap: playbooks,
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* 装配复合技能各子包的自省详情,将 Action 与 Playbook 的调用索引提升为完全限定 ID。
|
|
43
|
+
*/
|
|
44
|
+
export async function buildCompositeDetails(localSkill) {
|
|
45
|
+
const details = [];
|
|
46
|
+
for (const pkg of localSkill.packages) {
|
|
47
|
+
const detail = await getProjectDetailInfo(pkg.packageRoot);
|
|
48
|
+
const qualifiedActionsMap = new Map();
|
|
49
|
+
if (detail.actionsMap) {
|
|
50
|
+
for (const [actId, act] of detail.actionsMap.entries()) {
|
|
51
|
+
const fullId = `${pkg.packageId}/${actId}`;
|
|
52
|
+
qualifiedActionsMap.set(fullId, { id: fullId, description: act.description });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const qualifiedPlaybooksMap = new Map();
|
|
56
|
+
if (detail.playbooksMap) {
|
|
57
|
+
for (const [pbId, pb] of detail.playbooksMap.entries()) {
|
|
58
|
+
const fullId = `${pkg.packageId}/${pbId}`;
|
|
59
|
+
qualifiedPlaybooksMap.set(fullId, { ...pb, id: fullId });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
detail.actionsMap = qualifiedActionsMap;
|
|
63
|
+
detail.actions = Array.from(qualifiedActionsMap.keys());
|
|
64
|
+
detail.playbooksMap = qualifiedPlaybooksMap;
|
|
65
|
+
detail.playbooks = Array.from(qualifiedPlaybooksMap.keys());
|
|
66
|
+
details.push(detail);
|
|
67
|
+
}
|
|
68
|
+
return details;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 单包与复合技能共享相同的详情过滤规则(单一事实源)。
|
|
72
|
+
*/
|
|
73
|
+
export function filterProjectDetails(details, effectiveIntent, shouldFallback = false) {
|
|
74
|
+
if (!effectiveIntent) {
|
|
75
|
+
return { items: details, matchedCount: details.length, isFallback: false };
|
|
76
|
+
}
|
|
77
|
+
return filterWithFallbackInfo(details, effectiveIntent, [
|
|
78
|
+
(d) => d.id,
|
|
79
|
+
(d) => d.name,
|
|
80
|
+
(d) => d.description || "",
|
|
81
|
+
(d) => d.projectRoot,
|
|
82
|
+
(d) => d.actions.join(" "),
|
|
83
|
+
(d) => d.playbooks.join(" "),
|
|
84
|
+
], Boolean(shouldFallback));
|
|
85
|
+
}
|
|
41
86
|
/**
|
|
42
87
|
* 装配单个包的摘要聚合信息(不含动作与 Playbook 明细映射)。
|
|
43
88
|
*
|
|
@@ -63,7 +108,7 @@ export function getPackageSummary(root) {
|
|
|
63
108
|
}
|
|
64
109
|
/**
|
|
65
110
|
* 扫描本地候选包集合(当前工程根目录优先,叠加全局已链接包,按物理路径去重)。
|
|
66
|
-
*
|
|
111
|
+
* 失效或损坏链接给出明确诊断记录,不静默吞没也不中断其余条目聚合。
|
|
67
112
|
*
|
|
68
113
|
* @param context CLI 上下文(提供 customHome 定位全局注册表)
|
|
69
114
|
*/
|
|
@@ -73,25 +118,22 @@ export function scanLocalAggregatedPackages(context) {
|
|
|
73
118
|
const aggregated = [];
|
|
74
119
|
const seenPaths = new Set();
|
|
75
120
|
if (currentRoot) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
seenPaths.add(currentRoot);
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
// 忽略异常工程根目录(清单损坏)
|
|
82
|
-
}
|
|
121
|
+
aggregated.push(getPackageSummary(currentRoot));
|
|
122
|
+
seenPaths.add(currentRoot);
|
|
83
123
|
}
|
|
84
124
|
for (const pkg of linkedList) {
|
|
85
|
-
if (!existsSync(pkg.path))
|
|
125
|
+
if (!existsSync(pkg.path)) {
|
|
126
|
+
process.stderr.write(`[actiondock] Diagnostic: Skipping inaccessible linked package '${pkg.id}' at '${pkg.path}'\n`);
|
|
86
127
|
continue;
|
|
128
|
+
}
|
|
87
129
|
if (seenPaths.has(pkg.path))
|
|
88
130
|
continue;
|
|
89
131
|
try {
|
|
90
132
|
aggregated.push(getPackageSummary(pkg.path));
|
|
91
133
|
seenPaths.add(pkg.path);
|
|
92
134
|
}
|
|
93
|
-
catch {
|
|
94
|
-
|
|
135
|
+
catch (err) {
|
|
136
|
+
process.stderr.write(`[actiondock] Diagnostic: Skipping invalid linked package '${pkg.id}' at '${pkg.path}': ${err?.message || String(err)}\n`);
|
|
95
137
|
}
|
|
96
138
|
}
|
|
97
139
|
return { currentRoot, aggregated };
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED