voyageai-cli 1.29.0 → 1.30.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 (45) hide show
  1. package/README.md +82 -8
  2. package/package.json +1 -1
  3. package/src/commands/benchmark.js +22 -8
  4. package/src/commands/chat.js +18 -0
  5. package/src/commands/chunk.js +10 -0
  6. package/src/commands/demo.js +4 -0
  7. package/src/commands/embed.js +13 -0
  8. package/src/commands/estimate.js +3 -0
  9. package/src/commands/eval.js +6 -0
  10. package/src/commands/explain.js +2 -0
  11. package/src/commands/generate.js +2 -0
  12. package/src/commands/ingest.js +4 -0
  13. package/src/commands/init.js +2 -0
  14. package/src/commands/mcp-server.js +2 -0
  15. package/src/commands/models.js +2 -0
  16. package/src/commands/ping.js +7 -0
  17. package/src/commands/pipeline.js +15 -0
  18. package/src/commands/playground.js +52 -6
  19. package/src/commands/query.js +16 -0
  20. package/src/commands/rerank.js +12 -0
  21. package/src/commands/scaffold.js +2 -0
  22. package/src/commands/search.js +11 -0
  23. package/src/commands/similarity.js +9 -0
  24. package/src/commands/store.js +4 -0
  25. package/src/commands/workflow.js +286 -0
  26. package/src/lib/capability-report.js +134 -0
  27. package/src/lib/chat.js +32 -1
  28. package/src/lib/config.js +2 -0
  29. package/src/lib/cost-display.js +107 -0
  30. package/src/lib/explanations.js +6 -0
  31. package/src/lib/llm.js +125 -18
  32. package/src/lib/quality-audit.js +71 -0
  33. package/src/lib/security/blocked-domains.json +17 -0
  34. package/src/lib/security-audit.js +198 -0
  35. package/src/lib/telemetry.js +23 -1
  36. package/src/lib/workflow-scaffold.js +61 -0
  37. package/src/lib/workflow-test-runner.js +208 -0
  38. package/src/lib/workflow.js +128 -2
  39. package/src/playground/announcements.md +9 -0
  40. package/src/playground/assets/announcements/appstore.jpg +0 -0
  41. package/src/playground/assets/announcements/circuits.jpg +0 -0
  42. package/src/playground/assets/announcements/csvingest.jpg +0 -0
  43. package/src/playground/assets/announcements/green-wave.jpg +0 -0
  44. package/src/playground/help/workflow-nodes.js +472 -0
  45. package/src/playground/index.html +1482 -184
@@ -26,6 +26,19 @@ const INTEGRATION_TOOLS = new Set(['http']);
26
26
 
27
27
  const ALL_TOOLS = new Set([...VAI_TOOLS, ...CONTROL_FLOW_TOOLS, ...PROCESSING_TOOLS, ...INTEGRATION_TOOLS]);
28
28
 
29
+ // ════════════════════════════════════════════════════════════════════
30
+ // Schema Limits (Phase 1 Enhanced Validation)
31
+ // ════════════════════════════════════════════════════════════════════
32
+
33
+ const SCHEMA_LIMITS = {
34
+ maxSteps: 50,
35
+ maxInputs: 20,
36
+ maxTemplateDepth: 5,
37
+ maxNameLength: 64,
38
+ maxDescriptionLength: 500,
39
+ maxStepNameLength: 100,
40
+ };
41
+
29
42
  // ════════════════════════════════════════════════════════════════════
30
43
  // Validation
31
44
  // ════════════════════════════════════════════════════════════════════
@@ -180,6 +193,74 @@ function validateWorkflow(definition) {
180
193
  return errors;
181
194
  }
182
195
 
196
+ /**
197
+ * Enhanced schema validation for publishable workflows.
198
+ * Runs all existing validateWorkflow() checks plus additional quality gates.
199
+ *
200
+ * @param {object} definition - Parsed workflow JSON
201
+ * @returns {string[]} errors
202
+ */
203
+ function validateSchemaEnhanced(definition) {
204
+ const errors = validateWorkflow(definition);
205
+
206
+ if (!definition || typeof definition !== 'object') return errors;
207
+
208
+ // Step count limit
209
+ if (Array.isArray(definition.steps) && definition.steps.length > SCHEMA_LIMITS.maxSteps) {
210
+ errors.push(`Too many steps (${definition.steps.length}/${SCHEMA_LIMITS.maxSteps})`);
211
+ }
212
+
213
+ // Input count limit
214
+ if (definition.inputs && Object.keys(definition.inputs).length > SCHEMA_LIMITS.maxInputs) {
215
+ errors.push(`Too many inputs (${Object.keys(definition.inputs).length}/${SCHEMA_LIMITS.maxInputs})`);
216
+ }
217
+
218
+ // Name length
219
+ if (definition.name && definition.name.length > SCHEMA_LIMITS.maxNameLength) {
220
+ errors.push(`Workflow name too long (${definition.name.length}/${SCHEMA_LIMITS.maxNameLength})`);
221
+ }
222
+
223
+ // Description required (min 10 chars) for publishable workflows
224
+ if (!definition.description || definition.description.length < 10) {
225
+ errors.push('Description must be at least 10 characters');
226
+ }
227
+
228
+ // Description length limit
229
+ if (definition.description && definition.description.length > SCHEMA_LIMITS.maxDescriptionLength) {
230
+ errors.push(`Description too long (${definition.description.length}/${SCHEMA_LIMITS.maxDescriptionLength})`);
231
+ }
232
+
233
+ // Version must be valid semver
234
+ if (definition.version && !/^\d+\.\d+\.\d+/.test(definition.version)) {
235
+ errors.push('Version must be valid semver (e.g. 1.0.0)');
236
+ }
237
+
238
+ // Every input should have a description
239
+ for (const [key, spec] of Object.entries(definition.inputs || {})) {
240
+ if (!spec.description) {
241
+ errors.push(`Input "${key}" missing description`);
242
+ }
243
+ }
244
+
245
+ // Every step should have a human-readable name
246
+ if (Array.isArray(definition.steps)) {
247
+ for (const step of definition.steps) {
248
+ if (!step.name) {
249
+ errors.push(`Step "${step.id}" missing "name" field`);
250
+ } else if (step.name.length > SCHEMA_LIMITS.maxStepNameLength) {
251
+ errors.push(`Step "${step.id}" name too long (${step.name.length}/${SCHEMA_LIMITS.maxStepNameLength})`);
252
+ }
253
+ }
254
+ }
255
+
256
+ // Output section should exist
257
+ if (!definition.output || Object.keys(definition.output).length === 0) {
258
+ errors.push('Workflow should define an "output" section');
259
+ }
260
+
261
+ return errors;
262
+ }
263
+
183
264
  /**
184
265
  * Detect circular dependencies in steps using DFS.
185
266
  * @param {Array} steps
@@ -794,7 +875,9 @@ function executeChunk(inputs) {
794
875
  * @returns {Promise<{ status: number, statusText: string, headers: object, body: any, durationMs: number }>}
795
876
  */
796
877
  async function executeHttp(inputs) {
797
- const { url, method = 'GET', headers = {}, body, timeout = 30000, responseType = 'json', followRedirects = false } = inputs;
878
+ const effectiveResponseType = inputs.responseType || (inputs.extract === 'text' ? 'text' : 'json');
879
+ const { url, method = 'GET', headers = {}, body, timeout = 30000, followRedirects = false } = inputs;
880
+ const responseType = effectiveResponseType;
798
881
 
799
882
  if (!url || typeof url !== 'string') {
800
883
  throw new Error('http: "url" input is required');
@@ -857,6 +940,12 @@ async function executeHttp(inputs) {
857
940
  parsedBody = rawBody;
858
941
  }
859
942
 
943
+ // Strip HTML if extract: "text"
944
+ if (inputs.extract === 'text' && typeof parsedBody === 'string') {
945
+ const { stripHtml } = require('./readers');
946
+ parsedBody = stripHtml(parsedBody);
947
+ }
948
+
860
949
  // Collect response headers
861
950
  const respHeaders = {};
862
951
  response.headers.forEach((value, key) => {
@@ -1119,11 +1208,40 @@ async function executeIngest(inputs, defaults) {
1119
1208
  const { client, collection: col } = await getMongoCollection(db, collection);
1120
1209
  try {
1121
1210
  const result = await col.insertMany(docs);
1211
+
1212
+ // Auto-create vector search index if it doesn't exist
1213
+ // Note: Atlas vector search indexes take a few seconds to become ready after creation.
1214
+ // The search step may need a brief delay on first run.
1215
+ let indexCreated = false;
1216
+ try {
1217
+ const indexes = await col.listSearchIndexes().toArray();
1218
+ const hasVectorIndex = indexes.some(idx => idx.name === 'vector_index');
1219
+ if (!hasVectorIndex) {
1220
+ await col.createSearchIndex({
1221
+ name: 'vector_index',
1222
+ type: 'vectorSearch',
1223
+ definition: {
1224
+ fields: [{
1225
+ type: 'vector',
1226
+ path: 'embedding',
1227
+ numDimensions: embRes.data[0].embedding.length,
1228
+ similarity: 'cosine'
1229
+ }]
1230
+ }
1231
+ });
1232
+ indexCreated = true;
1233
+ }
1234
+ } catch (indexErr) {
1235
+ // Ignore errors: index may already exist, or createSearchIndex may not be
1236
+ // available on non-Atlas deployments
1237
+ }
1238
+
1122
1239
  return {
1123
1240
  insertedCount: result.insertedCount,
1124
1241
  chunks: chunks.length,
1125
1242
  source,
1126
1243
  model: embRes.model,
1244
+ indexCreated,
1127
1245
  };
1128
1246
  } finally {
1129
1247
  await client.close();
@@ -1475,7 +1593,12 @@ async function executeWorkflow(definition, opts = {}) {
1475
1593
  try {
1476
1594
  let output;
1477
1595
 
1478
- if (step.tool === 'conditional') {
1596
+ // Mock executor injection: if _mockExecutors has a mock for this tool, use it
1597
+ const mockExecutor = opts._mockExecutors && opts._mockExecutors[step.tool];
1598
+ if (mockExecutor && step.tool !== 'conditional') {
1599
+ const resolvedInputs = resolveTemplate(step.inputs || {}, context);
1600
+ output = await mockExecutor(resolvedInputs, step, context);
1601
+ } else if (step.tool === 'conditional') {
1479
1602
  // Special handling: resolve then/else but pass raw condition to evaluator
1480
1603
  const rawCondition = step.inputs.condition;
1481
1604
  const resolvedInputs = {
@@ -1738,7 +1861,9 @@ function loadWorkflow(nameOrPath) {
1738
1861
  module.exports = {
1739
1862
  // Validation
1740
1863
  validateWorkflow,
1864
+ validateSchemaEnhanced,
1741
1865
  detectCycles,
1866
+ SCHEMA_LIMITS,
1742
1867
 
1743
1868
  // Dependency resolution
1744
1869
  buildDependencyGraph,
@@ -1756,6 +1881,7 @@ module.exports = {
1756
1881
  executeLoop,
1757
1882
  executeChunk,
1758
1883
  executeHttp,
1884
+ executeIngest,
1759
1885
  executeAggregate,
1760
1886
 
1761
1887
  // Main execution
@@ -20,6 +20,8 @@ id: ann-voyage-4
20
20
  badge: New Model
21
21
  published: 2026-02-14
22
22
  expires: 2026-03-15
23
+ icon: 🚀
24
+ bg_image: /assets/announcements/circuits.jpg
23
25
  cta_label: Try It Now
24
26
  cta_action: navigate
25
27
  cta_target: /benchmark
@@ -34,6 +36,8 @@ id: ann-marketplace-workflows
34
36
  badge: New
35
37
  published: 2026-02-12
36
38
  expires: 2026-03-01
39
+ icon: 🏪
40
+ bg_image: /assets/announcements/green-wave.jpg
37
41
  cta_label: Explore Marketplace
38
42
  cta_action: navigate
39
43
  cta_target: /workflows
@@ -48,6 +52,9 @@ id: ann-csv-ingestion
48
52
  badge: Update
49
53
  published: 2026-02-10
50
54
  expires: 2026-04-01
55
+ icon: 📊
56
+ bg_image: /assets/announcements/csvingest.jpg
57
+ bg_color: linear-gradient(135deg, #1B5E20 0%, #2E7D32 100%)
51
58
  cta_label: Learn More
52
59
  cta_action: link
53
60
  cta_target: https://docs.vaicli.com/csv-import
@@ -62,6 +69,8 @@ id: ann-workflow-store
62
69
  badge: New
63
70
  published: 2026-02-13
64
71
  expires: 2026-03-15
72
+ icon: ⚡
73
+ bg_image: /assets/announcements/appstore.jpg
65
74
  cta_label: Browse Store
66
75
  cta_action: navigate
67
76
  cta_target: /workflows