voyageai-cli 1.24.0 → 1.26.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.
Files changed (43) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +2 -0
  3. package/src/commands/about.js +1 -1
  4. package/src/commands/bug.js +1 -1
  5. package/src/commands/playground.js +31 -0
  6. package/src/commands/scaffold.js +23 -1
  7. package/src/commands/workflow.js +336 -0
  8. package/src/lib/explanations.js +53 -0
  9. package/src/lib/scaffold-structure.js +8 -9
  10. package/src/lib/telemetry.js +1 -1
  11. package/src/lib/template-engine.js +240 -0
  12. package/src/lib/templates/nextjs/README.md.tpl +78 -55
  13. package/src/lib/templates/nextjs/favicon.svg.tpl +11 -0
  14. package/src/lib/templates/nextjs/footer.jsx.tpl +49 -0
  15. package/src/lib/templates/nextjs/layout.jsx.tpl +16 -10
  16. package/src/lib/templates/nextjs/lib-mongo.js.tpl +5 -5
  17. package/src/lib/templates/nextjs/lib-voyage.js.tpl +13 -8
  18. package/src/lib/templates/nextjs/navbar.jsx.tpl +98 -0
  19. package/src/lib/templates/nextjs/page-home.jsx.tpl +201 -0
  20. package/src/lib/templates/nextjs/page-search.jsx.tpl +184 -82
  21. package/src/lib/templates/nextjs/theme-registry.jsx.tpl +51 -0
  22. package/src/lib/templates/nextjs/theme.js.tpl +138 -65
  23. package/src/lib/templates/nextjs/vai-logo-256.png +0 -0
  24. package/src/lib/workflow-utils.js +65 -0
  25. package/src/lib/workflow.js +1259 -0
  26. package/src/mcp/tools/management.js +1 -60
  27. package/src/playground/icons/dark/128.png +0 -0
  28. package/src/playground/icons/dark/16.png +0 -0
  29. package/src/playground/icons/dark/256.png +0 -0
  30. package/src/playground/icons/dark/32.png +0 -0
  31. package/src/playground/icons/dark/64.png +0 -0
  32. package/src/playground/icons/light/128.png +0 -0
  33. package/src/playground/icons/light/16.png +0 -0
  34. package/src/playground/icons/light/256.png +0 -0
  35. package/src/playground/icons/light/32.png +0 -0
  36. package/src/playground/icons/light/64.png +0 -0
  37. package/src/playground/icons/watermark.png +0 -0
  38. package/src/playground/index.html +125 -73
  39. package/src/workflows/consistency-check.json +64 -0
  40. package/src/workflows/cost-analysis.json +69 -0
  41. package/src/workflows/multi-collection-search.json +80 -0
  42. package/src/workflows/research-and-summarize.json +46 -0
  43. package/src/workflows/smart-ingest.json +63 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voyageai-cli",
3
- "version": "1.24.0",
3
+ "version": "1.26.0",
4
4
  "description": "CLI for Voyage AI embeddings, reranking, and MongoDB Atlas Vector Search",
5
5
  "bin": {
6
6
  "vai": "./src/cli.js"
package/src/cli.js CHANGED
@@ -37,6 +37,7 @@ const { register: registerQuickstart } = require('./commands/quickstart');
37
37
  const { registerBug } = require('./commands/bug');
38
38
  const { registerChat } = require('./commands/chat');
39
39
  const { registerMcpServer } = require('./commands/mcp-server');
40
+ const { registerWorkflow } = require('./commands/workflow');
40
41
  const { showBanner, showQuickStart, getVersion } = require('./lib/banner');
41
42
 
42
43
  const version = getVersion();
@@ -78,6 +79,7 @@ registerQuickstart(program);
78
79
  registerBug(program);
79
80
  registerChat(program);
80
81
  registerMcpServer(program);
82
+ registerWorkflow(program);
81
83
 
82
84
  // Append disclaimer to all help output
83
85
  program.addHelpText('after', `
@@ -20,7 +20,7 @@ function registerAbout(program) {
20
20
  name: 'Michael Lynn',
21
21
  role: 'Principal Staff Developer Advocate, MongoDB',
22
22
  github: 'https://github.com/mrlynn',
23
- vai_website: 'https://vai.mlynn.org',
23
+ vai_website: 'https://vaicli.com',
24
24
  website: 'https://mlynn.org',
25
25
  },
26
26
  links: {
@@ -15,7 +15,7 @@ function getVersion() {
15
15
  }
16
16
 
17
17
  const GITHUB_ISSUES_URL = 'https://github.com/mrlynn/voyageai-cli/issues/new';
18
- const BUG_API_URL = 'https://vai.mlynn.org/api/bugs';
18
+ const BUG_API_URL = 'https://vaicli.com/api/bugs';
19
19
 
20
20
  /**
21
21
  * Generate a GitHub issue URL with pre-filled template
@@ -85,6 +85,23 @@ function createPlaygroundServer() {
85
85
  return;
86
86
  }
87
87
 
88
+ // Serve watermark image
89
+ if (req.method === 'GET' && req.url === '/icons/watermark.png') {
90
+ const wmPath = path.join(__dirname, '..', 'playground', 'icons', 'watermark.png');
91
+ if (fs.existsSync(wmPath)) {
92
+ const data = fs.readFileSync(wmPath);
93
+ res.writeHead(200, {
94
+ 'Content-Type': 'image/png',
95
+ 'Cache-Control': 'public, max-age=86400',
96
+ });
97
+ res.end(data);
98
+ } else {
99
+ res.writeHead(404);
100
+ res.end('Watermark not found');
101
+ }
102
+ return;
103
+ }
104
+
88
105
  // Serve icon assets: /icons/{dark|light}/{size}.png
89
106
  const iconMatch = req.url.match(/^\/icons\/(dark|light)\/(\d+)\.png$/);
90
107
  if (req.method === 'GET' && iconMatch) {
@@ -193,6 +210,20 @@ function createPlaygroundServer() {
193
210
  }
194
211
  }
195
212
 
213
+ // Add binary files
214
+ if (structure.binaryFiles) {
215
+ for (const file of structure.binaryFiles) {
216
+ const srcPath = path.join(__dirname, '..', 'lib', 'templates', target, file.source);
217
+ if (fs.existsSync(srcPath)) {
218
+ files.push({
219
+ name: `${projectName}/${file.output}`,
220
+ content: fs.readFileSync(srcPath),
221
+ binary: true,
222
+ });
223
+ }
224
+ }
225
+ }
226
+
196
227
  // Create ZIP
197
228
  const zipBuffer = createZip(files);
198
229
 
@@ -129,6 +129,23 @@ function registerScaffold(program) {
129
129
  });
130
130
  }
131
131
  }
132
+
133
+ // Add binary files (copied as-is from templates dir)
134
+ if (structure.binaryFiles) {
135
+ for (const file of structure.binaryFiles) {
136
+ const srcPath = path.join(__dirname, '..', 'lib', 'templates', target, file.source);
137
+ const destPath = path.join(projectDir, file.output);
138
+ const binarySize = fs.existsSync(srcPath) ? fs.statSync(srcPath).size : 0;
139
+ manifest.push({
140
+ path: file.output,
141
+ fullPath: destPath,
142
+ source: `${target}/${file.source}`,
143
+ size: binarySize,
144
+ binary: true,
145
+ binarySrc: srcPath,
146
+ });
147
+ }
148
+ }
132
149
 
133
150
  // JSON output mode
134
151
  if (opts.json) {
@@ -171,7 +188,12 @@ function registerScaffold(program) {
171
188
 
172
189
  // Write all files
173
190
  for (const file of manifest) {
174
- writeFile(file.fullPath, file.content);
191
+ if (file.binary) {
192
+ ensureDir(path.dirname(file.fullPath));
193
+ fs.copyFileSync(file.binarySrc, file.fullPath);
194
+ } else {
195
+ writeFile(file.fullPath, file.content);
196
+ }
175
197
  if (!opts.quiet) {
176
198
  console.log(` ${ui.cyan('✓')} ${file.path}`);
177
199
  }
@@ -0,0 +1,336 @@
1
+ 'use strict';
2
+
3
+ const pc = require('picocolors');
4
+ const ui = require('../lib/ui');
5
+
6
+ /**
7
+ * Parse repeatable --input key=value options into an object.
8
+ * Used as Commander's option reducer.
9
+ *
10
+ * @param {string} pair - "key=value" string
11
+ * @param {object} prev - Accumulated object
12
+ * @returns {object}
13
+ */
14
+ function collectInputs(pair, prev) {
15
+ const eq = pair.indexOf('=');
16
+ if (eq === -1) {
17
+ throw new Error(`Invalid input format: "${pair}". Expected key=value`);
18
+ }
19
+ const key = pair.slice(0, eq).trim();
20
+ const value = pair.slice(eq + 1).trim();
21
+ prev[key] = value;
22
+ return prev;
23
+ }
24
+
25
+ /**
26
+ * Register the workflow command on a Commander program.
27
+ * @param {import('commander').Command} program
28
+ */
29
+ function registerWorkflow(program) {
30
+ const wfCmd = program
31
+ .command('workflow')
32
+ .alias('wf')
33
+ .description('Run, validate, and manage composable RAG workflows');
34
+
35
+ // ── workflow run <file> ──
36
+ wfCmd
37
+ .command('run <file>')
38
+ .description('Execute a workflow file or built-in template')
39
+ .option('--input <key=value>', 'Set a workflow input (repeatable)', collectInputs, {})
40
+ .option('--db <name>', 'Override default database')
41
+ .option('--collection <name>', 'Override default collection')
42
+ .option('--json', 'Output results as JSON', false)
43
+ .option('--quiet', 'Suppress progress output', false)
44
+ .option('--dry-run', 'Show execution plan without running', false)
45
+ .option('--verbose', 'Show step details', false)
46
+ .action(async (file, opts) => {
47
+ const { loadWorkflow, executeWorkflow, buildExecutionPlan, validateWorkflow } = require('../lib/workflow');
48
+
49
+ let definition;
50
+ try {
51
+ definition = loadWorkflow(file);
52
+ } catch (err) {
53
+ console.error(ui.error(err.message));
54
+ process.exit(1);
55
+ }
56
+
57
+ // Validation
58
+ const errors = validateWorkflow(definition);
59
+ if (errors.length > 0) {
60
+ console.error(ui.error('Workflow validation failed:'));
61
+ for (const e of errors) console.error(` ${pc.red('-')} ${e}`);
62
+ process.exit(1);
63
+ }
64
+
65
+ const workflowName = definition.name || file;
66
+
67
+ if (opts.dryRun) {
68
+ // Dry run: show plan
69
+ const layers = buildExecutionPlan(definition.steps);
70
+ const stepMap = new Map(definition.steps.map(s => [s.id, s]));
71
+
72
+ if (opts.json) {
73
+ console.log(JSON.stringify({ name: workflowName, layers, steps: definition.steps, inputs: opts.input }, null, 2));
74
+ return;
75
+ }
76
+
77
+ console.log();
78
+ console.log(`${pc.bold('vai workflow:')} ${workflowName} ${pc.dim('(dry run)')}`);
79
+ console.log(pc.dim('═'.repeat(50)));
80
+ console.log();
81
+
82
+ // Show inputs
83
+ const inputKeys = Object.keys(opts.input);
84
+ if (inputKeys.length > 0) {
85
+ console.log(pc.bold('Inputs:'));
86
+ for (const [k, v] of Object.entries(opts.input)) {
87
+ console.log(` ${pc.cyan(k)}: ${JSON.stringify(v)}`);
88
+ }
89
+ console.log();
90
+ }
91
+
92
+ // Show execution plan
93
+ console.log(pc.bold('Execution plan:'));
94
+ let stepNum = 1;
95
+ for (let i = 0; i < layers.length; i++) {
96
+ const layer = layers[i];
97
+ for (const stepId of layer) {
98
+ const step = stepMap.get(stepId);
99
+ const toolDesc = `${step.tool}(${summarizeInputs(step.inputs)})`;
100
+ console.log(` ${pc.dim(`${stepNum}.`)} ${pc.cyan(stepId)} ${pc.dim('->')} ${toolDesc}`);
101
+ stepNum++;
102
+ }
103
+ if (layer.length > 1) {
104
+ console.log(` ${pc.dim(`(${layer.join(' and ')} run in parallel)`)}`);
105
+ }
106
+ }
107
+ console.log();
108
+ return;
109
+ }
110
+
111
+ // Execute workflow
112
+ try {
113
+ const result = await executeWorkflow(definition, {
114
+ inputs: opts.input,
115
+ db: opts.db,
116
+ collection: opts.collection,
117
+ dryRun: false,
118
+ verbose: opts.verbose,
119
+ json: opts.json,
120
+ onStepStart: !opts.quiet ? (stepId, step) => {
121
+ process.stderr.write(` ${pc.dim('...')} ${step.name || stepId}\r`);
122
+ } : undefined,
123
+ onStepComplete: !opts.quiet ? (stepId, output, durationMs) => {
124
+ const stepDef = definition.steps.find(s => s.id === stepId);
125
+ const summary = summarizeOutput(stepDef?.tool, output);
126
+ console.error(` ${pc.green('✔')} ${stepDef?.name || stepId} ${pc.dim(summary)} ${pc.dim(`[${durationMs}ms]`)}`);
127
+ } : undefined,
128
+ onStepSkip: !opts.quiet ? (stepId, reason) => {
129
+ const stepDef = definition.steps.find(s => s.id === stepId);
130
+ console.error(` ${pc.yellow('⊘')} ${stepDef?.name || stepId} ${pc.dim(`skipped: ${reason}`)}`);
131
+ } : undefined,
132
+ onStepError: !opts.quiet ? (stepId, err) => {
133
+ const stepDef = definition.steps.find(s => s.id === stepId);
134
+ console.error(` ${pc.red('✗')} ${stepDef?.name || stepId} ${pc.red(err.message)}`);
135
+ } : undefined,
136
+ });
137
+
138
+ if (!opts.quiet) {
139
+ console.error();
140
+ console.error(`${pc.bold('vai workflow:')} ${workflowName}`);
141
+ console.error(pc.dim('═'.repeat(50)));
142
+ // Step summaries already printed via callbacks
143
+ console.error();
144
+ console.error(`${pc.dim('Complete.')} ${result.steps.length} steps, ${result.totalTimeMs}ms total.`);
145
+ console.error();
146
+ }
147
+
148
+ // Output
149
+ if (opts.json) {
150
+ console.log(JSON.stringify(result.output, null, 2));
151
+ } else if (result.output) {
152
+ // Pretty-print top results if they exist
153
+ const output = result.output;
154
+ if (output.results && Array.isArray(output.results)) {
155
+ const top = output.results.slice(0, 5);
156
+ console.log(pc.bold('Top results:'));
157
+ for (let i = 0; i < top.length; i++) {
158
+ const r = top[i];
159
+ const source = r.source || r.text?.slice(0, 50) || `result ${i + 1}`;
160
+ const score = r.score != null ? ` (${r.score.toFixed(2)})` : '';
161
+ console.log(` ${pc.dim(`[${i + 1}]`)} ${source}${pc.dim(score)}`);
162
+ }
163
+ } else if (output.summary) {
164
+ console.log(output.summary);
165
+ } else if (output.comparison) {
166
+ console.log(pc.bold('Cost comparison:'));
167
+ for (const item of output.comparison) {
168
+ if (item && item.model) {
169
+ console.log(` ${pc.cyan(item.model)}: $${item.totalCost} total (embed: $${item.embeddingCost}, queries: $${item.monthlyQueryCost}/mo)`);
170
+ }
171
+ }
172
+ } else {
173
+ console.log(JSON.stringify(output, null, 2));
174
+ }
175
+ }
176
+ } catch (err) {
177
+ console.error(ui.error(err.message));
178
+ if (opts.verbose) {
179
+ console.error(pc.dim(err.stack));
180
+ }
181
+ process.exit(1);
182
+ }
183
+ });
184
+
185
+ // ── workflow validate <file> ──
186
+ wfCmd
187
+ .command('validate <file>')
188
+ .description('Validate a workflow file without executing')
189
+ .action((file) => {
190
+ const { loadWorkflow, validateWorkflow, buildExecutionPlan } = require('../lib/workflow');
191
+
192
+ let definition;
193
+ try {
194
+ definition = loadWorkflow(file);
195
+ } catch (err) {
196
+ console.error(ui.error(err.message));
197
+ process.exit(1);
198
+ }
199
+
200
+ const errors = validateWorkflow(definition);
201
+ if (errors.length > 0) {
202
+ console.error(ui.error(`Workflow has ${errors.length} error(s):`));
203
+ for (const e of errors) console.error(` ${pc.red('-')} ${e}`);
204
+ process.exit(1);
205
+ }
206
+
207
+ const layers = buildExecutionPlan(definition.steps);
208
+ console.log(ui.success(`${definition.name} is valid`));
209
+ console.log(` ${pc.dim('Steps:')} ${definition.steps.length}`);
210
+ console.log(` ${pc.dim('Layers:')} ${layers.length} (${layers.map(l => l.length).join(' + ')} steps)`);
211
+
212
+ if (definition.inputs) {
213
+ const required = Object.entries(definition.inputs).filter(([, s]) => s.required).map(([k]) => k);
214
+ if (required.length > 0) {
215
+ console.log(` ${pc.dim('Required inputs:')} ${required.join(', ')}`);
216
+ }
217
+ }
218
+ });
219
+
220
+ // ── workflow list ──
221
+ wfCmd
222
+ .command('list')
223
+ .description('List built-in workflow templates')
224
+ .action(() => {
225
+ const { listBuiltinWorkflows } = require('../lib/workflow');
226
+
227
+ const workflows = listBuiltinWorkflows();
228
+ if (workflows.length === 0) {
229
+ console.log(pc.dim('No built-in workflows found.'));
230
+ return;
231
+ }
232
+
233
+ console.log();
234
+ console.log(pc.bold('Built-in workflow templates:'));
235
+ console.log();
236
+ for (const wf of workflows) {
237
+ console.log(` ${pc.cyan(wf.name.padEnd(28))} ${wf.description}`);
238
+ }
239
+ console.log();
240
+ console.log(pc.dim('Run with: vai workflow run <name> --input key=value'));
241
+ console.log();
242
+ });
243
+
244
+ // ── workflow init ──
245
+ wfCmd
246
+ .command('init')
247
+ .description('Scaffold a new workflow file')
248
+ .option('--name <name>', 'Workflow name')
249
+ .option('--output <file>', 'Output file path')
250
+ .action((opts) => {
251
+ const fs = require('fs');
252
+ const name = opts.name || 'my-workflow';
253
+ const filename = opts.output || `./${name}.vai-workflow.json`;
254
+
255
+ const scaffold = {
256
+ $schema: 'https://vai.dev/schemas/workflow-v1.json',
257
+ name: name.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
258
+ description: 'TODO: describe what this workflow does',
259
+ version: '1.0.0',
260
+ inputs: {
261
+ query: {
262
+ type: 'string',
263
+ description: 'The search query',
264
+ required: true,
265
+ },
266
+ },
267
+ defaults: {},
268
+ steps: [
269
+ {
270
+ id: 'search',
271
+ name: 'Search knowledge base',
272
+ tool: 'query',
273
+ inputs: {
274
+ query: '{{ inputs.query }}',
275
+ limit: 10,
276
+ },
277
+ },
278
+ ],
279
+ output: {
280
+ results: '{{ search.output.results }}',
281
+ query: '{{ inputs.query }}',
282
+ },
283
+ };
284
+
285
+ fs.writeFileSync(filename, JSON.stringify(scaffold, null, 2) + '\n');
286
+ console.log(ui.success(`Created ${filename}`));
287
+ console.log(` ${pc.dim('Run with:')} vai workflow run ${filename} --input query="your question"`);
288
+ console.log(` ${pc.dim('Validate:')} vai workflow validate ${filename}`);
289
+ });
290
+ }
291
+
292
+ /**
293
+ * Summarize step inputs for dry-run display.
294
+ */
295
+ function summarizeInputs(inputs) {
296
+ if (!inputs) return '';
297
+ const parts = [];
298
+ for (const [k, v] of Object.entries(inputs)) {
299
+ if (typeof v === 'string' && v.includes('{{')) {
300
+ parts.push(`${k}=<ref>`);
301
+ } else if (typeof v === 'string') {
302
+ parts.push(`${k}=${v.length > 20 ? v.slice(0, 20) + '...' : v}`);
303
+ } else if (typeof v === 'number' || typeof v === 'boolean') {
304
+ parts.push(`${k}=${v}`);
305
+ }
306
+ }
307
+ return parts.join(', ');
308
+ }
309
+
310
+ /**
311
+ * Summarize step output for display.
312
+ */
313
+ function summarizeOutput(tool, output) {
314
+ if (!output) return '';
315
+ if (output.results && output.resultCount != null) {
316
+ return `${output.resultCount} results`;
317
+ }
318
+ if (output.insertedCount != null) {
319
+ return `${output.insertedCount} docs inserted`;
320
+ }
321
+ if (output.similarity != null) {
322
+ return `similarity: ${output.similarity.toFixed(4)}`;
323
+ }
324
+ if (output.text) {
325
+ return `${output.text.length} chars`;
326
+ }
327
+ if (output.embedding) {
328
+ return `${output.dimensions}d embedding`;
329
+ }
330
+ if (output.totalCost != null) {
331
+ return `$${output.totalCost}`;
332
+ }
333
+ return '';
334
+ }
335
+
336
+ module.exports = { registerWorkflow, collectInputs };
@@ -1434,6 +1434,50 @@ const concepts = {
1434
1434
  'vai chat --db myapp --collection knowledge',
1435
1435
  ],
1436
1436
  },
1437
+
1438
+ workflows: {
1439
+ title: 'Agentic Workflows',
1440
+ summary: 'Composable, multi-step RAG pipelines as JSON files',
1441
+ content: [
1442
+ `${pc.cyan('Workflows')} are composable, multi-step RAG pipelines defined as portable`,
1443
+ `JSON files. Think Docker Compose or GitHub Actions, but for search and retrieval`,
1444
+ `pipelines.`,
1445
+ ``,
1446
+ `${pc.bold('Why workflows?')} Instead of writing bash scripts to chain vai commands,`,
1447
+ `a workflow file captures the intent declaratively. Workflows are reproducible,`,
1448
+ `shareable (commit them to git), and inspectable.`,
1449
+ ``,
1450
+ `${pc.bold('How it works:')}`,
1451
+ `Each workflow defines a DAG (directed acyclic graph) of steps. Each step maps`,
1452
+ `to a vai operation (query, search, rerank, embed, etc.) or a control flow`,
1453
+ `operation (merge, filter, transform, generate). Steps reference outputs from`,
1454
+ `previous steps using ${pc.cyan('{{ template expressions }}')} for data flow.`,
1455
+ ``,
1456
+ `${pc.bold('Template expressions:')}`,
1457
+ ` ${pc.cyan('{{ inputs.query }}')} workflow input parameter`,
1458
+ ` ${pc.cyan('{{ search.output.results }}')} results from a previous step`,
1459
+ ` ${pc.cyan('{{ merge.output.results[0] }}')} array indexing`,
1460
+ ` ${pc.cyan('{{ defaults.db }}')} workflow default value`,
1461
+ ``,
1462
+ `${pc.bold('Parallel execution:')} The engine automatically detects independent steps`,
1463
+ `and runs them in parallel. No configuration needed.`,
1464
+ ``,
1465
+ `${pc.bold('Step types:')}`,
1466
+ ` ${pc.dim('VAI tools:')} query, search, rerank, embed, similarity, ingest,`,
1467
+ ` collections, models, explain, estimate`,
1468
+ ` ${pc.dim('Control flow:')} merge, filter, transform, generate`,
1469
+ ``,
1470
+ `${pc.bold('Built-in templates:')} Run ${pc.cyan('vai workflow list')} to see available`,
1471
+ `templates like multi-collection-search, smart-ingest, research-and-summarize.`,
1472
+ ].join('\n'),
1473
+ links: ['https://github.com/mrlynn/voyageai-cli#workflows'],
1474
+ tryIt: [
1475
+ 'vai workflow list',
1476
+ 'vai workflow init --name my-pipeline',
1477
+ 'vai workflow validate ./my-pipeline.vai-workflow.json',
1478
+ 'vai workflow run multi-collection-search --input query="test" --dry-run',
1479
+ ],
1480
+ },
1437
1481
  };
1438
1482
 
1439
1483
  /**
@@ -1598,6 +1642,15 @@ const aliases = {
1598
1642
  conversational: 'chat',
1599
1643
  'chat-history': 'chat',
1600
1644
  llm: 'chat',
1645
+ // Workflow aliases
1646
+ workflow: 'workflows',
1647
+ workflows: 'workflows',
1648
+ 'rag-pipeline': 'workflows',
1649
+ composable: 'workflows',
1650
+ 'workflow-engine': 'workflows',
1651
+ 'vai-workflow': 'workflows',
1652
+ pipeline: 'workflows',
1653
+ dag: 'workflows',
1601
1654
  };
1602
1655
 
1603
1656
  /**
@@ -31,23 +31,22 @@ const PROJECT_STRUCTURE = {
31
31
  { template: 'env.example', output: '.env.example' },
32
32
  { template: 'README.md', output: 'README.md' },
33
33
  { template: 'layout.jsx', output: 'app/layout.jsx' },
34
+ { template: 'page-home.jsx', output: 'app/page.jsx' },
34
35
  { template: 'page-search.jsx', output: 'app/search/page.jsx' },
35
36
  { template: 'route-search.js', output: 'app/api/search/route.js' },
36
37
  { template: 'route-ingest.js', output: 'app/api/ingest/route.js' },
37
38
  { template: 'lib-voyage.js', output: 'lib/voyage.js' },
38
39
  { template: 'lib-mongo.js', output: 'lib/mongodb.js' },
39
40
  { template: 'theme.js', output: 'lib/theme.js' },
41
+ { template: 'theme-registry.jsx', output: 'components/ThemeRegistry.jsx' },
42
+ { template: 'navbar.jsx', output: 'components/Navbar.jsx' },
43
+ { template: 'footer.jsx', output: 'components/Footer.jsx' },
44
+ { template: 'favicon.svg', output: 'public/favicon.svg' },
45
+ ],
46
+ binaryFiles: [
47
+ { source: 'vai-logo-256.png', output: 'public/vai-logo.png' },
40
48
  ],
41
49
  extraFiles: [
42
- {
43
- output: 'app/page.jsx',
44
- content: `'use client';
45
- import { redirect } from 'next/navigation';
46
- export default function Home() {
47
- redirect('/search');
48
- }
49
- `,
50
- },
51
50
  {
52
51
  output: 'next.config.js',
53
52
  content: `/** @type {import('next').NextConfig} */
@@ -9,7 +9,7 @@
9
9
  * - command name, version, platform, locale
10
10
  */
11
11
 
12
- const TELEMETRY_URL = 'https://vai.mlynn.org/api/telemetry';
12
+ const TELEMETRY_URL = 'https://vaicli.com/api/telemetry';
13
13
  const TIMEOUT_MS = 3000;
14
14
 
15
15
  /**