@actiondock/cli 2.0.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.
@@ -0,0 +1,398 @@
1
+ import {
2
+ createGlobalStorage,
3
+ createStorage,
4
+ filterWithFallbackInfo,
5
+ isSecretConfigKey,
6
+ loadProjectConfig,
7
+ maskSecretValue,
8
+ resolvePackageRoot,
9
+ type ConfigItemDefinition,
10
+ } from "@actiondock/core";
11
+ import { Command } from "commander";
12
+ import { resolveIntent } from "../utils/filter";
13
+
14
+ export function registerConfigCommands(program: Command): void {
15
+ const configCmd = program
16
+ .command("config")
17
+ .description("Manage runtime configuration store (Global & Project-level)");
18
+
19
+ // config schema / check
20
+ configCmd
21
+ .command("schema [identifier]")
22
+ .alias("check")
23
+ .description("Inspect declared configuration requirements and check resolution status")
24
+ .option("-P, --package <id>", "Target package ID or path")
25
+ .option("--json", "Output as JSON")
26
+ .action((identifier, options) => {
27
+ try {
28
+ const root = resolvePackageRoot(identifier || options.package);
29
+ if (!root) {
30
+ console.error(
31
+ "Error: Not in an ActionDock project.\nUsage: ac config schema [package-id] or cd into a project directory."
32
+ );
33
+ process.exit(1);
34
+ }
35
+
36
+ const projConfig = loadProjectConfig(root);
37
+ const declared = projConfig.config || {};
38
+ const declaredKeys = Object.keys(declared);
39
+
40
+ const globalStorage = createGlobalStorage();
41
+ const globalConfig = globalStorage.listConfig();
42
+ globalStorage.close();
43
+
44
+ const projectStorage = createStorage(projConfig.id, { projectRoot: root });
45
+ const projectConfig = projectStorage.listConfig();
46
+ projectStorage.close();
47
+
48
+ const items = declaredKeys.map((key) => {
49
+ const itemDef = declared[key];
50
+ const isSecret = isSecretConfigKey(key, itemDef);
51
+
52
+ let resolvedValue: unknown;
53
+ let source: "project" | "global" | "env" | "default" | "missing" = "missing";
54
+ let status: "SET" | "DEFAULT" | "MISSING" = "MISSING";
55
+
56
+ if (projectConfig[key] !== undefined) {
57
+ resolvedValue = projectConfig[key];
58
+ source = "project";
59
+ status = "SET";
60
+ } else if (globalConfig[key] !== undefined) {
61
+ resolvedValue = globalConfig[key];
62
+ source = "global";
63
+ status = "SET";
64
+ } else if (typeof process !== "undefined" && process.env && process.env[key] !== undefined) {
65
+ resolvedValue = process.env[key];
66
+ source = "env";
67
+ status = "SET";
68
+ } else if (itemDef.default !== undefined) {
69
+ resolvedValue = itemDef.default;
70
+ source = "default";
71
+ status = "DEFAULT";
72
+ } else {
73
+ status = "MISSING";
74
+ }
75
+
76
+ return {
77
+ key,
78
+ description: itemDef.description || "",
79
+ secret: isSecret,
80
+ default: itemDef.default,
81
+ status,
82
+ source,
83
+ value: isSecret && resolvedValue !== undefined ? maskSecretValue(resolvedValue) : resolvedValue,
84
+ required: itemDef.default === undefined,
85
+ };
86
+ });
87
+
88
+ const missingRequired = items.filter((i) => i.status === "MISSING");
89
+
90
+ if (options.json) {
91
+ console.log(
92
+ JSON.stringify(
93
+ {
94
+ packageId: projConfig.id,
95
+ projectRoot: root,
96
+ allReady: missingRequired.length === 0,
97
+ configs: items,
98
+ },
99
+ null,
100
+ 2
101
+ )
102
+ );
103
+ } else {
104
+ console.log(`Configuration Requirements for ${projConfig.id} (${root}):\n`);
105
+ if (items.length === 0) {
106
+ console.log(" (No configuration dependencies declared for this package)");
107
+ return;
108
+ }
109
+
110
+ console.log(
111
+ ` ${"KEY".padEnd(24)} ${"STATUS".padEnd(12)} ${"SOURCE".padEnd(10)} ${"SECRET".padEnd(8)} DESCRIPTION`
112
+ );
113
+ console.log(" " + "-".repeat(85));
114
+
115
+ for (const item of items) {
116
+ const statusLabel = item.status === "SET" ? "[SET]" : item.status === "DEFAULT" ? "[DEFAULT]" : "[MISSING]";
117
+ const secretLabel = item.secret ? "yes" : "no";
118
+ console.log(
119
+ ` ${item.key.padEnd(24)} ${statusLabel.padEnd(12)} ${item.source.padEnd(10)} ${secretLabel.padEnd(8)} ${item.description}`
120
+ );
121
+ }
122
+
123
+ if (missingRequired.length > 0) {
124
+ console.log(`\n[WARNING] ${missingRequired.length} required config(s) not set:`);
125
+ for (const m of missingRequired) {
126
+ console.log(` - ${m.key}: Run 'ac config set ${m.key} <value>' to configure.`);
127
+ }
128
+ } else {
129
+ console.log("\n[OK] All configuration dependencies are satisfied.");
130
+ }
131
+ }
132
+ } catch (err: any) {
133
+ console.error(`Error: ${err.message}`);
134
+ process.exit(1);
135
+ }
136
+ });
137
+
138
+ // config list
139
+ configCmd
140
+ .command("list [patterns...]")
141
+ .description("List configuration entries (Global & Project)")
142
+ .option("-P, --package <id>", "Target package ID or path")
143
+ .option("-g, --global", "Show only global configurations")
144
+ .option("-i, --intent <pattern>", "Regex or fuzzy intent filter; falls back to full list when no match")
145
+ .option("--reveal, --show-secrets", "Reveal plain text values for secrets")
146
+ .option("--no-fallback", "Disable fallback to full list when no items match intent")
147
+ .option("--json", "Output as JSON")
148
+ .action((patterns, options) => {
149
+ try {
150
+ const effectiveIntent = resolveIntent(options.intent, patterns);
151
+ const shouldFallback = options.fallback !== false;
152
+ const reveal = options.reveal || options.showSecrets;
153
+
154
+ const globalStorage = createGlobalStorage();
155
+ const globalConfig = globalStorage.listConfig();
156
+
157
+ const projectRoot = !options.global ? resolvePackageRoot(options.package) : null;
158
+ let projectStored: Record<string, unknown> = {};
159
+ let declaredDefaults: Record<string, ConfigItemDefinition> = {};
160
+ let packageId = "global";
161
+
162
+ if (projectRoot) {
163
+ try {
164
+ const projConfig = loadProjectConfig(projectRoot);
165
+ packageId = projConfig.id;
166
+ const projectStorage = createStorage(projConfig.id, { projectRoot });
167
+ projectStored = projectStorage.listConfig();
168
+ declaredDefaults = projConfig.config || {};
169
+ projectStorage.close();
170
+ } catch {
171
+ // Ignore project load error
172
+ }
173
+ }
174
+
175
+ globalStorage.close();
176
+
177
+ const allKeys = new Set([
178
+ ...Object.keys(declaredDefaults),
179
+ ...Object.keys(globalConfig),
180
+ ...Object.keys(projectStored),
181
+ ]);
182
+
183
+ const rawList = Array.from(allKeys).map((k) => {
184
+ let rawValue: unknown;
185
+ let source: "project" | "global" | "env" | "default" = "default";
186
+
187
+ if (projectStored[k] !== undefined) {
188
+ rawValue = projectStored[k];
189
+ source = "project";
190
+ } else if (globalConfig[k] !== undefined) {
191
+ rawValue = globalConfig[k];
192
+ source = "global";
193
+ } else if (typeof process !== "undefined" && process.env && process.env[k] !== undefined) {
194
+ rawValue = process.env[k];
195
+ source = "env";
196
+ } else {
197
+ rawValue = declaredDefaults[k]?.default;
198
+ source = "default";
199
+ }
200
+
201
+ const isSecret = isSecretConfigKey(k, declaredDefaults[k]);
202
+ const displayValue = !reveal && isSecret && rawValue !== undefined ? maskSecretValue(rawValue) : rawValue;
203
+
204
+ return {
205
+ key: k,
206
+ value: displayValue,
207
+ source,
208
+ secret: isSecret,
209
+ description: declaredDefaults[k]?.description || "",
210
+ };
211
+ });
212
+
213
+ const filterRes = filterWithFallbackInfo(
214
+ rawList,
215
+ effectiveIntent,
216
+ [(c) => c.key, (c) => c.value, (c) => c.description, (c) => c.source],
217
+ shouldFallback
218
+ );
219
+
220
+ if (options.json) {
221
+ console.log(JSON.stringify(filterRes.items, null, 2));
222
+ } else {
223
+ const scopeLabel = projectRoot ? `${packageId} (${projectRoot})` : "Global Scope";
224
+ console.log(`Configurations [${scopeLabel}]:\n`);
225
+ if (filterRes.isFallback && effectiveIntent) {
226
+ console.log(`(No config entries matched intent '${effectiveIntent}', showing all entries)\n`);
227
+ }
228
+ if (filterRes.items.length === 0) {
229
+ console.log(" (No configuration entries found)");
230
+ }
231
+ for (const item of filterRes.items) {
232
+ const valStr = typeof item.value === "string" && item.secret && !reveal ? item.value : JSON.stringify(item.value);
233
+ const secretBadge = item.secret ? ", secret" : "";
234
+ console.log(` ${item.key.padEnd(24)} = ${valStr} (${item.source}${secretBadge})`);
235
+ }
236
+ }
237
+ } catch (err: any) {
238
+ console.error(`Error: ${err.message}`);
239
+ process.exit(1);
240
+ }
241
+ });
242
+
243
+ // config get
244
+ configCmd
245
+ .command("get <key>")
246
+ .description("Get a configuration value")
247
+ .option("-P, --package <id>", "Target package ID or path")
248
+ .option("-g, --global", "Get from global configuration only")
249
+ .option("--reveal, --show-secrets", "Reveal plain text value for secret")
250
+ .option("--json", "Output as JSON")
251
+ .action((key, options) => {
252
+ try {
253
+ const reveal = options.reveal || options.showSecrets;
254
+ const globalStorage = createGlobalStorage();
255
+ const globalVal = globalStorage.getConfig(key);
256
+ globalStorage.close();
257
+
258
+ const projectRoot = !options.global ? resolvePackageRoot(options.package) : null;
259
+ let projVal: unknown = undefined;
260
+ let fallbackVal: unknown = undefined;
261
+ let declaredItem: ConfigItemDefinition | undefined;
262
+
263
+ if (projectRoot) {
264
+ try {
265
+ const projConfig = loadProjectConfig(projectRoot);
266
+ declaredItem = projConfig.config?.[key];
267
+ const projectStorage = createStorage(projConfig.id, { projectRoot });
268
+ projVal = projectStorage.getConfig(key);
269
+ fallbackVal = projConfig.config?.[key]?.default;
270
+ projectStorage.close();
271
+ } catch {
272
+ // Ignore
273
+ }
274
+ }
275
+
276
+ const envVal = typeof process !== "undefined" && process.env ? process.env[key] : undefined;
277
+
278
+ const rawEffective =
279
+ projVal !== undefined
280
+ ? projVal
281
+ : globalVal !== undefined
282
+ ? globalVal
283
+ : envVal !== undefined
284
+ ? envVal
285
+ : fallbackVal;
286
+
287
+ const source: string =
288
+ projVal !== undefined
289
+ ? "project"
290
+ : globalVal !== undefined
291
+ ? "global"
292
+ : envVal !== undefined
293
+ ? "env"
294
+ : fallbackVal !== undefined
295
+ ? "default"
296
+ : "undefined";
297
+
298
+ const isSecret = isSecretConfigKey(key, declaredItem);
299
+ const effective = !reveal && isSecret && rawEffective !== undefined ? maskSecretValue(rawEffective) : rawEffective;
300
+
301
+ if (options.json) {
302
+ console.log(
303
+ JSON.stringify(
304
+ {
305
+ key,
306
+ value: effective,
307
+ source,
308
+ secret: isSecret,
309
+ },
310
+ null,
311
+ 2
312
+ )
313
+ );
314
+ } else {
315
+ console.log(effective !== undefined ? (typeof effective === "string" && isSecret && !reveal ? effective : JSON.stringify(effective)) : "undefined");
316
+ }
317
+ } catch (err: any) {
318
+ console.error(`Error: ${err.message}`);
319
+ process.exit(1);
320
+ }
321
+ });
322
+
323
+ // config set
324
+ configCmd
325
+ .command("set <key> <value>")
326
+ .description("Set a configuration value (Global by default outside project, or use -g for global)")
327
+ .option("-P, --package <id>", "Target package ID or path")
328
+ .option("-g, --global", "Set globally across all packages")
329
+ .action((key, rawValue, options) => {
330
+ try {
331
+ let parsed: unknown = rawValue;
332
+ try {
333
+ parsed = JSON.parse(rawValue);
334
+ } catch {
335
+ parsed = rawValue;
336
+ }
337
+
338
+ const projectRoot = !options.global ? resolvePackageRoot(options.package) : null;
339
+ const isSecret = isSecretConfigKey(key);
340
+ const displayVal = isSecret ? maskSecretValue(parsed) : JSON.stringify(parsed);
341
+
342
+ if (options.global || !projectRoot) {
343
+ // Set in Global storage (~/.actiondock/global.db)
344
+ const globalStorage = createGlobalStorage();
345
+ globalStorage.setConfig(key, parsed);
346
+ globalStorage.close();
347
+ console.log(`[OK] Global config '${key}' set to ${displayVal}`);
348
+ } else {
349
+ // Set in Project storage
350
+ const projConfig = loadProjectConfig(projectRoot);
351
+ const storage = createStorage(projConfig.id, { projectRoot });
352
+ storage.setConfig(key, parsed);
353
+ storage.close();
354
+ console.log(`[OK] Config '${key}' set to ${displayVal} in ${projConfig.id}`);
355
+ }
356
+ } catch (err: any) {
357
+ console.error(`Error: ${err.message}`);
358
+ process.exit(1);
359
+ }
360
+ });
361
+
362
+ // config delete
363
+ configCmd
364
+ .command("delete <key>")
365
+ .alias("rm")
366
+ .description("Delete a configuration value")
367
+ .option("-P, --package <id>", "Target package ID or path")
368
+ .option("-g, --global", "Delete from global configuration")
369
+ .action((key, options) => {
370
+ try {
371
+ const projectRoot = !options.global ? resolvePackageRoot(options.package) : null;
372
+
373
+ if (options.global || !projectRoot) {
374
+ const globalStorage = createGlobalStorage();
375
+ const deleted = globalStorage.deleteConfig(key);
376
+ globalStorage.close();
377
+ if (deleted) {
378
+ console.log(`[OK] Global config '${key}' deleted`);
379
+ } else {
380
+ console.log(`Global config '${key}' was not found`);
381
+ }
382
+ } else {
383
+ const projConfig = loadProjectConfig(projectRoot);
384
+ const storage = createStorage(projConfig.id, { projectRoot });
385
+ const deleted = storage.deleteConfig(key);
386
+ storage.close();
387
+ if (deleted) {
388
+ console.log(`[OK] Config '${key}' deleted from ${projConfig.id}`);
389
+ } else {
390
+ console.log(`Config '${key}' was not set in database for ${projConfig.id}`);
391
+ }
392
+ }
393
+ } catch (err: any) {
394
+ console.error(`Error: ${err.message}`);
395
+ process.exit(1);
396
+ }
397
+ });
398
+ }
@@ -0,0 +1,64 @@
1
+ import { runDoctorChecks } from "@actiondock/core";
2
+ import { Command } from "commander";
3
+
4
+ export function registerDoctorCommand(program: Command): void {
5
+ program
6
+ .command("doctor")
7
+ .description("Check ActionDock environment, registry health, and project diagnostics")
8
+ .option("-P, --package <id|path>", "Target package ID or directory path for project diagnostics")
9
+ .option("--json", "Output diagnostics report in JSON format")
10
+ .action(async (options) => {
11
+ try {
12
+ const report = await runDoctorChecks({
13
+ packageIdOrPath: options.package,
14
+ });
15
+
16
+ if (options.json) {
17
+ console.log(JSON.stringify(report, null, 2));
18
+ if (!report.ok) {
19
+ process.exit(1);
20
+ }
21
+ return;
22
+ }
23
+
24
+ console.log("[DOCTOR] ActionDock System & Project Diagnostics\n");
25
+
26
+ // 1. Runtime & Environment Group
27
+ console.log("[Runtime & Environment]");
28
+ const envChecks = report.checks.filter((c) => c.category === "runtime" || c.category === "storage" || c.category === "registry");
29
+ for (const c of envChecks) {
30
+ const tag = c.status === "ok" ? "[OK]" : c.status === "warn" ? "[WARN]" : "[ERROR]";
31
+ console.log(` ${tag} ${c.name}: ${c.message}`);
32
+ if (c.fix) {
33
+ console.log(` Fix: ${c.fix}`);
34
+ }
35
+ }
36
+
37
+ // 2. Project Group
38
+ if (report.hasProject) {
39
+ console.log(`\n[Project: ${report.packageId || "unknown"}] (${report.projectRoot})`);
40
+ const projChecks = report.checks.filter((c) => c.category === "project");
41
+ for (const c of projChecks) {
42
+ const tag = c.status === "ok" ? "[OK]" : c.status === "warn" ? "[WARN]" : "[ERROR]";
43
+ console.log(` ${tag} ${c.name}: ${c.message}`);
44
+ if (c.fix) {
45
+ console.log(` Fix: ${c.fix}`);
46
+ }
47
+ }
48
+ } else {
49
+ console.log("\n[Project Context]");
50
+ console.log(" [INFO] Not inside an ActionDock project directory (skipped project checks)");
51
+ }
52
+
53
+ // 3. Summary
54
+ console.log(`\n[Summary] ${report.summary.ok} passed, ${report.summary.warn} warning(s), ${report.summary.error} error(s)`);
55
+
56
+ if (!report.ok) {
57
+ process.exit(1);
58
+ }
59
+ } catch (err: any) {
60
+ console.error(`[ERROR] Doctor failed to run diagnostics: ${err.message}`);
61
+ process.exit(1);
62
+ }
63
+ });
64
+ }
@@ -0,0 +1,70 @@
1
+ import { exportSkill, findProjectRoot, resolvePackageRoot } from "@actiondock/core";
2
+ import { Command } from "commander";
3
+
4
+ export function registerExportCommand(program: Command): void {
5
+ const exportCmd = program
6
+ .command("export")
7
+ .description("Export project artifacts");
8
+
9
+ exportCmd
10
+ .command("skill")
11
+ .description("Export Skill directory for AI Agents (default: source skill; use -s/--standalone for pre-built standalone binary)")
12
+ .option("-P, --package <id>", "Target package ID or path")
13
+ .option("-s, --standalone", "Export pre-compiled standalone binary skill (for environments without ActionDock runtime)")
14
+ .option("-t, --target <target>", "Target compilation platform for standalone mode (e.g. host, linux-x64, darwin-arm64, windows-x64)")
15
+ .option("-o, --out <path>", "Output skill directory")
16
+ .option("-p, --playbook <playbooks...>", "Only export specific playbook(s) and their dependent actions (Playbook-driven minimal export)")
17
+ .option("-a, --actions <actions...>", "Only export specific action(s)")
18
+ .option("-m, --minify", "Minify bundled JavaScript in standalone mode (default: true)", true)
19
+ .option("--no-minify", "Disable JavaScript minification in standalone mode")
20
+ .option("--bytecode", "Compile JavaScript to bytecode in standalone mode (default: true)", true)
21
+ .option("--no-bytecode", "Disable bytecode compilation in standalone mode")
22
+ .option("-z, --archive", "Create a .zip archive of the exported skill")
23
+ .action(async (options) => {
24
+ const root = options.package
25
+ ? resolvePackageRoot(options.package)
26
+ : findProjectRoot();
27
+
28
+ if (!root) {
29
+ if (options.package) {
30
+ console.error(`Error: Package '${options.package}' not found in linked packages or path`);
31
+ } else {
32
+ console.error(
33
+ "Error: Not in an ActionDock project (actiondock.json not found).\nPlease specify -P, --package <id> or cd into a project directory."
34
+ );
35
+ }
36
+ process.exit(1);
37
+ }
38
+
39
+ const isStandalone = Boolean(options.standalone);
40
+
41
+ try {
42
+ console.log(`Exporting ${isStandalone ? "standalone binary" : "source"} Skill artifact...`);
43
+ const result = await exportSkill({
44
+ projectRoot: root,
45
+ mode: isStandalone ? "standalone" : "source",
46
+ standalone: isStandalone,
47
+ target: options.target,
48
+ outDir: options.out,
49
+ archive: options.archive,
50
+ playbooks: options.playbook,
51
+ actions: options.actions,
52
+ minify: options.minify,
53
+ bytecode: options.bytecode,
54
+ });
55
+
56
+ console.log(`[OK] Successfully exported ${result.mode === "standalone" ? "Standalone" : "Source"} Skill: ${result.packageId} (v${result.version})`);
57
+ console.log(` Mode: ${result.mode}${result.mode === "standalone" ? ` (target: ${result.target})` : ""}`);
58
+ console.log(` Actions: ${result.actionsCount}`);
59
+ console.log(` Playbooks: ${result.playbooksCount}`);
60
+ console.log(` Skill Dir: ${result.skillDir}`);
61
+ if (result.archivePath) {
62
+ console.log(` Archive: ${result.archivePath}`);
63
+ }
64
+ } catch (err: any) {
65
+ console.error(`Export failed: ${err.message}`);
66
+ process.exit(1);
67
+ }
68
+ });
69
+ }
70
+
@@ -0,0 +1,43 @@
1
+ import { Command } from "commander";
2
+ import { registerActionCommands } from "./action";
3
+ import { registerBuildCommand } from "./build";
4
+ import { registerConfigCommands } from "./config";
5
+ import { registerDoctorCommand } from "./doctor";
6
+ import { registerExportCommand } from "./export";
7
+ import { registerInfoCommand } from "./info";
8
+ import { registerInitCommand } from "./init";
9
+ import { registerLinkCommands } from "./link";
10
+ import { registerMcpCommands } from "./mcp";
11
+ import { registerPlaybookCommands } from "./playbook";
12
+ import { registerProfileCommands } from "./profile";
13
+ import { registerRunsCommands } from "./runs";
14
+ import { registerServeCommand } from "./serve";
15
+ import { registerStateCommands } from "./state";
16
+ import { registerTestCommand } from "./test";
17
+
18
+ export function createCliProgram(): Command {
19
+ const program = new Command();
20
+
21
+ program
22
+ .name("ac")
23
+ .description("ActionDock (ac) 2.0 - Toolchain for building and shipping standalone AI Agent Actions & Skills")
24
+ .version("2.0.0");
25
+
26
+ registerInitCommand(program);
27
+ registerInfoCommand(program);
28
+ registerDoctorCommand(program);
29
+ registerActionCommands(program);
30
+ registerPlaybookCommands(program);
31
+ registerConfigCommands(program);
32
+ registerStateCommands(program);
33
+ registerRunsCommands(program);
34
+ registerTestCommand(program);
35
+ registerBuildCommand(program);
36
+ registerExportCommand(program);
37
+ registerLinkCommands(program);
38
+ registerProfileCommands(program);
39
+ registerServeCommand(program);
40
+ registerMcpCommands(program);
41
+
42
+ return program;
43
+ }