@pixelml/agenticflow-cli 1.0.4 → 1.0.6
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 +42 -10
- package/dist/bin/agenticflow.js +11 -1
- package/dist/bin/agenticflow.js.map +1 -1
- package/dist/cli/local-validation.d.ts +16 -0
- package/dist/cli/local-validation.d.ts.map +1 -0
- package/dist/cli/local-validation.js +298 -0
- package/dist/cli/local-validation.js.map +1 -0
- package/dist/cli/main.d.ts +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +953 -61
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/playbooks.d.ts.map +1 -1
- package/dist/cli/playbooks.js +87 -0
- package/dist/cli/playbooks.js.map +1 -1
- package/dist/cli/template-cache.d.ts +41 -0
- package/dist/cli/template-cache.d.ts.map +1 -0
- package/dist/cli/template-cache.js +131 -0
- package/dist/cli/template-cache.js.map +1 -0
- package/dist/cli/template-duplicate.d.ts +24 -0
- package/dist/cli/template-duplicate.d.ts.map +1 -0
- package/dist/cli/template-duplicate.js +171 -0
- package/dist/cli/template-duplicate.js.map +1 -0
- package/package.json +3 -2
package/dist/cli/main.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Main CLI program definition with Commander.js.
|
|
3
3
|
* Resource commands (workflow, agent, node-types, connections, uploads)
|
|
4
4
|
* use the SDK resource classes. Generic commands (call, ops, catalog,
|
|
5
|
-
* doctor, auth, policy, playbook) remain spec-based.
|
|
5
|
+
* doctor, auth, policy, playbook, templates) remain spec-based.
|
|
6
6
|
*/
|
|
7
7
|
import { Command } from "commander";
|
|
8
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
|
9
|
-
import { resolve, dirname, join } from "node:path";
|
|
8
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync, readdirSync } from "node:fs";
|
|
9
|
+
import { resolve, dirname, join, basename } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { createInterface } from "node:readline";
|
|
@@ -15,17 +15,137 @@ import { OperationRegistry, defaultSpecPath, loadOpenapiSpec, isPublic, } from "
|
|
|
15
15
|
import { listPlaybooks, getPlaybook } from "./playbooks.js";
|
|
16
16
|
import { loadPolicy, writeDefaultPolicy, policyFilePath, } from "./policy.js";
|
|
17
17
|
import { parseKeyValuePairs, loadJsonPayload, buildRequestSpec } from "./client.js";
|
|
18
|
+
import { TEMPLATE_CACHE_SCHEMA_VERSION, readTemplateCacheManifest, writeTemplateCache, } from "./template-cache.js";
|
|
19
|
+
import { validateWorkflowCreatePayload, validateWorkflowUpdatePayload, validateWorkflowRunPayload, validateAgentCreatePayload, validateAgentUpdatePayload, validateAgentStreamPayload, } from "./local-validation.js";
|
|
20
|
+
import { buildWorkflowCreatePayloadFromTemplate, extractAgentTemplateWorkflowReferences, buildAgentCreatePayloadFromTemplate, indexTemplatesById, } from "./template-duplicate.js";
|
|
18
21
|
// --- Constants ---
|
|
19
22
|
const AUTH_ENV_API_KEY = "AGENTICFLOW_PUBLIC_API_KEY";
|
|
20
23
|
const DOCTOR_SCHEMA_VERSION = "agenticflow.doctor.v1";
|
|
21
24
|
const CATALOG_EXPORT_SCHEMA_VERSION = "agenticflow.catalog.export.v1";
|
|
22
25
|
const CATALOG_RANK_SCHEMA_VERSION = "agenticflow.catalog.rank.v1";
|
|
26
|
+
const ERROR_SCHEMA_VERSION = "agenticflow.error.v1";
|
|
27
|
+
const PLAYBOOK_LIST_SCHEMA_VERSION = "agenticflow.playbook.list.v1";
|
|
28
|
+
const PLAYBOOK_SCHEMA_VERSION = "agenticflow.playbook.v1";
|
|
29
|
+
const DISCOVER_SCHEMA_VERSION = "agenticflow.discover.v1";
|
|
30
|
+
const TEMPLATE_SYNC_SCHEMA_VERSION = "agenticflow.templates.sync.v1";
|
|
31
|
+
const TEMPLATE_INDEX_SCHEMA_VERSION = "agenticflow.templates.index.v1";
|
|
32
|
+
const TEMPLATE_DUPLICATE_SCHEMA_VERSION = "agenticflow.templates.duplicate.v1";
|
|
33
|
+
const LOCAL_VALIDATION_SCHEMA_VERSION = "agenticflow.local_validation.v1";
|
|
23
34
|
// ═══════════════════════════════════════════════════════════════════
|
|
24
35
|
// Helpers
|
|
25
36
|
// ═══════════════════════════════════════════════════════════════════
|
|
26
37
|
function printJson(data) {
|
|
27
38
|
console.log(JSON.stringify(data, null, 2));
|
|
28
39
|
}
|
|
40
|
+
function isJsonFlagEnabled() {
|
|
41
|
+
return process.argv.includes("--json");
|
|
42
|
+
}
|
|
43
|
+
function printError(code, message, hint, details) {
|
|
44
|
+
if (isJsonFlagEnabled()) {
|
|
45
|
+
const payload = {
|
|
46
|
+
schema: ERROR_SCHEMA_VERSION,
|
|
47
|
+
code,
|
|
48
|
+
message,
|
|
49
|
+
};
|
|
50
|
+
if (hint)
|
|
51
|
+
payload["hint"] = hint;
|
|
52
|
+
if (details !== undefined)
|
|
53
|
+
payload["details"] = details;
|
|
54
|
+
printJson(payload);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
console.error(`Error: ${message}`);
|
|
58
|
+
if (hint)
|
|
59
|
+
console.error(`Hint: ${hint}`);
|
|
60
|
+
}
|
|
61
|
+
function fail(code, message, hint, details) {
|
|
62
|
+
printError(code, message, hint, details);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
function ensureLocalValidation(target, issues) {
|
|
66
|
+
if (issues.length === 0)
|
|
67
|
+
return;
|
|
68
|
+
fail("local_schema_validation_failed", `Local schema validation failed for ${target} payload (${issues.length} issue${issues.length === 1 ? "" : "s"}).`, "Fix payload fields and retry. Use `agenticflow discover --json` and `agenticflow playbook first-touch` for payload guidance.", {
|
|
69
|
+
schema: LOCAL_VALIDATION_SCHEMA_VERSION,
|
|
70
|
+
target,
|
|
71
|
+
issues,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function parseOptionalInteger(rawValue, optionName, minimum) {
|
|
75
|
+
if (rawValue == null)
|
|
76
|
+
return undefined;
|
|
77
|
+
const value = rawValue.trim();
|
|
78
|
+
if (!/^-?\d+$/.test(value)) {
|
|
79
|
+
const floor = minimum === 0 ? "0 or higher" : `${minimum} or higher`;
|
|
80
|
+
fail("invalid_option_value", `Invalid value for ${optionName}: ${rawValue}`, `Use an integer ${floor}.`);
|
|
81
|
+
}
|
|
82
|
+
const parsed = Number.parseInt(value, 10);
|
|
83
|
+
if (!Number.isFinite(parsed) || parsed < minimum) {
|
|
84
|
+
const floor = minimum === 0 ? "0 or higher" : `${minimum} or higher`;
|
|
85
|
+
fail("invalid_option_value", `Invalid value for ${optionName}: ${rawValue}`, `Use an integer ${floor}.`);
|
|
86
|
+
}
|
|
87
|
+
return parsed;
|
|
88
|
+
}
|
|
89
|
+
function readJsonFileWithWarnings(filePath, warnings) {
|
|
90
|
+
try {
|
|
91
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
92
|
+
return JSON.parse(raw);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
96
|
+
warnings.push(`${filePath}: ${message}`);
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function loadLocalWorkflowTemplateCache(cacheDir) {
|
|
101
|
+
const resolvedCacheDir = resolve(cacheDir);
|
|
102
|
+
const warnings = [];
|
|
103
|
+
const items = [];
|
|
104
|
+
const collectionPath = join(resolvedCacheDir, "workflow_templates.json");
|
|
105
|
+
if (existsSync(collectionPath)) {
|
|
106
|
+
const collection = readJsonFileWithWarnings(collectionPath, warnings);
|
|
107
|
+
if (Array.isArray(collection)) {
|
|
108
|
+
items.push(...collection);
|
|
109
|
+
}
|
|
110
|
+
else if (collection !== undefined) {
|
|
111
|
+
warnings.push(`${collectionPath}: expected array payload`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const itemDir = join(resolvedCacheDir, "workflow");
|
|
115
|
+
if (existsSync(itemDir)) {
|
|
116
|
+
const files = readdirSync(itemDir, { withFileTypes: true });
|
|
117
|
+
for (const file of files) {
|
|
118
|
+
if (!file.isFile() || !file.name.endsWith(".json"))
|
|
119
|
+
continue;
|
|
120
|
+
const itemPath = join(itemDir, file.name);
|
|
121
|
+
const item = readJsonFileWithWarnings(itemPath, warnings);
|
|
122
|
+
if (item !== undefined)
|
|
123
|
+
items.push(item);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const byId = indexTemplatesById(items);
|
|
127
|
+
return {
|
|
128
|
+
cacheDir: resolvedCacheDir,
|
|
129
|
+
byId,
|
|
130
|
+
warnings,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function inferTemplateCacheDirFromTemplateFile(templateFile) {
|
|
134
|
+
const absolute = resolve(templateFile);
|
|
135
|
+
const parent = dirname(absolute);
|
|
136
|
+
const parentName = basename(parent).toLowerCase();
|
|
137
|
+
if (parentName === "workflow" || parentName === "agent" || parentName === "workforce") {
|
|
138
|
+
return dirname(parent);
|
|
139
|
+
}
|
|
140
|
+
const fileName = basename(absolute).toLowerCase();
|
|
141
|
+
if (fileName.endsWith("_templates.json")) {
|
|
142
|
+
return parent;
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
function shouldUseColor(parentOpts) {
|
|
147
|
+
return process.stdout.isTTY && process.stderr.isTTY && parentOpts.color !== false && !("NO_COLOR" in process.env);
|
|
148
|
+
}
|
|
29
149
|
/** Print an SDK result in CLI-friendly format. */
|
|
30
150
|
function printResult(data) {
|
|
31
151
|
printJson(data);
|
|
@@ -84,8 +204,8 @@ async function run(fn) {
|
|
|
84
204
|
printResult(result);
|
|
85
205
|
}
|
|
86
206
|
catch (err) {
|
|
87
|
-
|
|
88
|
-
|
|
207
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
208
|
+
fail("request_failed", message);
|
|
89
209
|
}
|
|
90
210
|
}
|
|
91
211
|
// ═══════════════════════════════════════════════════════════════════
|
|
@@ -111,6 +231,109 @@ function catalogOperationItem(op) {
|
|
|
111
231
|
public: isPublic(op),
|
|
112
232
|
};
|
|
113
233
|
}
|
|
234
|
+
function normalizeForMatch(value) {
|
|
235
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
236
|
+
}
|
|
237
|
+
function levenshteinDistance(a, b) {
|
|
238
|
+
if (a === b)
|
|
239
|
+
return 0;
|
|
240
|
+
if (!a)
|
|
241
|
+
return b.length;
|
|
242
|
+
if (!b)
|
|
243
|
+
return a.length;
|
|
244
|
+
const previousRow = new Array(b.length + 1);
|
|
245
|
+
const currentRow = new Array(b.length + 1);
|
|
246
|
+
for (let j = 0; j <= b.length; j++)
|
|
247
|
+
previousRow[j] = j;
|
|
248
|
+
for (let i = 1; i <= a.length; i++) {
|
|
249
|
+
currentRow[0] = i;
|
|
250
|
+
for (let j = 1; j <= b.length; j++) {
|
|
251
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
252
|
+
currentRow[j] = Math.min(currentRow[j - 1] + 1, previousRow[j] + 1, previousRow[j - 1] + cost);
|
|
253
|
+
}
|
|
254
|
+
for (let j = 0; j <= b.length; j++)
|
|
255
|
+
previousRow[j] = currentRow[j];
|
|
256
|
+
}
|
|
257
|
+
return previousRow[b.length];
|
|
258
|
+
}
|
|
259
|
+
function suggestOperationIds(registry, rawQuery) {
|
|
260
|
+
const query = normalizeForMatch(rawQuery);
|
|
261
|
+
if (!query)
|
|
262
|
+
return [];
|
|
263
|
+
const scored = registry.listOperations().map((op) => {
|
|
264
|
+
const id = op.operationId;
|
|
265
|
+
const normalizedId = normalizeForMatch(id);
|
|
266
|
+
let score = 0;
|
|
267
|
+
if (normalizedId === query)
|
|
268
|
+
score += 10_000;
|
|
269
|
+
if (normalizedId.startsWith(query))
|
|
270
|
+
score += 500;
|
|
271
|
+
if (normalizedId.includes(query))
|
|
272
|
+
score += 250;
|
|
273
|
+
if (query.includes(normalizedId))
|
|
274
|
+
score += 100;
|
|
275
|
+
for (const term of query.split(/\s+/)) {
|
|
276
|
+
if (term.length >= 3 && normalizedId.includes(term))
|
|
277
|
+
score += 25;
|
|
278
|
+
}
|
|
279
|
+
const distance = levenshteinDistance(query, normalizedId);
|
|
280
|
+
const similarity = 1 - distance / Math.max(query.length, normalizedId.length);
|
|
281
|
+
score += Math.round(similarity * 100);
|
|
282
|
+
return { id, score };
|
|
283
|
+
});
|
|
284
|
+
return scored
|
|
285
|
+
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
286
|
+
.slice(0, 5)
|
|
287
|
+
.map((item) => item.id);
|
|
288
|
+
}
|
|
289
|
+
function formatOperationHint(suggestions) {
|
|
290
|
+
if (suggestions.length === 0)
|
|
291
|
+
return undefined;
|
|
292
|
+
return `Try one of: ${suggestions.join(", ")}`;
|
|
293
|
+
}
|
|
294
|
+
function suggestPlaybookTopics(rawQuery) {
|
|
295
|
+
const query = normalizeForMatch(rawQuery);
|
|
296
|
+
const topics = listPlaybooks().map((pb) => pb.topic);
|
|
297
|
+
if (!query)
|
|
298
|
+
return topics.slice(0, 5);
|
|
299
|
+
const scored = topics.map((topic) => {
|
|
300
|
+
const normalizedTopic = normalizeForMatch(topic);
|
|
301
|
+
let score = 0;
|
|
302
|
+
if (normalizedTopic === query)
|
|
303
|
+
score += 10_000;
|
|
304
|
+
if (normalizedTopic.startsWith(query))
|
|
305
|
+
score += 500;
|
|
306
|
+
if (normalizedTopic.includes(query))
|
|
307
|
+
score += 250;
|
|
308
|
+
for (const term of query.split(/\s+/)) {
|
|
309
|
+
if (term.length >= 2 && normalizedTopic.includes(term))
|
|
310
|
+
score += 25;
|
|
311
|
+
}
|
|
312
|
+
return { topic, score };
|
|
313
|
+
});
|
|
314
|
+
return scored
|
|
315
|
+
.sort((a, b) => b.score - a.score || a.topic.localeCompare(b.topic))
|
|
316
|
+
.slice(0, 5)
|
|
317
|
+
.map((item) => item.topic);
|
|
318
|
+
}
|
|
319
|
+
function describeCommand(cmd, depth = 0) {
|
|
320
|
+
const options = cmd.options
|
|
321
|
+
.filter((opt) => !opt.flags.includes("--help"))
|
|
322
|
+
.map((opt) => ({
|
|
323
|
+
flags: opt.flags,
|
|
324
|
+
description: opt.description ?? "",
|
|
325
|
+
}));
|
|
326
|
+
const description = cmd.description();
|
|
327
|
+
const descriptor = {
|
|
328
|
+
name: cmd.name(),
|
|
329
|
+
description: typeof description === "string" ? description : "",
|
|
330
|
+
options,
|
|
331
|
+
};
|
|
332
|
+
if (depth < 1 && cmd.commands.length > 0) {
|
|
333
|
+
descriptor["subcommands"] = cmd.commands.map((sub) => describeCommand(sub, depth + 1));
|
|
334
|
+
}
|
|
335
|
+
return descriptor;
|
|
336
|
+
}
|
|
114
337
|
// --- Auth helpers ---
|
|
115
338
|
function defaultAuthConfigPath() {
|
|
116
339
|
const envDir = process.env["AGENTICFLOW_CLI_DIR"];
|
|
@@ -146,6 +369,13 @@ function parseKeyValueEnv(line) {
|
|
|
146
369
|
// ═══════════════════════════════════════════════════════════════════
|
|
147
370
|
export function createProgram() {
|
|
148
371
|
const program = new Command();
|
|
372
|
+
program.configureOutput({
|
|
373
|
+
outputError: (str, write) => {
|
|
374
|
+
if (isJsonFlagEnabled())
|
|
375
|
+
return;
|
|
376
|
+
write(str);
|
|
377
|
+
},
|
|
378
|
+
});
|
|
149
379
|
// Read version from package.json so --version stays in sync
|
|
150
380
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
151
381
|
const pkgPath = join(__dirname, "..", "..", "package.json");
|
|
@@ -158,14 +388,65 @@ export function createProgram() {
|
|
|
158
388
|
.option("--workspace-id <id>", "Default workspace ID")
|
|
159
389
|
.option("--project-id <id>", "Default project ID")
|
|
160
390
|
.option("--spec-file <path>", "Path to OpenAPI spec JSON file")
|
|
391
|
+
.option("--no-color", "Disable ANSI colors in text output")
|
|
161
392
|
.option("--json", "Force JSON output");
|
|
393
|
+
if (isJsonFlagEnabled()) {
|
|
394
|
+
program.showSuggestionAfterError(false);
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
program.showSuggestionAfterError(true);
|
|
398
|
+
}
|
|
399
|
+
program.exitOverride();
|
|
162
400
|
// ═════════════════════════════════════════════════════════════════
|
|
163
401
|
// doctor
|
|
164
402
|
// ═════════════════════════════════════════════════════════════════
|
|
403
|
+
program
|
|
404
|
+
.command("discover")
|
|
405
|
+
.description("Machine-readable CLI capability index for autonomous agents.")
|
|
406
|
+
.option("--json", "JSON output")
|
|
407
|
+
.action((opts) => {
|
|
408
|
+
const parentOpts = program.opts();
|
|
409
|
+
const commands = program.commands
|
|
410
|
+
.filter((cmd) => cmd.name() !== "discover")
|
|
411
|
+
.map((cmd) => describeCommand(cmd));
|
|
412
|
+
const payload = {
|
|
413
|
+
schema: DISCOVER_SCHEMA_VERSION,
|
|
414
|
+
cli: {
|
|
415
|
+
name: program.name(),
|
|
416
|
+
version: program.version(),
|
|
417
|
+
},
|
|
418
|
+
entrypoints: {
|
|
419
|
+
first_touch: "agenticflow playbook first-touch",
|
|
420
|
+
discover_playbooks: "agenticflow playbook --list --json",
|
|
421
|
+
strict_preflight: "agenticflow doctor --json --strict",
|
|
422
|
+
seed_templates: "agenticflow templates sync --json",
|
|
423
|
+
duplicate_from_template: "agenticflow templates duplicate workflow --template-id <id> --json",
|
|
424
|
+
},
|
|
425
|
+
contracts: {
|
|
426
|
+
error_schema: ERROR_SCHEMA_VERSION,
|
|
427
|
+
playbook_list_schema: PLAYBOOK_LIST_SCHEMA_VERSION,
|
|
428
|
+
playbook_schema: PLAYBOOK_SCHEMA_VERSION,
|
|
429
|
+
doctor_schema: DOCTOR_SCHEMA_VERSION,
|
|
430
|
+
template_cache_schema: TEMPLATE_CACHE_SCHEMA_VERSION,
|
|
431
|
+
local_validation_schema: LOCAL_VALIDATION_SCHEMA_VERSION,
|
|
432
|
+
},
|
|
433
|
+
commands,
|
|
434
|
+
};
|
|
435
|
+
if (opts.json || parentOpts.json) {
|
|
436
|
+
printJson(payload);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
console.log("AgenticFlow CLI Capability Index");
|
|
440
|
+
console.log(`- Version: ${program.version()}`);
|
|
441
|
+
console.log(`- First touch: ${payload.entrypoints.first_touch}`);
|
|
442
|
+
console.log("- Use `agenticflow discover --json` for machine-readable capability metadata.");
|
|
443
|
+
}
|
|
444
|
+
});
|
|
165
445
|
program
|
|
166
446
|
.command("doctor")
|
|
167
447
|
.description("Preflight checks for CLI configuration and connectivity.")
|
|
168
448
|
.option("--json", "JSON output")
|
|
449
|
+
.option("--strict", "Exit non-zero when any required check fails")
|
|
169
450
|
.action(async (opts) => {
|
|
170
451
|
const parentOpts = program.opts();
|
|
171
452
|
const baseUrl = DEFAULT_BASE_URL;
|
|
@@ -202,12 +483,13 @@ export function createProgram() {
|
|
|
202
483
|
specFile,
|
|
203
484
|
operationsLoaded: registry?.listOperations().length ?? 0,
|
|
204
485
|
};
|
|
486
|
+
const hasFailures = !checks.token || !checks.health || checks.operationsLoaded <= 0;
|
|
205
487
|
if (opts.json || parentOpts.json) {
|
|
206
488
|
printJson({ schema: DOCTOR_SCHEMA_VERSION, ...checks });
|
|
207
489
|
}
|
|
208
490
|
else {
|
|
209
491
|
const ok = (v) => v ? "✓" : "✗";
|
|
210
|
-
const dim = (s) => `\x1b[2m${s}\x1b[0m
|
|
492
|
+
const dim = (s) => shouldUseColor(parentOpts) ? `\x1b[2m${s}\x1b[0m` : s;
|
|
211
493
|
console.log("");
|
|
212
494
|
console.log(" Environment");
|
|
213
495
|
console.log(` └ Version: ${program.version()}`);
|
|
@@ -229,6 +511,8 @@ export function createProgram() {
|
|
|
229
511
|
console.log(` └ Operations: ${checks.operationsLoaded} loaded`);
|
|
230
512
|
console.log("");
|
|
231
513
|
}
|
|
514
|
+
if (opts.strict && hasFailures)
|
|
515
|
+
process.exitCode = 1;
|
|
232
516
|
});
|
|
233
517
|
// ═════════════════════════════════════════════════════════════════
|
|
234
518
|
// ops
|
|
@@ -241,19 +525,27 @@ export function createProgram() {
|
|
|
241
525
|
.description("List available operations.")
|
|
242
526
|
.option("--public-only", "Show only public operations")
|
|
243
527
|
.option("--tag <tag>", "Filter by tag")
|
|
528
|
+
.option("--json", "JSON output")
|
|
244
529
|
.action((opts) => {
|
|
245
530
|
const parentOpts = program.opts();
|
|
246
531
|
const specFile = parentOpts.specFile ?? defaultSpecPath();
|
|
247
532
|
const registry = loadRegistry(specFile);
|
|
248
|
-
if (!registry)
|
|
249
|
-
|
|
250
|
-
process.exit(1);
|
|
251
|
-
}
|
|
533
|
+
if (!registry)
|
|
534
|
+
fail("spec_load_failed", "Failed to load OpenAPI spec.");
|
|
252
535
|
const operations = registry.listOperations({ publicOnly: opts.publicOnly, tag: opts.tag });
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
536
|
+
if (opts.json || parentOpts.json) {
|
|
537
|
+
printJson({
|
|
538
|
+
schema: CATALOG_EXPORT_SCHEMA_VERSION,
|
|
539
|
+
count: operations.length,
|
|
540
|
+
operations: operations.map(catalogOperationItem),
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
console.log(`${operations.length} operations found:\n`);
|
|
545
|
+
for (const op of operations) {
|
|
546
|
+
console.log(` ${op.method.padEnd(7)} ${op.path}`);
|
|
547
|
+
console.log(` ${op.operationId}`);
|
|
548
|
+
}
|
|
257
549
|
}
|
|
258
550
|
});
|
|
259
551
|
opsCmd
|
|
@@ -263,14 +555,12 @@ export function createProgram() {
|
|
|
263
555
|
const parentOpts = program.opts();
|
|
264
556
|
const specFile = parentOpts.specFile ?? defaultSpecPath();
|
|
265
557
|
const registry = loadRegistry(specFile);
|
|
266
|
-
if (!registry)
|
|
267
|
-
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
558
|
+
if (!registry)
|
|
559
|
+
fail("spec_load_failed", "Failed to load OpenAPI spec.");
|
|
270
560
|
const operation = registry.getOperationById(operationId);
|
|
271
561
|
if (!operation) {
|
|
272
|
-
|
|
273
|
-
|
|
562
|
+
const hint = formatOperationHint(suggestOperationIds(registry, operationId));
|
|
563
|
+
fail("operation_not_found", `Operation not found: ${operationId}`, hint);
|
|
274
564
|
}
|
|
275
565
|
printJson(catalogOperationItem(operation));
|
|
276
566
|
});
|
|
@@ -289,10 +579,8 @@ export function createProgram() {
|
|
|
289
579
|
const parentOpts = program.opts();
|
|
290
580
|
const specFile = parentOpts.specFile ?? defaultSpecPath();
|
|
291
581
|
const registry = loadRegistry(specFile);
|
|
292
|
-
if (!registry)
|
|
293
|
-
|
|
294
|
-
process.exit(1);
|
|
295
|
-
}
|
|
582
|
+
if (!registry)
|
|
583
|
+
fail("spec_load_failed", "Failed to load OpenAPI spec.");
|
|
296
584
|
const operations = registry.listOperations({ publicOnly: opts.publicOnly });
|
|
297
585
|
const items = operations.map(catalogOperationItem);
|
|
298
586
|
if (opts.json || parentOpts.json) {
|
|
@@ -316,10 +604,8 @@ export function createProgram() {
|
|
|
316
604
|
const parentOpts = program.opts();
|
|
317
605
|
const specFile = parentOpts.specFile ?? defaultSpecPath();
|
|
318
606
|
const registry = loadRegistry(specFile);
|
|
319
|
-
if (!registry)
|
|
320
|
-
|
|
321
|
-
process.exit(1);
|
|
322
|
-
}
|
|
607
|
+
if (!registry)
|
|
608
|
+
fail("spec_load_failed", "Failed to load OpenAPI spec.");
|
|
323
609
|
const operations = registry.listOperations({ publicOnly: opts.publicOnly });
|
|
324
610
|
const task = opts.task.toLowerCase();
|
|
325
611
|
const taskTerms = [...new Set(task.split(/\s+/))];
|
|
@@ -333,7 +619,8 @@ export function createProgram() {
|
|
|
333
619
|
return { op, score };
|
|
334
620
|
});
|
|
335
621
|
scored.sort((a, b) => b.score - a.score);
|
|
336
|
-
const
|
|
622
|
+
const topCount = parseOptionalInteger(opts.top, "--top", 1) ?? 10;
|
|
623
|
+
const top = scored.slice(0, topCount);
|
|
337
624
|
if (opts.json || parentOpts.json) {
|
|
338
625
|
printJson({
|
|
339
626
|
schema: CATALOG_RANK_SCHEMA_VERSION,
|
|
@@ -354,9 +641,23 @@ export function createProgram() {
|
|
|
354
641
|
.command("playbook [topic]")
|
|
355
642
|
.description("View built-in playbooks for AgenticFlow workflows.")
|
|
356
643
|
.option("--list", "List available playbooks")
|
|
644
|
+
.option("--json", "JSON output")
|
|
357
645
|
.action((topic, opts) => {
|
|
646
|
+
const parentOpts = program.opts();
|
|
358
647
|
if (opts.list || !topic) {
|
|
359
648
|
const playbooks = listPlaybooks();
|
|
649
|
+
if (opts.json || parentOpts.json) {
|
|
650
|
+
printJson({
|
|
651
|
+
schema: PLAYBOOK_LIST_SCHEMA_VERSION,
|
|
652
|
+
count: playbooks.length,
|
|
653
|
+
playbooks: playbooks.map((pb) => ({
|
|
654
|
+
topic: pb.topic,
|
|
655
|
+
title: pb.title,
|
|
656
|
+
summary: pb.summary,
|
|
657
|
+
})),
|
|
658
|
+
});
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
360
661
|
for (const pb of playbooks) {
|
|
361
662
|
console.log(` ${pb.topic.padEnd(20)} ${pb.title} — ${pb.summary}`);
|
|
362
663
|
}
|
|
@@ -364,8 +665,16 @@ export function createProgram() {
|
|
|
364
665
|
}
|
|
365
666
|
const pb = getPlaybook(topic);
|
|
366
667
|
if (!pb) {
|
|
367
|
-
|
|
368
|
-
|
|
668
|
+
const suggestions = suggestPlaybookTopics(topic);
|
|
669
|
+
const hint = suggestions.length > 0 ? `Available topics: ${suggestions.join(", ")}` : undefined;
|
|
670
|
+
fail("playbook_not_found", `Playbook not found: ${topic}`, hint);
|
|
671
|
+
}
|
|
672
|
+
if (opts.json || parentOpts.json) {
|
|
673
|
+
printJson({
|
|
674
|
+
schema: PLAYBOOK_SCHEMA_VERSION,
|
|
675
|
+
playbook: pb,
|
|
676
|
+
});
|
|
677
|
+
return;
|
|
369
678
|
}
|
|
370
679
|
console.log(`# ${pb.title}\n`);
|
|
371
680
|
console.log(pb.content);
|
|
@@ -608,14 +917,16 @@ export function createProgram() {
|
|
|
608
917
|
const token = resolveToken(parentOpts);
|
|
609
918
|
const specFile = parentOpts.specFile ?? defaultSpecPath();
|
|
610
919
|
const registry = loadRegistry(specFile);
|
|
611
|
-
if (!registry)
|
|
612
|
-
|
|
613
|
-
process.exit(1);
|
|
614
|
-
}
|
|
920
|
+
if (!registry)
|
|
921
|
+
fail("spec_load_failed", "Failed to load OpenAPI spec.");
|
|
615
922
|
// Resolve operation
|
|
616
923
|
let operation = null;
|
|
617
924
|
if (opts.operationId) {
|
|
618
925
|
operation = registry.getOperationById(opts.operationId);
|
|
926
|
+
if (!operation) {
|
|
927
|
+
const hint = formatOperationHint(suggestOperationIds(registry, opts.operationId));
|
|
928
|
+
fail("operation_not_found", `Operation not found: ${opts.operationId}`, hint);
|
|
929
|
+
}
|
|
619
930
|
}
|
|
620
931
|
else if (opts.method && opts.path) {
|
|
621
932
|
operation = registry.getOperationByMethodPath(opts.method, opts.path);
|
|
@@ -630,14 +941,20 @@ export function createProgram() {
|
|
|
630
941
|
};
|
|
631
942
|
}
|
|
632
943
|
if (!operation) {
|
|
633
|
-
|
|
634
|
-
|
|
944
|
+
fail("operation_unresolved", "Unable to resolve operation.", "Provide --operation-id, or both --method and --path.");
|
|
945
|
+
}
|
|
946
|
+
let requestSpec;
|
|
947
|
+
try {
|
|
948
|
+
const pathParams = opts.pathParam ? parseKeyValuePairs(opts.pathParam) : {};
|
|
949
|
+
const queryParams = opts.queryParam ? parseKeyValuePairs(opts.queryParam) : {};
|
|
950
|
+
const headers = opts.header ? parseKeyValuePairs(opts.header) : {};
|
|
951
|
+
const body = opts.body ? loadJsonPayload(opts.body) : undefined;
|
|
952
|
+
requestSpec = buildRequestSpec(operation, baseUrl, pathParams, queryParams, headers, token, body);
|
|
953
|
+
}
|
|
954
|
+
catch (err) {
|
|
955
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
956
|
+
fail("invalid_request_options", message);
|
|
635
957
|
}
|
|
636
|
-
const pathParams = opts.pathParam ? parseKeyValuePairs(opts.pathParam) : {};
|
|
637
|
-
const queryParams = opts.queryParam ? parseKeyValuePairs(opts.queryParam) : {};
|
|
638
|
-
const headers = opts.header ? parseKeyValuePairs(opts.header) : {};
|
|
639
|
-
const body = opts.body ? loadJsonPayload(opts.body) : undefined;
|
|
640
|
-
const requestSpec = buildRequestSpec(operation, baseUrl, pathParams, queryParams, headers, token, body);
|
|
641
958
|
if (opts.dryRun) {
|
|
642
959
|
printJson({
|
|
643
960
|
dry_run: true,
|
|
@@ -672,8 +989,538 @@ export function createProgram() {
|
|
|
672
989
|
process.exitCode = 1;
|
|
673
990
|
}
|
|
674
991
|
catch (err) {
|
|
675
|
-
|
|
676
|
-
|
|
992
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
993
|
+
fail("request_failed", `Request failed: ${message}`);
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
// ═════════════════════════════════════════════════════════════════
|
|
997
|
+
// templates (spec-backed, local bootstrap cache)
|
|
998
|
+
// ═════════════════════════════════════════════════════════════════
|
|
999
|
+
const templatesCmd = program
|
|
1000
|
+
.command("templates")
|
|
1001
|
+
.description("Template bootstrap helpers for cold-start agents.");
|
|
1002
|
+
templatesCmd
|
|
1003
|
+
.command("sync")
|
|
1004
|
+
.description("Fetch workflow/agent/workforce templates and serialize them to a local cache.")
|
|
1005
|
+
.option("--dir <path>", "Output directory for template cache", ".agenticflow/templates")
|
|
1006
|
+
.option("--limit <n>", "Template limit per source", "100")
|
|
1007
|
+
.option("--offset <n>", "Template offset per source", "0")
|
|
1008
|
+
.option("--sort-order <order>", "Workflow sort order: asc or desc", "desc")
|
|
1009
|
+
.option("--workforce-id <id>", "Optional workforce ID filter for workforce template fetch")
|
|
1010
|
+
.option("--strict", "Exit non-zero if any template source fails")
|
|
1011
|
+
.option("--json", "JSON output")
|
|
1012
|
+
.action(async (opts) => {
|
|
1013
|
+
const parentOpts = program.opts();
|
|
1014
|
+
const baseUrl = DEFAULT_BASE_URL;
|
|
1015
|
+
const token = resolveToken(parentOpts);
|
|
1016
|
+
const limit = parseOptionalInteger(opts.limit, "--limit", 1) ?? 100;
|
|
1017
|
+
const offset = parseOptionalInteger(opts.offset, "--offset", 0) ?? 0;
|
|
1018
|
+
const sortOrder = String(opts.sortOrder ?? "desc").toLowerCase();
|
|
1019
|
+
if (sortOrder !== "asc" && sortOrder !== "desc") {
|
|
1020
|
+
fail("invalid_option_value", `Invalid value for --sort-order: ${opts.sortOrder}`, "Use either 'asc' or 'desc'.");
|
|
1021
|
+
}
|
|
1022
|
+
const specFile = parentOpts.specFile ?? defaultSpecPath();
|
|
1023
|
+
const registry = loadRegistry(specFile);
|
|
1024
|
+
if (!registry)
|
|
1025
|
+
fail("spec_load_failed", "Failed to load OpenAPI spec.");
|
|
1026
|
+
const sourceConfigs = [
|
|
1027
|
+
{
|
|
1028
|
+
kind: "workflow",
|
|
1029
|
+
operationId: "get_workflow_templates_v1_workflow_templates__get",
|
|
1030
|
+
query: {
|
|
1031
|
+
limit: String(limit),
|
|
1032
|
+
offset: String(offset),
|
|
1033
|
+
sort_order: sortOrder,
|
|
1034
|
+
},
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
kind: "agent",
|
|
1038
|
+
operationId: "get_public_v1_agent_templates_public_get",
|
|
1039
|
+
query: {
|
|
1040
|
+
limit: String(limit),
|
|
1041
|
+
offset: String(offset),
|
|
1042
|
+
},
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
kind: "workforce",
|
|
1046
|
+
operationId: "get_mas_templates_v1_mas_templates__get",
|
|
1047
|
+
query: {
|
|
1048
|
+
limit: String(limit),
|
|
1049
|
+
offset: String(offset),
|
|
1050
|
+
workforce_id: opts.workforceId,
|
|
1051
|
+
},
|
|
1052
|
+
},
|
|
1053
|
+
];
|
|
1054
|
+
const datasets = [];
|
|
1055
|
+
const issues = [];
|
|
1056
|
+
for (const source of sourceConfigs) {
|
|
1057
|
+
const operation = registry.getOperationById(source.operationId);
|
|
1058
|
+
if (!operation) {
|
|
1059
|
+
issues.push({
|
|
1060
|
+
kind: source.kind,
|
|
1061
|
+
code: "operation_not_found",
|
|
1062
|
+
message: `Operation not found in spec: ${source.operationId}`,
|
|
1063
|
+
hint: "Update the bundled OpenAPI spec or pass --spec-file with a newer spec.",
|
|
1064
|
+
});
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
const queryParams = {};
|
|
1068
|
+
for (const [key, value] of Object.entries(source.query)) {
|
|
1069
|
+
if (typeof value === "string" && value.length > 0)
|
|
1070
|
+
queryParams[key] = value;
|
|
1071
|
+
}
|
|
1072
|
+
let requestSpec;
|
|
1073
|
+
try {
|
|
1074
|
+
requestSpec = buildRequestSpec(operation, baseUrl, {}, queryParams, {}, token, undefined);
|
|
1075
|
+
}
|
|
1076
|
+
catch (err) {
|
|
1077
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1078
|
+
issues.push({
|
|
1079
|
+
kind: source.kind,
|
|
1080
|
+
code: "invalid_request_options",
|
|
1081
|
+
message,
|
|
1082
|
+
});
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
const query = new URLSearchParams(requestSpec.params).toString();
|
|
1086
|
+
const url = query ? `${requestSpec.url}?${query}` : requestSpec.url;
|
|
1087
|
+
try {
|
|
1088
|
+
const response = await fetch(url, {
|
|
1089
|
+
method: requestSpec.method,
|
|
1090
|
+
headers: requestSpec.headers,
|
|
1091
|
+
});
|
|
1092
|
+
const text = await response.text();
|
|
1093
|
+
let data = text;
|
|
1094
|
+
try {
|
|
1095
|
+
data = JSON.parse(text);
|
|
1096
|
+
}
|
|
1097
|
+
catch {
|
|
1098
|
+
// non-json responses are captured below
|
|
1099
|
+
}
|
|
1100
|
+
if (!response.ok) {
|
|
1101
|
+
const hint = source.kind === "workforce" && !opts.workforceId && response.status === 400
|
|
1102
|
+
? "Retry with --workforce-id <id> if your backend requires a source workforce filter."
|
|
1103
|
+
: undefined;
|
|
1104
|
+
issues.push({
|
|
1105
|
+
kind: source.kind,
|
|
1106
|
+
code: "template_source_failed",
|
|
1107
|
+
message: `Template fetch failed for ${source.kind} source (${response.status}).`,
|
|
1108
|
+
status: response.status,
|
|
1109
|
+
hint,
|
|
1110
|
+
});
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
if (!Array.isArray(data)) {
|
|
1114
|
+
issues.push({
|
|
1115
|
+
kind: source.kind,
|
|
1116
|
+
code: "unexpected_payload_shape",
|
|
1117
|
+
message: `Expected an array response for ${source.kind} templates.`,
|
|
1118
|
+
hint: "Validate response schema for this operation or inspect the endpoint manually with `agenticflow call`.",
|
|
1119
|
+
});
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
datasets.push({
|
|
1123
|
+
kind: source.kind,
|
|
1124
|
+
operationId: source.operationId,
|
|
1125
|
+
query: source.query,
|
|
1126
|
+
items: data,
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
catch (err) {
|
|
1130
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1131
|
+
issues.push({
|
|
1132
|
+
kind: source.kind,
|
|
1133
|
+
code: "request_failed",
|
|
1134
|
+
message: `Template fetch request failed for ${source.kind}: ${message}`,
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
const manifest = writeTemplateCache(opts.dir, datasets, issues);
|
|
1139
|
+
const fetchedCount = datasets.reduce((sum, dataset) => sum + dataset.items.length, 0);
|
|
1140
|
+
const payload = {
|
|
1141
|
+
schema: TEMPLATE_SYNC_SCHEMA_VERSION,
|
|
1142
|
+
ok: issues.length === 0,
|
|
1143
|
+
cache: manifest,
|
|
1144
|
+
fetched_templates: fetchedCount,
|
|
1145
|
+
source_count: sourceConfigs.length,
|
|
1146
|
+
};
|
|
1147
|
+
if (opts.json || parentOpts.json) {
|
|
1148
|
+
printJson(payload);
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
console.log("Template cache sync complete.");
|
|
1152
|
+
console.log(`- Cache dir: ${manifest.cache_dir}`);
|
|
1153
|
+
for (const dataset of manifest.datasets) {
|
|
1154
|
+
console.log(`- ${dataset.kind}: ${dataset.count} templates`);
|
|
1155
|
+
}
|
|
1156
|
+
if (manifest.issues.length > 0) {
|
|
1157
|
+
console.log(`- Issues: ${manifest.issues.length} (inspect ${manifest.cache_dir}/manifest.json)`);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
if (opts.strict && issues.length > 0) {
|
|
1161
|
+
process.exitCode = 1;
|
|
1162
|
+
}
|
|
1163
|
+
if (datasets.length === 0) {
|
|
1164
|
+
process.exitCode = 1;
|
|
1165
|
+
}
|
|
1166
|
+
});
|
|
1167
|
+
const templatesDuplicateCmd = templatesCmd
|
|
1168
|
+
.command("duplicate")
|
|
1169
|
+
.description("Create new resources from template samples.");
|
|
1170
|
+
templatesDuplicateCmd
|
|
1171
|
+
.command("workflow")
|
|
1172
|
+
.description("Duplicate a workflow template into a new workflow.")
|
|
1173
|
+
.option("--template-id <id>", "Workflow template ID")
|
|
1174
|
+
.option("--template-file <path>", "Local workflow template JSON file")
|
|
1175
|
+
.option("--cache-dir <path>", "Local template cache dir (from `templates sync`)")
|
|
1176
|
+
.option("--workspace-id <id>", "Workspace ID override")
|
|
1177
|
+
.option("--project-id <id>", "Project ID override")
|
|
1178
|
+
.option("--name-suffix <suffix>", "Suffix for duplicated workflow name", " [Copy]")
|
|
1179
|
+
.option("--dry-run", "Build and print create payload without creating workflow")
|
|
1180
|
+
.option("--json", "JSON output")
|
|
1181
|
+
.action(async (opts) => {
|
|
1182
|
+
const parentOpts = program.opts();
|
|
1183
|
+
const client = buildClient(parentOpts);
|
|
1184
|
+
const templateId = opts.templateId;
|
|
1185
|
+
const templateFile = opts.templateFile;
|
|
1186
|
+
const explicitCacheDir = opts.cacheDir;
|
|
1187
|
+
if ((templateId == null || templateId.trim() === "") && !templateFile) {
|
|
1188
|
+
fail("missing_required_option", "Provide --template-id or --template-file.", "Use `templates sync` to fetch templates locally, then pass --template-file.");
|
|
1189
|
+
}
|
|
1190
|
+
if (templateId && templateFile) {
|
|
1191
|
+
fail("invalid_request_options", "Use either --template-id or --template-file, not both.");
|
|
1192
|
+
}
|
|
1193
|
+
if (explicitCacheDir && !existsSync(resolve(explicitCacheDir))) {
|
|
1194
|
+
fail("template_cache_not_found", `Template cache directory not found: ${resolve(explicitCacheDir)}`, "Run `agenticflow templates sync --dir <path>` first or use an existing cache dir.");
|
|
1195
|
+
}
|
|
1196
|
+
const inferredCacheDir = templateFile ? inferTemplateCacheDirFromTemplateFile(templateFile) : undefined;
|
|
1197
|
+
const cacheDir = explicitCacheDir ?? inferredCacheDir;
|
|
1198
|
+
const localWorkflowCache = cacheDir ? loadLocalWorkflowTemplateCache(cacheDir) : null;
|
|
1199
|
+
const projectId = resolveProjectId(opts.projectId);
|
|
1200
|
+
if (!projectId) {
|
|
1201
|
+
fail("missing_project_id", "Project ID is required to duplicate templates.", "Set AGENTICFLOW_PROJECT_ID or pass --project-id.");
|
|
1202
|
+
}
|
|
1203
|
+
let templateData;
|
|
1204
|
+
let templateSource = "api";
|
|
1205
|
+
if (templateFile) {
|
|
1206
|
+
templateData = loadJsonPayload(`@${templateFile}`);
|
|
1207
|
+
templateSource = "file";
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
const localTemplate = localWorkflowCache?.byId.get(templateId);
|
|
1211
|
+
if (localTemplate !== undefined) {
|
|
1212
|
+
templateData = localTemplate;
|
|
1213
|
+
templateSource = "cache";
|
|
1214
|
+
}
|
|
1215
|
+
else {
|
|
1216
|
+
try {
|
|
1217
|
+
templateData = (await client.sdk.get(`/v1/workflow_templates/${templateId}`)).data;
|
|
1218
|
+
templateSource = "api";
|
|
1219
|
+
}
|
|
1220
|
+
catch (err) {
|
|
1221
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1222
|
+
fail("request_failed", message);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
let createPayload;
|
|
1227
|
+
try {
|
|
1228
|
+
createPayload = buildWorkflowCreatePayloadFromTemplate(templateData, projectId, opts.nameSuffix);
|
|
1229
|
+
}
|
|
1230
|
+
catch (err) {
|
|
1231
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1232
|
+
fail("template_payload_invalid", message);
|
|
1233
|
+
}
|
|
1234
|
+
ensureLocalValidation("workflow.create", validateWorkflowCreatePayload(createPayload));
|
|
1235
|
+
if (opts.dryRun) {
|
|
1236
|
+
const payload = {
|
|
1237
|
+
schema: TEMPLATE_DUPLICATE_SCHEMA_VERSION,
|
|
1238
|
+
kind: "workflow",
|
|
1239
|
+
dry_run: true,
|
|
1240
|
+
template_source: templateSource,
|
|
1241
|
+
cache_dir: localWorkflowCache?.cacheDir ?? null,
|
|
1242
|
+
cache_warnings: localWorkflowCache?.warnings ?? [],
|
|
1243
|
+
create_payload: createPayload,
|
|
1244
|
+
};
|
|
1245
|
+
if (opts.json || parentOpts.json) {
|
|
1246
|
+
printJson(payload);
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
console.log("Workflow template duplication dry-run payload:");
|
|
1250
|
+
printJson(payload);
|
|
1251
|
+
}
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
const workspaceId = resolveWorkspaceId(opts.workspaceId);
|
|
1255
|
+
if (!workspaceId) {
|
|
1256
|
+
fail("missing_workspace_id", "Workspace ID is required to create duplicated workflows.", "Set AGENTICFLOW_WORKSPACE_ID or pass --workspace-id.");
|
|
1257
|
+
}
|
|
1258
|
+
try {
|
|
1259
|
+
const created = await client.workflows.create(createPayload, workspaceId);
|
|
1260
|
+
const payload = {
|
|
1261
|
+
schema: TEMPLATE_DUPLICATE_SCHEMA_VERSION,
|
|
1262
|
+
kind: "workflow",
|
|
1263
|
+
dry_run: false,
|
|
1264
|
+
template_source: templateSource,
|
|
1265
|
+
cache_dir: localWorkflowCache?.cacheDir ?? null,
|
|
1266
|
+
cache_warnings: localWorkflowCache?.warnings ?? [],
|
|
1267
|
+
created,
|
|
1268
|
+
};
|
|
1269
|
+
if (opts.json || parentOpts.json) {
|
|
1270
|
+
printJson(payload);
|
|
1271
|
+
}
|
|
1272
|
+
else {
|
|
1273
|
+
console.log("Workflow duplicated from template successfully.");
|
|
1274
|
+
printJson(payload);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
catch (err) {
|
|
1278
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1279
|
+
fail("request_failed", message);
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
templatesDuplicateCmd
|
|
1283
|
+
.command("agent")
|
|
1284
|
+
.description("Duplicate an agent template and its workflow-template tools.")
|
|
1285
|
+
.option("--template-id <id>", "Agent template ID")
|
|
1286
|
+
.option("--template-file <path>", "Local agent template JSON file")
|
|
1287
|
+
.option("--cache-dir <path>", "Local template cache dir (from `templates sync`)")
|
|
1288
|
+
.option("--workspace-id <id>", "Workspace ID override")
|
|
1289
|
+
.option("--project-id <id>", "Project ID override")
|
|
1290
|
+
.option("--name-suffix <suffix>", "Suffix for duplicated agent name", " [Copy]")
|
|
1291
|
+
.option("--workflow-name-suffix <suffix>", "Suffix for duplicated tool workflows", " [Tool Copy]")
|
|
1292
|
+
.option("--skip-missing-tools", "Skip tools whose workflow templates cannot be duplicated")
|
|
1293
|
+
.option("--dry-run", "Build and print create payloads without creating resources")
|
|
1294
|
+
.option("--json", "JSON output")
|
|
1295
|
+
.action(async (opts) => {
|
|
1296
|
+
const parentOpts = program.opts();
|
|
1297
|
+
const client = buildClient(parentOpts);
|
|
1298
|
+
const templateId = opts.templateId;
|
|
1299
|
+
const templateFile = opts.templateFile;
|
|
1300
|
+
const explicitCacheDir = opts.cacheDir;
|
|
1301
|
+
if ((templateId == null || templateId.trim() === "") && !templateFile) {
|
|
1302
|
+
fail("missing_required_option", "Provide --template-id or --template-file.", "Use `templates sync` to fetch templates locally, then pass --template-file.");
|
|
1303
|
+
}
|
|
1304
|
+
if (templateId && templateFile) {
|
|
1305
|
+
fail("invalid_request_options", "Use either --template-id or --template-file, not both.");
|
|
1306
|
+
}
|
|
1307
|
+
if (explicitCacheDir && !existsSync(resolve(explicitCacheDir))) {
|
|
1308
|
+
fail("template_cache_not_found", `Template cache directory not found: ${resolve(explicitCacheDir)}`, "Run `agenticflow templates sync --dir <path>` first or use an existing cache dir.");
|
|
1309
|
+
}
|
|
1310
|
+
const inferredCacheDir = templateFile ? inferTemplateCacheDirFromTemplateFile(templateFile) : undefined;
|
|
1311
|
+
const cacheDir = explicitCacheDir ?? inferredCacheDir;
|
|
1312
|
+
const localWorkflowCache = cacheDir ? loadLocalWorkflowTemplateCache(cacheDir) : null;
|
|
1313
|
+
const projectId = resolveProjectId(opts.projectId);
|
|
1314
|
+
if (!projectId) {
|
|
1315
|
+
fail("missing_project_id", "Project ID is required to duplicate templates.", "Set AGENTICFLOW_PROJECT_ID or pass --project-id.");
|
|
1316
|
+
}
|
|
1317
|
+
const workspaceId = resolveWorkspaceId(opts.workspaceId);
|
|
1318
|
+
if (!workspaceId && !opts.dryRun) {
|
|
1319
|
+
fail("missing_workspace_id", "Workspace ID is required to duplicate agent template tools.", "Set AGENTICFLOW_WORKSPACE_ID or pass --workspace-id.");
|
|
1320
|
+
}
|
|
1321
|
+
let agentTemplate;
|
|
1322
|
+
if (templateFile) {
|
|
1323
|
+
agentTemplate = loadJsonPayload(`@${templateFile}`);
|
|
1324
|
+
}
|
|
1325
|
+
else {
|
|
1326
|
+
try {
|
|
1327
|
+
agentTemplate = (await client.sdk.get(`/v1/agent-templates/${templateId}`)).data;
|
|
1328
|
+
}
|
|
1329
|
+
catch (err) {
|
|
1330
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1331
|
+
fail("request_failed", message);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
const toolRefs = extractAgentTemplateWorkflowReferences(agentTemplate);
|
|
1335
|
+
const duplicatedTools = [];
|
|
1336
|
+
const createdWorkflows = [];
|
|
1337
|
+
const skippedTools = [];
|
|
1338
|
+
const toolTemplateResolution = {
|
|
1339
|
+
from_cache: 0,
|
|
1340
|
+
from_api: 0,
|
|
1341
|
+
};
|
|
1342
|
+
for (let i = 0; i < toolRefs.length; i++) {
|
|
1343
|
+
const ref = toolRefs[i];
|
|
1344
|
+
let workflowTemplate;
|
|
1345
|
+
const localTemplate = localWorkflowCache?.byId.get(ref.workflowTemplateId);
|
|
1346
|
+
if (localTemplate !== undefined) {
|
|
1347
|
+
workflowTemplate = localTemplate;
|
|
1348
|
+
toolTemplateResolution.from_cache += 1;
|
|
1349
|
+
}
|
|
1350
|
+
else {
|
|
1351
|
+
try {
|
|
1352
|
+
workflowTemplate = (await client.sdk.get(`/v1/workflow_templates/${ref.workflowTemplateId}`)).data;
|
|
1353
|
+
toolTemplateResolution.from_api += 1;
|
|
1354
|
+
}
|
|
1355
|
+
catch (err) {
|
|
1356
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1357
|
+
if (opts.skipMissingTools) {
|
|
1358
|
+
const reason = localWorkflowCache
|
|
1359
|
+
? `Not found in local cache and API fetch failed: ${message}`
|
|
1360
|
+
: message;
|
|
1361
|
+
skippedTools.push({
|
|
1362
|
+
workflow_template_id: ref.workflowTemplateId,
|
|
1363
|
+
reason,
|
|
1364
|
+
});
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
fail("request_failed", `Unable to fetch tool workflow template ${ref.workflowTemplateId}: ${message}`, "Use --skip-missing-tools to proceed without unavailable tool templates.");
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
let workflowCreatePayload;
|
|
1371
|
+
try {
|
|
1372
|
+
workflowCreatePayload = buildWorkflowCreatePayloadFromTemplate(workflowTemplate, projectId, opts.workflowNameSuffix);
|
|
1373
|
+
}
|
|
1374
|
+
catch (err) {
|
|
1375
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1376
|
+
if (opts.skipMissingTools) {
|
|
1377
|
+
skippedTools.push({
|
|
1378
|
+
workflow_template_id: ref.workflowTemplateId,
|
|
1379
|
+
reason: message,
|
|
1380
|
+
});
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
fail("template_payload_invalid", message);
|
|
1384
|
+
}
|
|
1385
|
+
ensureLocalValidation("workflow.create", validateWorkflowCreatePayload(workflowCreatePayload));
|
|
1386
|
+
if (opts.dryRun) {
|
|
1387
|
+
const placeholderWorkflowId = `__tool_workflow_${i + 1}__`;
|
|
1388
|
+
duplicatedTools.push({
|
|
1389
|
+
workflowTemplateId: ref.workflowTemplateId,
|
|
1390
|
+
workflowId: placeholderWorkflowId,
|
|
1391
|
+
runBehavior: ref.runBehavior,
|
|
1392
|
+
description: ref.description,
|
|
1393
|
+
timeout: ref.timeout,
|
|
1394
|
+
inputConfig: ref.inputConfig,
|
|
1395
|
+
});
|
|
1396
|
+
createdWorkflows.push({
|
|
1397
|
+
workflow_template_id: ref.workflowTemplateId,
|
|
1398
|
+
dry_run_workflow_id: placeholderWorkflowId,
|
|
1399
|
+
create_payload: workflowCreatePayload,
|
|
1400
|
+
});
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
try {
|
|
1404
|
+
const created = await client.workflows.create(workflowCreatePayload, workspaceId);
|
|
1405
|
+
const createdRecord = (created && typeof created === "object")
|
|
1406
|
+
? created
|
|
1407
|
+
: {};
|
|
1408
|
+
const createdId = typeof createdRecord["id"] === "string" ? createdRecord["id"] : null;
|
|
1409
|
+
if (!createdId) {
|
|
1410
|
+
fail("template_duplicate_failed", `Duplicated tool workflow from ${ref.workflowTemplateId} has no id in response.`);
|
|
1411
|
+
}
|
|
1412
|
+
duplicatedTools.push({
|
|
1413
|
+
workflowTemplateId: ref.workflowTemplateId,
|
|
1414
|
+
workflowId: createdId,
|
|
1415
|
+
runBehavior: ref.runBehavior,
|
|
1416
|
+
description: ref.description,
|
|
1417
|
+
timeout: ref.timeout,
|
|
1418
|
+
inputConfig: ref.inputConfig,
|
|
1419
|
+
});
|
|
1420
|
+
createdWorkflows.push({
|
|
1421
|
+
workflow_template_id: ref.workflowTemplateId,
|
|
1422
|
+
workflow: createdRecord,
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
catch (err) {
|
|
1426
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1427
|
+
if (opts.skipMissingTools) {
|
|
1428
|
+
skippedTools.push({
|
|
1429
|
+
workflow_template_id: ref.workflowTemplateId,
|
|
1430
|
+
reason: message,
|
|
1431
|
+
});
|
|
1432
|
+
continue;
|
|
1433
|
+
}
|
|
1434
|
+
fail("request_failed", `Failed to create tool workflow for template ${ref.workflowTemplateId}: ${message}`, "Use --skip-missing-tools to proceed without unavailable tool templates.");
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
let agentCreatePayload;
|
|
1438
|
+
try {
|
|
1439
|
+
agentCreatePayload = buildAgentCreatePayloadFromTemplate(agentTemplate, projectId, duplicatedTools, opts.nameSuffix);
|
|
1440
|
+
}
|
|
1441
|
+
catch (err) {
|
|
1442
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1443
|
+
fail("template_payload_invalid", message);
|
|
1444
|
+
}
|
|
1445
|
+
ensureLocalValidation("agent.create", validateAgentCreatePayload(agentCreatePayload));
|
|
1446
|
+
if (opts.dryRun) {
|
|
1447
|
+
const payload = {
|
|
1448
|
+
schema: TEMPLATE_DUPLICATE_SCHEMA_VERSION,
|
|
1449
|
+
kind: "agent",
|
|
1450
|
+
dry_run: true,
|
|
1451
|
+
cache_dir: localWorkflowCache?.cacheDir ?? null,
|
|
1452
|
+
cache_warnings: localWorkflowCache?.warnings ?? [],
|
|
1453
|
+
tool_template_resolution: toolTemplateResolution,
|
|
1454
|
+
created_tool_workflows: createdWorkflows,
|
|
1455
|
+
skipped_tools: skippedTools,
|
|
1456
|
+
create_payload: agentCreatePayload,
|
|
1457
|
+
};
|
|
1458
|
+
if (opts.json || parentOpts.json) {
|
|
1459
|
+
printJson(payload);
|
|
1460
|
+
}
|
|
1461
|
+
else {
|
|
1462
|
+
console.log("Agent template duplication dry-run payload:");
|
|
1463
|
+
printJson(payload);
|
|
1464
|
+
}
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
try {
|
|
1468
|
+
const createdAgent = await client.agents.create(agentCreatePayload);
|
|
1469
|
+
const payload = {
|
|
1470
|
+
schema: TEMPLATE_DUPLICATE_SCHEMA_VERSION,
|
|
1471
|
+
kind: "agent",
|
|
1472
|
+
dry_run: false,
|
|
1473
|
+
cache_dir: localWorkflowCache?.cacheDir ?? null,
|
|
1474
|
+
cache_warnings: localWorkflowCache?.warnings ?? [],
|
|
1475
|
+
tool_template_resolution: toolTemplateResolution,
|
|
1476
|
+
created_tool_workflows: createdWorkflows,
|
|
1477
|
+
skipped_tools: skippedTools,
|
|
1478
|
+
created_agent: createdAgent,
|
|
1479
|
+
};
|
|
1480
|
+
if (opts.json || parentOpts.json) {
|
|
1481
|
+
printJson(payload);
|
|
1482
|
+
}
|
|
1483
|
+
else {
|
|
1484
|
+
console.log("Agent duplicated from template successfully.");
|
|
1485
|
+
printJson(payload);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
catch (err) {
|
|
1489
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1490
|
+
fail("request_failed", message);
|
|
1491
|
+
}
|
|
1492
|
+
});
|
|
1493
|
+
templatesCmd
|
|
1494
|
+
.command("index")
|
|
1495
|
+
.description("Inspect a local template cache manifest.")
|
|
1496
|
+
.option("--dir <path>", "Template cache directory", ".agenticflow/templates")
|
|
1497
|
+
.option("--json", "JSON output")
|
|
1498
|
+
.action((opts) => {
|
|
1499
|
+
const parentOpts = program.opts();
|
|
1500
|
+
let manifest;
|
|
1501
|
+
try {
|
|
1502
|
+
manifest = readTemplateCacheManifest(opts.dir);
|
|
1503
|
+
}
|
|
1504
|
+
catch (err) {
|
|
1505
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1506
|
+
fail("template_cache_not_found", `Unable to read template cache manifest at ${resolve(opts.dir)}.`, "Run `agenticflow templates sync --json` first.", { error: message });
|
|
1507
|
+
}
|
|
1508
|
+
const payload = {
|
|
1509
|
+
schema: TEMPLATE_INDEX_SCHEMA_VERSION,
|
|
1510
|
+
cache: manifest,
|
|
1511
|
+
};
|
|
1512
|
+
if (opts.json || parentOpts.json) {
|
|
1513
|
+
printJson(payload);
|
|
1514
|
+
}
|
|
1515
|
+
else {
|
|
1516
|
+
console.log(`Template cache: ${manifest.cache_dir}`);
|
|
1517
|
+
console.log(`Fetched at: ${manifest.fetched_at}`);
|
|
1518
|
+
for (const dataset of manifest.datasets) {
|
|
1519
|
+
console.log(`- ${dataset.kind}: ${dataset.count} templates`);
|
|
1520
|
+
}
|
|
1521
|
+
if (manifest.issues.length > 0) {
|
|
1522
|
+
console.log(`Issues: ${manifest.issues.length} (see manifest.json for details)`);
|
|
1523
|
+
}
|
|
677
1524
|
}
|
|
678
1525
|
});
|
|
679
1526
|
// ═════════════════════════════════════════════════════════════════
|
|
@@ -696,8 +1543,8 @@ export function createProgram() {
|
|
|
696
1543
|
workspaceId: opts.workspaceId,
|
|
697
1544
|
projectId: opts.projectId,
|
|
698
1545
|
searchQuery: opts.search,
|
|
699
|
-
limit: opts.limit
|
|
700
|
-
offset: opts.offset
|
|
1546
|
+
limit: parseOptionalInteger(opts.limit, "--limit", 1),
|
|
1547
|
+
offset: parseOptionalInteger(opts.offset, "--offset", 0),
|
|
701
1548
|
}));
|
|
702
1549
|
});
|
|
703
1550
|
workflowCmd
|
|
@@ -722,6 +1569,7 @@ export function createProgram() {
|
|
|
722
1569
|
.action(async (opts) => {
|
|
723
1570
|
const client = buildClient(program.opts());
|
|
724
1571
|
const body = loadJsonPayload(opts.body);
|
|
1572
|
+
ensureLocalValidation("workflow.create", validateWorkflowCreatePayload(body));
|
|
725
1573
|
await run(() => client.workflows.create(body, opts.workspaceId));
|
|
726
1574
|
});
|
|
727
1575
|
workflowCmd
|
|
@@ -733,6 +1581,7 @@ export function createProgram() {
|
|
|
733
1581
|
.action(async (opts) => {
|
|
734
1582
|
const client = buildClient(program.opts());
|
|
735
1583
|
const body = loadJsonPayload(opts.body);
|
|
1584
|
+
ensureLocalValidation("workflow.update", validateWorkflowUpdatePayload(body));
|
|
736
1585
|
await run(() => client.workflows.update(opts.workflowId, body, opts.workspaceId));
|
|
737
1586
|
});
|
|
738
1587
|
workflowCmd
|
|
@@ -756,6 +1605,7 @@ export function createProgram() {
|
|
|
756
1605
|
const body = { workflow_id: opts.workflowId };
|
|
757
1606
|
if (opts.input)
|
|
758
1607
|
body["input"] = loadJsonPayload(opts.input);
|
|
1608
|
+
ensureLocalValidation("workflow.run", validateWorkflowRunPayload(body));
|
|
759
1609
|
const executeRun = () => token
|
|
760
1610
|
? client.workflows.run(body)
|
|
761
1611
|
: client.workflows.runAnonymous(body);
|
|
@@ -768,8 +1618,7 @@ export function createProgram() {
|
|
|
768
1618
|
// Detect "Connection X not found" pattern
|
|
769
1619
|
const connMatch = errMsg.match(/[Cc]onnection\s+([0-9a-f-]{36})\s+not\s+found/);
|
|
770
1620
|
if (!connMatch) {
|
|
771
|
-
|
|
772
|
-
process.exit(1);
|
|
1621
|
+
fail("request_failed", errMsg);
|
|
773
1622
|
}
|
|
774
1623
|
const missingConnId = connMatch[1];
|
|
775
1624
|
console.error(`\n⚠ Connection ${missingConnId} not found.`);
|
|
@@ -914,8 +1763,8 @@ export function createProgram() {
|
|
|
914
1763
|
const client = buildClient(program.opts());
|
|
915
1764
|
await run(() => client.workflows.listRuns(opts.workflowId, {
|
|
916
1765
|
workspaceId: opts.workspaceId,
|
|
917
|
-
limit: opts.limit
|
|
918
|
-
offset: opts.offset
|
|
1766
|
+
limit: parseOptionalInteger(opts.limit, "--limit", 1),
|
|
1767
|
+
offset: parseOptionalInteger(opts.offset, "--offset", 0),
|
|
919
1768
|
sortOrder: opts.sortOrder,
|
|
920
1769
|
}));
|
|
921
1770
|
});
|
|
@@ -923,9 +1772,27 @@ export function createProgram() {
|
|
|
923
1772
|
.command("validate")
|
|
924
1773
|
.description("Validate a workflow payload.")
|
|
925
1774
|
.requiredOption("--body <body>", "JSON body (inline or @file)")
|
|
1775
|
+
.option("--local-only", "Validate locally only (skip API validate endpoint)")
|
|
926
1776
|
.action(async (opts) => {
|
|
1777
|
+
const parentOpts = program.opts();
|
|
927
1778
|
const client = buildClient(program.opts());
|
|
928
1779
|
const body = loadJsonPayload(opts.body);
|
|
1780
|
+
ensureLocalValidation("workflow.create", validateWorkflowCreatePayload(body));
|
|
1781
|
+
if (opts.localOnly) {
|
|
1782
|
+
const payload = {
|
|
1783
|
+
schema: LOCAL_VALIDATION_SCHEMA_VERSION,
|
|
1784
|
+
target: "workflow.create",
|
|
1785
|
+
valid: true,
|
|
1786
|
+
issues: [],
|
|
1787
|
+
};
|
|
1788
|
+
if (opts.json || parentOpts.json) {
|
|
1789
|
+
printJson(payload);
|
|
1790
|
+
}
|
|
1791
|
+
else {
|
|
1792
|
+
console.log("Local validation passed for workflow.create payload.");
|
|
1793
|
+
}
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
929
1796
|
await run(() => client.workflows.validate(body));
|
|
930
1797
|
});
|
|
931
1798
|
workflowCmd
|
|
@@ -937,8 +1804,8 @@ export function createProgram() {
|
|
|
937
1804
|
.action(async (opts) => {
|
|
938
1805
|
const client = buildClient(program.opts());
|
|
939
1806
|
await run(() => client.workflows.runHistory(opts.workflowId, {
|
|
940
|
-
limit: opts.limit
|
|
941
|
-
offset: opts.offset
|
|
1807
|
+
limit: parseOptionalInteger(opts.limit, "--limit", 1),
|
|
1808
|
+
offset: parseOptionalInteger(opts.offset, "--offset", 0),
|
|
942
1809
|
}));
|
|
943
1810
|
});
|
|
944
1811
|
workflowCmd
|
|
@@ -975,8 +1842,8 @@ export function createProgram() {
|
|
|
975
1842
|
await run(() => client.agents.list({
|
|
976
1843
|
projectId: opts.projectId,
|
|
977
1844
|
searchQuery: opts.search,
|
|
978
|
-
limit: opts.limit
|
|
979
|
-
offset: opts.offset
|
|
1845
|
+
limit: parseOptionalInteger(opts.limit, "--limit", 1),
|
|
1846
|
+
offset: parseOptionalInteger(opts.offset, "--offset", 0),
|
|
980
1847
|
}));
|
|
981
1848
|
});
|
|
982
1849
|
agentCmd
|
|
@@ -1000,6 +1867,7 @@ export function createProgram() {
|
|
|
1000
1867
|
.action(async (opts) => {
|
|
1001
1868
|
const client = buildClient(program.opts());
|
|
1002
1869
|
const body = loadJsonPayload(opts.body);
|
|
1870
|
+
ensureLocalValidation("agent.create", validateAgentCreatePayload(body));
|
|
1003
1871
|
await run(() => client.agents.create(body));
|
|
1004
1872
|
});
|
|
1005
1873
|
agentCmd
|
|
@@ -1010,6 +1878,7 @@ export function createProgram() {
|
|
|
1010
1878
|
.action(async (opts) => {
|
|
1011
1879
|
const client = buildClient(program.opts());
|
|
1012
1880
|
const body = loadJsonPayload(opts.body);
|
|
1881
|
+
ensureLocalValidation("agent.update", validateAgentUpdatePayload(body));
|
|
1013
1882
|
await run(() => client.agents.update(opts.agentId, body));
|
|
1014
1883
|
});
|
|
1015
1884
|
agentCmd
|
|
@@ -1029,6 +1898,7 @@ export function createProgram() {
|
|
|
1029
1898
|
const client = buildClient(program.opts());
|
|
1030
1899
|
const token = resolveToken(program.opts());
|
|
1031
1900
|
const body = loadJsonPayload(opts.body);
|
|
1901
|
+
ensureLocalValidation("agent.stream", validateAgentStreamPayload(body));
|
|
1032
1902
|
if (token) {
|
|
1033
1903
|
await run(() => client.agents.stream(opts.agentId, body));
|
|
1034
1904
|
}
|
|
@@ -1058,10 +1928,12 @@ export function createProgram() {
|
|
|
1058
1928
|
.action(async (opts) => {
|
|
1059
1929
|
const client = buildClient(program.opts());
|
|
1060
1930
|
const queryParams = {};
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
if (
|
|
1064
|
-
queryParams["
|
|
1931
|
+
const limit = parseOptionalInteger(opts.limit, "--limit", 1);
|
|
1932
|
+
const offset = parseOptionalInteger(opts.offset, "--offset", 0);
|
|
1933
|
+
if (limit != null)
|
|
1934
|
+
queryParams["limit"] = limit;
|
|
1935
|
+
if (offset != null)
|
|
1936
|
+
queryParams["offset"] = offset;
|
|
1065
1937
|
await run(() => client.nodeTypes.list(queryParams));
|
|
1066
1938
|
});
|
|
1067
1939
|
nodeTypesCmd
|
|
@@ -1118,8 +1990,8 @@ export function createProgram() {
|
|
|
1118
1990
|
await run(() => client.connections.list({
|
|
1119
1991
|
workspaceId: opts.workspaceId,
|
|
1120
1992
|
projectId: opts.projectId,
|
|
1121
|
-
limit: opts.limit
|
|
1122
|
-
offset: opts.offset
|
|
1993
|
+
limit: parseOptionalInteger(opts.limit, "--limit", 1),
|
|
1994
|
+
offset: parseOptionalInteger(opts.offset, "--offset", 0),
|
|
1123
1995
|
}));
|
|
1124
1996
|
});
|
|
1125
1997
|
connectionsCmd
|
|
@@ -1179,6 +2051,26 @@ export function createProgram() {
|
|
|
1179
2051
|
}
|
|
1180
2052
|
export async function runCli(argv) {
|
|
1181
2053
|
const program = createProgram();
|
|
1182
|
-
|
|
2054
|
+
try {
|
|
2055
|
+
await program.parseAsync(argv ?? process.argv);
|
|
2056
|
+
}
|
|
2057
|
+
catch (err) {
|
|
2058
|
+
const code = typeof err === "object" && err != null && "code" in err
|
|
2059
|
+
? String(err.code)
|
|
2060
|
+
: "";
|
|
2061
|
+
if (code.startsWith("commander.")) {
|
|
2062
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2063
|
+
if (code === "commander.helpDisplayed" || code === "commander.version") {
|
|
2064
|
+
process.exit(0);
|
|
2065
|
+
}
|
|
2066
|
+
if (isJsonFlagEnabled()) {
|
|
2067
|
+
fail("cli_parse_error", message);
|
|
2068
|
+
}
|
|
2069
|
+
process.exit(typeof err === "object" && err != null && "exitCode" in err
|
|
2070
|
+
? Number(err.exitCode) || 1
|
|
2071
|
+
: 1);
|
|
2072
|
+
}
|
|
2073
|
+
throw err;
|
|
2074
|
+
}
|
|
1183
2075
|
}
|
|
1184
2076
|
//# sourceMappingURL=main.js.map
|