voyageai-cli 1.28.0 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/commands/app.js +15 -0
- package/src/commands/playground.js +638 -7
- package/src/commands/workflow.js +417 -14
- package/src/lib/explanations.js +88 -0
- package/src/lib/npm-utils.js +265 -0
- package/src/lib/workflow-registry.js +416 -0
- package/src/lib/workflow-scaffold.js +319 -0
- package/src/lib/workflow.js +433 -7
- package/src/playground/announcements.md +71 -0
- package/src/playground/icons/V.png +0 -0
- package/src/playground/index.html +2204 -94
- package/src/workflows/consistency-check.json +4 -0
- package/src/workflows/cost-analysis.json +4 -0
- package/src/workflows/enrich-and-ingest.json +56 -0
- package/src/workflows/intelligent-ingest.json +66 -0
- package/src/workflows/kb-health-report.json +45 -0
- package/src/workflows/multi-collection-search.json +4 -0
- package/src/workflows/research-and-summarize.json +4 -0
- package/src/workflows/search-with-fallback.json +66 -0
- package/src/workflows/smart-ingest.json +4 -0
package/src/commands/workflow.js
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const path = require('path');
|
|
3
4
|
const pc = require('picocolors');
|
|
4
5
|
const ui = require('../lib/ui');
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Try to get the git user name for default author.
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
function getGitAuthor() {
|
|
12
|
+
try {
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
return execSync('git config user.name', { encoding: 'utf8' }).trim();
|
|
15
|
+
} catch {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
6
20
|
/**
|
|
7
21
|
* Parse repeatable --input key=value options into an object.
|
|
8
22
|
* Used as Commander's option reducer.
|
|
@@ -84,11 +98,31 @@ function registerWorkflow(program) {
|
|
|
84
98
|
.option('--verbose', 'Show step details', false)
|
|
85
99
|
.option('--no-interactive', 'Disable interactive input prompting')
|
|
86
100
|
.action(async (file, opts) => {
|
|
87
|
-
const {
|
|
101
|
+
const { executeWorkflow, buildExecutionPlan, validateWorkflow } = require('../lib/workflow');
|
|
102
|
+
const { resolveWorkflow } = require('../lib/workflow-registry');
|
|
88
103
|
|
|
89
104
|
let definition;
|
|
90
105
|
try {
|
|
91
|
-
|
|
106
|
+
const resolved = resolveWorkflow(file);
|
|
107
|
+
definition = resolved.definition;
|
|
108
|
+
|
|
109
|
+
// Show workflow source notice
|
|
110
|
+
if ((resolved.source === 'community' || resolved.source === 'official') && !opts.quiet) {
|
|
111
|
+
const pkg = resolved.metadata?.package;
|
|
112
|
+
const author = typeof pkg?.author === 'string' ? pkg.author : pkg?.author?.name || 'unknown';
|
|
113
|
+
const tools = (pkg?.vai?.tools || []).join(', ');
|
|
114
|
+
const isOfficial = resolved.source === 'official';
|
|
115
|
+
const label = isOfficial ? 'official catalog workflow' : 'community workflow';
|
|
116
|
+
console.error(`${pc.dim('ℹ')} Running ${label}: ${pc.cyan(pkg?.name || file)} ${pc.dim(`v${pkg?.version || '?'}`)}`);
|
|
117
|
+
console.error(` ${pc.dim(`by ${author}`)}${tools ? pc.dim(` | Tools: ${tools}`) : ''}`);
|
|
118
|
+
if (!isOfficial) {
|
|
119
|
+
console.error(` ${pc.dim('This is a community-contributed workflow, not maintained by the vai project.')}`);
|
|
120
|
+
}
|
|
121
|
+
console.error();
|
|
122
|
+
for (const w of resolved.metadata?.warnings || []) {
|
|
123
|
+
console.error(` ${pc.yellow('⚠')} ${w}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
92
126
|
} catch (err) {
|
|
93
127
|
console.error(ui.error(err.message));
|
|
94
128
|
process.exit(1);
|
|
@@ -265,27 +299,396 @@ function registerWorkflow(program) {
|
|
|
265
299
|
// ── workflow list ──
|
|
266
300
|
wfCmd
|
|
267
301
|
.command('list')
|
|
268
|
-
.description('List built-in
|
|
269
|
-
.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
302
|
+
.description('List available workflows (built-in + official + community)')
|
|
303
|
+
.option('--built-in', 'Show only built-in workflows', false)
|
|
304
|
+
.option('--official', 'Show only official @vaicli workflows', false)
|
|
305
|
+
.option('--community', 'Show only community workflows', false)
|
|
306
|
+
.option('--category <name>', 'Filter by category')
|
|
307
|
+
.option('--tag <name>', 'Filter by tag')
|
|
308
|
+
.option('--json', 'Output JSON', false)
|
|
309
|
+
.action((opts) => {
|
|
310
|
+
const { getRegistry } = require('../lib/workflow-registry');
|
|
311
|
+
const registry = getRegistry({ force: true });
|
|
312
|
+
|
|
313
|
+
const showBuiltIn = !opts.community && !opts.official;
|
|
314
|
+
const showOfficial = !opts.builtIn && !opts.community;
|
|
315
|
+
const showCommunity = !opts.builtIn && !opts.official;
|
|
316
|
+
|
|
317
|
+
if (opts.json) {
|
|
318
|
+
const out = {};
|
|
319
|
+
if (showBuiltIn) out.builtIn = registry.builtIn;
|
|
320
|
+
if (showOfficial) out.official = registry.official.filter(c => c.errors.length === 0);
|
|
321
|
+
if (showCommunity) out.community = registry.community.filter(c => c.errors.length === 0);
|
|
322
|
+
console.log(JSON.stringify(out, null, 2));
|
|
275
323
|
return;
|
|
276
324
|
}
|
|
277
325
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
326
|
+
/**
|
|
327
|
+
* Display a list of package-based workflows (official or community).
|
|
328
|
+
*/
|
|
329
|
+
function displayPackageList(items, label, emptyHint) {
|
|
330
|
+
let filtered = items.filter(c => c.errors.length === 0);
|
|
331
|
+
if (opts.category) {
|
|
332
|
+
filtered = filtered.filter(c => (c.pkg?.vai?.category || 'utility') === opts.category);
|
|
333
|
+
}
|
|
334
|
+
if (opts.tag) {
|
|
335
|
+
filtered = filtered.filter(c => (c.pkg?.vai?.tags || []).includes(opts.tag));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
console.log();
|
|
339
|
+
console.log(pc.bold(`${label} (${filtered.length})`));
|
|
340
|
+
if (filtered.length === 0) {
|
|
341
|
+
console.log(pc.dim(` (${emptyHint})`));
|
|
342
|
+
} else {
|
|
343
|
+
for (const wf of filtered) {
|
|
344
|
+
const pkg = wf.pkg || {};
|
|
345
|
+
const author = typeof pkg.author === 'string' ? pkg.author : pkg.author?.name || '';
|
|
346
|
+
const tags = (pkg.vai?.tags || []).join(' · ');
|
|
347
|
+
console.log(` ${pc.cyan(wf.name.padEnd(42))} ${pkg.description || ''}`);
|
|
348
|
+
if (author || tags) {
|
|
349
|
+
console.log(` ${pc.dim(`by ${author}`)}${pkg.version ? pc.dim(` | v${pkg.version}`) : ''}${tags ? pc.dim(` | ${tags}`) : ''}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Show invalid packages as warnings
|
|
355
|
+
const invalid = items.filter(c => c.errors.length > 0);
|
|
356
|
+
if (invalid.length > 0) {
|
|
357
|
+
console.log();
|
|
358
|
+
for (const inv of invalid) {
|
|
359
|
+
console.error(` ${pc.yellow('⚠')} ${inv.name}: ${inv.errors[0]}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Built-in
|
|
365
|
+
if (showBuiltIn) {
|
|
366
|
+
console.log();
|
|
367
|
+
console.log(pc.bold(`Built-in Workflows (${registry.builtIn.length})`));
|
|
368
|
+
if (registry.builtIn.length === 0) {
|
|
369
|
+
console.log(pc.dim(' (none)'));
|
|
370
|
+
} else {
|
|
371
|
+
for (const wf of registry.builtIn) {
|
|
372
|
+
console.log(` ${pc.cyan(wf.name.padEnd(28))} ${wf.description}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
283
375
|
}
|
|
376
|
+
|
|
377
|
+
// Official Catalog (@vaicli)
|
|
378
|
+
if (showOfficial) {
|
|
379
|
+
displayPackageList(registry.official, 'Official Catalog (@vaicli)', 'none installed');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Community
|
|
383
|
+
if (showCommunity) {
|
|
384
|
+
displayPackageList(registry.community, 'Community Workflows', 'none installed — install with: vai workflow install <package-name>');
|
|
385
|
+
}
|
|
386
|
+
|
|
284
387
|
console.log();
|
|
285
388
|
console.log(pc.dim('Run with: vai workflow run <name> --input key=value'));
|
|
286
389
|
console.log();
|
|
287
390
|
});
|
|
288
391
|
|
|
392
|
+
// ── workflow install ──
|
|
393
|
+
wfCmd
|
|
394
|
+
.command('install <package>')
|
|
395
|
+
.description('Install a workflow from npm')
|
|
396
|
+
.option('--global', 'Install globally', false)
|
|
397
|
+
.option('--json', 'Output JSON', false)
|
|
398
|
+
.action(async (packageName, opts) => {
|
|
399
|
+
const { installPackage, WORKFLOW_PREFIX, isWorkflowPackage, isOfficialPackage } = require('../lib/npm-utils');
|
|
400
|
+
const { validatePackage, clearRegistryCache } = require('../lib/workflow-registry');
|
|
401
|
+
|
|
402
|
+
// Auto-prefix if needed (but not for scoped packages)
|
|
403
|
+
if (!packageName.startsWith('@') && !packageName.startsWith(WORKFLOW_PREFIX)) {
|
|
404
|
+
packageName = WORKFLOW_PREFIX + packageName;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
console.log(`Installing ${pc.cyan(packageName)}...`);
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
const result = installPackage(packageName, { global: opts.global });
|
|
411
|
+
console.log(`${pc.green('✔')} Downloaded ${pc.cyan(packageName)}@${result.version}`);
|
|
412
|
+
|
|
413
|
+
// Validate
|
|
414
|
+
if (result.path) {
|
|
415
|
+
const validation = validatePackage(result.path);
|
|
416
|
+
if (validation.errors.length === 0) {
|
|
417
|
+
const steps = validation.definition?.steps?.length || 0;
|
|
418
|
+
const tools = (validation.pkg?.vai?.tools || []).join(', ');
|
|
419
|
+
console.log(`${pc.green('✔')} Validated workflow definition (${steps} steps${tools ? `, tools: ${tools}` : ''})`);
|
|
420
|
+
} else {
|
|
421
|
+
console.log(`${pc.yellow('⚠')} Validation issues:`);
|
|
422
|
+
for (const e of validation.errors) {
|
|
423
|
+
console.log(` ${pc.yellow('-')} ${e}`);
|
|
424
|
+
}
|
|
425
|
+
console.log();
|
|
426
|
+
console.log(pc.dim('The package was installed but the workflow may not execute correctly.'));
|
|
427
|
+
}
|
|
428
|
+
for (const w of validation.warnings) {
|
|
429
|
+
console.log(`${pc.yellow('⚠')} ${w}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
clearRegistryCache();
|
|
434
|
+
|
|
435
|
+
if (opts.json) {
|
|
436
|
+
console.log(JSON.stringify(result, null, 2));
|
|
437
|
+
} else {
|
|
438
|
+
console.log();
|
|
439
|
+
console.log(`Installed. Run with:`);
|
|
440
|
+
console.log(` ${pc.cyan(`vai workflow run ${packageName}`)} --input key=value`);
|
|
441
|
+
}
|
|
442
|
+
} catch (err) {
|
|
443
|
+
console.error(ui.error(err.message));
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// ── workflow uninstall ──
|
|
449
|
+
wfCmd
|
|
450
|
+
.command('uninstall <package>')
|
|
451
|
+
.description('Remove a workflow package')
|
|
452
|
+
.option('--global', 'Uninstall globally', false)
|
|
453
|
+
.action((packageName, opts) => {
|
|
454
|
+
const { uninstallPackage, WORKFLOW_PREFIX } = require('../lib/npm-utils');
|
|
455
|
+
const { clearRegistryCache } = require('../lib/workflow-registry');
|
|
456
|
+
|
|
457
|
+
if (!packageName.startsWith('@') && !packageName.startsWith(WORKFLOW_PREFIX)) {
|
|
458
|
+
packageName = WORKFLOW_PREFIX + packageName;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
console.log(`Uninstalling ${pc.cyan(packageName)}...`);
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
uninstallPackage(packageName, { global: opts.global });
|
|
465
|
+
clearRegistryCache();
|
|
466
|
+
console.log(`${pc.green('✔')} Removed ${packageName}`);
|
|
467
|
+
} catch (err) {
|
|
468
|
+
console.error(ui.error(err.message));
|
|
469
|
+
process.exit(1);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// ── workflow search ──
|
|
474
|
+
wfCmd
|
|
475
|
+
.command('search <query>')
|
|
476
|
+
.description('Search npm for community workflows')
|
|
477
|
+
.option('--limit <n>', 'Maximum results', '10')
|
|
478
|
+
.option('--json', 'Output JSON', false)
|
|
479
|
+
.action(async (query, opts) => {
|
|
480
|
+
const { searchNpm } = require('../lib/npm-utils');
|
|
481
|
+
|
|
482
|
+
console.log(`Searching npm for vai-workflow packages matching "${query}"...`);
|
|
483
|
+
console.log();
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
const results = await searchNpm(query, { limit: parseInt(opts.limit, 10) });
|
|
487
|
+
|
|
488
|
+
if (opts.json) {
|
|
489
|
+
console.log(JSON.stringify(results, null, 2));
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (results.length === 0) {
|
|
494
|
+
console.log(pc.dim(' No matching workflow packages found.'));
|
|
495
|
+
console.log();
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
for (const r of results) {
|
|
500
|
+
const badge = r.official ? ` ${pc.green('[OFFICIAL]')}` : '';
|
|
501
|
+
console.log(` ${pc.cyan(r.name)} ${pc.dim(`v${r.version}`)}${badge}`);
|
|
502
|
+
if (r.description) console.log(` ${r.description}`);
|
|
503
|
+
console.log(` ${pc.dim(`by ${r.author}`)}${r.keywords.length ? pc.dim(` | ${r.keywords.slice(0, 5).join(', ')}`) : ''}`);
|
|
504
|
+
console.log();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
console.log(pc.dim(`Install: vai workflow install <package-name>`));
|
|
508
|
+
console.log();
|
|
509
|
+
} catch (err) {
|
|
510
|
+
console.error(ui.error(err.message));
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// ── workflow info ──
|
|
516
|
+
wfCmd
|
|
517
|
+
.command('info <name>')
|
|
518
|
+
.description('Show detailed info about an installed workflow')
|
|
519
|
+
.option('--json', 'Output JSON', false)
|
|
520
|
+
.action((name, opts) => {
|
|
521
|
+
const { resolveWorkflow, getRegistry } = require('../lib/workflow-registry');
|
|
522
|
+
const { WORKFLOW_PREFIX } = require('../lib/npm-utils');
|
|
523
|
+
|
|
524
|
+
try {
|
|
525
|
+
const resolved = resolveWorkflow(name);
|
|
526
|
+
|
|
527
|
+
if (opts.json) {
|
|
528
|
+
console.log(JSON.stringify({ source: resolved.source, definition: resolved.definition, metadata: resolved.metadata }, null, 2));
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const def = resolved.definition;
|
|
533
|
+
console.log();
|
|
534
|
+
|
|
535
|
+
if (resolved.source === 'community' || resolved.source === 'official') {
|
|
536
|
+
const pkg = resolved.metadata?.package || {};
|
|
537
|
+
const author = typeof pkg.author === 'string' ? pkg.author : pkg.author?.name || 'unknown';
|
|
538
|
+
const vai = pkg.vai || {};
|
|
539
|
+
|
|
540
|
+
console.log(`${pc.bold(pc.cyan(pkg.name || name))} ${pc.dim(`v${pkg.version || '?'}`)}`);
|
|
541
|
+
console.log(` ${pkg.description || def.description || ''}`);
|
|
542
|
+
console.log();
|
|
543
|
+
console.log(` ${pc.dim('Author:')} ${author}`);
|
|
544
|
+
console.log(` ${pc.dim('License:')} ${pkg.license || 'unknown'}`);
|
|
545
|
+
console.log(` ${pc.dim('Category:')} ${vai.category || 'utility'}`);
|
|
546
|
+
if (vai.tags?.length) console.log(` ${pc.dim('Tags:')} ${vai.tags.join(', ')}`);
|
|
547
|
+
if (vai.minVaiVersion) console.log(` ${pc.dim('Min vai:')} v${vai.minVaiVersion}`);
|
|
548
|
+
if (vai.tools?.length) console.log(` ${pc.dim('Tools:')} ${vai.tools.join(', ')}`);
|
|
549
|
+
console.log(` ${pc.dim('Steps:')} ${def.steps?.length || 0}`);
|
|
550
|
+
console.log(` ${pc.dim('Source:')} ${resolved.metadata?.path || 'unknown'}`);
|
|
551
|
+
if (pkg.name) console.log(` ${pc.dim('npm:')} https://www.npmjs.com/package/${pkg.name}`);
|
|
552
|
+
} else {
|
|
553
|
+
console.log(`${pc.bold(pc.cyan(def.name || name))} ${pc.dim(`[${resolved.source}]`)}`);
|
|
554
|
+
console.log(` ${def.description || ''}`);
|
|
555
|
+
console.log(` ${pc.dim('Steps:')} ${def.steps?.length || 0}`);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Show inputs
|
|
559
|
+
if (def.inputs && Object.keys(def.inputs).length > 0) {
|
|
560
|
+
console.log();
|
|
561
|
+
console.log(` ${pc.bold('Inputs:')}`);
|
|
562
|
+
for (const [key, schema] of Object.entries(def.inputs)) {
|
|
563
|
+
const req = schema.required ? pc.red('(required)') : pc.dim(`(default: ${schema.default ?? 'none'})`);
|
|
564
|
+
const desc = schema.description || '';
|
|
565
|
+
console.log(` ${pc.cyan(key.padEnd(16))} ${(schema.type || 'string').padEnd(8)} ${req} ${pc.dim(desc)}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
console.log();
|
|
570
|
+
} catch (err) {
|
|
571
|
+
console.error(ui.error(err.message));
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// ── workflow create ──
|
|
577
|
+
wfCmd
|
|
578
|
+
.command('create')
|
|
579
|
+
.description('Scaffold a publish-ready npm package from a workflow')
|
|
580
|
+
.option('--from <file>', 'Existing workflow JSON to package')
|
|
581
|
+
.option('--name <name>', 'Package name (without vai-workflow- prefix)')
|
|
582
|
+
.option('--author <name>', 'Author name')
|
|
583
|
+
.option('--description <desc>', 'Package description')
|
|
584
|
+
.option('--category <cat>', 'Category (retrieval, analysis, ingestion, domain-specific, utility, integration)')
|
|
585
|
+
.option('--scope <scope>', 'Package scope (e.g. "vaicli" for @vaicli/vai-workflow-*)')
|
|
586
|
+
.option('--output <dir>', 'Output directory')
|
|
587
|
+
.action(async (opts) => {
|
|
588
|
+
const { scaffoldPackage, toPackageName, CATEGORIES, emptyWorkflowTemplate } = require('../lib/workflow-scaffold');
|
|
589
|
+
const { loadWorkflow } = require('../lib/workflow');
|
|
590
|
+
|
|
591
|
+
let definition;
|
|
592
|
+
let name = opts.name;
|
|
593
|
+
let author = opts.author;
|
|
594
|
+
let description = opts.description;
|
|
595
|
+
let category = opts.category;
|
|
596
|
+
|
|
597
|
+
if (opts.from) {
|
|
598
|
+
// Package an existing workflow
|
|
599
|
+
try {
|
|
600
|
+
definition = loadWorkflow(opts.from);
|
|
601
|
+
} catch (err) {
|
|
602
|
+
console.error(ui.error(err.message));
|
|
603
|
+
process.exit(1);
|
|
604
|
+
}
|
|
605
|
+
if (!name) {
|
|
606
|
+
name = definition.name || path.basename(opts.from, '.json').replace('.vai-workflow', '');
|
|
607
|
+
}
|
|
608
|
+
if (!description) {
|
|
609
|
+
description = definition.description;
|
|
610
|
+
}
|
|
611
|
+
} else if (process.stdin.isTTY) {
|
|
612
|
+
// Interactive mode
|
|
613
|
+
try {
|
|
614
|
+
const p = require('@clack/prompts');
|
|
615
|
+
p.intro(pc.bold('Create a new workflow package'));
|
|
616
|
+
|
|
617
|
+
const answers = await p.group({
|
|
618
|
+
name: () => p.text({ message: 'Workflow name', placeholder: 'my-workflow', validate: v => v ? undefined : 'Required' }),
|
|
619
|
+
description: () => p.text({ message: 'Description', placeholder: 'A brief description of what this workflow does' }),
|
|
620
|
+
category: () => p.select({
|
|
621
|
+
message: 'Category',
|
|
622
|
+
options: CATEGORIES.map(c => ({ value: c, label: c })),
|
|
623
|
+
}),
|
|
624
|
+
author: () => p.text({ message: 'Author', placeholder: 'Your Name', defaultValue: getGitAuthor() }),
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
if (p.isCancel(answers)) {
|
|
628
|
+
p.cancel('Cancelled.');
|
|
629
|
+
process.exit(0);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
name = answers.name;
|
|
633
|
+
description = answers.description;
|
|
634
|
+
category = answers.category;
|
|
635
|
+
author = answers.author;
|
|
636
|
+
definition = emptyWorkflowTemplate();
|
|
637
|
+
definition.name = name;
|
|
638
|
+
definition.description = description || '';
|
|
639
|
+
// Add a placeholder step so validation passes
|
|
640
|
+
definition.steps = [{
|
|
641
|
+
id: 'search',
|
|
642
|
+
tool: 'query',
|
|
643
|
+
name: 'Search',
|
|
644
|
+
inputs: { query: '{{ inputs.query }}' },
|
|
645
|
+
}];
|
|
646
|
+
definition.inputs = {
|
|
647
|
+
query: { type: 'string', required: true, description: 'Search query' },
|
|
648
|
+
};
|
|
649
|
+
} catch (err) {
|
|
650
|
+
console.error(ui.error(`Interactive mode failed: ${err.message}`));
|
|
651
|
+
process.exit(1);
|
|
652
|
+
}
|
|
653
|
+
} else {
|
|
654
|
+
console.error(ui.error('Provide --from <file> or run interactively (TTY required).'));
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (!name) {
|
|
659
|
+
console.error(ui.error('Workflow name is required. Use --name <name>.'));
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
try {
|
|
664
|
+
const result = scaffoldPackage({
|
|
665
|
+
definition,
|
|
666
|
+
name,
|
|
667
|
+
author,
|
|
668
|
+
description,
|
|
669
|
+
category,
|
|
670
|
+
scope: opts.scope,
|
|
671
|
+
outputDir: opts.output,
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const pkgName = toPackageName(name, { scope: opts.scope });
|
|
675
|
+
console.log();
|
|
676
|
+
console.log(`${pc.green('✔')} Created ${pc.cyan(pkgName)}/`);
|
|
677
|
+
for (const f of result.files) {
|
|
678
|
+
console.log(` ${pc.dim('├──')} ${f}`);
|
|
679
|
+
}
|
|
680
|
+
console.log();
|
|
681
|
+
console.log('Next steps:');
|
|
682
|
+
console.log(` 1. ${opts.from ? '' : pc.dim('Edit workflow.json with your workflow definition')}${opts.from ? 'Review README.md' : ''}`);
|
|
683
|
+
console.log(` 2. cd ${pkgName}`);
|
|
684
|
+
console.log(` 3. npm publish`);
|
|
685
|
+
console.log();
|
|
686
|
+
} catch (err) {
|
|
687
|
+
console.error(ui.error(err.message));
|
|
688
|
+
process.exit(1);
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
289
692
|
// ── workflow init ──
|
|
290
693
|
wfCmd
|
|
291
694
|
.command('init')
|
package/src/lib/explanations.js
CHANGED
|
@@ -1478,6 +1478,87 @@ const concepts = {
|
|
|
1478
1478
|
'vai workflow run multi-collection-search --input query="test" --dry-run',
|
|
1479
1479
|
],
|
|
1480
1480
|
},
|
|
1481
|
+
|
|
1482
|
+
'workflow-publishing': {
|
|
1483
|
+
title: 'Publishing vai Workflows to npm',
|
|
1484
|
+
summary: 'Share workflows as npm packages using the vai-workflow-* convention',
|
|
1485
|
+
content: [
|
|
1486
|
+
`${pc.bold('WHAT IS A WORKFLOW PACKAGE?')}`,
|
|
1487
|
+
``,
|
|
1488
|
+
`A vai workflow package is an npm package containing a reusable workflow`,
|
|
1489
|
+
`definition. It follows the naming convention ${pc.cyan('vai-workflow-<name>')} and contains:`,
|
|
1490
|
+
``,
|
|
1491
|
+
` • ${pc.cyan('workflow.json')} — The workflow definition (same format as any vai workflow)`,
|
|
1492
|
+
` • ${pc.cyan('package.json')} — npm metadata + vai-specific fields`,
|
|
1493
|
+
` • ${pc.cyan('README.md')} — Usage instructions`,
|
|
1494
|
+
``,
|
|
1495
|
+
`No JavaScript, no build step, no dependencies.`,
|
|
1496
|
+
``,
|
|
1497
|
+
`${pc.bold('HOW TO PUBLISH')}`,
|
|
1498
|
+
``,
|
|
1499
|
+
`Option 1: Package an existing workflow`,
|
|
1500
|
+
``,
|
|
1501
|
+
` ${pc.cyan('vai workflow create --from my-workflow.json --name my-workflow')}`,
|
|
1502
|
+
``,
|
|
1503
|
+
` This creates a ${pc.cyan('vai-workflow-my-workflow/')} directory with everything needed.`,
|
|
1504
|
+
` Review it, then:`,
|
|
1505
|
+
``,
|
|
1506
|
+
` ${pc.cyan('cd vai-workflow-my-workflow')}`,
|
|
1507
|
+
` ${pc.cyan('npm publish')}`,
|
|
1508
|
+
``,
|
|
1509
|
+
`Option 2: Start from scratch`,
|
|
1510
|
+
``,
|
|
1511
|
+
` ${pc.cyan('vai workflow create --name my-workflow')}`,
|
|
1512
|
+
``,
|
|
1513
|
+
` This creates an interactive template you can fill in.`,
|
|
1514
|
+
``,
|
|
1515
|
+
`${pc.bold('PACKAGE.JSON REQUIREMENTS')}`,
|
|
1516
|
+
``,
|
|
1517
|
+
`Your package.json needs a ${pc.cyan('"vai"')} field with:`,
|
|
1518
|
+
``,
|
|
1519
|
+
` {`,
|
|
1520
|
+
` "vai": {`,
|
|
1521
|
+
` "workflowVersion": "1.0",`,
|
|
1522
|
+
` "tools": ["query", "rerank"],`,
|
|
1523
|
+
` "category": "retrieval"`,
|
|
1524
|
+
` }`,
|
|
1525
|
+
` }`,
|
|
1526
|
+
``,
|
|
1527
|
+
`${pc.cyan('vai workflow create')} fills this in automatically.`,
|
|
1528
|
+
``,
|
|
1529
|
+
`${pc.bold('NAMING')}`,
|
|
1530
|
+
``,
|
|
1531
|
+
`Package names must start with ${pc.cyan('vai-workflow-')} followed by a lowercase, hyphenated name:`,
|
|
1532
|
+
``,
|
|
1533
|
+
` ${pc.green('✓')} vai-workflow-legal-research`,
|
|
1534
|
+
` ${pc.green('✓')} vai-workflow-code-review`,
|
|
1535
|
+
` ${pc.red('✗')} vai-legal-workflow (wrong prefix)`,
|
|
1536
|
+
``,
|
|
1537
|
+
`${pc.bold('CATEGORIES')}`,
|
|
1538
|
+
``,
|
|
1539
|
+
` ${pc.cyan('retrieval')} — Search and retrieval pipelines`,
|
|
1540
|
+
` ${pc.cyan('analysis')} — Comparison, consistency checking`,
|
|
1541
|
+
` ${pc.cyan('ingestion')} — Document processing and storage`,
|
|
1542
|
+
` ${pc.cyan('domain-specific')} — Industry-focused (legal, clinical, etc.)`,
|
|
1543
|
+
` ${pc.cyan('utility')} — Cost estimation, benchmarking`,
|
|
1544
|
+
` ${pc.cyan('integration')} — Multi-system workflows`,
|
|
1545
|
+
``,
|
|
1546
|
+
`${pc.bold('TESTING BEFORE PUBLISHING')}`,
|
|
1547
|
+
``,
|
|
1548
|
+
` ${pc.cyan('vai workflow validate ./vai-workflow-my-workflow/workflow.json')}`,
|
|
1549
|
+
` ${pc.cyan('npm install ./vai-workflow-my-workflow')}`,
|
|
1550
|
+
` ${pc.cyan('vai workflow run vai-workflow-my-workflow --dry-run')}`,
|
|
1551
|
+
].join('\n'),
|
|
1552
|
+
links: [
|
|
1553
|
+
'https://github.com/mrlynn/voyageai-cli',
|
|
1554
|
+
],
|
|
1555
|
+
tryIt: [
|
|
1556
|
+
'vai workflow create --from my-pipeline.json --name my-pipeline',
|
|
1557
|
+
'vai workflow create',
|
|
1558
|
+
'vai workflow search legal',
|
|
1559
|
+
'vai workflow install vai-workflow-legal-research',
|
|
1560
|
+
],
|
|
1561
|
+
},
|
|
1481
1562
|
};
|
|
1482
1563
|
|
|
1483
1564
|
/**
|
|
@@ -1651,6 +1732,13 @@ const aliases = {
|
|
|
1651
1732
|
'vai-workflow': 'workflows',
|
|
1652
1733
|
pipeline: 'workflows',
|
|
1653
1734
|
dag: 'workflows',
|
|
1735
|
+
// Workflow publishing aliases
|
|
1736
|
+
'workflow-publishing': 'workflow-publishing',
|
|
1737
|
+
'publish-workflow': 'workflow-publishing',
|
|
1738
|
+
'workflow-registry': 'workflow-publishing',
|
|
1739
|
+
'community-workflows': 'workflow-publishing',
|
|
1740
|
+
'share-workflow': 'workflow-publishing',
|
|
1741
|
+
'npm-workflow': 'workflow-publishing',
|
|
1654
1742
|
};
|
|
1655
1743
|
|
|
1656
1744
|
/**
|