@actiondock/cli 2.0.11 → 2.2.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 +41 -21
- package/bin/ad.js +29 -31
- package/dist/commands/action/create.d.ts +10 -0
- package/dist/commands/action/create.js +120 -0
- package/dist/commands/add.d.ts +7 -0
- package/dist/commands/add.js +144 -0
- package/dist/commands/build.d.ts +3 -0
- package/dist/commands/build.js +83 -0
- package/dist/commands/config/delete.d.ts +9 -0
- package/dist/commands/config/delete.js +52 -0
- package/dist/commands/config/env.d.ts +9 -0
- package/dist/commands/config/env.js +85 -0
- package/dist/commands/config/get.d.ts +9 -0
- package/dist/commands/config/get.js +120 -0
- package/dist/commands/config/index.d.ts +9 -0
- package/dist/commands/config/index.js +23 -0
- package/dist/commands/config/list.d.ts +9 -0
- package/dist/commands/config/list.js +137 -0
- package/dist/commands/config/schema.d.ts +9 -0
- package/dist/commands/config/schema.js +121 -0
- package/dist/commands/config/set.d.ts +9 -0
- package/dist/commands/config/set.js +85 -0
- package/dist/commands/describe.d.ts +9 -0
- package/dist/commands/describe.js +68 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.js +82 -0
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.js +212 -0
- package/dist/commands/generate.d.ts +9 -0
- package/dist/commands/generate.js +44 -0
- package/dist/commands/index.d.ts +32 -0
- package/dist/commands/index.js +111 -0
- package/dist/commands/info.d.ts +9 -0
- package/dist/commands/info.js +210 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +24 -0
- package/dist/commands/link.d.ts +2 -0
- package/dist/commands/link.js +71 -0
- package/dist/commands/list.d.ts +9 -0
- package/dist/commands/list.js +77 -0
- package/dist/commands/mcp.d.ts +9 -0
- package/dist/commands/mcp.js +172 -0
- package/dist/commands/new.d.ts +9 -0
- package/dist/commands/new.js +33 -0
- package/dist/commands/pack.d.ts +3 -0
- package/dist/commands/pack.js +63 -0
- package/dist/commands/playbook.d.ts +10 -0
- package/dist/commands/playbook.js +433 -0
- package/dist/commands/profile.d.ts +2 -0
- package/dist/commands/profile.js +253 -0
- package/dist/commands/remove.d.ts +7 -0
- package/dist/commands/remove.js +118 -0
- package/dist/commands/run.d.ts +13 -0
- package/dist/commands/run.js +146 -0
- package/dist/commands/runs.d.ts +9 -0
- package/dist/commands/runs.js +245 -0
- package/dist/commands/serve.d.ts +9 -0
- package/dist/commands/serve.js +129 -0
- package/dist/commands/state-list.d.ts +24 -0
- package/dist/commands/state-list.js +82 -0
- package/dist/commands/state.d.ts +9 -0
- package/dist/commands/state.js +310 -0
- package/dist/commands/test.d.ts +2 -0
- package/dist/commands/test.js +44 -0
- package/dist/commands/validate.d.ts +9 -0
- package/dist/commands/validate.js +106 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.js +103 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +47 -65969
- package/dist/prompt.d.ts +53 -0
- package/dist/prompt.js +192 -0
- package/dist/renderer.d.ts +197 -0
- package/dist/renderer.js +502 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/inspect.d.ts +23 -0
- package/dist/services/inspect.js +96 -0
- package/dist/standalone/index.d.ts +16 -0
- package/dist/standalone/index.js +22 -0
- package/dist/types.d.ts +132 -0
- package/dist/types.js +9 -0
- package/dist/utils/bytes.d.ts +1 -0
- package/dist/utils/bytes.js +1 -0
- package/dist/utils/duration.js +1 -0
- package/dist/utils/filter.d.ts +1 -0
- package/dist/utils/filter.js +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/spawn.d.ts +27 -0
- package/dist/utils/spawn.js +35 -0
- package/dist/utils/target.d.ts +45 -0
- package/dist/utils/target.js +78 -0
- package/dist/utils/utils.d.ts +38 -0
- package/dist/utils/utils.js +111 -0
- package/package.json +20 -17
- package/src/commands/action/create.ts +0 -129
- package/src/commands/action/index.ts +0 -27
- package/src/commands/action/list.ts +0 -247
- package/src/commands/action/run.ts +0 -197
- package/src/commands/action/show.ts +0 -128
- package/src/commands/action/sync.ts +0 -76
- package/src/commands/action/validate.ts +0 -102
- package/src/commands/action.ts +0 -1
- package/src/commands/build.ts +0 -56
- package/src/commands/config.ts +0 -583
- package/src/commands/doctor.ts +0 -84
- package/src/commands/export.ts +0 -196
- package/src/commands/index.ts +0 -75
- package/src/commands/info.ts +0 -537
- package/src/commands/init.ts +0 -24
- package/src/commands/link.ts +0 -73
- package/src/commands/mcp.ts +0 -195
- package/src/commands/playbook.ts +0 -566
- package/src/commands/profile.ts +0 -296
- package/src/commands/runs.ts +0 -452
- package/src/commands/serve.ts +0 -132
- package/src/commands/state.ts +0 -590
- package/src/commands/test.ts +0 -48
- package/src/index.ts +0 -58
- package/src/utils/bytes.ts +0 -2
- package/src/utils/filter.ts +0 -2
- /package/{src/utils/duration.ts → dist/utils/duration.d.ts} +0 -0
package/src/commands/playbook.ts
DELETED
|
@@ -1,566 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
fetchRemotePlaybooks,
|
|
5
|
-
fetchRemotePlaybookShow,
|
|
6
|
-
filterWithFallbackInfo,
|
|
7
|
-
findProjectRoot,
|
|
8
|
-
listLinkedPackages,
|
|
9
|
-
loadActions,
|
|
10
|
-
loadManifest,
|
|
11
|
-
loadPlaybooks,
|
|
12
|
-
loadProjectConfig,
|
|
13
|
-
resolveActionProject,
|
|
14
|
-
resolvePackageRoot,
|
|
15
|
-
resolvePlaybookProject,
|
|
16
|
-
resolveTarget,
|
|
17
|
-
} from "@actiondock/core";
|
|
18
|
-
import type { PlaybookDefinition } from "@actiondock/core";
|
|
19
|
-
import { Command } from "commander";
|
|
20
|
-
import {
|
|
21
|
-
ArgumentError,
|
|
22
|
-
CliError,
|
|
23
|
-
ExecutionError,
|
|
24
|
-
getEffectiveOptions,
|
|
25
|
-
renderResult,
|
|
26
|
-
} from "@actiondock/runtime-cli";
|
|
27
|
-
import { resolveIntent } from "../utils/filter";
|
|
28
|
-
|
|
29
|
-
export function registerPlaybookCommands(program: Command): void {
|
|
30
|
-
const pbCmd = program
|
|
31
|
-
.command("playbook")
|
|
32
|
-
.description("Manage task Playbooks (Task SOPs for AI Agents)");
|
|
33
|
-
|
|
34
|
-
// playbook list
|
|
35
|
-
pbCmd
|
|
36
|
-
.command("list [patterns...]")
|
|
37
|
-
.description("List playbooks in current project or linked packages")
|
|
38
|
-
.option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
|
|
39
|
-
.option("-P, --package <id>", "Target package ID or path")
|
|
40
|
-
.option("-p, --profile <name>", "Query against a specific profile")
|
|
41
|
-
.option("-s, --server <url>", "Remote server URL")
|
|
42
|
-
.option("-t, --token <token>", "Auth token for remote server")
|
|
43
|
-
.option("--fallback", "Enable fallback to full list when no items match intent")
|
|
44
|
-
.option("--no-fallback", "Disable fallback to full list when no items match intent")
|
|
45
|
-
.option("--json", "Output as JSON")
|
|
46
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
47
|
-
.action(async (patterns, rawOptions, cmd) => {
|
|
48
|
-
try {
|
|
49
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
50
|
-
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
51
|
-
const isMachine = Boolean(options.json || options.envelope);
|
|
52
|
-
const fallbackExplicit = options.fallback === true || (Array.isArray(process.argv) && process.argv.includes("--fallback"));
|
|
53
|
-
const shouldFallback = isMachine ? fallbackExplicit : options.fallback !== false;
|
|
54
|
-
|
|
55
|
-
const target = resolveTarget({
|
|
56
|
-
profile: options.profile,
|
|
57
|
-
server: options.server,
|
|
58
|
-
token: options.token,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
if (target.type === "remote") {
|
|
62
|
-
let remotePbs = await fetchRemotePlaybooks(target.serverUrl!, target.token, {
|
|
63
|
-
intent: effectiveIntent,
|
|
64
|
-
package: options.package,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
let isFallback = false;
|
|
68
|
-
if (remotePbs.length === 0 && effectiveIntent && shouldFallback) {
|
|
69
|
-
remotePbs = await fetchRemotePlaybooks(target.serverUrl!, target.token, {
|
|
70
|
-
package: options.package,
|
|
71
|
-
});
|
|
72
|
-
isFallback = true;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (isFallback && isMachine) {
|
|
76
|
-
renderResult(
|
|
77
|
-
{ items: remotePbs, isFallback: true, matchedCount: 0 },
|
|
78
|
-
{ json: options.json, envelope: options.envelope }
|
|
79
|
-
);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
renderResult(remotePbs, {
|
|
84
|
-
json: options.json,
|
|
85
|
-
envelope: options.envelope,
|
|
86
|
-
humanFormatter: () => {
|
|
87
|
-
const lines = [
|
|
88
|
-
`Playbooks on remote server ${target.serverUrl}${target.profileName ? ` (Profile: ${target.profileName})` : ""}:\n`,
|
|
89
|
-
];
|
|
90
|
-
if (isFallback && effectiveIntent) {
|
|
91
|
-
lines.push(`(No remote playbooks matched intent '${effectiveIntent}', showing all playbooks)\n`);
|
|
92
|
-
}
|
|
93
|
-
if (remotePbs.length === 0) {
|
|
94
|
-
lines.push(" (No playbooks found)");
|
|
95
|
-
} else {
|
|
96
|
-
for (const p of remotePbs) {
|
|
97
|
-
lines.push(` ${p.id.padEnd(24)} ${p.description} (Package: ${p.packageId})`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return lines.join("\n");
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
let targetRoot: string | null = null;
|
|
107
|
-
if (options.package) {
|
|
108
|
-
targetRoot = resolvePackageRoot(options.package);
|
|
109
|
-
if (!targetRoot) {
|
|
110
|
-
throw new ArgumentError(
|
|
111
|
-
`Package '${options.package}' not found in linked packages or path`
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
targetRoot = findProjectRoot();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (targetRoot) {
|
|
119
|
-
const config = loadProjectConfig(targetRoot);
|
|
120
|
-
const playbooks = loadPlaybooks(targetRoot, config.playbooksDir);
|
|
121
|
-
const rawList = Array.from(playbooks.values()).map((p) => ({
|
|
122
|
-
id: p.id,
|
|
123
|
-
description: p.description || "",
|
|
124
|
-
actions: p.actions || [],
|
|
125
|
-
file: p.filePath,
|
|
126
|
-
packageId: config.id,
|
|
127
|
-
}));
|
|
128
|
-
|
|
129
|
-
const filterRes = filterWithFallbackInfo(
|
|
130
|
-
rawList,
|
|
131
|
-
effectiveIntent,
|
|
132
|
-
[(p) => p.id, (p) => p.description, (p) => p.actions, (p) => p.file],
|
|
133
|
-
shouldFallback
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (filterRes.isFallback && isMachine) {
|
|
137
|
-
renderResult(
|
|
138
|
-
{ items: filterRes.items, isFallback: true, matchedCount: 0 },
|
|
139
|
-
{ json: options.json, envelope: options.envelope }
|
|
140
|
-
);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
renderResult(filterRes.items, {
|
|
145
|
-
json: options.json,
|
|
146
|
-
envelope: options.envelope,
|
|
147
|
-
humanFormatter: () => {
|
|
148
|
-
const lines = [`Playbooks in ${config.id} (${targetRoot}):\n`];
|
|
149
|
-
if (filterRes.isFallback && effectiveIntent) {
|
|
150
|
-
lines.push(`(No playbooks matched intent '${effectiveIntent}', showing all playbooks)\n`);
|
|
151
|
-
}
|
|
152
|
-
for (const p of filterRes.items) {
|
|
153
|
-
lines.push(` ${p.id.padEnd(24)} ${p.description}`);
|
|
154
|
-
}
|
|
155
|
-
return lines.join("\n");
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
// List playbooks across all linked packages
|
|
161
|
-
const linkedList = listLinkedPackages();
|
|
162
|
-
if (linkedList.length === 0) {
|
|
163
|
-
renderResult([], {
|
|
164
|
-
json: options.json,
|
|
165
|
-
envelope: options.envelope,
|
|
166
|
-
humanFormatter: () =>
|
|
167
|
-
"No ActionDock project in current directory, and no packages linked.\nRun 'ad link' inside an Action package to register it.",
|
|
168
|
-
});
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const aggregated: Array<{
|
|
173
|
-
packageId: string;
|
|
174
|
-
packageName: string;
|
|
175
|
-
path: string;
|
|
176
|
-
playbooks: Array<{
|
|
177
|
-
id: string;
|
|
178
|
-
description: string;
|
|
179
|
-
actions: string[];
|
|
180
|
-
file: string;
|
|
181
|
-
}>;
|
|
182
|
-
}> = [];
|
|
183
|
-
|
|
184
|
-
for (const pkg of linkedList) {
|
|
185
|
-
if (!existsSync(pkg.path)) continue;
|
|
186
|
-
try {
|
|
187
|
-
const config = loadProjectConfig(pkg.path);
|
|
188
|
-
const playbooks = loadPlaybooks(pkg.path, config.playbooksDir);
|
|
189
|
-
const pkgPlaybooks = Array.from(playbooks.values()).map((p) => ({
|
|
190
|
-
id: p.id,
|
|
191
|
-
description: p.description || "",
|
|
192
|
-
actions: p.actions || [],
|
|
193
|
-
file: p.filePath,
|
|
194
|
-
}));
|
|
195
|
-
|
|
196
|
-
aggregated.push({
|
|
197
|
-
packageId: pkg.id,
|
|
198
|
-
packageName: pkg.name,
|
|
199
|
-
path: pkg.path,
|
|
200
|
-
playbooks: pkgPlaybooks,
|
|
201
|
-
});
|
|
202
|
-
} catch {
|
|
203
|
-
// Ignore broken linked package
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
let filteredPackages: typeof aggregated = [];
|
|
208
|
-
let isFallback = false;
|
|
209
|
-
if (!effectiveIntent) {
|
|
210
|
-
filteredPackages = aggregated;
|
|
211
|
-
} else {
|
|
212
|
-
for (const pkg of aggregated) {
|
|
213
|
-
const pkgMatches = filterWithFallbackInfo(
|
|
214
|
-
[pkg],
|
|
215
|
-
effectiveIntent,
|
|
216
|
-
[(p) => p.packageId, (p) => p.packageName, (p) => p.path],
|
|
217
|
-
false
|
|
218
|
-
).matchedCount > 0;
|
|
219
|
-
|
|
220
|
-
if (pkgMatches) {
|
|
221
|
-
filteredPackages.push(pkg);
|
|
222
|
-
} else {
|
|
223
|
-
const matchedPlaybooks = filterWithFallbackInfo(
|
|
224
|
-
pkg.playbooks,
|
|
225
|
-
effectiveIntent,
|
|
226
|
-
[(p) => p.id, (p) => p.description, (p) => p.actions, (p) => p.file],
|
|
227
|
-
false
|
|
228
|
-
).items;
|
|
229
|
-
|
|
230
|
-
if (matchedPlaybooks.length > 0) {
|
|
231
|
-
filteredPackages.push({
|
|
232
|
-
...pkg,
|
|
233
|
-
playbooks: matchedPlaybooks,
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (filteredPackages.length === 0 && shouldFallback) {
|
|
240
|
-
filteredPackages = aggregated;
|
|
241
|
-
isFallback = true;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (isFallback && isMachine) {
|
|
246
|
-
renderResult(
|
|
247
|
-
{ packages: filteredPackages, isFallback: true, matchedCount: 0 },
|
|
248
|
-
{ json: options.json, envelope: options.envelope }
|
|
249
|
-
);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
renderResult(filteredPackages, {
|
|
254
|
-
json: options.json,
|
|
255
|
-
envelope: options.envelope,
|
|
256
|
-
humanFormatter: () => {
|
|
257
|
-
const lines = ["Playbooks in Linked Packages:\n"];
|
|
258
|
-
for (const pkg of filteredPackages) {
|
|
259
|
-
lines.push(`* Package: ${pkg.packageId} (${pkg.path})`);
|
|
260
|
-
if (pkg.playbooks.length === 0) {
|
|
261
|
-
lines.push(" (No playbooks)");
|
|
262
|
-
} else {
|
|
263
|
-
for (const p of pkg.playbooks) {
|
|
264
|
-
lines.push(` - ${p.id.padEnd(26)} ${p.description}`);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return lines.join("\n");
|
|
269
|
-
},
|
|
270
|
-
});
|
|
271
|
-
} catch (err: any) {
|
|
272
|
-
if (err instanceof ArgumentError || err instanceof CliError) throw err;
|
|
273
|
-
throw new ExecutionError(err.message);
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// playbook create / new
|
|
278
|
-
pbCmd
|
|
279
|
-
.command("create <id>")
|
|
280
|
-
.alias("new")
|
|
281
|
-
.description("Scaffold a new Playbook markdown file")
|
|
282
|
-
.option("-d, --desc <description>", "Playbook description")
|
|
283
|
-
.option("-a, --actions <actions...>", "Referenced action IDs")
|
|
284
|
-
.option("-f, --file <filePath>", "Target file path relative to playbooks dir")
|
|
285
|
-
.action((id, options) => {
|
|
286
|
-
const root = findProjectRoot();
|
|
287
|
-
if (!root) {
|
|
288
|
-
throw new ExecutionError("Not in an ActionDock project (actiondock.json not found)");
|
|
289
|
-
}
|
|
290
|
-
try {
|
|
291
|
-
const config = loadProjectConfig(root);
|
|
292
|
-
const pbDir = resolve(root, config.playbooksDir || "playbooks");
|
|
293
|
-
if (!existsSync(pbDir)) {
|
|
294
|
-
mkdirSync(pbDir, { recursive: true });
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const cleanName = id.replace(/[^a-zA-Z0-9-_]/g, "-");
|
|
298
|
-
const targetRelFile = options.file || `${cleanName}.md`;
|
|
299
|
-
const targetFullFile = resolve(pbDir, targetRelFile);
|
|
300
|
-
|
|
301
|
-
if (existsSync(targetFullFile)) {
|
|
302
|
-
throw new ExecutionError(`File '${targetFullFile}' already exists`);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
mkdirSync(dirname(targetFullFile), { recursive: true });
|
|
306
|
-
|
|
307
|
-
const desc = options.desc || `SOP guide for ${id}`;
|
|
308
|
-
const actionsList = Array.isArray(options.actions) ? options.actions : [];
|
|
309
|
-
const actionsYaml =
|
|
310
|
-
actionsList.length > 0
|
|
311
|
-
? `actions:\n${actionsList.map((a: string) => ` - ${a}`).join("\n")}\n`
|
|
312
|
-
: "actions: []\n";
|
|
313
|
-
|
|
314
|
-
const template = `---
|
|
315
|
-
id: ${id}
|
|
316
|
-
description: ${desc}
|
|
317
|
-
${actionsYaml}---
|
|
318
|
-
|
|
319
|
-
# ${id.replace(/[-_]/g, " ").replace(/\b\w/g, (c: string) => c.toUpperCase())} SOP
|
|
320
|
-
|
|
321
|
-
This playbook provides task execution guidance for AI Agents.
|
|
322
|
-
|
|
323
|
-
## Instructions
|
|
324
|
-
|
|
325
|
-
1. Inspect available actions with \`<binary> list --json\`.
|
|
326
|
-
2. Follow the required steps to complete the task.
|
|
327
|
-
`;
|
|
328
|
-
|
|
329
|
-
writeFileSync(targetFullFile, template, "utf-8");
|
|
330
|
-
console.log(`[OK] Created Playbook '${id}' at ${targetFullFile}`);
|
|
331
|
-
} catch (err: any) {
|
|
332
|
-
if (err instanceof CliError) {
|
|
333
|
-
throw err;
|
|
334
|
-
}
|
|
335
|
-
throw new ExecutionError(err.message);
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// playbook show
|
|
340
|
-
pbCmd
|
|
341
|
-
.command("show <id>")
|
|
342
|
-
.description("Show playbook content and metadata (from current project or linked packages)")
|
|
343
|
-
.option("-P, --package <id>", "Target package ID or path")
|
|
344
|
-
.option("-p, --profile <name>", "Query against a specific profile")
|
|
345
|
-
.option("-s, --server <url>", "Remote server URL")
|
|
346
|
-
.option("-t, --token <token>", "Auth token for remote server")
|
|
347
|
-
.option("--json", "Output as JSON")
|
|
348
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
349
|
-
.action(async (id, rawOptions, cmd) => {
|
|
350
|
-
try {
|
|
351
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
352
|
-
if (!id) {
|
|
353
|
-
throw new ArgumentError("Playbook ID is required for show");
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const target = resolveTarget({
|
|
357
|
-
profile: options.profile,
|
|
358
|
-
server: options.server,
|
|
359
|
-
token: options.token,
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
if (target.type === "remote") {
|
|
363
|
-
const pb = await fetchRemotePlaybookShow(target.serverUrl!, id, target.token);
|
|
364
|
-
renderResult(pb, {
|
|
365
|
-
json: options.json,
|
|
366
|
-
envelope: options.envelope,
|
|
367
|
-
humanFormatter: () => {
|
|
368
|
-
const lines = [`Playbook: ${pb.id} (Package: ${pb.packageId})`];
|
|
369
|
-
if (pb.description) lines.push(`Description: ${pb.description}`);
|
|
370
|
-
if (pb.actions && pb.actions.length > 0) {
|
|
371
|
-
lines.push(`Actions: ${pb.actions.join(", ")}`);
|
|
372
|
-
}
|
|
373
|
-
if (pb.filePath) lines.push(`File: ${pb.filePath}\n`);
|
|
374
|
-
lines.push("--- Content ---", pb.content);
|
|
375
|
-
return lines.join("\n");
|
|
376
|
-
},
|
|
377
|
-
});
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
let showTarget = id;
|
|
382
|
-
if (options.package && !id.includes("/") && !id.includes(":")) {
|
|
383
|
-
const pkgRoot = resolvePackageRoot(options.package);
|
|
384
|
-
if (!pkgRoot) {
|
|
385
|
-
throw new ArgumentError(
|
|
386
|
-
`Package '${options.package}' not found in linked packages or path`
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
showTarget = `${options.package}/${id}`;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
let resolved;
|
|
393
|
-
try {
|
|
394
|
-
resolved = resolvePlaybookProject(showTarget);
|
|
395
|
-
} catch (err: any) {
|
|
396
|
-
if (err.message?.includes("not found") || err.message?.includes("no longer exists")) {
|
|
397
|
-
throw new ArgumentError(err.message);
|
|
398
|
-
}
|
|
399
|
-
throw new ExecutionError(err.message);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const pb = resolved.playbook;
|
|
403
|
-
const payload = { ...pb, packageId: resolved.packageId };
|
|
404
|
-
|
|
405
|
-
renderResult(payload, {
|
|
406
|
-
json: options.json,
|
|
407
|
-
envelope: options.envelope,
|
|
408
|
-
humanFormatter: () => {
|
|
409
|
-
const lines = [`Playbook: ${pb.id} (Package: ${resolved.packageId})`];
|
|
410
|
-
if (pb.description) lines.push(`Description: ${pb.description}`);
|
|
411
|
-
if (pb.actions && pb.actions.length > 0) {
|
|
412
|
-
lines.push(`Actions: ${pb.actions.join(", ")}`);
|
|
413
|
-
}
|
|
414
|
-
if (pb.filePath) lines.push(`File: ${pb.filePath}\n`);
|
|
415
|
-
lines.push("--- Content ---", pb.content);
|
|
416
|
-
return lines.join("\n");
|
|
417
|
-
},
|
|
418
|
-
});
|
|
419
|
-
} catch (err: any) {
|
|
420
|
-
if (err instanceof ArgumentError || err instanceof CliError) throw err;
|
|
421
|
-
throw new ExecutionError(err.message);
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
// playbook validate
|
|
426
|
-
pbCmd
|
|
427
|
-
.command("validate [id]")
|
|
428
|
-
.description("Validate playbook format and action references (in current project or linked packages)")
|
|
429
|
-
.option("-P, --package <id>", "Target package ID or path")
|
|
430
|
-
.option("--json", "Output as JSON")
|
|
431
|
-
.option("--envelope", "Wrap JSON output in standard envelope")
|
|
432
|
-
.action(async (id, rawOptions, cmd) => {
|
|
433
|
-
try {
|
|
434
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
435
|
-
let root: string | null = null;
|
|
436
|
-
if (options.package) {
|
|
437
|
-
root = resolvePackageRoot(options.package);
|
|
438
|
-
if (!root) {
|
|
439
|
-
throw new ArgumentError(
|
|
440
|
-
`Package '${options.package}' not found in linked packages or path`
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
} else {
|
|
444
|
-
root = findProjectRoot();
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const targets: Array<{ root: string; packageId: string; playbooks: PlaybookDefinition[] }> = [];
|
|
448
|
-
|
|
449
|
-
if (root) {
|
|
450
|
-
const config = loadProjectConfig(root);
|
|
451
|
-
const playbooks = loadPlaybooks(root, config.playbooksDir);
|
|
452
|
-
if (id) {
|
|
453
|
-
const pb = playbooks.get(id);
|
|
454
|
-
if (!pb) {
|
|
455
|
-
throw new ArgumentError(`Playbook '${id}' not found in package '${config.id}'`);
|
|
456
|
-
}
|
|
457
|
-
targets.push({ root, packageId: config.id, playbooks: [pb] });
|
|
458
|
-
} else {
|
|
459
|
-
targets.push({ root, packageId: config.id, playbooks: Array.from(playbooks.values()) });
|
|
460
|
-
}
|
|
461
|
-
} else if (id) {
|
|
462
|
-
const resolved = resolvePlaybookProject(id);
|
|
463
|
-
targets.push({
|
|
464
|
-
root: resolved.projectRoot,
|
|
465
|
-
packageId: resolved.packageId,
|
|
466
|
-
playbooks: [resolved.playbook],
|
|
467
|
-
});
|
|
468
|
-
} else {
|
|
469
|
-
// Outside project: validate all linked packages
|
|
470
|
-
const linkedList = listLinkedPackages();
|
|
471
|
-
if (linkedList.length === 0) {
|
|
472
|
-
throw new ArgumentError("Not in an ActionDock project, and no packages linked.");
|
|
473
|
-
}
|
|
474
|
-
for (const pkg of linkedList) {
|
|
475
|
-
if (!existsSync(pkg.path)) continue;
|
|
476
|
-
try {
|
|
477
|
-
const config = loadProjectConfig(pkg.path);
|
|
478
|
-
const playbooks = loadPlaybooks(pkg.path, config.playbooksDir);
|
|
479
|
-
targets.push({
|
|
480
|
-
root: pkg.path,
|
|
481
|
-
packageId: pkg.id,
|
|
482
|
-
playbooks: Array.from(playbooks.values()),
|
|
483
|
-
});
|
|
484
|
-
} catch {}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const results: Array<{ id: string; packageId: string; valid: boolean; warnings: string[]; errors: string[] }> = [];
|
|
489
|
-
|
|
490
|
-
for (const target of targets) {
|
|
491
|
-
const config = loadProjectConfig(target.root);
|
|
492
|
-
let actionIds = new Set<string>();
|
|
493
|
-
const manifest = loadManifest(target.root);
|
|
494
|
-
if (manifest?.actions) {
|
|
495
|
-
actionIds = new Set(Object.keys(manifest.actions));
|
|
496
|
-
} else {
|
|
497
|
-
try {
|
|
498
|
-
const actions = await loadActions(target.root, config.actionsDir, { autoInstall: false });
|
|
499
|
-
actionIds = new Set(actions.keys());
|
|
500
|
-
} catch {
|
|
501
|
-
// Ignore action loading failure during validation
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
for (const pb of target.playbooks) {
|
|
506
|
-
if (!pb) continue;
|
|
507
|
-
const errors: string[] = [];
|
|
508
|
-
const warnings: string[] = [];
|
|
509
|
-
|
|
510
|
-
if (!pb.id) errors.push("Missing playbook id");
|
|
511
|
-
if (!pb.content) warnings.push("Playbook content is empty");
|
|
512
|
-
|
|
513
|
-
if (pb.actions) {
|
|
514
|
-
for (const actId of pb.actions) {
|
|
515
|
-
if (actionIds.has(actId)) {
|
|
516
|
-
continue;
|
|
517
|
-
}
|
|
518
|
-
try {
|
|
519
|
-
await resolveActionProject(actId, target.root);
|
|
520
|
-
} catch (err: any) {
|
|
521
|
-
warnings.push(err.message);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
results.push({
|
|
527
|
-
id: pb.id,
|
|
528
|
-
packageId: target.packageId,
|
|
529
|
-
valid: errors.length === 0,
|
|
530
|
-
warnings,
|
|
531
|
-
errors,
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const allValid = results.every((r) => r.valid);
|
|
537
|
-
renderResult(
|
|
538
|
-
{ valid: allValid, results },
|
|
539
|
-
{
|
|
540
|
-
json: options.json,
|
|
541
|
-
envelope: options.envelope,
|
|
542
|
-
humanFormatter: () => {
|
|
543
|
-
const lines: string[] = [];
|
|
544
|
-
for (const r of results) {
|
|
545
|
-
const prefix = targets.length > 1 || !root ? `[${r.packageId}] ` : "";
|
|
546
|
-
if (r.valid) {
|
|
547
|
-
const warn = r.warnings.length > 0 ? ` (Warnings: ${r.warnings.join("; ")})` : "";
|
|
548
|
-
lines.push(`[OK] ${prefix}${r.id}: Valid${warn}`);
|
|
549
|
-
} else {
|
|
550
|
-
lines.push(`[FAIL] ${prefix}${r.id}: ${r.errors.join("; ")}`);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
return lines.join("\n");
|
|
554
|
-
},
|
|
555
|
-
}
|
|
556
|
-
);
|
|
557
|
-
if (!allValid) process.exitCode = 1;
|
|
558
|
-
} catch (err: any) {
|
|
559
|
-
if (err instanceof ArgumentError || err instanceof CliError) {
|
|
560
|
-
throw err;
|
|
561
|
-
}
|
|
562
|
-
throw new ExecutionError(err.message);
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
|