@pixelml/agenticflow-cli 0.1.0 → 1.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,1158 @@
1
+ /**
2
+ * Main CLI program definition with Commander.js.
3
+ * Resource commands (workflow, agent, node-types, connections, uploads)
4
+ * use the SDK resource classes. Generic commands (call, ops, catalog,
5
+ * doctor, auth, policy, playbook) remain spec-based.
6
+ */
7
+ import { Command } from "commander";
8
+ import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
9
+ import { resolve, dirname } from "node:path";
10
+ import { homedir } from "node:os";
11
+ import { createInterface } from "node:readline";
12
+ import { createClient, DEFAULT_BASE_URL, AGENTICFLOW_API_KEY, } from "@pixelml/agenticflow-sdk";
13
+ import { OperationRegistry, defaultSpecPath, loadOpenapiSpec, isPublic, } from "./spec.js";
14
+ import { listPlaybooks, getPlaybook } from "./playbooks.js";
15
+ import { loadPolicy, writeDefaultPolicy, policyFilePath, } from "./policy.js";
16
+ import { parseKeyValuePairs, loadJsonPayload, buildRequestSpec } from "./client.js";
17
+ // --- Constants ---
18
+ const AUTH_ENV_API_KEY = "AGENTICFLOW_PUBLIC_API_KEY";
19
+ const DOCTOR_SCHEMA_VERSION = "agenticflow.doctor.v1";
20
+ const CATALOG_EXPORT_SCHEMA_VERSION = "agenticflow.catalog.export.v1";
21
+ const CATALOG_RANK_SCHEMA_VERSION = "agenticflow.catalog.rank.v1";
22
+ // ═══════════════════════════════════════════════════════════════════
23
+ // Helpers
24
+ // ═══════════════════════════════════════════════════════════════════
25
+ function printJson(data) {
26
+ console.log(JSON.stringify(data, null, 2));
27
+ }
28
+ /** Print an SDK APIResponse in CLI-friendly format. */
29
+ function printResult(response) {
30
+ printJson({ status: response.statusCode, body: response.data });
31
+ if (response.statusCode >= 400)
32
+ process.exitCode = 1;
33
+ }
34
+ /** Load the active auth profile from ~/.agenticflow/auth.json */
35
+ function loadActiveProfile() {
36
+ try {
37
+ const config = loadAuthFile(defaultAuthConfigPath());
38
+ const profileName = config["default_profile"] ?? "default";
39
+ const profiles = config["profiles"];
40
+ return profiles?.[profileName] ?? {};
41
+ }
42
+ catch {
43
+ return {};
44
+ }
45
+ }
46
+ /**
47
+ * Resolve a value with priority: flag → env var → auth.json profile → fallback.
48
+ */
49
+ function resolveToken(options) {
50
+ if (options.apiKey)
51
+ return options.apiKey;
52
+ const fromEnv = process.env[AGENTICFLOW_API_KEY] ?? process.env[AUTH_ENV_API_KEY];
53
+ if (fromEnv)
54
+ return fromEnv;
55
+ return loadActiveProfile()["api_key"] ?? null;
56
+ }
57
+ function resolveWorkspaceId(explicit) {
58
+ if (explicit)
59
+ return explicit;
60
+ const fromEnv = process.env["AGENTICFLOW_WORKSPACE_ID"];
61
+ if (fromEnv)
62
+ return fromEnv;
63
+ return loadActiveProfile()["workspace_id"] ?? undefined;
64
+ }
65
+ function resolveProjectId(explicit) {
66
+ if (explicit)
67
+ return explicit;
68
+ const fromEnv = process.env["AGENTICFLOW_PROJECT_ID"];
69
+ if (fromEnv)
70
+ return fromEnv;
71
+ return loadActiveProfile()["project_id"] ?? undefined;
72
+ }
73
+ /** Build an SDK client from global CLI options. */
74
+ function buildClient(parentOpts) {
75
+ return createClient({
76
+ apiKey: resolveToken(parentOpts),
77
+ workspaceId: resolveWorkspaceId(parentOpts.workspaceId),
78
+ projectId: resolveProjectId(parentOpts.projectId),
79
+ });
80
+ }
81
+ /** Wrap an async SDK call with error handling. */
82
+ async function run(fn) {
83
+ try {
84
+ const result = await fn();
85
+ printResult(result);
86
+ }
87
+ catch (err) {
88
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
89
+ process.exit(1);
90
+ }
91
+ }
92
+ // ═══════════════════════════════════════════════════════════════════
93
+ // Spec-based helpers (for generic commands: call, ops, catalog, doctor)
94
+ // ═══════════════════════════════════════════════════════════════════
95
+ function loadRegistry(specFile) {
96
+ try {
97
+ const spec = loadOpenapiSpec(specFile);
98
+ return OperationRegistry.fromSpec(spec);
99
+ }
100
+ catch (err) {
101
+ console.error(`Warning: Unable to load OpenAPI spec from ${specFile}: ${err}`);
102
+ return null;
103
+ }
104
+ }
105
+ function catalogOperationItem(op) {
106
+ return {
107
+ operation_id: op.operationId,
108
+ method: op.method,
109
+ path: op.path,
110
+ summary: op.summary ?? "",
111
+ tags: op.tags,
112
+ public: isPublic(op),
113
+ };
114
+ }
115
+ // --- Auth helpers ---
116
+ function defaultAuthConfigPath() {
117
+ const envDir = process.env["AGENTICFLOW_CLI_DIR"];
118
+ const dir = envDir ?? resolve(homedir(), ".agenticflow");
119
+ return resolve(dir, "auth.json");
120
+ }
121
+ function loadAuthFile(path) {
122
+ if (!existsSync(path))
123
+ return {};
124
+ try {
125
+ return JSON.parse(readFileSync(path, "utf-8"));
126
+ }
127
+ catch {
128
+ return {};
129
+ }
130
+ }
131
+ function parseKeyValueEnv(line) {
132
+ const trimmed = line.trim();
133
+ if (!trimmed || trimmed.startsWith("#"))
134
+ return null;
135
+ const idx = trimmed.indexOf("=");
136
+ if (idx === -1)
137
+ return null;
138
+ const key = trimmed.slice(0, idx).trim();
139
+ let value = trimmed.slice(idx + 1).trim();
140
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
141
+ value = value.slice(1, -1);
142
+ }
143
+ return [key, value];
144
+ }
145
+ // ═══════════════════════════════════════════════════════════════════
146
+ // Main program
147
+ // ═══════════════════════════════════════════════════════════════════
148
+ export function createProgram() {
149
+ const program = new Command();
150
+ program
151
+ .name("agenticflow")
152
+ .description("AgenticFlow CLI for agent-native API operations.")
153
+ .version("1.0.0")
154
+ .option("--api-key <key>", "API key for authentication")
155
+ .option("--workspace-id <id>", "Default workspace ID")
156
+ .option("--project-id <id>", "Default project ID")
157
+ .option("--spec-file <path>", "Path to OpenAPI spec JSON file")
158
+ .option("--json", "Force JSON output");
159
+ // ═════════════════════════════════════════════════════════════════
160
+ // doctor
161
+ // ═════════════════════════════════════════════════════════════════
162
+ program
163
+ .command("doctor")
164
+ .description("Preflight checks for CLI configuration and connectivity.")
165
+ .option("--json", "JSON output")
166
+ .action(async (opts) => {
167
+ const parentOpts = program.opts();
168
+ const baseUrl = DEFAULT_BASE_URL;
169
+ const token = resolveToken(parentOpts);
170
+ const wsId = resolveWorkspaceId(parentOpts.workspaceId);
171
+ const projId = resolveProjectId(parentOpts.projectId);
172
+ const specFile = parentOpts.specFile ?? defaultSpecPath();
173
+ const registry = loadRegistry(specFile);
174
+ const configPath = defaultAuthConfigPath();
175
+ const configExists = existsSync(configPath);
176
+ const tokenSource = parentOpts.apiKey ? "flag" : (process.env[AUTH_ENV_API_KEY] ? "env" : (configExists ? "config" : "none"));
177
+ // Health check
178
+ let healthOk = false;
179
+ let healthStatus = 0;
180
+ let healthError = "";
181
+ try {
182
+ const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/v1/health`);
183
+ healthOk = response.ok;
184
+ healthStatus = response.status;
185
+ }
186
+ catch (err) {
187
+ healthError = err instanceof Error ? err.message : String(err);
188
+ }
189
+ const checks = {
190
+ config: configExists,
191
+ token: !!token,
192
+ tokenSource,
193
+ workspaceId: wsId ?? null,
194
+ projectId: projId ?? null,
195
+ baseUrl,
196
+ health: healthOk,
197
+ healthStatus,
198
+ healthError,
199
+ specFile,
200
+ operationsLoaded: registry?.listOperations().length ?? 0,
201
+ };
202
+ if (opts.json || parentOpts.json) {
203
+ printJson({ schema: DOCTOR_SCHEMA_VERSION, ...checks });
204
+ }
205
+ else {
206
+ const ok = (v) => v ? "✓" : "✗";
207
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
208
+ console.log("");
209
+ console.log(" Environment");
210
+ console.log(` └ Version: ${program.version()}`);
211
+ console.log(` └ Node.js: ${process.version}`);
212
+ console.log(` └ Platform: ${process.platform} ${process.arch}`);
213
+ console.log("");
214
+ console.log(" Authentication");
215
+ console.log(` └ API Key: ${token ? `${ok(true)} present ${dim(`(source: ${tokenSource})`)}` : `${ok(false)} not set`}`);
216
+ console.log(` └ Workspace ID: ${wsId ?? "not set"}`);
217
+ console.log(` └ Project ID: ${projId ?? "not set"}`);
218
+ console.log(` └ Config: ${configExists ? configPath : `${ok(false)} not found`}`);
219
+ console.log("");
220
+ console.log(" API Connectivity");
221
+ console.log(` └ Base URL: ${baseUrl}`);
222
+ console.log(` └ Health: ${healthOk ? `${ok(true)} reachable ${dim(`(HTTP ${healthStatus})`)}` : `${ok(false)} ${healthError || `HTTP ${healthStatus}`}`}`);
223
+ console.log("");
224
+ console.log(" OpenAPI Spec");
225
+ console.log(` └ Spec file: ${registry ? ok(true) : ok(false)} ${specFile}`);
226
+ console.log(` └ Operations: ${checks.operationsLoaded} loaded`);
227
+ console.log("");
228
+ }
229
+ });
230
+ // ═════════════════════════════════════════════════════════════════
231
+ // ops
232
+ // ═════════════════════════════════════════════════════════════════
233
+ const opsCmd = program
234
+ .command("ops")
235
+ .description("OpenAPI operation discovery.");
236
+ opsCmd
237
+ .command("list")
238
+ .description("List available operations.")
239
+ .option("--public-only", "Show only public operations")
240
+ .option("--tag <tag>", "Filter by tag")
241
+ .action((opts) => {
242
+ const parentOpts = program.opts();
243
+ const specFile = parentOpts.specFile ?? defaultSpecPath();
244
+ const registry = loadRegistry(specFile);
245
+ if (!registry) {
246
+ console.error("Failed to load OpenAPI spec.");
247
+ process.exit(1);
248
+ }
249
+ const operations = registry.listOperations({ publicOnly: opts.publicOnly, tag: opts.tag });
250
+ console.log(`${operations.length} operations found:\n`);
251
+ for (const op of operations) {
252
+ console.log(` ${op.method.padEnd(7)} ${op.path}`);
253
+ console.log(` ${op.operationId}`);
254
+ }
255
+ });
256
+ opsCmd
257
+ .command("show <operationId>")
258
+ .description("Show details for a specific operation.")
259
+ .action((operationId) => {
260
+ const parentOpts = program.opts();
261
+ const specFile = parentOpts.specFile ?? defaultSpecPath();
262
+ const registry = loadRegistry(specFile);
263
+ if (!registry) {
264
+ console.error("Failed to load OpenAPI spec.");
265
+ process.exit(1);
266
+ }
267
+ const operation = registry.getOperationById(operationId);
268
+ if (!operation) {
269
+ console.error(`Operation not found: ${operationId}`);
270
+ process.exit(1);
271
+ }
272
+ printJson(catalogOperationItem(operation));
273
+ });
274
+ // ═════════════════════════════════════════════════════════════════
275
+ // catalog
276
+ // ═════════════════════════════════════════════════════════════════
277
+ const catalogCmd = program
278
+ .command("catalog")
279
+ .description("Operation catalog tools.");
280
+ catalogCmd
281
+ .command("export")
282
+ .description("Export operation catalog.")
283
+ .option("--public-only", "Export only public operations")
284
+ .option("--json", "JSON output")
285
+ .action((opts) => {
286
+ const parentOpts = program.opts();
287
+ const specFile = parentOpts.specFile ?? defaultSpecPath();
288
+ const registry = loadRegistry(specFile);
289
+ if (!registry) {
290
+ console.error("Failed to load OpenAPI spec.");
291
+ process.exit(1);
292
+ }
293
+ const operations = registry.listOperations({ publicOnly: opts.publicOnly });
294
+ const items = operations.map(catalogOperationItem);
295
+ if (opts.json || parentOpts.json) {
296
+ printJson({ schema: CATALOG_EXPORT_SCHEMA_VERSION, count: items.length, operations: items });
297
+ }
298
+ else {
299
+ for (const item of items) {
300
+ console.log(`${item["method"].padEnd(7)} ${item["path"]} ${item["operation_id"]}`);
301
+ }
302
+ console.log(`\n${items.length} operations.`);
303
+ }
304
+ });
305
+ catalogCmd
306
+ .command("rank")
307
+ .description("Rank operations for a task.")
308
+ .requiredOption("--task <task>", "Task description")
309
+ .option("--public-only", "Only public operations")
310
+ .option("--json", "JSON output")
311
+ .option("--top <n>", "Top N results", "10")
312
+ .action((opts) => {
313
+ const parentOpts = program.opts();
314
+ const specFile = parentOpts.specFile ?? defaultSpecPath();
315
+ const registry = loadRegistry(specFile);
316
+ if (!registry) {
317
+ console.error("Failed to load OpenAPI spec.");
318
+ process.exit(1);
319
+ }
320
+ const operations = registry.listOperations({ publicOnly: opts.publicOnly });
321
+ const task = opts.task.toLowerCase();
322
+ const taskTerms = [...new Set(task.split(/\s+/))];
323
+ const scored = operations.map((op) => {
324
+ const text = [op.operationId, op.summary ?? "", op.description ?? "", ...op.tags, op.method, op.path].join(" ").toLowerCase();
325
+ let score = 0;
326
+ for (const term of taskTerms) {
327
+ if (text.includes(term))
328
+ score += 1;
329
+ }
330
+ return { op, score };
331
+ });
332
+ scored.sort((a, b) => b.score - a.score);
333
+ const top = scored.slice(0, parseInt(opts.top, 10));
334
+ if (opts.json || parentOpts.json) {
335
+ printJson({
336
+ schema: CATALOG_RANK_SCHEMA_VERSION,
337
+ task: opts.task,
338
+ results: top.map((r) => ({ ...catalogOperationItem(r.op), score: r.score })),
339
+ });
340
+ }
341
+ else {
342
+ for (const r of top) {
343
+ console.log(`[${r.score}] ${r.op.method.padEnd(7)} ${r.op.path} ${r.op.operationId}`);
344
+ }
345
+ }
346
+ });
347
+ // ═════════════════════════════════════════════════════════════════
348
+ // playbook
349
+ // ═════════════════════════════════════════════════════════════════
350
+ program
351
+ .command("playbook [topic]")
352
+ .description("View built-in playbooks for AgenticFlow workflows.")
353
+ .option("--list", "List available playbooks")
354
+ .action((topic, opts) => {
355
+ if (opts.list || !topic) {
356
+ const playbooks = listPlaybooks();
357
+ for (const pb of playbooks) {
358
+ console.log(` ${pb.topic.padEnd(20)} ${pb.title} — ${pb.summary}`);
359
+ }
360
+ return;
361
+ }
362
+ const pb = getPlaybook(topic);
363
+ if (!pb) {
364
+ console.error(`Playbook not found: ${topic}`);
365
+ process.exit(1);
366
+ }
367
+ console.log(`# ${pb.title}\n`);
368
+ console.log(pb.content);
369
+ });
370
+ // ═════════════════════════════════════════════════════════════════
371
+ // login (top-level)
372
+ // ═════════════════════════════════════════════════════════════════
373
+ program
374
+ .command("login")
375
+ .description("Interactively configure your credentials.")
376
+ .option("--profile <profile>", "Profile name", "default")
377
+ .action(async (opts) => {
378
+ const parentOpts = program.opts();
379
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
380
+ const ask = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
381
+ console.log("\n🔑 AgenticFlow Login\n");
382
+ const apiKey = parentOpts.apiKey || await ask(" API Key: ");
383
+ if (!apiKey) {
384
+ console.error("\n✗ API key is required.");
385
+ rl.close();
386
+ process.exit(1);
387
+ }
388
+ if (parentOpts.apiKey)
389
+ console.log(" API Key: ••••••••");
390
+ const workspaceId = parentOpts.workspaceId || await ask(" Workspace ID: ");
391
+ if (parentOpts.workspaceId)
392
+ console.log(` Workspace ID: ${parentOpts.workspaceId}`);
393
+ const projectId = parentOpts.projectId || await ask(" Project ID: ");
394
+ if (parentOpts.projectId)
395
+ console.log(` Project ID: ${parentOpts.projectId}`);
396
+ rl.close();
397
+ // Validate the API key by calling the health endpoint
398
+ console.log("\n Verifying credentials...");
399
+ try {
400
+ const client = createClient({ apiKey });
401
+ await client.sdk.get("/health");
402
+ console.log(" ✓ API key is valid.\n");
403
+ }
404
+ catch {
405
+ console.error(" ✗ Could not verify API key. Saving anyway.\n");
406
+ }
407
+ const configPath = defaultAuthConfigPath();
408
+ const config = loadAuthFile(configPath);
409
+ const profiles = config["profiles"] ?? {};
410
+ const profile = { api_key: apiKey };
411
+ if (workspaceId)
412
+ profile["workspace_id"] = workspaceId;
413
+ if (projectId)
414
+ profile["project_id"] = projectId;
415
+ profiles[opts.profile] = profile;
416
+ if (!config["default_profile"])
417
+ config["default_profile"] = opts.profile;
418
+ config["profiles"] = profiles;
419
+ mkdirSync(dirname(configPath), { recursive: true });
420
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
421
+ console.log(`Saved to profile '${opts.profile}' at ${configPath}`);
422
+ });
423
+ // ═════════════════════════════════════════════════════════════════
424
+ // logout (top-level)
425
+ // ═════════════════════════════════════════════════════════════════
426
+ program
427
+ .command("logout")
428
+ .description("Remove saved credentials.")
429
+ .option("--profile <profile>", "Profile to remove (default: all)")
430
+ .option("-y, --yes", "Skip confirmation")
431
+ .action(async (opts) => {
432
+ const configPath = defaultAuthConfigPath();
433
+ if (!existsSync(configPath)) {
434
+ console.log("No credentials found. Already logged out.");
435
+ return;
436
+ }
437
+ if (!opts.yes) {
438
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
439
+ const answer = await new Promise((res) => rl.question(opts.profile
440
+ ? `Remove profile '${opts.profile}'? (y/N) `
441
+ : "Remove all saved credentials? (y/N) ", (a) => { res(a.trim().toLowerCase()); rl.close(); }));
442
+ if (answer !== "y" && answer !== "yes") {
443
+ console.log("Cancelled.");
444
+ return;
445
+ }
446
+ }
447
+ if (opts.profile) {
448
+ // Remove a single profile
449
+ const config = loadAuthFile(configPath);
450
+ const profiles = config["profiles"];
451
+ if (profiles && opts.profile in profiles) {
452
+ delete profiles[opts.profile];
453
+ if (config["default_profile"] === opts.profile) {
454
+ const remaining = Object.keys(profiles);
455
+ config["default_profile"] = remaining[0] ?? "default";
456
+ }
457
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
458
+ console.log(`✓ Removed profile '${opts.profile}'.`);
459
+ }
460
+ else {
461
+ console.log(`Profile '${opts.profile}' not found.`);
462
+ }
463
+ }
464
+ else {
465
+ // Remove the entire auth file
466
+ unlinkSync(configPath);
467
+ console.log(`✓ Removed ${configPath}`);
468
+ }
469
+ });
470
+ // ═════════════════════════════════════════════════════════════════
471
+ // whoami (top-level)
472
+ // ═════════════════════════════════════════════════════════════════
473
+ program
474
+ .command("whoami")
475
+ .description("Show current authentication state.")
476
+ .option("--json", "JSON output")
477
+ .action((opts) => {
478
+ const parentOpts = program.opts();
479
+ const token = resolveToken(parentOpts);
480
+ const wsId = resolveWorkspaceId(parentOpts.workspaceId);
481
+ const projId = resolveProjectId(parentOpts.projectId);
482
+ const configPath = defaultAuthConfigPath();
483
+ const config = loadAuthFile(configPath);
484
+ const profileName = config["default_profile"] ?? "default";
485
+ const result = {
486
+ profile: profileName,
487
+ api_key_present: !!token,
488
+ workspace_id: wsId ?? "not set",
489
+ project_id: projId ?? "not set",
490
+ config_path: configPath,
491
+ };
492
+ if (opts.json || parentOpts.json) {
493
+ printJson(result);
494
+ }
495
+ else {
496
+ console.log(`Profile: ${result.profile}`);
497
+ console.log(`API Key: ${result.api_key_present ? "present" : "not set"}`);
498
+ console.log(`Workspace ID: ${result.workspace_id}`);
499
+ console.log(`Project ID: ${result.project_id}`);
500
+ console.log(`Config: ${result.config_path}`);
501
+ }
502
+ });
503
+ // ═════════════════════════════════════════════════════════════════
504
+ // auth (import-env stays here)
505
+ // ═════════════════════════════════════════════════════════════════
506
+ const authCmd = program
507
+ .command("auth")
508
+ .description("Authentication management.");
509
+ authCmd
510
+ .command("import-env")
511
+ .description("Import credentials from an env file.")
512
+ .requiredOption("--file <path>", "Path to .env file")
513
+ .option("--profile <profile>", "Profile name", "default")
514
+ .action((opts) => {
515
+ const envPath = resolve(opts.file);
516
+ if (!existsSync(envPath)) {
517
+ console.error(`File not found: ${envPath}`);
518
+ process.exit(1);
519
+ }
520
+ const content = readFileSync(envPath, "utf-8");
521
+ const env = {};
522
+ for (const line of content.split("\n")) {
523
+ const parsed = parseKeyValueEnv(line);
524
+ if (parsed)
525
+ env[parsed[0]] = parsed[1];
526
+ }
527
+ const apiKey = env["AGENTICFLOW_API_KEY"] ?? env["AGENTICFLOW_PUBLIC_API_KEY"];
528
+ const workspaceId = env["AGENTICFLOW_WORKSPACE_ID"];
529
+ const projectId = env["AGENTICFLOW_PROJECT_ID"];
530
+ if (!apiKey) {
531
+ console.error("No AGENTICFLOW_API_KEY found in env file.");
532
+ process.exit(1);
533
+ }
534
+ const configPath = defaultAuthConfigPath();
535
+ const config = loadAuthFile(configPath);
536
+ const profiles = config["profiles"] ?? {};
537
+ const profile = { api_key: apiKey };
538
+ if (workspaceId)
539
+ profile["workspace_id"] = workspaceId;
540
+ if (projectId)
541
+ profile["project_id"] = projectId;
542
+ profiles[opts.profile] = profile;
543
+ if (!config["default_profile"])
544
+ config["default_profile"] = opts.profile;
545
+ config["profiles"] = profiles;
546
+ mkdirSync(dirname(configPath), { recursive: true });
547
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
548
+ console.log(`Imported credentials to profile '${opts.profile}' at ${configPath}`);
549
+ });
550
+ // ═════════════════════════════════════════════════════════════════
551
+ // policy
552
+ // ═════════════════════════════════════════════════════════════════
553
+ const policyCmd = program
554
+ .command("policy")
555
+ .description("Local policy guardrails management.");
556
+ policyCmd
557
+ .command("show")
558
+ .description("Show current policy configuration.")
559
+ .option("--json", "JSON output")
560
+ .action((opts) => {
561
+ const parentOpts = program.opts();
562
+ try {
563
+ const policy = loadPolicy();
564
+ const filePath = policyFilePath();
565
+ if (opts.json || parentOpts.json) {
566
+ printJson({ file: filePath, ...policy });
567
+ }
568
+ else {
569
+ console.log(`Policy file: ${filePath}`);
570
+ console.log(`Spend ceiling: ${policy.spendCeiling ?? "none"}`);
571
+ console.log(`Allowlist: ${policy.allowlist.length > 0 ? policy.allowlist.join(", ") : "none"}`);
572
+ console.log(`Blocklist: ${policy.blocklist.length > 0 ? policy.blocklist.join(", ") : "none"}`);
573
+ }
574
+ }
575
+ catch (err) {
576
+ console.error(`Policy error: ${err instanceof Error ? err.message : err}`);
577
+ process.exit(1);
578
+ }
579
+ });
580
+ policyCmd
581
+ .command("init")
582
+ .description("Initialize default policy file.")
583
+ .option("--force", "Overwrite existing policy file")
584
+ .option("--spend-ceiling <amount>", "Set spend ceiling")
585
+ .action((opts) => {
586
+ try {
587
+ const filePath = writeDefaultPolicy({
588
+ force: opts.force,
589
+ spendCeiling: opts.spendCeiling ? parseFloat(opts.spendCeiling) : undefined,
590
+ });
591
+ console.log(`Policy file created: ${filePath}`);
592
+ }
593
+ catch (err) {
594
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
595
+ process.exit(1);
596
+ }
597
+ });
598
+ // ═════════════════════════════════════════════════════════════════
599
+ // call (generic, spec-based)
600
+ // ═════════════════════════════════════════════════════════════════
601
+ program
602
+ .command("call")
603
+ .description("Low-level OpenAPI transport — execute an operation directly.")
604
+ .option("--operation-id <id>", "Operation ID to invoke")
605
+ .option("--method <method>", "HTTP method")
606
+ .option("--path <path>", "API path")
607
+ .option("-P, --path-param <params...>", "Path parameters (key=value)")
608
+ .option("-Q, --query-param <params...>", "Query parameters (key=value)")
609
+ .option("-H, --header <headers...>", "Extra headers (key=value)")
610
+ .option("--body <body>", "JSON body (inline or @file)")
611
+ .option("--dry-run", "Show request without executing")
612
+ .action(async (opts) => {
613
+ const parentOpts = program.opts();
614
+ const baseUrl = DEFAULT_BASE_URL;
615
+ const token = resolveToken(parentOpts);
616
+ const specFile = parentOpts.specFile ?? defaultSpecPath();
617
+ const registry = loadRegistry(specFile);
618
+ if (!registry) {
619
+ console.error("Failed to load OpenAPI spec.");
620
+ process.exit(1);
621
+ }
622
+ // Resolve operation
623
+ let operation = null;
624
+ if (opts.operationId) {
625
+ operation = registry.getOperationById(opts.operationId);
626
+ }
627
+ else if (opts.method && opts.path) {
628
+ operation = registry.getOperationByMethodPath(opts.method, opts.path);
629
+ }
630
+ if (!operation && opts.method && opts.path) {
631
+ operation = {
632
+ operationId: `${opts.method.toLowerCase()}_${opts.path.replace(/^\//, "").replace(/\//g, "_")}`,
633
+ method: opts.method.toUpperCase(),
634
+ path: opts.path,
635
+ tags: [], security: [], parameters: [],
636
+ requestBody: null, summary: null, description: null, raw: {},
637
+ };
638
+ }
639
+ if (!operation) {
640
+ console.error("Unable to resolve operation.");
641
+ process.exit(1);
642
+ }
643
+ const pathParams = opts.pathParam ? parseKeyValuePairs(opts.pathParam) : {};
644
+ const queryParams = opts.queryParam ? parseKeyValuePairs(opts.queryParam) : {};
645
+ const headers = opts.header ? parseKeyValuePairs(opts.header) : {};
646
+ const body = opts.body ? loadJsonPayload(opts.body) : undefined;
647
+ const requestSpec = buildRequestSpec(operation, baseUrl, pathParams, queryParams, headers, token, body);
648
+ if (opts.dryRun) {
649
+ printJson({
650
+ dry_run: true,
651
+ operation_id: operation.operationId,
652
+ method: requestSpec.method,
653
+ url: requestSpec.url,
654
+ params: requestSpec.params,
655
+ headers: Object.fromEntries(Object.entries(requestSpec.headers).map(([k, v]) => k.toLowerCase() === "authorization" ? [k, "Bearer ***"] : [k, v])),
656
+ body: requestSpec.body ?? null,
657
+ });
658
+ return;
659
+ }
660
+ // Execute request
661
+ try {
662
+ const response = await fetch(requestSpec.url + (Object.keys(requestSpec.params).length > 0
663
+ ? "?" + new URLSearchParams(requestSpec.params).toString()
664
+ : ""), {
665
+ method: requestSpec.method,
666
+ headers: requestSpec.headers,
667
+ body: requestSpec.body != null ? JSON.stringify(requestSpec.body) : undefined,
668
+ });
669
+ const text = await response.text();
670
+ let data;
671
+ try {
672
+ data = JSON.parse(text);
673
+ }
674
+ catch {
675
+ data = text;
676
+ }
677
+ printJson({ status: response.status, body: data });
678
+ if (!response.ok)
679
+ process.exitCode = 1;
680
+ }
681
+ catch (err) {
682
+ console.error(`Request failed: ${err instanceof Error ? err.message : err}`);
683
+ process.exit(1);
684
+ }
685
+ });
686
+ // ═════════════════════════════════════════════════════════════════
687
+ // workflow (SDK-based)
688
+ // ═════════════════════════════════════════════════════════════════
689
+ const workflowCmd = program
690
+ .command("workflow")
691
+ .description("Workflow management commands.");
692
+ workflowCmd
693
+ .command("list")
694
+ .description("List workflows.")
695
+ .option("--workspace-id <id>", "Workspace ID (overrides global)")
696
+ .option("--project-id <id>", "Project ID")
697
+ .option("--search <query>", "Search query")
698
+ .option("--limit <n>", "Limit results")
699
+ .option("--offset <n>", "Offset")
700
+ .action(async (opts) => {
701
+ const client = buildClient(program.opts());
702
+ await run(() => client.workflows.list({
703
+ workspaceId: opts.workspaceId,
704
+ projectId: opts.projectId,
705
+ searchQuery: opts.search,
706
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
707
+ offset: opts.offset ? parseInt(opts.offset) : undefined,
708
+ }));
709
+ });
710
+ workflowCmd
711
+ .command("get")
712
+ .description("Get a workflow by ID.")
713
+ .requiredOption("--workflow-id <id>", "Workflow ID")
714
+ .action(async (opts) => {
715
+ const client = buildClient(program.opts());
716
+ const token = resolveToken(program.opts());
717
+ if (token) {
718
+ await run(() => client.workflows.get(opts.workflowId));
719
+ }
720
+ else {
721
+ await run(() => client.workflows.getAnonymous(opts.workflowId));
722
+ }
723
+ });
724
+ workflowCmd
725
+ .command("create")
726
+ .description("Create a new workflow.")
727
+ .option("--workspace-id <id>", "Workspace ID")
728
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
729
+ .action(async (opts) => {
730
+ const client = buildClient(program.opts());
731
+ const body = loadJsonPayload(opts.body);
732
+ await run(() => client.workflows.create(body, opts.workspaceId));
733
+ });
734
+ workflowCmd
735
+ .command("update")
736
+ .description("Update a workflow.")
737
+ .option("--workspace-id <id>", "Workspace ID")
738
+ .requiredOption("--workflow-id <id>", "Workflow ID")
739
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
740
+ .action(async (opts) => {
741
+ const client = buildClient(program.opts());
742
+ const body = loadJsonPayload(opts.body);
743
+ await run(() => client.workflows.update(opts.workflowId, body, opts.workspaceId));
744
+ });
745
+ workflowCmd
746
+ .command("delete")
747
+ .description("Delete a workflow.")
748
+ .option("--workspace-id <id>", "Workspace ID")
749
+ .requiredOption("--workflow-id <id>", "Workflow ID")
750
+ .action(async (opts) => {
751
+ const client = buildClient(program.opts());
752
+ await run(() => client.workflows.delete(opts.workflowId, opts.workspaceId));
753
+ });
754
+ workflowCmd
755
+ .command("run")
756
+ .description("Run a workflow.")
757
+ .requiredOption("--workflow-id <id>", "Workflow ID")
758
+ .option("--input <input>", "JSON input (inline or @file)")
759
+ .action(async (opts) => {
760
+ const client = buildClient(program.opts());
761
+ const token = resolveToken(program.opts());
762
+ const body = { workflow_id: opts.workflowId };
763
+ if (opts.input)
764
+ body["input"] = loadJsonPayload(opts.input);
765
+ if (token) {
766
+ await run(() => client.workflows.run(body));
767
+ }
768
+ else {
769
+ await run(() => client.workflows.runAnonymous(body));
770
+ }
771
+ });
772
+ workflowCmd
773
+ .command("run-status")
774
+ .description("Get workflow run status.")
775
+ .requiredOption("--workflow-run-id <id>", "Workflow run ID")
776
+ .action(async (opts) => {
777
+ const client = buildClient(program.opts());
778
+ const token = resolveToken(program.opts());
779
+ if (token) {
780
+ await run(() => client.workflows.getRun(opts.workflowRunId));
781
+ }
782
+ else {
783
+ await run(() => client.workflows.getRunAnonymous(opts.workflowRunId));
784
+ }
785
+ });
786
+ workflowCmd
787
+ .command("list-runs")
788
+ .description("List runs for a workflow.")
789
+ .requiredOption("--workflow-id <id>", "Workflow ID")
790
+ .option("--workspace-id <id>", "Workspace ID")
791
+ .option("--limit <n>", "Limit")
792
+ .option("--offset <n>", "Offset")
793
+ .option("--sort-order <order>", "Sort order (asc|desc)")
794
+ .action(async (opts) => {
795
+ const client = buildClient(program.opts());
796
+ await run(() => client.workflows.listRuns(opts.workflowId, {
797
+ workspaceId: opts.workspaceId,
798
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
799
+ offset: opts.offset ? parseInt(opts.offset) : undefined,
800
+ sortOrder: opts.sortOrder,
801
+ }));
802
+ });
803
+ workflowCmd
804
+ .command("validate")
805
+ .description("Validate a workflow payload.")
806
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
807
+ .action(async (opts) => {
808
+ const client = buildClient(program.opts());
809
+ const body = loadJsonPayload(opts.body);
810
+ await run(() => client.workflows.validate(body));
811
+ });
812
+ workflowCmd
813
+ .command("run-history")
814
+ .description("Get run history for a workflow.")
815
+ .requiredOption("--workflow-id <id>", "Workflow ID")
816
+ .option("--limit <n>", "Limit")
817
+ .option("--offset <n>", "Offset")
818
+ .action(async (opts) => {
819
+ const client = buildClient(program.opts());
820
+ await run(() => client.workflows.runHistory(opts.workflowId, {
821
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
822
+ offset: opts.offset ? parseInt(opts.offset) : undefined,
823
+ }));
824
+ });
825
+ workflowCmd
826
+ .command("like")
827
+ .description("Like a workflow.")
828
+ .requiredOption("--workflow-id <id>", "Workflow ID")
829
+ .action(async (opts) => {
830
+ const client = buildClient(program.opts());
831
+ await run(() => client.workflows.like(opts.workflowId));
832
+ });
833
+ workflowCmd
834
+ .command("unlike")
835
+ .description("Unlike a workflow.")
836
+ .requiredOption("--workflow-id <id>", "Workflow ID")
837
+ .action(async (opts) => {
838
+ const client = buildClient(program.opts());
839
+ await run(() => client.workflows.unlike(opts.workflowId));
840
+ });
841
+ workflowCmd
842
+ .command("like-status")
843
+ .description("Get like status for a workflow.")
844
+ .requiredOption("--workflow-id <id>", "Workflow ID")
845
+ .action(async (opts) => {
846
+ const client = buildClient(program.opts());
847
+ await run(() => client.workflows.getLikeStatus(opts.workflowId));
848
+ });
849
+ workflowCmd
850
+ .command("reference-impact")
851
+ .description("Get reference impact analysis for a workflow.")
852
+ .requiredOption("--workflow-id <id>", "Workflow ID")
853
+ .action(async (opts) => {
854
+ const client = buildClient(program.opts());
855
+ await run(() => client.workflows.getReferenceImpact(opts.workflowId));
856
+ });
857
+ // ═════════════════════════════════════════════════════════════════
858
+ // agent (SDK-based)
859
+ // ═════════════════════════════════════════════════════════════════
860
+ const agentCmd = program
861
+ .command("agent")
862
+ .description("Agent management commands.");
863
+ agentCmd
864
+ .command("list")
865
+ .description("List agents.")
866
+ .option("--project-id <id>", "Project ID")
867
+ .option("--search <query>", "Search query")
868
+ .option("--limit <n>", "Limit results")
869
+ .option("--offset <n>", "Offset")
870
+ .action(async (opts) => {
871
+ const client = buildClient(program.opts());
872
+ await run(() => client.agents.list({
873
+ projectId: opts.projectId,
874
+ searchQuery: opts.search,
875
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
876
+ offset: opts.offset ? parseInt(opts.offset) : undefined,
877
+ }));
878
+ });
879
+ agentCmd
880
+ .command("get")
881
+ .description("Get an agent by ID.")
882
+ .requiredOption("--agent-id <id>", "Agent ID")
883
+ .action(async (opts) => {
884
+ const client = buildClient(program.opts());
885
+ const token = resolveToken(program.opts());
886
+ if (token) {
887
+ await run(() => client.agents.get(opts.agentId));
888
+ }
889
+ else {
890
+ await run(() => client.agents.getAnonymous(opts.agentId));
891
+ }
892
+ });
893
+ agentCmd
894
+ .command("create")
895
+ .description("Create an agent.")
896
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
897
+ .action(async (opts) => {
898
+ const client = buildClient(program.opts());
899
+ const body = loadJsonPayload(opts.body);
900
+ await run(() => client.agents.create(body));
901
+ });
902
+ agentCmd
903
+ .command("update")
904
+ .description("Update an agent.")
905
+ .requiredOption("--agent-id <id>", "Agent ID")
906
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
907
+ .action(async (opts) => {
908
+ const client = buildClient(program.opts());
909
+ const body = loadJsonPayload(opts.body);
910
+ await run(() => client.agents.update(opts.agentId, body));
911
+ });
912
+ agentCmd
913
+ .command("delete")
914
+ .description("Delete an agent.")
915
+ .requiredOption("--agent-id <id>", "Agent ID")
916
+ .action(async (opts) => {
917
+ const client = buildClient(program.opts());
918
+ await run(() => client.agents.delete(opts.agentId));
919
+ });
920
+ agentCmd
921
+ .command("stream")
922
+ .description("Stream interaction with an agent.")
923
+ .requiredOption("--agent-id <id>", "Agent ID")
924
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
925
+ .action(async (opts) => {
926
+ const client = buildClient(program.opts());
927
+ const token = resolveToken(program.opts());
928
+ const body = loadJsonPayload(opts.body);
929
+ if (token) {
930
+ await run(() => client.agents.stream(opts.agentId, body));
931
+ }
932
+ else {
933
+ await run(() => client.agents.streamAnonymous(opts.agentId, body));
934
+ }
935
+ });
936
+ agentCmd
937
+ .command("publish-info")
938
+ .description("Get publish info for an agent.")
939
+ .requiredOption("--agent-id <id>", "Agent ID")
940
+ .option("--platform <platform>", "Filter by platform")
941
+ .action(async (opts) => {
942
+ const client = buildClient(program.opts());
943
+ await run(() => client.agents.getPublishInfo(opts.agentId, { platform: opts.platform }));
944
+ });
945
+ agentCmd
946
+ .command("publish")
947
+ .description("Publish an agent.")
948
+ .requiredOption("--agent-id <id>", "Agent ID")
949
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
950
+ .action(async (opts) => {
951
+ const client = buildClient(program.opts());
952
+ const body = loadJsonPayload(opts.body);
953
+ await run(() => client.agents.publish(opts.agentId, body));
954
+ });
955
+ agentCmd
956
+ .command("unpublish")
957
+ .description("Unpublish an agent.")
958
+ .requiredOption("--agent-id <id>", "Agent ID")
959
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
960
+ .action(async (opts) => {
961
+ const client = buildClient(program.opts());
962
+ const body = loadJsonPayload(opts.body);
963
+ await run(() => client.agents.unpublish(opts.agentId, body));
964
+ });
965
+ agentCmd
966
+ .command("reference-impact")
967
+ .description("Get reference impact analysis for an agent.")
968
+ .requiredOption("--agent-id <id>", "Agent ID")
969
+ .action(async (opts) => {
970
+ const client = buildClient(program.opts());
971
+ await run(() => client.agents.getReferenceImpact(opts.agentId));
972
+ });
973
+ agentCmd
974
+ .command("save-as-template")
975
+ .description("Save an agent as a template.")
976
+ .requiredOption("--agent-id <id>", "Agent ID")
977
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
978
+ .action(async (opts) => {
979
+ const client = buildClient(program.opts());
980
+ const body = loadJsonPayload(opts.body);
981
+ await run(() => client.agents.saveAsTemplate(opts.agentId, body));
982
+ });
983
+ // ═════════════════════════════════════════════════════════════════
984
+ // node-types (SDK-based)
985
+ // ═════════════════════════════════════════════════════════════════
986
+ const nodeTypesCmd = program
987
+ .command("node-types")
988
+ .description("Node type discovery commands.");
989
+ nodeTypesCmd
990
+ .command("list")
991
+ .description("List available node types.")
992
+ .action(async () => {
993
+ const client = buildClient(program.opts());
994
+ await run(() => client.nodeTypes.list());
995
+ });
996
+ nodeTypesCmd
997
+ .command("get")
998
+ .description("Get a specific node type.")
999
+ .requiredOption("--name <name>", "Node type name")
1000
+ .action(async (opts) => {
1001
+ const client = buildClient(program.opts());
1002
+ await run(() => client.nodeTypes.get(opts.name));
1003
+ });
1004
+ nodeTypesCmd
1005
+ .command("search")
1006
+ .description("Search node types.")
1007
+ .requiredOption("--query <query>", "Search query")
1008
+ .action(async (opts) => {
1009
+ const client = buildClient(program.opts());
1010
+ await run(() => client.nodeTypes.search(opts.query));
1011
+ });
1012
+ nodeTypesCmd
1013
+ .command("dynamic-options")
1014
+ .description("Get dynamic options for a node type field.")
1015
+ .requiredOption("--name <name>", "Node type name")
1016
+ .requiredOption("--field-name <field>", "Field name")
1017
+ .option("--project-id <id>", "Project ID")
1018
+ .option("--input-config <json>", "Input config JSON")
1019
+ .option("--connection <name>", "Connection name")
1020
+ .option("--search-term <term>", "Search term")
1021
+ .action(async (opts) => {
1022
+ const client = buildClient(program.opts());
1023
+ await run(() => client.nodeTypes.dynamicOptions({
1024
+ name: opts.name,
1025
+ fieldName: opts.fieldName,
1026
+ projectId: opts.projectId,
1027
+ inputConfig: opts.inputConfig ? JSON.parse(opts.inputConfig) : undefined,
1028
+ connection: opts.connection,
1029
+ searchTerm: opts.searchTerm,
1030
+ }));
1031
+ });
1032
+ // ═════════════════════════════════════════════════════════════════
1033
+ // connections (SDK-based)
1034
+ // ═════════════════════════════════════════════════════════════════
1035
+ const connectionsCmd = program
1036
+ .command("connections")
1037
+ .description("App connection management.");
1038
+ connectionsCmd
1039
+ .command("list")
1040
+ .description("List connections.")
1041
+ .option("--workspace-id <id>", "Workspace ID")
1042
+ .option("--project-id <id>", "Project ID")
1043
+ .option("--limit <n>", "Limit")
1044
+ .option("--offset <n>", "Offset")
1045
+ .action(async (opts) => {
1046
+ const client = buildClient(program.opts());
1047
+ await run(() => client.connections.list({
1048
+ workspaceId: opts.workspaceId,
1049
+ projectId: opts.projectId,
1050
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
1051
+ offset: opts.offset ? parseInt(opts.offset) : undefined,
1052
+ }));
1053
+ });
1054
+ connectionsCmd
1055
+ .command("create")
1056
+ .description("Create a connection.")
1057
+ .option("--workspace-id <id>", "Workspace ID")
1058
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
1059
+ .action(async (opts) => {
1060
+ const client = buildClient(program.opts());
1061
+ const body = loadJsonPayload(opts.body);
1062
+ await run(() => client.connections.create(body, opts.workspaceId));
1063
+ });
1064
+ connectionsCmd
1065
+ .command("get-default")
1066
+ .description("Get default connection for a category.")
1067
+ .requiredOption("--category <name>", "Category name")
1068
+ .option("--workspace-id <id>", "Workspace ID")
1069
+ .option("--project-id <id>", "Project ID")
1070
+ .action(async (opts) => {
1071
+ const client = buildClient(program.opts());
1072
+ await run(() => client.connections.getDefault({
1073
+ categoryName: opts.category,
1074
+ workspaceId: opts.workspaceId,
1075
+ projectId: opts.projectId,
1076
+ }));
1077
+ });
1078
+ connectionsCmd
1079
+ .command("update")
1080
+ .description("Update a connection.")
1081
+ .requiredOption("--connection-id <id>", "Connection ID")
1082
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
1083
+ .option("--workspace-id <id>", "Workspace ID")
1084
+ .action(async (opts) => {
1085
+ const client = buildClient(program.opts());
1086
+ const body = loadJsonPayload(opts.body);
1087
+ await run(() => client.connections.update(opts.connectionId, body, opts.workspaceId));
1088
+ });
1089
+ connectionsCmd
1090
+ .command("delete")
1091
+ .description("Delete a connection.")
1092
+ .requiredOption("--connection-id <id>", "Connection ID")
1093
+ .option("--workspace-id <id>", "Workspace ID")
1094
+ .action(async (opts) => {
1095
+ const client = buildClient(program.opts());
1096
+ await run(() => client.connections.delete(opts.connectionId, opts.workspaceId));
1097
+ });
1098
+ connectionsCmd
1099
+ .command("categories")
1100
+ .description("List connection categories.")
1101
+ .option("--workspace-id <id>", "Workspace ID")
1102
+ .option("--limit <n>", "Limit")
1103
+ .option("--offset <n>", "Offset")
1104
+ .action(async (opts) => {
1105
+ const client = buildClient(program.opts());
1106
+ await run(() => client.connections.categories({
1107
+ workspaceId: opts.workspaceId,
1108
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
1109
+ offset: opts.offset ? parseInt(opts.offset) : undefined,
1110
+ }));
1111
+ });
1112
+ connectionsCmd
1113
+ .command("health-check-pre")
1114
+ .description("Pre-create health check for a connection.")
1115
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
1116
+ .action(async (opts) => {
1117
+ const client = buildClient(program.opts());
1118
+ const body = loadJsonPayload(opts.body);
1119
+ await run(() => client.connections.healthCheckPreCreate(body));
1120
+ });
1121
+ connectionsCmd
1122
+ .command("health-check-post")
1123
+ .description("Post-create health check for a connection.")
1124
+ .requiredOption("--connection-id <id>", "Connection ID")
1125
+ .action(async (opts) => {
1126
+ const client = buildClient(program.opts());
1127
+ await run(() => client.connections.healthCheckPostCreate(opts.connectionId));
1128
+ });
1129
+ // ═════════════════════════════════════════════════════════════════
1130
+ // uploads (SDK-based)
1131
+ // ═════════════════════════════════════════════════════════════════
1132
+ const uploadsCmd = program
1133
+ .command("uploads")
1134
+ .description("Upload session management.");
1135
+ uploadsCmd
1136
+ .command("create")
1137
+ .description("Create an upload session.")
1138
+ .requiredOption("--body <body>", "JSON body (inline or @file)")
1139
+ .action(async (opts) => {
1140
+ const client = buildClient(program.opts());
1141
+ const body = loadJsonPayload(opts.body);
1142
+ await run(() => client.uploads.inputCreate(body));
1143
+ });
1144
+ uploadsCmd
1145
+ .command("status")
1146
+ .description("Get upload session status.")
1147
+ .requiredOption("--session-id <id>", "Session ID")
1148
+ .action(async (opts) => {
1149
+ const client = buildClient(program.opts());
1150
+ await run(() => client.uploads.inputStatus(opts.sessionId));
1151
+ });
1152
+ return program;
1153
+ }
1154
+ export async function runCli(argv) {
1155
+ const program = createProgram();
1156
+ await program.parseAsync(argv ?? process.argv);
1157
+ }
1158
+ //# sourceMappingURL=main.js.map