@actiondock/cli 2.0.12 → 2.2.1
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 -34
- package/bin/ad.js +28 -41
- package/dist/commands/action/create.d.ts +9 -1
- 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 +2 -1
- 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 +2 -1
- package/dist/commands/doctor.js +82 -0
- package/dist/commands/export.d.ts +2 -1
- 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 +31 -1
- package/dist/commands/index.js +111 -0
- package/dist/commands/info.d.ts +9 -2
- package/dist/commands/info.js +210 -0
- package/dist/commands/init.js +24 -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 -2
- package/dist/commands/mcp.js +173 -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 +7 -3
- package/dist/commands/playbook.js +433 -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 -2
- package/dist/commands/runs.js +245 -0
- package/dist/commands/serve.d.ts +9 -2
- package/dist/commands/serve.js +135 -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 -2
- package/dist/commands/state.js +310 -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 +8 -1
- package/dist/index.js +41 -1000
- 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 -1
- package/dist/utils/bytes.js +1 -0
- package/dist/utils/duration.js +1 -0
- package/dist/utils/filter.d.ts +1 -1
- 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 +10 -12
- package/dist/commands/action/index.d.ts +0 -16
- package/dist/commands/action/list.d.ts +0 -5
- package/dist/commands/action/run.d.ts +0 -4
- package/dist/commands/action/show.d.ts +0 -5
- package/dist/commands/action/sync.d.ts +0 -2
- package/dist/commands/action/validate.d.ts +0 -5
- package/dist/commands/action.d.ts +0 -1
- package/dist/commands/config.d.ts +0 -2
package/dist/index.js
CHANGED
|
@@ -1,1009 +1,50 @@
|
|
|
1
|
-
// packages/cli/src/index.ts
|
|
2
|
-
import { formatError } from "@actiondock/runtime-cli";
|
|
3
|
-
import { setupNodeRuntime } from "@actiondock/runtime-node";
|
|
4
1
|
import { CommanderError } from "commander";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const config = loadProjectConfig(root);
|
|
35
|
-
const actionsDir = resolve(root, config.actionsDir || "actions");
|
|
36
|
-
if (!existsSync(actionsDir)) {
|
|
37
|
-
mkdirSync(actionsDir, { recursive: true });
|
|
38
|
-
}
|
|
39
|
-
if (options.file && isAbsolute(options.file)) {
|
|
40
|
-
throw new ExecutionError(`--file option must be a relative path, received: ${options.file}`);
|
|
41
|
-
}
|
|
42
|
-
const cleanName = getPackageSlug(id);
|
|
43
|
-
const targetRelFile = options.file || `${cleanName}.ts`;
|
|
44
|
-
const targetFullFile = resolve(actionsDir, targetRelFile);
|
|
45
|
-
assertPathWithinRoot(actionsDir, targetFullFile, "action file");
|
|
46
|
-
if (existsSync(targetFullFile)) {
|
|
47
|
-
throw new ExecutionError(`Target action file already exists at ${targetFullFile}`);
|
|
48
|
-
}
|
|
49
|
-
const desc = options.desc || `Action ${id}`;
|
|
50
|
-
const template = `import { defineAction } from "@actiondock/sdk";
|
|
51
|
-
|
|
52
|
-
export interface Input {
|
|
53
|
-
exampleParam?: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface Output {
|
|
57
|
-
success: boolean;
|
|
58
|
-
result?: unknown;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export default defineAction<Input, Output>({
|
|
62
|
-
id: "${id}",
|
|
63
|
-
description: "${desc}",
|
|
64
|
-
|
|
65
|
-
inputSchema: {
|
|
66
|
-
type: "object",
|
|
67
|
-
properties: {
|
|
68
|
-
exampleParam: {
|
|
69
|
-
type: "string",
|
|
70
|
-
description: "Example parameter description",
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
required: [],
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
outputSchema: {
|
|
77
|
-
type: "object",
|
|
78
|
-
properties: {
|
|
79
|
-
success: { type: "boolean" },
|
|
80
|
-
result: {},
|
|
81
|
-
},
|
|
82
|
-
required: ["success"],
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
async run(input, ctx) {
|
|
86
|
-
ctx.log.info("Running ${id}", input);
|
|
87
|
-
|
|
88
|
-
// Access config: ctx.config.get("MY_CONFIG")
|
|
89
|
-
// Access state: await ctx.state.get("my_key") / await ctx.state.set("my_key", val)
|
|
90
|
-
// Call action: await ctx.actions.invoke(otherAction, input)
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
success: true,
|
|
94
|
-
result: input.exampleParam || "done",
|
|
95
|
-
};
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
`;
|
|
99
|
-
writeFileSync(targetFullFile, template, "utf-8");
|
|
100
|
-
const manifest = loadManifest(root) || { schemaVersion: 1, actions: {}, assets: [] };
|
|
101
|
-
manifest.actions = manifest.actions || {};
|
|
102
|
-
manifest.actions[id] = {
|
|
103
|
-
entry: join(config.actionsDir || "actions", targetRelFile).replace(/\\/g, "/"),
|
|
104
|
-
description: desc,
|
|
105
|
-
inputSchema: {
|
|
106
|
-
type: "object",
|
|
107
|
-
properties: {
|
|
108
|
-
exampleParam: {
|
|
109
|
-
type: "string",
|
|
110
|
-
description: "Example parameter description"
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
required: []
|
|
114
|
-
},
|
|
115
|
-
outputSchema: {
|
|
116
|
-
type: "object",
|
|
117
|
-
properties: {
|
|
118
|
-
success: { type: "boolean" },
|
|
119
|
-
result: {}
|
|
120
|
-
},
|
|
121
|
-
required: ["success"]
|
|
122
|
-
},
|
|
123
|
-
uses: [],
|
|
124
|
-
tags: []
|
|
125
|
-
};
|
|
126
|
-
saveManifest(root, manifest);
|
|
127
|
-
console.log(`[OK] Created Action '${id}' at ${targetFullFile}`);
|
|
128
|
-
console.log(`
|
|
129
|
-
To run this action:`);
|
|
130
|
-
console.log(` ad action run ${id} --input '{"exampleParam": "hello"}'`);
|
|
131
|
-
} catch (err) {
|
|
132
|
-
if (err instanceof ExecutionError)
|
|
133
|
-
throw err;
|
|
134
|
-
throw new ExecutionError(err.message);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// packages/cli/src/commands/action/sync.ts
|
|
140
|
-
import { findProjectRoot as findProjectRoot2, syncManifest } from "@actiondock/core";
|
|
141
|
-
import { ExecutionError as ExecutionError2, getEffectiveOptions, renderResult } from "@actiondock/runtime-cli";
|
|
142
|
-
function registerActionSyncCommand(actionCmd) {
|
|
143
|
-
actionCmd.command("sync").description("Synchronize action metadata manifest with TypeScript definitions").option("--check", "Check if manifest is synchronized without modifying files").option("--no-prune", "Do not remove deleted actions from manifest").option("--json", "Output as JSON").option("--envelope", "Wrap JSON output in standard envelope").action(async (rawOptions, cmd) => {
|
|
144
|
-
const options = getEffectiveOptions(rawOptions, cmd);
|
|
145
|
-
const root = findProjectRoot2();
|
|
146
|
-
if (!root) {
|
|
147
|
-
throw new ExecutionError2("Not in an ActionDock project (actiondock.json not found)");
|
|
148
|
-
}
|
|
149
|
-
try {
|
|
150
|
-
const result = await syncManifest(root, {
|
|
151
|
-
check: Boolean(options.check),
|
|
152
|
-
prune: options.prune !== false
|
|
153
|
-
});
|
|
154
|
-
renderResult(result, {
|
|
155
|
-
json: options.json,
|
|
156
|
-
envelope: options.envelope,
|
|
157
|
-
humanFormatter: () => {
|
|
158
|
-
const lines = [];
|
|
159
|
-
if (options.check) {
|
|
160
|
-
if (result.inSync) {
|
|
161
|
-
lines.push("[OK] actiondock.manifest.json is up to date.");
|
|
162
|
-
} else {
|
|
163
|
-
lines.push(`[FAIL] actiondock.manifest.json is out of sync with action definitions:
|
|
164
|
-
`);
|
|
165
|
-
if (result.added.length > 0) {
|
|
166
|
-
lines.push(` - Added actions: ${result.added.join(", ")}`);
|
|
167
|
-
}
|
|
168
|
-
if (result.updated.length > 0) {
|
|
169
|
-
lines.push(` - Updated actions: ${result.updated.join(", ")}`);
|
|
170
|
-
}
|
|
171
|
-
if (result.removed.length > 0) {
|
|
172
|
-
lines.push(` - Removed actions: ${result.removed.join(", ")}`);
|
|
173
|
-
}
|
|
174
|
-
lines.push(`
|
|
175
|
-
Run 'ad action sync' to synchronize metadata manifest.`);
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
if (result.inSync) {
|
|
179
|
-
lines.push(`[OK] actiondock.manifest.json is already up to date (${result.unchanged.length} action(s)).`);
|
|
180
|
-
} else {
|
|
181
|
-
lines.push(`[OK] Successfully synchronized actiondock.manifest.json:
|
|
182
|
-
`);
|
|
183
|
-
if (result.added.length > 0) {
|
|
184
|
-
lines.push(` - Added: ${result.added.join(", ")}`);
|
|
185
|
-
}
|
|
186
|
-
if (result.updated.length > 0) {
|
|
187
|
-
lines.push(` - Updated: ${result.updated.join(", ")}`);
|
|
188
|
-
}
|
|
189
|
-
if (result.removed.length > 0) {
|
|
190
|
-
lines.push(` - Removed: ${result.removed.join(", ")}`);
|
|
191
|
-
}
|
|
192
|
-
lines.push(`
|
|
193
|
-
Manifest updated at ${result.manifestPath}`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
return lines.join(`
|
|
197
|
-
`);
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
if (options.check && !result.inSync) {
|
|
201
|
-
process.exitCode = 1;
|
|
202
|
-
}
|
|
203
|
-
} catch (err) {
|
|
204
|
-
if (err instanceof ExecutionError2)
|
|
205
|
-
throw err;
|
|
206
|
-
throw new ExecutionError2(err.message);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
// packages/cli/src/commands/action/list.ts
|
|
211
|
-
import { registerActionCommands } from "@actiondock/runtime-cli";
|
|
212
|
-
// packages/cli/src/commands/action/show.ts
|
|
213
|
-
import { registerActionCommands as registerActionCommands2 } from "@actiondock/runtime-cli";
|
|
214
|
-
// packages/cli/src/commands/action/validate.ts
|
|
215
|
-
import { registerActionCommands as registerActionCommands3 } from "@actiondock/runtime-cli";
|
|
216
|
-
// packages/cli/src/commands/action/run.ts
|
|
217
|
-
import { executeAction, getEffectiveOptions as getEffectiveOptions2 } from "@actiondock/runtime-cli";
|
|
218
|
-
// packages/cli/src/commands/action/index.ts
|
|
219
|
-
function registerActionCommands4(program) {
|
|
220
|
-
registerRuntimeActionCommands(program);
|
|
221
|
-
const actionCmd = program.commands.find((c) => c.name() === "action") || program.command("action");
|
|
222
|
-
registerActionCreateCommand(actionCmd);
|
|
223
|
-
registerActionSyncCommand(actionCmd);
|
|
224
|
-
}
|
|
225
|
-
// packages/cli/src/commands/build.ts
|
|
226
|
-
import { buildProject } from "@actiondock/builder";
|
|
227
|
-
import { findProjectRoot as findProjectRoot3, resolvePackageRoot } from "@actiondock/core";
|
|
228
|
-
import { ExecutionError as ExecutionError3 } from "@actiondock/runtime-cli";
|
|
229
|
-
function registerBuildCommand(program) {
|
|
230
|
-
program.command("build").description("Build project actions into a single standalone executable").option("-P, --package <id>", "Target package ID or path").option("-t, --target <target>", "Target compilation platform (e.g. bun, linux-x64, darwin-arm64, windows-x64)").option("-o, --out <path>", "Output executable path").option("-a, --actions <actions...>", "Only build specific action(s) into the standalone binary").option("-m, --minify", "Minify bundled JavaScript (default: true)", true).option("--no-minify", "Disable JavaScript minification").option("--bytecode", "Compile JavaScript to bytecode for faster startup (default: true)", true).option("--no-bytecode", "Disable bytecode compilation").action(async (options) => {
|
|
231
|
-
const root = options.package ? resolvePackageRoot(options.package) : findProjectRoot3();
|
|
232
|
-
if (!root) {
|
|
233
|
-
if (options.package) {
|
|
234
|
-
throw new ExecutionError3(`Package '${options.package}' not found in linked packages or path`);
|
|
235
|
-
}
|
|
236
|
-
throw new ExecutionError3(`Not in an ActionDock project (actiondock.json not found).
|
|
237
|
-
Please specify -P, --package <id> or cd into a project directory.`);
|
|
238
|
-
}
|
|
239
|
-
console.log("Building standalone executable...");
|
|
240
|
-
let result;
|
|
241
|
-
try {
|
|
242
|
-
result = await buildProject({
|
|
243
|
-
projectRoot: root,
|
|
244
|
-
target: options.target,
|
|
245
|
-
outfile: options.out,
|
|
246
|
-
minify: options.minify,
|
|
247
|
-
bytecode: options.bytecode,
|
|
248
|
-
actions: options.actions
|
|
249
|
-
});
|
|
250
|
-
} catch (err) {
|
|
251
|
-
throw new ExecutionError3(`Build failed: ${err.message}`);
|
|
252
|
-
}
|
|
253
|
-
console.log(`[OK] Successfully compiled ${result.packageId} (v${result.version})`);
|
|
254
|
-
console.log(` Target: ${result.target}`);
|
|
255
|
-
console.log(` Actions: ${result.actions.join(", ")}`);
|
|
256
|
-
console.log(` Executable: ${result.executablePath}`);
|
|
257
|
-
if (result.metadataPath) {
|
|
258
|
-
console.log(` Metadata: ${result.metadataPath}`);
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// packages/cli/src/commands/config.ts
|
|
264
|
-
import { registerConfigCommands as registerRuntimeConfigCommands } from "@actiondock/runtime-cli";
|
|
265
|
-
function registerConfigCommands(program) {
|
|
266
|
-
registerRuntimeConfigCommands(program);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// packages/cli/src/commands/doctor.ts
|
|
270
|
-
import { fetchRemoteDoctor, resolveTarget, runDoctorChecks } from "@actiondock/core";
|
|
271
|
-
import { ExecutionError as ExecutionError4, getEffectiveOptions as getEffectiveOptions3, renderResult as renderResult2 } from "@actiondock/runtime-cli";
|
|
272
|
-
function registerDoctorCommand(program) {
|
|
273
|
-
program.command("doctor").description("Check ActionDock environment, registry health, and project diagnostics").option("-P, --package <id|path>", "Target package ID or directory path for project diagnostics").option("-p, --profile <name>", "Query doctor diagnostics on a remote target").option("-s, --server <url>", "Remote server URL").option("-t, --token <token>", "Auth token for remote server").option("--json", "Output diagnostics report in JSON format").option("--envelope", "Wrap JSON output in standard envelope").action(async (rawOptions, cmd) => {
|
|
274
|
-
try {
|
|
275
|
-
const options = getEffectiveOptions3(rawOptions, cmd);
|
|
276
|
-
const target = resolveTarget({
|
|
277
|
-
profile: options.profile,
|
|
278
|
-
server: options.server,
|
|
279
|
-
token: options.token
|
|
280
|
-
});
|
|
281
|
-
let report;
|
|
282
|
-
if (target.type === "remote") {
|
|
283
|
-
const remoteRes = await fetchRemoteDoctor(target.serverUrl, target.token, options.package);
|
|
284
|
-
report = remoteRes.report || remoteRes;
|
|
285
|
-
} else {
|
|
286
|
-
report = await runDoctorChecks({
|
|
287
|
-
packageIdOrPath: options.package
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
renderResult2(report, {
|
|
291
|
-
json: options.json,
|
|
292
|
-
envelope: options.envelope,
|
|
293
|
-
humanFormatter: () => {
|
|
294
|
-
const lines = [];
|
|
295
|
-
const title = target.type === "remote" ? `[DOCTOR] Remote ActionDock Server Diagnostics (${target.serverUrl}${target.profileName ? ` - Profile: ${target.profileName}` : ""})
|
|
296
|
-
` : `[DOCTOR] ActionDock System & Project Diagnostics
|
|
297
|
-
`;
|
|
298
|
-
lines.push(title);
|
|
299
|
-
lines.push("[Runtime & Environment]");
|
|
300
|
-
const envChecks = (report.checks || []).filter((c) => c.category === "runtime" || c.category === "storage" || c.category === "registry");
|
|
301
|
-
for (const c of envChecks) {
|
|
302
|
-
const tag = c.status === "ok" ? "[OK]" : c.status === "warn" ? "[WARN]" : "[ERROR]";
|
|
303
|
-
lines.push(` ${tag} ${c.name}: ${c.message}`);
|
|
304
|
-
if (c.fix) {
|
|
305
|
-
lines.push(` Fix: ${c.fix}`);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if (report.hasProject) {
|
|
309
|
-
lines.push(`
|
|
310
|
-
[Project: ${report.packageId || "unknown"}] (${report.projectRoot})`);
|
|
311
|
-
const projChecks = (report.checks || []).filter((c) => c.category === "project");
|
|
312
|
-
for (const c of projChecks) {
|
|
313
|
-
const tag = c.status === "ok" ? "[OK]" : c.status === "warn" ? "[WARN]" : "[ERROR]";
|
|
314
|
-
lines.push(` ${tag} ${c.name}: ${c.message}`);
|
|
315
|
-
if (c.fix) {
|
|
316
|
-
lines.push(` Fix: ${c.fix}`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
} else {
|
|
320
|
-
lines.push(`
|
|
321
|
-
[Project Context]`);
|
|
322
|
-
lines.push(" [INFO] Not inside an ActionDock project directory (skipped project checks)");
|
|
323
|
-
}
|
|
324
|
-
lines.push(`
|
|
325
|
-
[Summary] ${report.summary.ok} passed, ${report.summary.warn} warning(s), ${report.summary.error} error(s)`);
|
|
326
|
-
return lines.join(`
|
|
327
|
-
`);
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
if (!report.ok) {
|
|
331
|
-
process.exitCode = 1;
|
|
332
|
-
}
|
|
333
|
-
} catch (err) {
|
|
334
|
-
throw new ExecutionError4(`Doctor failed to run diagnostics: ${err.message}`);
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// packages/cli/src/commands/export.ts
|
|
340
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
341
|
-
import { basename } from "node:path";
|
|
342
|
-
import { exportCompositeSkill, exportSkill, exportSkillBatch } from "@actiondock/builder";
|
|
343
|
-
import { discoverProjects, findProjectRoot as findProjectRoot4, listLinkedPackages, resolvePackageRoot as resolvePackageRoot2 } from "@actiondock/core";
|
|
344
|
-
import { ExecutionError as ExecutionError5 } from "@actiondock/runtime-cli";
|
|
345
|
-
function parseListOption(val, prev = []) {
|
|
346
|
-
const parts = val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
347
|
-
return [...prev, ...parts];
|
|
348
|
-
}
|
|
349
|
-
function registerExportCommand(program) {
|
|
350
|
-
const exportCmd = program.command("export").description("Export project artifacts");
|
|
351
|
-
exportCmd.command("skill").description("Export Skill directory for AI Agents (default: source skill; use -s/--standalone for pre-built standalone binary)").option("-P, --package <id...>", "Target package ID(s) or path(s) (can be specified multiple times or comma-separated)", parseListOption, []).option("--workspace", "Export all packages discovered in current workspace").option("--all", "Export all linked packages from global registry").option("--bundle [name]", "Export multiple packages as a unified composite Skill bundle (defaults to workspace directory name if omitted)").option("-s, --standalone", "Export pre-compiled standalone binary skill (for environments without ActionDock runtime)").option("-t, --target <target>", "Target compilation platform for standalone mode (e.g. host, linux-x64, darwin-arm64, windows-x64)").option("-o, --out <path>", "Output skill directory").option("-p, --playbook <playbooks...>", "Only export specific playbook(s) and their dependent actions (Playbook-driven minimal export)").option("-a, --actions <actions...>", "Only export specific action(s)").option("-m, --minify", "Minify bundled JavaScript in standalone mode (default: true)", true).option("--no-minify", "Disable JavaScript minification in standalone mode").option("--bytecode", "Compile JavaScript to bytecode in standalone mode (default: true)", true).option("--no-bytecode", "Disable bytecode compilation in standalone mode").option("-z, --archive", "Create a .zip archive of the exported skill").option("--skill-md <path>", "Use specified existing SKILL.md file instead of auto-generating").action(async (options) => {
|
|
352
|
-
const isStandalone = Boolean(options.standalone);
|
|
353
|
-
if (options.bundle && isStandalone) {
|
|
354
|
-
throw new ExecutionError5("Composite Skill export (--bundle) currently only supports source mode. Please remove --standalone to export as a composite workspace Skill.");
|
|
355
|
-
}
|
|
356
|
-
const sourceCount = (options.all ? 1 : 0) + (options.workspace ? 1 : 0) + (options.package && options.package.length > 0 ? 1 : 0);
|
|
357
|
-
if (sourceCount > 1) {
|
|
358
|
-
throw new ExecutionError5("Options -P/--package, --workspace, and --all are mutually exclusive. Please specify only one package selection mode.");
|
|
359
|
-
}
|
|
360
|
-
let roots = [];
|
|
361
|
-
if (options.all) {
|
|
362
|
-
const linked = listLinkedPackages();
|
|
363
|
-
roots = linked.map((p) => p.path).filter((p) => existsSync2(p));
|
|
364
|
-
if (roots.length === 0) {
|
|
365
|
-
throw new ExecutionError5("No linked packages found in global registry.");
|
|
366
|
-
}
|
|
367
|
-
} else if (options.workspace) {
|
|
368
|
-
const cwd = process.cwd();
|
|
369
|
-
const discovered = discoverProjects(cwd);
|
|
370
|
-
roots = discovered.length > 0 ? discovered : findProjectRoot4(cwd) ? [findProjectRoot4(cwd)] : [];
|
|
371
|
-
if (roots.length === 0) {
|
|
372
|
-
throw new ExecutionError5(`No ActionDock packages found in workspace '${cwd}'.
|
|
373
|
-
Make sure child directories contain actiondock.json.`);
|
|
374
|
-
}
|
|
375
|
-
} else if (options.package && options.package.length > 0) {
|
|
376
|
-
for (const pkgIdOrPath of options.package) {
|
|
377
|
-
const root = resolvePackageRoot2(pkgIdOrPath);
|
|
378
|
-
if (!root) {
|
|
379
|
-
throw new ExecutionError5(`Package '${pkgIdOrPath}' not found in linked packages or path`);
|
|
380
|
-
}
|
|
381
|
-
if (!roots.includes(root)) {
|
|
382
|
-
roots.push(root);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
} else {
|
|
386
|
-
const root = findProjectRoot4();
|
|
387
|
-
if (root) {
|
|
388
|
-
roots.push(root);
|
|
389
|
-
} else {
|
|
390
|
-
throw new ExecutionError5(`Not in an ActionDock project (actiondock.json not found).
|
|
391
|
-
Please specify -P, --package <id>, --workspace, --all, or cd into a project directory.`);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if (roots.length > 1 && (options.actions || options.playbook)) {
|
|
395
|
-
throw new ExecutionError5("Filtering options (--actions, --playbook) can only be used when exporting a single package.");
|
|
396
|
-
}
|
|
397
|
-
try {
|
|
398
|
-
if (options.bundle !== undefined) {
|
|
399
|
-
const bundleName = typeof options.bundle === "string" && options.bundle.trim() ? options.bundle.trim() : basename(process.cwd());
|
|
400
|
-
console.log(`Exporting composite Skill bundle '${bundleName}' (${roots.length} package${roots.length > 1 ? "s" : ""})...`);
|
|
401
|
-
const result = await exportCompositeSkill({
|
|
402
|
-
bundleName,
|
|
403
|
-
projectRoots: roots,
|
|
404
|
-
outDir: options.out,
|
|
405
|
-
archive: options.archive,
|
|
406
|
-
workspaceRoot: options.workspace ? process.cwd() : undefined,
|
|
407
|
-
skillMdPath: options.skillMd
|
|
408
|
-
});
|
|
409
|
-
console.log(`[OK] Successfully exported Composite Skill: ${result.bundleName}`);
|
|
410
|
-
console.log(` Packages: ${result.packagesCount}`);
|
|
411
|
-
console.log(` Actions: ${result.actionsCount}`);
|
|
412
|
-
console.log(` Playbooks: ${result.playbooksCount}`);
|
|
413
|
-
console.log(` Skill Dir: ${result.skillDir}`);
|
|
414
|
-
if (result.usedExistingSkillMd) {
|
|
415
|
-
console.log(` SKILL.md: Reused existing file from ${result.usedExistingSkillMd}`);
|
|
416
|
-
}
|
|
417
|
-
if (result.archivePath) {
|
|
418
|
-
console.log(` Archive: ${result.archivePath}`);
|
|
419
|
-
}
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
if (roots.length > 1) {
|
|
423
|
-
console.log(`Batch exporting ${roots.length} Skill packages...`);
|
|
424
|
-
const batchRes = await exportSkillBatch({
|
|
425
|
-
projectRoots: roots,
|
|
426
|
-
mode: isStandalone ? "standalone" : "source",
|
|
427
|
-
standalone: isStandalone,
|
|
428
|
-
target: options.target,
|
|
429
|
-
outDir: options.out,
|
|
430
|
-
archive: options.archive,
|
|
431
|
-
playbooks: options.playbook,
|
|
432
|
-
actions: options.actions,
|
|
433
|
-
minify: options.minify,
|
|
434
|
-
bytecode: options.bytecode,
|
|
435
|
-
skillMdPath: options.skillMd
|
|
436
|
-
});
|
|
437
|
-
console.log(`[OK] Successfully batch exported ${batchRes.results.length} Skill packages to: ${batchRes.outDir}`);
|
|
438
|
-
for (const res of batchRes.results) {
|
|
439
|
-
console.log(` - ${res.packageId} (v${res.version}): ${res.actionsCount} actions, ${res.playbooksCount} playbooks -> ${res.skillDir}`);
|
|
440
|
-
}
|
|
441
|
-
console.log(` Total Actions: ${batchRes.totalActions}`);
|
|
442
|
-
console.log(` Total Playbooks: ${batchRes.totalPlaybooks}`);
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
const root = roots[0];
|
|
446
|
-
console.log(`Exporting ${isStandalone ? "standalone binary" : "source"} Skill artifact...`);
|
|
447
|
-
const result = await exportSkill({
|
|
448
|
-
projectRoot: root,
|
|
449
|
-
mode: isStandalone ? "standalone" : "source",
|
|
450
|
-
standalone: isStandalone,
|
|
451
|
-
target: options.target,
|
|
452
|
-
outDir: options.out,
|
|
453
|
-
archive: options.archive,
|
|
454
|
-
playbooks: options.playbook,
|
|
455
|
-
actions: options.actions,
|
|
456
|
-
minify: options.minify,
|
|
457
|
-
bytecode: options.bytecode,
|
|
458
|
-
skillMdPath: options.skillMd
|
|
459
|
-
});
|
|
460
|
-
console.log(`[OK] Successfully exported ${result.mode === "standalone" ? "Standalone" : "Source"} Skill: ${result.packageId} (v${result.version})`);
|
|
461
|
-
console.log(` Mode: ${result.mode}${result.mode === "standalone" ? ` (target: ${result.target})` : ""}`);
|
|
462
|
-
console.log(` Actions: ${result.actionsCount}`);
|
|
463
|
-
console.log(` Playbooks: ${result.playbooksCount}`);
|
|
464
|
-
console.log(` Skill Dir: ${result.skillDir}`);
|
|
465
|
-
if (result.usedExistingSkillMd) {
|
|
466
|
-
console.log(` SKILL.md: Reused existing file from ${result.usedExistingSkillMd}`);
|
|
467
|
-
}
|
|
468
|
-
if (result.archivePath) {
|
|
469
|
-
console.log(` Archive: ${result.archivePath}`);
|
|
470
|
-
}
|
|
471
|
-
} catch (err) {
|
|
472
|
-
throw new ExecutionError5(`Export failed: ${err.message}`);
|
|
473
|
-
}
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// packages/cli/src/commands/info.ts
|
|
478
|
-
import { registerInfoCommand as registerRuntimeInfoCommand } from "@actiondock/runtime-cli";
|
|
479
|
-
function registerInfoCommand(program) {
|
|
480
|
-
registerRuntimeInfoCommand(program);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// packages/cli/src/commands/init.ts
|
|
484
|
-
import { initProject } from "@actiondock/core";
|
|
485
|
-
import { ExecutionError as ExecutionError6 } from "@actiondock/runtime-cli";
|
|
486
|
-
function registerInitCommand(program) {
|
|
487
|
-
program.command("init [directory]").description("Initialize a new ActionDock project").option("-i, --id <id>", "Project identifier").option("-n, --name <name>", "Project display name").option("-d, --desc <description>", "Project description").action((dir = ".", options) => {
|
|
488
|
-
try {
|
|
489
|
-
initProject(dir, {
|
|
490
|
-
id: options.id,
|
|
491
|
-
name: options.name,
|
|
492
|
-
description: options.desc
|
|
493
|
-
});
|
|
494
|
-
console.log(`Successfully initialized ActionDock project in ${dir}`);
|
|
495
|
-
} catch (err) {
|
|
496
|
-
throw new ExecutionError6(err.message);
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// packages/cli/src/commands/link.ts
|
|
502
|
-
import {
|
|
503
|
-
linkPackage,
|
|
504
|
-
pruneRegistry,
|
|
505
|
-
unlinkPackage
|
|
506
|
-
} from "@actiondock/core";
|
|
507
|
-
import { ExecutionError as ExecutionError7 } from "@actiondock/runtime-cli";
|
|
508
|
-
function registerLinkCommands(program) {
|
|
509
|
-
program.command("link [path]").option("-r, --recursive", "Recursively discover and link packages in subdirectories").description("Link local Action package(s) or workspace directory into global registry for instant cross-directory execution").action(async (targetPath, options) => {
|
|
510
|
-
try {
|
|
511
|
-
const result = linkPackage(targetPath || process.cwd(), undefined, {
|
|
512
|
-
recursive: options.recursive
|
|
513
|
-
});
|
|
514
|
-
if (result.isWorkspace) {
|
|
515
|
-
console.log(`[OK] Linked workspace '${result.path}' (${result.entries.length} package${result.entries.length > 1 ? "s" : ""}):`);
|
|
516
|
-
for (const e of result.entries) {
|
|
517
|
-
console.log(` - ${e.id} (v${e.version}) -> ${e.path}`);
|
|
518
|
-
}
|
|
519
|
-
console.log("[INFO] Sub-packages added to this workspace will be automatically discovered.");
|
|
520
|
-
} else {
|
|
521
|
-
console.log(`[OK] Linked package '${result.id}' (v${result.version}) from ${result.path}`);
|
|
522
|
-
}
|
|
523
|
-
} catch (err) {
|
|
524
|
-
throw new ExecutionError7(err.message);
|
|
525
|
-
}
|
|
526
|
-
});
|
|
527
|
-
program.command("unlink [identifier]").option("-p, --prune", "Automatically scan and prune any stale/missing paths from registry").description("Unlink a package, workspace, or prune stale entries from global developer registry").action((identifier, options) => {
|
|
528
|
-
try {
|
|
529
|
-
if (options.prune || identifier === "--prune") {
|
|
530
|
-
const result = pruneRegistry();
|
|
531
|
-
const totalPruned = result.prunedPackages.length + result.prunedWorkspaces.length;
|
|
532
|
-
if (totalPruned === 0) {
|
|
533
|
-
console.log("[OK] No stale registry entries found.");
|
|
534
|
-
} else {
|
|
535
|
-
console.log(`[OK] Pruned ${result.prunedWorkspaces.length} workspace(s) and ${result.prunedPackages.length} package(s) from registry.`);
|
|
536
|
-
for (const ws of result.prunedWorkspaces) {
|
|
537
|
-
console.log(` - [workspace] ${ws.path}`);
|
|
538
|
-
}
|
|
539
|
-
for (const pkg of result.prunedPackages) {
|
|
540
|
-
console.log(` - [package] ${pkg.id} (${pkg.path})`);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
const removed = unlinkPackage(identifier || process.cwd());
|
|
546
|
-
if (removed) {
|
|
547
|
-
if (removed.type === "workspace") {
|
|
548
|
-
console.log(`[OK] Unlinked workspace '${removed.path}' (${removed.packagesCount || 0} package${(removed.packagesCount || 0) > 1 ? "s" : ""} unlinked)`);
|
|
549
|
-
} else {
|
|
550
|
-
console.log(`[OK] Unlinked package '${removed.id}' (${removed.path})`);
|
|
551
|
-
}
|
|
552
|
-
} else {
|
|
553
|
-
console.log(`Package or workspace '${identifier || process.cwd()}' was not linked in registry`);
|
|
554
|
-
}
|
|
555
|
-
} catch (err) {
|
|
556
|
-
throw new ExecutionError7(err.message);
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// packages/cli/src/commands/mcp.ts
|
|
562
|
-
import { registerMcpCommands as registerRuntimeMcpCommands } from "@actiondock/runtime-cli";
|
|
563
|
-
function registerMcpCommands(program) {
|
|
564
|
-
registerRuntimeMcpCommands(program);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// packages/cli/src/commands/playbook.ts
|
|
568
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
569
|
-
import { dirname, isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
570
|
-
import { assertPathWithinRoot as assertPathWithinRoot2, findProjectRoot as findProjectRoot5, loadProjectConfig as loadProjectConfig2 } from "@actiondock/core";
|
|
571
|
-
import {
|
|
572
|
-
CliError,
|
|
573
|
-
ExecutionError as ExecutionError8,
|
|
574
|
-
registerPlaybookCommands as registerRuntimePlaybookCommands
|
|
575
|
-
} from "@actiondock/runtime-cli";
|
|
576
|
-
function registerPlaybookCommands(program) {
|
|
577
|
-
registerRuntimePlaybookCommands(program);
|
|
578
|
-
const pbCmd = program.commands.find((c) => c.name() === "playbook") || program.command("playbook");
|
|
579
|
-
pbCmd.command("create <id>").alias("new").description("Scaffold a new Playbook markdown file").option("-d, --desc <description>", "Playbook description").option("-a, --actions <actions...>", "Referenced action IDs").option("-f, --file <filePath>", "Target file path relative to playbooks dir").action((id, options) => {
|
|
580
|
-
const root = findProjectRoot5();
|
|
581
|
-
if (!root) {
|
|
582
|
-
throw new ExecutionError8("Not in an ActionDock project (actiondock.json not found)");
|
|
583
|
-
}
|
|
584
|
-
try {
|
|
585
|
-
const config = loadProjectConfig2(root);
|
|
586
|
-
const pbDir = resolve2(root, config.playbooksDir || "playbooks");
|
|
587
|
-
if (!existsSync3(pbDir)) {
|
|
588
|
-
mkdirSync2(pbDir, { recursive: true });
|
|
589
|
-
}
|
|
590
|
-
if (options.file && isAbsolute2(options.file)) {
|
|
591
|
-
throw new ExecutionError8(`--file option must be a relative path, received: ${options.file}`);
|
|
592
|
-
}
|
|
593
|
-
const cleanName = id.replace(/[^a-zA-Z0-9-_]/g, "-");
|
|
594
|
-
const targetRelFile = options.file || `${cleanName}.md`;
|
|
595
|
-
const targetFullFile = resolve2(pbDir, targetRelFile);
|
|
596
|
-
assertPathWithinRoot2(pbDir, targetFullFile, "playbook file");
|
|
597
|
-
if (existsSync3(targetFullFile)) {
|
|
598
|
-
throw new ExecutionError8(`File '${targetFullFile}' already exists`);
|
|
599
|
-
}
|
|
600
|
-
mkdirSync2(dirname(targetFullFile), { recursive: true });
|
|
601
|
-
const desc = options.desc || `SOP guide for ${id}`;
|
|
602
|
-
const actionsList = Array.isArray(options.actions) ? options.actions : [];
|
|
603
|
-
const actionsYaml = actionsList.length > 0 ? `actions:
|
|
604
|
-
${actionsList.map((a) => ` - ${a}`).join(`
|
|
605
|
-
`)}
|
|
606
|
-
` : `actions: []
|
|
607
|
-
`;
|
|
608
|
-
const template = `---
|
|
609
|
-
id: ${id}
|
|
610
|
-
description: ${desc}
|
|
611
|
-
${actionsYaml}---
|
|
612
|
-
|
|
613
|
-
# ${id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())} SOP
|
|
614
|
-
|
|
615
|
-
This playbook provides task execution guidance for AI Agents.
|
|
616
|
-
|
|
617
|
-
## Instructions
|
|
618
|
-
|
|
619
|
-
- Inspect available actions with \`<binary> list --json\`.
|
|
620
|
-
- Follow the required steps to complete the task.
|
|
621
|
-
`;
|
|
622
|
-
writeFileSync2(targetFullFile, template, "utf-8");
|
|
623
|
-
console.log(`[OK] Created Playbook '${id}' at ${targetFullFile}`);
|
|
624
|
-
} catch (err) {
|
|
625
|
-
if (err instanceof CliError) {
|
|
626
|
-
throw err;
|
|
627
|
-
}
|
|
628
|
-
throw new ExecutionError8(err.message);
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// packages/cli/src/commands/profile.ts
|
|
634
|
-
import {
|
|
635
|
-
addProfile,
|
|
636
|
-
checkRemoteHealth,
|
|
637
|
-
filterWithFallbackInfo,
|
|
638
|
-
getProfile,
|
|
639
|
-
listProfiles,
|
|
640
|
-
loadProfiles,
|
|
641
|
-
maskSecretValue,
|
|
642
|
-
removeProfile,
|
|
643
|
-
resolveProfileToken,
|
|
644
|
-
resolveTarget as resolveTarget2,
|
|
645
|
-
toSnakeUpperCase,
|
|
646
|
-
useProfile
|
|
647
|
-
} from "@actiondock/core";
|
|
648
|
-
import {
|
|
649
|
-
ArgumentError,
|
|
650
|
-
CliError as CliError2,
|
|
651
|
-
ExecutionError as ExecutionError9,
|
|
652
|
-
getEffectiveOptions as getEffectiveOptions4,
|
|
653
|
-
renderResult as renderResult3
|
|
654
|
-
} from "@actiondock/runtime-cli";
|
|
655
|
-
|
|
656
|
-
// packages/cli/src/utils/filter.ts
|
|
657
|
-
import { resolveIntent } from "@actiondock/runtime-cli";
|
|
658
|
-
|
|
659
|
-
// packages/cli/src/commands/profile.ts
|
|
660
|
-
function registerProfileCommands(program) {
|
|
661
|
-
const profileCmd = program.command("profile").description("Manage multi-cloud and remote execution profiles");
|
|
662
|
-
profileCmd.command("list [patterns...]").description("List all configured profiles").option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match").option("--reveal, --show-secrets", "Reveal plain text values for tokens").option("--fallback", "Enable fallback to full list when no items match intent").option("--no-fallback", "Disable fallback to full list when no items match intent").option("--json", "Output as JSON").option("--envelope", "Wrap JSON output in standard envelope").action((patterns, rawOptions, cmd) => {
|
|
663
|
-
try {
|
|
664
|
-
const options = getEffectiveOptions4(rawOptions, cmd);
|
|
665
|
-
const effectiveIntent = resolveIntent(options.intent, patterns);
|
|
666
|
-
const isMachine = Boolean(options.json || options.envelope);
|
|
667
|
-
const fallbackExplicit = options.fallback === true || Array.isArray(process.argv) && process.argv.includes("--fallback");
|
|
668
|
-
const shouldFallback = isMachine ? fallbackExplicit : options.fallback !== false;
|
|
669
|
-
const reveal = Boolean(options.reveal || options.showSecrets);
|
|
670
|
-
const list = listProfiles();
|
|
671
|
-
const enriched = list.map((p) => {
|
|
672
|
-
const resolved = resolveProfileToken(p.name, p.entry);
|
|
673
|
-
return {
|
|
674
|
-
name: p.name,
|
|
675
|
-
isCurrent: p.isCurrent,
|
|
676
|
-
serverUrl: p.entry.serverUrl,
|
|
677
|
-
description: p.entry.description || "",
|
|
678
|
-
tokenEnv: p.entry.tokenEnv,
|
|
679
|
-
tokenConfigured: resolved.source !== "none",
|
|
680
|
-
tokenSource: resolved.source,
|
|
681
|
-
token: reveal ? resolved.token : resolved.token ? maskSecretValue(resolved.token) : undefined
|
|
682
|
-
};
|
|
683
|
-
});
|
|
684
|
-
const filterRes = filterWithFallbackInfo(enriched, effectiveIntent, [(p) => p.name, (p) => p.serverUrl, (p) => p.description, (p) => p.tokenSource], shouldFallback);
|
|
685
|
-
if (filterRes.isFallback && isMachine) {
|
|
686
|
-
renderResult3({ items: filterRes.items, isFallback: true, matchedCount: 0 }, { json: options.json, envelope: options.envelope });
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
renderResult3(filterRes.items, {
|
|
690
|
-
json: options.json,
|
|
691
|
-
envelope: options.envelope,
|
|
692
|
-
humanFormatter: () => {
|
|
693
|
-
const lines = [`ActionDock Execution Profiles:
|
|
694
|
-
`];
|
|
695
|
-
if (filterRes.isFallback && effectiveIntent) {
|
|
696
|
-
lines.push(`(No profiles matched intent '${effectiveIntent}', showing all profiles)
|
|
697
|
-
`);
|
|
698
|
-
}
|
|
699
|
-
for (const item of filterRes.items) {
|
|
700
|
-
const currentMarker = item.isCurrent ? "* " : " ";
|
|
701
|
-
let tokenInfo = "";
|
|
702
|
-
if (item.tokenSource === "tokenEnv") {
|
|
703
|
-
tokenInfo = ` [token: env(${item.tokenEnv})]`;
|
|
704
|
-
} else if (item.tokenSource === "profileEnv") {
|
|
705
|
-
tokenInfo = ` [token: env(ACTIONDOCK_${toSnakeUpperCase(item.name)}_TOKEN)]`;
|
|
706
|
-
} else if (item.tokenSource === "profile") {
|
|
707
|
-
tokenInfo = ` [token: stored in profile]`;
|
|
708
|
-
} else if (item.tokenSource === "globalEnv") {
|
|
709
|
-
tokenInfo = ` [token: env(ACTIONDOCK_TOKEN)]`;
|
|
710
|
-
}
|
|
711
|
-
if (reveal && item.token) {
|
|
712
|
-
tokenInfo += ` = ${item.token}`;
|
|
2
|
+
import { createCliProgram } from "./commands/index.js";
|
|
3
|
+
import { formatError } from "./errors.js";
|
|
4
|
+
import { renderError } from "./renderer.js";
|
|
5
|
+
export * from "./types.js";
|
|
6
|
+
export * from "./errors.js";
|
|
7
|
+
export * from "./renderer.js";
|
|
8
|
+
export * from "./prompt.js";
|
|
9
|
+
export * from "./standalone/index.js";
|
|
10
|
+
export * from "./utils/index.js";
|
|
11
|
+
export * from "./services/index.js";
|
|
12
|
+
export * from "./commands/index.js";
|
|
13
|
+
/**
|
|
14
|
+
* ActionDock CLI 主执行入口函数。
|
|
15
|
+
*
|
|
16
|
+
* @param argv 命令行参数数组(默认使用 process.argv)
|
|
17
|
+
* @returns 进程退出码
|
|
18
|
+
*/
|
|
19
|
+
export async function main(argv = process.argv) {
|
|
20
|
+
const program = createCliProgram();
|
|
21
|
+
const isMachine = argv.includes("--json") || argv.includes("--envelope");
|
|
22
|
+
try {
|
|
23
|
+
await program.parseAsync(argv);
|
|
24
|
+
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
if (err instanceof CommanderError) {
|
|
28
|
+
if (err.exitCode === 0) {
|
|
29
|
+
return 0;
|
|
713
30
|
}
|
|
714
|
-
const desc = item.description ? ` - ${item.description}` : "";
|
|
715
|
-
lines.push(`${currentMarker}${item.name.padEnd(20)} ${item.serverUrl}${tokenInfo}${desc}`);
|
|
716
|
-
}
|
|
717
|
-
lines.push(`
|
|
718
|
-
Use 'ad profile use <name>' to switch or 'ad run <action> --profile <name>' to execute on a specific target.`);
|
|
719
|
-
return lines.join(`
|
|
720
|
-
`);
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
} catch (err) {
|
|
724
|
-
if (err instanceof ArgumentError || err instanceof CliError2)
|
|
725
|
-
throw err;
|
|
726
|
-
throw new ExecutionError9(err.message);
|
|
727
|
-
}
|
|
728
|
-
});
|
|
729
|
-
profileCmd.command("add <name>").description("Add or update a remote execution profile").requiredOption("-s, --server <url>", "Remote ActionDock server URL (e.g. http://1.2.3.4:5177)").option("-t, --token <token>", "Authentication token for the remote server (deprecated; prefer --token-env)").option("--token-env <env>", "Environment variable name containing the authentication token").option("-d, --desc <description>", "Description of this profile/machine").action((name, options) => {
|
|
730
|
-
try {
|
|
731
|
-
if (options.token) {
|
|
732
|
-
console.warn("Warning: storing tokens directly in profiles.json is deprecated. Use --token-env or standard environment variables (e.g. ACTIONDOCK_<PROFILE>_TOKEN) instead.");
|
|
733
|
-
}
|
|
734
|
-
addProfile(name, {
|
|
735
|
-
serverUrl: options.server,
|
|
736
|
-
token: options.token,
|
|
737
|
-
tokenEnv: options.tokenEnv,
|
|
738
|
-
description: options.desc
|
|
739
|
-
});
|
|
740
|
-
console.log(`[OK] Profile '${name}' configured for server: ${options.server}`);
|
|
741
|
-
} catch (err) {
|
|
742
|
-
throw new ExecutionError9(err.message);
|
|
743
|
-
}
|
|
744
|
-
});
|
|
745
|
-
profileCmd.command("use <name>").description("Switch active default profile").action((name) => {
|
|
746
|
-
try {
|
|
747
|
-
useProfile(name);
|
|
748
|
-
console.log(`[OK] Active profile switched to '${name}'`);
|
|
749
|
-
} catch (err) {
|
|
750
|
-
throw new ExecutionError9(err.message);
|
|
751
|
-
}
|
|
752
|
-
});
|
|
753
|
-
profileCmd.command("show [name]").description("Display details of a profile (defaults to active profile)").option("--reveal, --show-secrets", "Reveal plain text values for tokens").option("--json", "Output as JSON").option("--envelope", "Wrap JSON output in standard envelope").action((name, rawOptions, cmd) => {
|
|
754
|
-
try {
|
|
755
|
-
const options = getEffectiveOptions4(rawOptions, cmd);
|
|
756
|
-
const config = loadProfiles();
|
|
757
|
-
const targetName = name || config.currentProfile || "local";
|
|
758
|
-
const entry = getProfile(targetName);
|
|
759
|
-
const reveal = Boolean(options.reveal || options.showSecrets);
|
|
760
|
-
if (!entry && targetName !== "local") {
|
|
761
|
-
throw new ArgumentError(`Profile '${targetName}' not found.`);
|
|
762
|
-
}
|
|
763
|
-
const resolved = resolveProfileToken(targetName, entry);
|
|
764
|
-
const displayToken = reveal ? resolved.token : resolved.token ? maskSecretValue(resolved.token) : undefined;
|
|
765
|
-
const data = {
|
|
766
|
-
name: targetName,
|
|
767
|
-
isCurrent: config.currentProfile === targetName,
|
|
768
|
-
serverUrl: entry?.serverUrl || "local",
|
|
769
|
-
tokenConfigured: resolved.source !== "none",
|
|
770
|
-
tokenSource: resolved.source,
|
|
771
|
-
tokenEnv: entry?.tokenEnv,
|
|
772
|
-
token: displayToken,
|
|
773
|
-
description: entry?.description || ""
|
|
774
|
-
};
|
|
775
|
-
renderResult3(data, {
|
|
776
|
-
json: options.json,
|
|
777
|
-
envelope: options.envelope,
|
|
778
|
-
humanFormatter: () => {
|
|
779
|
-
const lines = [];
|
|
780
|
-
lines.push(`Profile: ${data.name}${data.isCurrent ? " (Active)" : ""}`);
|
|
781
|
-
lines.push(`Server URL: ${data.serverUrl}`);
|
|
782
|
-
let sourceDetail = "None";
|
|
783
|
-
if (data.tokenSource === "tokenEnv") {
|
|
784
|
-
sourceDetail = `Environment Variable ($${data.tokenEnv})`;
|
|
785
|
-
} else if (data.tokenSource === "profileEnv") {
|
|
786
|
-
sourceDetail = `Profile Environment Variable ($ACTIONDOCK_${toSnakeUpperCase(data.name)}_TOKEN)`;
|
|
787
|
-
} else if (data.tokenSource === "profile") {
|
|
788
|
-
sourceDetail = "Stored in profiles.json (Deprecated)";
|
|
789
|
-
} else if (data.tokenSource === "globalEnv") {
|
|
790
|
-
sourceDetail = "Global Environment Variable ($ACTIONDOCK_TOKEN)";
|
|
791
|
-
}
|
|
792
|
-
lines.push(`Auth Source: ${sourceDetail}`);
|
|
793
|
-
if (data.tokenConfigured) {
|
|
794
|
-
lines.push(`Token Value: ${data.token}`);
|
|
795
|
-
}
|
|
796
|
-
if (data.description) {
|
|
797
|
-
lines.push(`Description: ${data.description}`);
|
|
798
|
-
}
|
|
799
|
-
return lines.join(`
|
|
800
|
-
`);
|
|
801
31
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
throw new ExecutionError9(err.message);
|
|
808
|
-
}
|
|
809
|
-
});
|
|
810
|
-
profileCmd.command("rm <name>").alias("remove").description("Remove a profile").action((name) => {
|
|
811
|
-
try {
|
|
812
|
-
if (name === "local") {
|
|
813
|
-
throw new ExecutionError9("Cannot remove built-in 'local' profile");
|
|
814
|
-
}
|
|
815
|
-
const removed = removeProfile(name);
|
|
816
|
-
if (!removed) {
|
|
817
|
-
throw new ExecutionError9(`Profile '${name}' not found`);
|
|
818
|
-
}
|
|
819
|
-
console.log(`[OK] Profile '${name}' removed`);
|
|
820
|
-
} catch (err) {
|
|
821
|
-
if (err instanceof CliError2) {
|
|
822
|
-
throw err;
|
|
823
|
-
}
|
|
824
|
-
throw new ExecutionError9(err.message);
|
|
825
|
-
}
|
|
826
|
-
});
|
|
827
|
-
profileCmd.command("test [name]").description("Test connection latency and health of a profile").option("--json", "Output as JSON").option("--envelope", "Wrap JSON output in standard envelope").action(async (name, rawOptions, cmd) => {
|
|
828
|
-
try {
|
|
829
|
-
const options = getEffectiveOptions4(rawOptions, cmd);
|
|
830
|
-
const target = resolveTarget2({ profile: name });
|
|
831
|
-
if (target.type === "local") {
|
|
832
|
-
renderResult3({ ok: true, type: "local", message: "Local execution" }, {
|
|
833
|
-
json: options.json,
|
|
834
|
-
envelope: options.envelope,
|
|
835
|
-
humanFormatter: () => `Target profile '${target.profileName || "local"}' is local (runs in local runtime).`
|
|
32
|
+
// 复用 renderer 的 renderError 统一错误信封,消除手工拼装的双实现
|
|
33
|
+
renderError(err, {
|
|
34
|
+
json: isMachine,
|
|
35
|
+
envelope: isMachine,
|
|
836
36
|
});
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
const health = await checkRemoteHealth(target.serverUrl, target.token);
|
|
840
|
-
renderResult3(health, {
|
|
841
|
-
json: options.json,
|
|
842
|
-
envelope: options.envelope,
|
|
843
|
-
humanFormatter: () => {
|
|
844
|
-
if (health.ok) {
|
|
845
|
-
return `[OK] Connected to ${target.serverUrl} (${health.latencyMs}ms) - Version: ${health.version}, Status: ${health.status}`;
|
|
846
|
-
}
|
|
847
|
-
return `[FAIL] Connection to ${target.serverUrl} failed (${health.latencyMs}ms): ${health.error}`;
|
|
848
|
-
}
|
|
849
|
-
});
|
|
850
|
-
if (!health.ok) {
|
|
851
|
-
process.exitCode = 1;
|
|
852
|
-
}
|
|
853
|
-
} catch (err) {
|
|
854
|
-
if (err instanceof ArgumentError || err instanceof CliError2)
|
|
855
|
-
throw err;
|
|
856
|
-
throw new ExecutionError9(err.message);
|
|
37
|
+
process.exitCode = formatError(err).exitCode;
|
|
38
|
+
return process.exitCode;
|
|
857
39
|
}
|
|
858
|
-
});
|
|
859
40
|
}
|
|
860
|
-
|
|
861
|
-
// packages/cli/src/commands/runs.ts
|
|
862
|
-
import { registerRunsCommands as registerRuntimeRunsCommands } from "@actiondock/runtime-cli";
|
|
863
|
-
function registerRunsCommands(program) {
|
|
864
|
-
registerRuntimeRunsCommands(program);
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
// packages/cli/src/commands/serve.ts
|
|
868
|
-
import { registerServeCommand as registerRuntimeServeCommand } from "@actiondock/runtime-cli";
|
|
869
|
-
function registerServeCommand(program) {
|
|
870
|
-
registerRuntimeServeCommand(program);
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
// packages/cli/src/commands/state.ts
|
|
874
|
-
import { registerStateCommands as registerRuntimeStateCommands } from "@actiondock/runtime-cli";
|
|
875
|
-
function registerStateCommands(program) {
|
|
876
|
-
registerRuntimeStateCommands(program);
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
// packages/cli/src/commands/test.ts
|
|
880
|
-
import { spawnSync } from "node:child_process";
|
|
881
|
-
import { existsSync as existsSync4, readFileSync } from "node:fs";
|
|
882
|
-
import { join as join2 } from "node:path";
|
|
883
|
-
import { findProjectRoot as findProjectRoot6 } from "@actiondock/core";
|
|
884
|
-
function registerTestCommand(program) {
|
|
885
|
-
program.command("test [pattern]").description("Run project tests using configured test runner (node:test or bun test)").action((pattern) => {
|
|
886
|
-
const root = findProjectRoot6();
|
|
887
|
-
const cwd = root || process.cwd();
|
|
888
|
-
const pkgJsonPath = join2(cwd, "package.json");
|
|
889
|
-
let testCmd = "npm";
|
|
890
|
-
let testArgs = ["test"];
|
|
891
|
-
if (existsSync4(pkgJsonPath)) {
|
|
892
|
-
try {
|
|
893
|
-
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
894
|
-
if (pkg.scripts?.test?.includes("bun test") && typeof globalThis.Bun !== "undefined") {
|
|
895
|
-
testCmd = "bun";
|
|
896
|
-
testArgs = ["test"];
|
|
897
|
-
}
|
|
898
|
-
} catch {}
|
|
899
|
-
}
|
|
900
|
-
if (pattern) {
|
|
901
|
-
if (testCmd === "npm") {
|
|
902
|
-
testArgs.push("--", pattern);
|
|
903
|
-
} else {
|
|
904
|
-
testArgs.push(pattern);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
const proc = spawnSync(testCmd, testArgs, {
|
|
908
|
-
cwd,
|
|
909
|
-
stdio: "inherit"
|
|
910
|
-
});
|
|
911
|
-
if (proc.status !== 0) {
|
|
912
|
-
process.exitCode = proc.status ?? 1;
|
|
913
|
-
}
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
// packages/cli/src/commands/index.ts
|
|
918
|
-
function applyCommonOptions(cmd) {
|
|
919
|
-
const hasOpt = (flagName) => cmd.options.some((o) => o.name() === flagName || o.long === `--${flagName}`);
|
|
920
|
-
if (!hasOpt("json")) {
|
|
921
|
-
cmd.option("--json", "Output as JSON");
|
|
922
|
-
}
|
|
923
|
-
if (!hasOpt("envelope")) {
|
|
924
|
-
cmd.option("--envelope", "Wrap JSON output in standard envelope");
|
|
925
|
-
}
|
|
926
|
-
if (!hasOpt("dataDir") && !hasOpt("data-dir")) {
|
|
927
|
-
cmd.option("--data-dir <path>", "Custom database storage directory");
|
|
928
|
-
}
|
|
929
|
-
for (const sub of cmd.commands) {
|
|
930
|
-
applyCommonOptions(sub);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
function createCliProgram() {
|
|
934
|
-
const program = new Command;
|
|
935
|
-
program.name("ad").description("ActionDock (ad) 2.0 - Toolchain for building and shipping standalone AI Agent Actions & Skills").version("2.0.12", "-v, --version");
|
|
936
|
-
program.option("-V", "output the version number");
|
|
937
|
-
program.on("option:V", () => {
|
|
938
|
-
program._outputConfiguration.writeOut(`${program.version()}
|
|
939
|
-
`);
|
|
940
|
-
program._exit(0, "commander.version", program.version());
|
|
941
|
-
});
|
|
942
|
-
program.exitOverride((err) => {
|
|
943
|
-
throw err;
|
|
944
|
-
});
|
|
945
|
-
registerInitCommand(program);
|
|
946
|
-
registerInfoCommand(program);
|
|
947
|
-
registerDoctorCommand(program);
|
|
948
|
-
registerActionCommands4(program);
|
|
949
|
-
registerPlaybookCommands(program);
|
|
950
|
-
registerConfigCommands(program);
|
|
951
|
-
registerStateCommands(program);
|
|
952
|
-
registerRunsCommands(program);
|
|
953
|
-
registerTestCommand(program);
|
|
954
|
-
registerBuildCommand(program);
|
|
955
|
-
registerExportCommand(program);
|
|
956
|
-
registerLinkCommands(program);
|
|
957
|
-
registerProfileCommands(program);
|
|
958
|
-
registerServeCommand(program);
|
|
959
|
-
registerMcpCommands(program);
|
|
960
|
-
applyCommonOptions(program);
|
|
961
|
-
return program;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// packages/cli/src/index.ts
|
|
965
41
|
import { fileURLToPath } from "node:url";
|
|
966
|
-
import { resolve
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
const program = createCliProgram();
|
|
970
|
-
const isMachine = argv.includes("--json") || argv.includes("--envelope");
|
|
971
|
-
try {
|
|
972
|
-
await program.parseAsync(argv);
|
|
973
|
-
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
974
|
-
} catch (err) {
|
|
975
|
-
if (err instanceof CommanderError) {
|
|
976
|
-
if (err.exitCode === 0) {
|
|
977
|
-
return 0;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
const formatted = formatError(err);
|
|
981
|
-
if (isMachine) {
|
|
982
|
-
const errorEnv = {
|
|
983
|
-
ok: false,
|
|
984
|
-
error: {
|
|
985
|
-
code: formatted.code,
|
|
986
|
-
message: formatted.message,
|
|
987
|
-
...formatted.details !== undefined ? { details: formatted.details } : {}
|
|
988
|
-
}
|
|
989
|
-
};
|
|
990
|
-
console.log(JSON.stringify(errorEnv, null, 2));
|
|
991
|
-
} else {
|
|
992
|
-
const msg = formatted.message.startsWith("Error: ") ? formatted.message : `Error: ${formatted.message}`;
|
|
993
|
-
console.error(msg);
|
|
994
|
-
}
|
|
995
|
-
process.exitCode = formatted.exitCode;
|
|
996
|
-
return formatted.exitCode;
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
var isDirectRun = Boolean(process.argv[1]) && resolve3(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
42
|
+
import { resolve } from "node:path";
|
|
43
|
+
const isDirectRun = Boolean(process.argv[1]) &&
|
|
44
|
+
resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
1000
45
|
if (isDirectRun) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
46
|
+
main().catch((err) => {
|
|
47
|
+
console.error("Fatal error:", err);
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
});
|
|
1005
50
|
}
|
|
1006
|
-
export {
|
|
1007
|
-
createCliProgram,
|
|
1008
|
-
main
|
|
1009
|
-
};
|