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.
- package/README.md +82 -8
- package/package.json +1 -1
- package/src/commands/benchmark.js +22 -8
- package/src/commands/chat.js +18 -0
- package/src/commands/chunk.js +10 -0
- package/src/commands/demo.js +4 -0
- package/src/commands/embed.js +13 -0
- package/src/commands/estimate.js +3 -0
- package/src/commands/eval.js +6 -0
- package/src/commands/explain.js +2 -0
- package/src/commands/generate.js +2 -0
- package/src/commands/ingest.js +4 -0
- package/src/commands/init.js +2 -0
- package/src/commands/mcp-server.js +2 -0
- package/src/commands/models.js +2 -0
- package/src/commands/ping.js +7 -0
- package/src/commands/pipeline.js +15 -0
- package/src/commands/playground.js +52 -6
- package/src/commands/query.js +16 -0
- package/src/commands/rerank.js +12 -0
- package/src/commands/scaffold.js +2 -0
- package/src/commands/search.js +11 -0
- package/src/commands/similarity.js +9 -0
- package/src/commands/store.js +4 -0
- package/src/commands/workflow.js +286 -0
- package/src/lib/capability-report.js +134 -0
- package/src/lib/chat.js +32 -1
- package/src/lib/config.js +2 -0
- package/src/lib/cost-display.js +107 -0
- package/src/lib/explanations.js +6 -0
- package/src/lib/llm.js +125 -18
- package/src/lib/quality-audit.js +71 -0
- package/src/lib/security/blocked-domains.json +17 -0
- package/src/lib/security-audit.js +198 -0
- package/src/lib/telemetry.js +23 -1
- package/src/lib/workflow-scaffold.js +61 -0
- package/src/lib/workflow-test-runner.js +208 -0
- package/src/lib/workflow.js +128 -2
- package/src/playground/announcements.md +9 -0
- package/src/playground/assets/announcements/appstore.jpg +0 -0
- package/src/playground/assets/announcements/circuits.jpg +0 -0
- package/src/playground/assets/announcements/csvingest.jpg +0 -0
- package/src/playground/assets/announcements/green-wave.jpg +0 -0
- package/src/playground/help/workflow-nodes.js +472 -0
- package/src/playground/index.html +1482 -184
package/src/lib/workflow.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|