voyageai-cli 1.12.0 → 1.13.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 (44) hide show
  1. package/README.md +3 -3
  2. package/demo-readme.gif +0 -0
  3. package/package.json +4 -1
  4. package/src/commands/models.js +3 -3
  5. package/src/commands/ping.js +32 -2
  6. package/src/commands/playground.js +1 -1
  7. package/src/commands/search.js +2 -2
  8. package/src/commands/store.js +1 -1
  9. package/src/lib/api.js +18 -14
  10. package/src/lib/catalog.js +3 -3
  11. package/.github/workflows/ci.yml +0 -22
  12. package/CONTRIBUTING.md +0 -81
  13. package/demo.gif +0 -0
  14. package/demo.tape +0 -39
  15. package/scripts/record-demo.sh +0 -63
  16. package/test/commands/about.test.js +0 -23
  17. package/test/commands/benchmark.test.js +0 -319
  18. package/test/commands/completions.test.js +0 -166
  19. package/test/commands/config.test.js +0 -35
  20. package/test/commands/demo.test.js +0 -46
  21. package/test/commands/embed.test.js +0 -42
  22. package/test/commands/explain.test.js +0 -207
  23. package/test/commands/ingest.test.js +0 -261
  24. package/test/commands/models.test.js +0 -132
  25. package/test/commands/ping.test.js +0 -172
  26. package/test/commands/playground.test.js +0 -137
  27. package/test/commands/rerank.test.js +0 -32
  28. package/test/commands/similarity.test.js +0 -79
  29. package/test/commands/store.test.js +0 -26
  30. package/test/fixtures/sample.csv +0 -6
  31. package/test/fixtures/sample.json +0 -7
  32. package/test/fixtures/sample.jsonl +0 -5
  33. package/test/fixtures/sample.txt +0 -5
  34. package/test/lib/api.test.js +0 -134
  35. package/test/lib/banner.test.js +0 -44
  36. package/test/lib/catalog.test.js +0 -99
  37. package/test/lib/config.test.js +0 -124
  38. package/test/lib/explanations.test.js +0 -141
  39. package/test/lib/format.test.js +0 -75
  40. package/test/lib/input.test.js +0 -48
  41. package/test/lib/math.test.js +0 -43
  42. package/test/lib/ui.test.js +0 -79
  43. package/voyageai-cli-playground.png +0 -0
  44. package/voyageai-cli.png +0 -0
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # voyageai-cli
2
2
 
3
3
  <p align="center">
4
- <img src="https://raw.githubusercontent.com/mrlynn/voyageai-cli/main/voyageai-cli.png" alt="voyageai-cli" width="600" />
4
+ <img src="https://raw.githubusercontent.com/mrlynn/voyageai-cli/main/demo-readme.gif" alt="voyageai-cli demo" width="800" />
5
5
  </p>
6
6
 
7
7
  [![CI](https://github.com/mrlynn/voyageai-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/mrlynn/voyageai-cli/actions/workflows/ci.yml) [![npm version](https://img.shields.io/npm/v/voyageai-cli.svg)](https://www.npmjs.com/package/voyageai-cli) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/node/v/voyageai-cli.svg)](https://nodejs.org)
8
8
 
9
- CLI for [Voyage AI](https://www.mongodb.com/docs/voyageai/) embeddings, reranking, and [MongoDB Atlas Vector Search](https://www.mongodb.com/docs/atlas/atlas-vector-search/). Pure Node.js — no Python required.
9
+ CLI for [Voyage AI](https://www.mongodb.com/docs/voyageai/) embeddings, reranking, and [MongoDB Atlas Vector Search](https://www.mongodb.com/docs/atlas/atlas-vector-search/). Embed text, benchmark models, compare quantization tradeoffs, and search — all from the terminal. Pure Node.js — no Python required.
10
10
 
11
- Generate embeddings, rerank search results, store vectors in Atlas, and run semantic search — all from the command line.
11
+ **16 commands · 201 tests · Interactive playground · Quantization benchmarks**
12
12
 
13
13
  > **⚠️ Disclaimer:** This is an independent, community-built tool. It is **not** an official product of MongoDB, Inc. or Voyage AI. It is not supported, endorsed, or maintained by either company. For official documentation, support, and products, visit:
14
14
  > - **MongoDB:** [mongodb.com](https://www.mongodb.com) | [MongoDB Atlas](https://www.mongodb.com/atlas) | [Support](https://support.mongodb.com)
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voyageai-cli",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "CLI for Voyage AI embeddings, reranking, and MongoDB Atlas Vector Search",
5
5
  "bin": {
6
6
  "vai": "./src/cli.js"
@@ -40,5 +40,8 @@
40
40
  "ora": "^9.1.0",
41
41
  "picocolors": "^1.1.1",
42
42
  "update-notifier": "^7.3.1"
43
+ },
44
+ "devDependencies": {
45
+ "playwright": "^1.58.1"
43
46
  }
44
47
  }
@@ -53,7 +53,7 @@ function registerModels(program) {
53
53
  const legacyModels = models.filter(m => m.legacy);
54
54
 
55
55
  if (opts.type !== 'all') {
56
- models = models.filter(m => m.type === opts.type);
56
+ models = models.filter(m => opts.type === 'embedding' ? m.type.startsWith('embedding') : m.type === opts.type);
57
57
  }
58
58
 
59
59
  if (!showLegacy) {
@@ -84,14 +84,14 @@ function registerModels(program) {
84
84
 
85
85
  const formatWideRow = (m) => {
86
86
  const name = ui.cyan(m.name);
87
- const type = m.type === 'embedding' ? ui.green(m.type) : ui.yellow(m.type);
87
+ const type = m.type.startsWith('embedding') ? ui.green(m.type) : ui.yellow(m.type);
88
88
  const price = ui.dim(m.price);
89
89
  return [name, type, m.context, m.dimensions, price, m.bestFor];
90
90
  };
91
91
 
92
92
  const formatCompactRow = (m) => {
93
93
  const name = ui.cyan(m.name);
94
- const type = m.type === 'embedding' ? ui.green('embed') : ui.yellow('rerank');
94
+ const type = m.type.startsWith('embedding') ? ui.green(m.multimodal ? 'multi' : 'embed') : ui.yellow('rerank');
95
95
  const dims = compactDimensions(m.dimensions);
96
96
  const price = ui.dim(compactPrice(m.price));
97
97
  return [name, type, dims, price, m.shortFor || m.bestFor];
@@ -13,7 +13,12 @@ function registerPing(program) {
13
13
  .description('Test connectivity to Voyage AI API (and optionally MongoDB)')
14
14
  .option('--json', 'Machine-readable JSON output')
15
15
  .option('-q, --quiet', 'Suppress non-essential output')
16
+ .option('--mask', 'Mask sensitive info (cluster hostnames, endpoints) in output. Also enabled by VAI_MASK=1 env var.')
16
17
  .action(async (opts) => {
18
+ // Support env var so all recordings are masked without remembering the flag
19
+ if (process.env.VAI_MASK === '1' || process.env.VAI_MASK === 'true') {
20
+ opts.mask = true;
21
+ }
17
22
  const results = {};
18
23
 
19
24
  // ── Voyage AI ping ──
@@ -28,6 +33,31 @@ function registerPing(program) {
28
33
  const useColor = !opts.json;
29
34
  const useSpinner = useColor && !opts.quiet;
30
35
 
36
+ // Masking helper: "performance.zbcul.mongodb.net" → "perfo*****.mongodb.net"
37
+ const PUBLIC_HOSTS = ['ai.mongodb.com', 'api.voyageai.com'];
38
+ const maskHost = (host) => {
39
+ if (!opts.mask || !host) return host;
40
+ if (PUBLIC_HOSTS.includes(host)) return host;
41
+ const parts = host.split('.');
42
+ if (parts.length >= 3) {
43
+ const name = parts[0];
44
+ const masked = name.slice(0, Math.min(5, name.length)) + '*****';
45
+ return [masked, ...parts.slice(1)].join('.');
46
+ }
47
+ return host.slice(0, 5) + '*****';
48
+ };
49
+
50
+ const maskUrl = (url) => {
51
+ if (!opts.mask || !url) return url;
52
+ try {
53
+ const u = new URL(url);
54
+ u.hostname = maskHost(u.hostname);
55
+ return u.toString().replace(/\/$/, '');
56
+ } catch {
57
+ return url;
58
+ }
59
+ };
60
+
31
61
  const apiBase = getApiBase();
32
62
  const model = 'voyage-4-lite';
33
63
  const startTime = Date.now();
@@ -94,7 +124,7 @@ function registerPing(program) {
94
124
  console.log(`ok ${elapsed}ms`);
95
125
  } else {
96
126
  console.log(ui.success(`Connected to Voyage AI API ${ui.dim('(' + elapsed + 'ms)')}`));
97
- console.log(ui.label('Endpoint', apiBase));
127
+ console.log(ui.label('Endpoint', maskUrl(apiBase)));
98
128
  console.log(ui.label('Model', model));
99
129
  console.log(ui.label('Dimensions', String(dims)));
100
130
  console.log(ui.label('Tokens', String(tokens)));
@@ -145,7 +175,7 @@ function registerPing(program) {
145
175
  if (!opts.json && !opts.quiet) {
146
176
  console.log('');
147
177
  console.log(ui.success(`Connected to MongoDB Atlas ${ui.dim('(' + mongoElapsed + 'ms)')}`));
148
- console.log(ui.label('Cluster', cluster));
178
+ console.log(ui.label('Cluster', maskHost(cluster)));
149
179
  }
150
180
 
151
181
  await client.close();
@@ -84,7 +84,7 @@ function createPlaygroundServer() {
84
84
 
85
85
  // API: Models
86
86
  if (req.method === 'GET' && req.url === '/api/models') {
87
- const models = MODEL_CATALOG.filter(m => !m.legacy && !m.local);
87
+ const models = MODEL_CATALOG.filter(m => !m.legacy && !m.local && !m.unreleased);
88
88
  res.writeHead(200, { 'Content-Type': 'application/json' });
89
89
  res.end(JSON.stringify({ models }));
90
90
  return;
@@ -16,8 +16,8 @@ function registerSearch(program) {
16
16
  .requiredOption('--query <text>', 'Search query text')
17
17
  .requiredOption('--db <database>', 'Database name')
18
18
  .requiredOption('--collection <name>', 'Collection name')
19
- .requiredOption('--index <name>', 'Vector search index name')
20
- .requiredOption('--field <name>', 'Embedding field name')
19
+ .option('--index <name>', 'Vector search index name', 'vector_index')
20
+ .option('--field <name>', 'Embedding field name', 'embedding')
21
21
  .option('-m, --model <model>', 'Embedding model', getDefaultModel())
22
22
  .option('--input-type <type>', 'Input type for query embedding', 'query')
23
23
  .option('-d, --dimensions <n>', 'Output dimensions', (v) => parseInt(v, 10))
@@ -17,7 +17,7 @@ function registerStore(program) {
17
17
  .description('Embed text and store in MongoDB Atlas')
18
18
  .requiredOption('--db <database>', 'Database name')
19
19
  .requiredOption('--collection <name>', 'Collection name')
20
- .requiredOption('--field <name>', 'Embedding field name')
20
+ .option('--field <name>', 'Embedding field name', 'embedding')
21
21
  .option('--text <text>', 'Text to embed and store')
22
22
  .option('-f, --file <path>', 'File to embed and store (text file or .jsonl for batch mode)')
23
23
  .option('-m, --model <model>', 'Embedding model', getDefaultModel())
package/src/lib/api.js CHANGED
@@ -96,25 +96,29 @@ async function apiRequest(endpoint, body) {
96
96
  } catch {
97
97
  errorDetail = await response.text();
98
98
  }
99
- console.error(`API Error (${response.status}): ${errorDetail}`);
99
+ const errMsg = `API Error (${response.status}): ${errorDetail}`;
100
100
 
101
101
  // Help users diagnose endpoint mismatch
102
+ let hint = '';
102
103
  if (response.status === 403 && base === ATLAS_API_BASE) {
103
- console.error('');
104
- console.error('Hint: 403 on ai.mongodb.com often means your key is for the Voyage AI');
105
- console.error('platform, not MongoDB Atlas. Try switching the base URL:');
106
- console.error('');
107
- console.error(' vai config set base-url https://api.voyageai.com/v1/');
108
- console.error('');
109
- console.error('Or set VOYAGE_API_BASE=https://api.voyageai.com/v1/ in your environment.');
104
+ hint = '\n\nHint: 403 on ai.mongodb.com often means your key is for the Voyage AI' +
105
+ '\nplatform, not MongoDB Atlas. Try switching the base URL:' +
106
+ '\n\n vai config set base-url https://api.voyageai.com/v1/' +
107
+ '\n\nOr set VOYAGE_API_BASE=https://api.voyageai.com/v1/ in your environment.';
110
108
  } else if (response.status === 401 && base === VOYAGE_API_BASE) {
111
- console.error('');
112
- console.error('Hint: 401 on api.voyageai.com may mean your key is an Atlas AI key.');
113
- console.error('Try switching back:');
114
- console.error('');
115
- console.error(' vai config set base-url https://ai.mongodb.com/v1/');
109
+ hint = '\n\nHint: 401 on api.voyageai.com may mean your key is an Atlas AI key.' +
110
+ '\nTry switching back:' +
111
+ '\n\n vai config set base-url https://ai.mongodb.com/v1/';
116
112
  }
117
- process.exit(1);
113
+
114
+ // Log the error + hint to stderr for CLI users
115
+ console.error(errMsg);
116
+ if (hint) console.error(hint);
117
+
118
+ // Throw instead of process.exit so callers (like playground) can catch gracefully
119
+ const err = new Error(errMsg);
120
+ err.statusCode = response.status;
121
+ throw err;
118
122
  }
119
123
 
120
124
  return response.json();
@@ -32,8 +32,8 @@ const MODEL_CATALOG = [
32
32
  { name: 'voyage-code-3', type: 'embedding', context: '32K', dimensions: '1024 (default), 256, 512, 2048', price: '$0.18/1M tokens', bestFor: 'Code retrieval', shortFor: 'Code' },
33
33
  { name: 'voyage-finance-2', type: 'embedding', context: '32K', dimensions: '1024', price: '$0.12/1M tokens', bestFor: 'Finance', shortFor: 'Finance' },
34
34
  { name: 'voyage-law-2', type: 'embedding', context: '16K', dimensions: '1024', price: '$0.12/1M tokens', bestFor: 'Legal', shortFor: 'Legal' },
35
- { name: 'voyage-context-3', type: 'embedding', context: '32K', dimensions: '1024 (default), 256, 512, 2048', price: '$0.18/1M tokens', bestFor: 'Contextualized chunks', shortFor: 'Context chunks' },
36
- { name: 'voyage-multimodal-3.5', type: 'embedding', context: '32K', dimensions: '1024 (default), 256, 512, 2048', price: '$0.12/M + $0.60/B px', bestFor: 'Text + images + video', shortFor: 'Multimodal' },
35
+ { name: 'voyage-context-3', type: 'embedding', context: '32K', dimensions: '1024 (default), 256, 512, 2048', price: '$0.18/1M tokens', bestFor: 'Contextualized chunks', shortFor: 'Context chunks', unreleased: true },
36
+ { name: 'voyage-multimodal-3.5', type: 'embedding-multimodal', context: '32K', dimensions: '1024 (default), 256, 512, 2048', price: '$0.12/M + $0.60/B px', bestFor: 'Text + images + video', shortFor: 'Multimodal', multimodal: true },
37
37
  { name: 'rerank-2.5', type: 'reranking', context: '32K', dimensions: '—', price: '$0.05/1M tokens', bestFor: 'Best quality reranking', shortFor: 'Best reranker' },
38
38
  { name: 'rerank-2.5-lite', type: 'reranking', context: '32K', dimensions: '—', price: '$0.02/1M tokens', bestFor: 'Fast reranking', shortFor: 'Fast reranker' },
39
39
  { name: 'voyage-4-nano', type: 'embedding', context: '32K', dimensions: '512 (default), 128, 256', price: 'Open-weight', bestFor: 'Open-weight / edge', shortFor: 'Open / edge', local: true },
@@ -42,7 +42,7 @@ const MODEL_CATALOG = [
42
42
  { name: 'voyage-3.5', type: 'embedding', context: '32K', dimensions: '1024 (default), 256, 512, 2048', price: '$0.06/1M tokens', bestFor: 'Previous gen balanced', shortFor: 'Previous gen balanced', legacy: true },
43
43
  { name: 'voyage-3.5-lite', type: 'embedding', context: '32K', dimensions: '1024 (default), 256, 512, 2048', price: '$0.02/1M tokens', bestFor: 'Previous gen budget', shortFor: 'Previous gen budget', legacy: true },
44
44
  { name: 'voyage-code-2', type: 'embedding', context: '16K', dimensions: '1536', price: '$0.12/1M tokens', bestFor: 'Legacy code', shortFor: 'Legacy code', legacy: true },
45
- { name: 'voyage-multimodal-3', type: 'embedding', context: '32K', dimensions: '1024', price: '$0.12/1M tokens', bestFor: 'Legacy multimodal', shortFor: 'Legacy multimodal', legacy: true },
45
+ { name: 'voyage-multimodal-3', type: 'embedding-multimodal', context: '32K', dimensions: '1024', price: '$0.12/1M tokens', bestFor: 'Legacy multimodal', shortFor: 'Legacy multimodal', legacy: true, multimodal: true },
46
46
  { name: 'rerank-2', type: 'reranking', context: '16K', dimensions: '—', price: '$0.05/1M tokens', bestFor: 'Legacy reranker', shortFor: 'Legacy reranker', legacy: true },
47
47
  { name: 'rerank-2-lite', type: 'reranking', context: '8K', dimensions: '—', price: '$0.02/1M tokens', bestFor: 'Legacy fast reranker', shortFor: 'Legacy fast reranker', legacy: true },
48
48
  ];
@@ -1,22 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
8
- workflow_dispatch:
9
-
10
- jobs:
11
- test:
12
- runs-on: ubuntu-latest
13
- strategy:
14
- matrix:
15
- node-version: [18, 20, 22]
16
- steps:
17
- - uses: actions/checkout@v4
18
- - uses: actions/setup-node@v4
19
- with:
20
- node-version: ${{ matrix.node-version }}
21
- - run: npm ci
22
- - run: npm test
package/CONTRIBUTING.md DELETED
@@ -1,81 +0,0 @@
1
- # Contributing to voyageai-cli
2
-
3
- Thanks for your interest in contributing! Here's how to get started.
4
-
5
- ## Development Setup
6
-
7
- ```bash
8
- git clone https://github.com/mrlynn/voyageai-cli.git
9
- cd voyageai-cli
10
- npm install
11
- npm link # makes `vai` available globally for testing
12
- ```
13
-
14
- ## Running Tests
15
-
16
- ```bash
17
- npm test
18
- ```
19
-
20
- Tests use Node.js built-in test runner (`node:test`). No external test framework needed.
21
-
22
- ## Project Structure
23
-
24
- ```
25
- src/
26
- ├── cli.js # Entry point
27
- ├── commands/ # One file per command
28
- │ ├── embed.js
29
- │ ├── rerank.js
30
- │ ├── store.js
31
- │ ├── search.js
32
- │ ├── index.js
33
- │ ├── models.js
34
- │ ├── ping.js
35
- │ ├── config.js
36
- │ └── demo.js
37
- └── lib/ # Shared utilities
38
- ├── api.js # Voyage AI API client
39
- ├── mongo.js # MongoDB connection
40
- ├── catalog.js # Model catalog
41
- ├── config.js # Config file management
42
- ├── format.js # Table formatting
43
- ├── input.js # Text input resolution
44
- ├── ui.js # Colors, spinners, output helpers
45
- └── banner.js # ASCII banner
46
- test/
47
- ├── commands/ # Command tests
48
- └── lib/ # Library tests
49
- ```
50
-
51
- ## Adding a New Command
52
-
53
- 1. Create `src/commands/mycommand.js` exporting a `registerMyCommand(program)` function
54
- 2. Register it in `src/cli.js`
55
- 3. Add tests in `test/commands/mycommand.test.js`
56
- 4. Update README.md with usage examples
57
-
58
- ## Code Style
59
-
60
- - CommonJS (`require`/`module.exports`)
61
- - `'use strict';` at the top of every file
62
- - JSDoc comments on exported functions
63
- - `parseInt(x, 10)` — always include radix
64
- - Errors go to stderr (`console.error`)
65
- - Support `--json` and `--quiet` flags on all commands
66
- - No colors or spinners in `--json` mode
67
-
68
- ## Pull Requests
69
-
70
- - Create a feature branch from `main`
71
- - Include tests for new functionality
72
- - Run `npm test` before submitting
73
- - Write clear commit messages
74
-
75
- ## Reporting Issues
76
-
77
- Open an issue at https://github.com/mrlynn/voyageai-cli/issues with:
78
- - Node.js version (`node --version`)
79
- - OS and version
80
- - Steps to reproduce
81
- - Expected vs actual behavior
package/demo.gif DELETED
Binary file
package/demo.tape DELETED
@@ -1,39 +0,0 @@
1
- # VHS demo tape for voyageai-cli
2
- # Run: vhs demo.tape
3
- # Requires: VOYAGE_API_KEY set in environment
4
-
5
- Output demo.gif
6
-
7
- Set FontSize 16
8
- Set Width 900
9
- Set Height 600
10
- Set Theme "Catppuccin Mocha"
11
- Set Padding 20
12
-
13
- # Show version
14
- Type "vai --version"
15
- Enter
16
- Sleep 1.5s
17
-
18
- # List embedding models
19
- Type "vai models --type embedding"
20
- Enter
21
- Sleep 3s
22
-
23
- # Generate an embedding
24
- Type 'vai embed "What is MongoDB Atlas?"'
25
- Enter
26
- Sleep 4s
27
-
28
- # Explain embeddings (first few lines)
29
- Type "vai explain embeddings"
30
- Enter
31
- Sleep 4s
32
-
33
- # Compare similarity
34
- Type 'vai similarity "MongoDB is great" "MongoDB Atlas is amazing"'
35
- Enter
36
- Sleep 4s
37
-
38
- # Pause at end to show results
39
- Sleep 2s
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Record a demo GIF for voyageai-cli
3
- # Requires: VOYAGE_API_KEY environment variable
4
- #
5
- # Usage:
6
- # ./scripts/record-demo.sh # Uses vhs (preferred)
7
- # ./scripts/record-demo.sh asciinema # Uses asciinema instead
8
- #
9
- # Output:
10
- # demo.gif (vhs) or demo.cast (asciinema)
11
-
12
- set -euo pipefail
13
- cd "$(dirname "$0")/.."
14
-
15
- METHOD="${1:-vhs}"
16
-
17
- if [ "$METHOD" = "vhs" ]; then
18
- if ! command -v vhs &>/dev/null; then
19
- echo "❌ vhs not found. Install: brew install charmbracelet/tap/vhs"
20
- echo " Or run: ./scripts/record-demo.sh asciinema"
21
- exit 1
22
- fi
23
-
24
- if [ -z "${VOYAGE_API_KEY:-}" ]; then
25
- echo "⚠️ VOYAGE_API_KEY not set. Commands that call the API will fail."
26
- echo " Set it: export VOYAGE_API_KEY=your-key"
27
- exit 1
28
- fi
29
-
30
- echo "🎬 Recording demo with vhs..."
31
- vhs demo.tape
32
- echo "✅ Demo GIF saved to demo.gif"
33
-
34
- elif [ "$METHOD" = "asciinema" ]; then
35
- if ! command -v asciinema &>/dev/null; then
36
- echo "❌ asciinema not found. Install: brew install asciinema"
37
- exit 1
38
- fi
39
-
40
- CAST_FILE="demo.cast"
41
- echo "🎬 Recording demo with asciinema..."
42
- echo " Run the following commands, then press Ctrl-D when done:"
43
- echo ""
44
- echo " vai --version"
45
- echo " vai models --type embedding"
46
- echo ' vai embed "What is MongoDB Atlas?"'
47
- echo " vai explain embeddings"
48
- echo ' vai similarity "MongoDB is great" "MongoDB Atlas is amazing"'
49
- echo ""
50
-
51
- asciinema rec "$CAST_FILE"
52
- echo "✅ Recording saved to $CAST_FILE"
53
- echo ""
54
- echo "Convert to GIF with agg or svg-term-cli:"
55
- echo " agg $CAST_FILE demo.gif"
56
- echo " # or"
57
- echo " npx svg-term-cli --in $CAST_FILE --out demo.svg --window"
58
-
59
- else
60
- echo "Unknown method: $METHOD"
61
- echo "Usage: $0 [vhs|asciinema]"
62
- exit 1
63
- fi
@@ -1,23 +0,0 @@
1
- 'use strict';
2
-
3
- const { describe, it } = require('node:test');
4
- const assert = require('node:assert/strict');
5
- const { Command } = require('commander');
6
- const { registerAbout } = require('../../src/commands/about');
7
-
8
- describe('about command', () => {
9
- it('registers correctly on a program', () => {
10
- const program = new Command();
11
- registerAbout(program);
12
- const aboutCmd = program.commands.find(c => c.name() === 'about');
13
- assert.ok(aboutCmd, 'about command should be registered');
14
- });
15
-
16
- it('has --json option', () => {
17
- const program = new Command();
18
- registerAbout(program);
19
- const aboutCmd = program.commands.find(c => c.name() === 'about');
20
- const optionNames = aboutCmd.options.map(o => o.long);
21
- assert.ok(optionNames.includes('--json'), 'should have --json option');
22
- });
23
- });