ruvector 0.2.7 → 0.2.9

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/bin/cli.js CHANGED
@@ -159,7 +159,7 @@ async function proxyFetch(url, opts) {
159
159
 
160
160
  // Fallback: shell out to curl (which natively respects proxy env vars)
161
161
  const { execFileSync } = require('child_process');
162
- const args = ['-sS', '-L', '--max-time', '30'];
162
+ const args = ['-sS', '-L', '--max-time', '30', '-w', '\n%{http_code}'];
163
163
  if (opts && opts.method) { args.push('-X', opts.method); }
164
164
  if (opts && opts.headers) {
165
165
  for (const [k, v] of Object.entries(opts.headers)) {
@@ -170,13 +170,16 @@ async function proxyFetch(url, opts) {
170
170
  args.push(target);
171
171
  try {
172
172
  const stdout = execFileSync('curl', args, { encoding: 'utf8', timeout: 35000 });
173
- const body = stdout.trim();
173
+ const lines = stdout.trimEnd().split('\n');
174
+ const statusCode = parseInt(lines.pop(), 10) || 200;
175
+ const body = lines.join('\n').trim();
176
+ const ok = statusCode >= 200 && statusCode < 300;
174
177
  return {
175
- ok: true,
176
- status: 200,
177
- statusText: 'OK',
178
+ ok,
179
+ status: statusCode,
180
+ statusText: ok ? 'OK' : `HTTP ${statusCode}`,
178
181
  text: async () => body,
179
- json: async () => JSON.parse(body),
182
+ json: async () => body ? JSON.parse(body) : {},
180
183
  headers: new Map(),
181
184
  };
182
185
  } catch (e) {
@@ -7952,6 +7955,7 @@ async function brainFetch(config, endpoint, opts = {}) {
7952
7955
  const errText = await resp.text().catch(() => resp.statusText);
7953
7956
  throw new Error(`${resp.status} ${errText}`);
7954
7957
  }
7958
+ if (resp.status === 204 || resp.headers.get('content-length') === '0') return {};
7955
7959
  return resp.json();
7956
7960
  }
7957
7961
 
@@ -7997,10 +8001,12 @@ brainCmd.command('share <title>')
7997
8001
  .option('--code <snippet>', 'Code snippet')
7998
8002
  .option('--url <url>', 'Brain server URL')
7999
8003
  .option('--key <key>', 'Pi key')
8004
+ .option('--json', 'Output as JSON')
8000
8005
  .action(async (title, opts) => {
8001
8006
  const config = getBrainConfig(opts);
8002
8007
  try {
8003
8008
  const result = await brainFetch(config, '/v1/memories', { body: { title, content: opts.content || title, category: opts.category, tags: opts.tags ? opts.tags.split(',').map(t => t.trim()) : [], code_snippet: opts.code } });
8009
+ if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(result, null, 2)); return; }
8004
8010
  console.log(chalk.green(`Shared: ${result.id || 'OK'}`));
8005
8011
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
8006
8012
  });
@@ -8027,10 +8033,12 @@ brainCmd.command('vote <id> <direction>')
8027
8033
  .description('Quality vote on a memory (up or down)')
8028
8034
  .option('--url <url>', 'Brain server URL')
8029
8035
  .option('--key <key>', 'Pi key')
8036
+ .option('--json', 'Output as JSON')
8030
8037
  .action(async (id, direction, opts) => {
8031
8038
  const config = getBrainConfig(opts);
8032
8039
  try {
8033
- await brainFetch(config, `/v1/memories/${id}/vote`, { body: { direction } });
8040
+ const result = await brainFetch(config, `/v1/memories/${id}/vote`, { body: { direction } });
8041
+ if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(result, null, 2)); return; }
8034
8042
  console.log(chalk.green(`Voted ${direction} on ${id}`));
8035
8043
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
8036
8044
  });
@@ -8067,10 +8075,12 @@ brainCmd.command('delete <id>')
8067
8075
  .description('Delete your own contribution')
8068
8076
  .option('--url <url>', 'Brain server URL')
8069
8077
  .option('--key <key>', 'Pi key')
8078
+ .option('--json', 'Output as JSON')
8070
8079
  .action(async (id, opts) => {
8071
8080
  const config = getBrainConfig(opts);
8072
8081
  try {
8073
- await brainFetch(config, `/v1/memories/${id}`, { method: 'DELETE' });
8082
+ const result = await brainFetch(config, `/v1/memories/${id}`, { method: 'DELETE' });
8083
+ if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(result, null, 2)); return; }
8074
8084
  console.log(chalk.green(`Deleted: ${id}`));
8075
8085
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
8076
8086
  });
@@ -8165,10 +8175,12 @@ brainCmd.command('sync [direction]')
8165
8175
  .description('Synchronize LoRA weights (pull, push, or both)')
8166
8176
  .option('--url <url>', 'Brain server URL')
8167
8177
  .option('--key <key>', 'Pi key')
8178
+ .option('--json', 'Output as JSON')
8168
8179
  .action(async (direction, opts) => {
8169
8180
  const config = getBrainConfig(opts);
8170
8181
  try {
8171
8182
  const result = await brainFetch(config, '/v1/lora/latest', { params: { direction: direction || 'both' } });
8183
+ if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(result, null, 2)); return; }
8172
8184
  console.log(chalk.green(`Sync ${direction || 'both'}: ${result.status || 'OK'}`));
8173
8185
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
8174
8186
  });
@@ -8270,6 +8282,7 @@ async function fetchBrainEndpoint(config, endpoint) {
8270
8282
  if (config.key) headers['Authorization'] = `Bearer ${config.key}`;
8271
8283
  const resp = await proxyFetch(url, { headers, signal: AbortSignal.timeout(30000) });
8272
8284
  if (!resp.ok) throw new Error(`${resp.status} ${resp.statusText}`);
8285
+ if (resp.status === 204 || resp.headers.get('content-length') === '0') return {};
8273
8286
  return resp.json();
8274
8287
  }
8275
8288
 
package/bin/mcp-server.js CHANGED
@@ -428,7 +428,7 @@ class Intelligence {
428
428
  const server = new Server(
429
429
  {
430
430
  name: 'ruvector',
431
- version: '0.2.7',
431
+ version: '0.2.9',
432
432
  },
433
433
  {
434
434
  capabilities: {
@@ -1395,7 +1395,10 @@ const TOOLS = [
1395
1395
  type: 'object',
1396
1396
  properties: {
1397
1397
  category: { type: 'string', description: 'Filter by category' },
1398
- limit: { type: 'number', description: 'Max results (default 20)' }
1398
+ limit: { type: 'number', description: 'Max results (default 20)' },
1399
+ offset: { type: 'number', description: 'Skip first N results for pagination (default 0)' },
1400
+ sort: { type: 'string', description: 'Sort by: updated_at, quality, votes (default updated_at)' },
1401
+ tags: { type: 'string', description: 'Filter by tags (comma-separated)' }
1399
1402
  }
1400
1403
  }
1401
1404
  },
@@ -1492,6 +1495,117 @@ const TOOLS = [
1492
1495
  description: 'Show backend feature flag state (RVF, AGI, midstream flags)',
1493
1496
  inputSchema: { type: 'object', properties: {} }
1494
1497
  },
1498
+ // ── Brainpedia Page Tools (5) ── Knowledge page management ──
1499
+ {
1500
+ name: 'brain_page_list',
1501
+ description: 'List Brainpedia knowledge pages with pagination and status filter',
1502
+ inputSchema: {
1503
+ type: 'object',
1504
+ properties: {
1505
+ limit: { type: 'number', description: 'Max pages to return (default 20)' },
1506
+ offset: { type: 'number', description: 'Skip first N pages for pagination (default 0)' },
1507
+ status: { type: 'string', description: 'Filter by status: draft, canonical, contested, archived' }
1508
+ }
1509
+ }
1510
+ },
1511
+ {
1512
+ name: 'brain_page_get',
1513
+ description: 'Get a Brainpedia page by ID with delta log and evidence links',
1514
+ inputSchema: {
1515
+ type: 'object',
1516
+ properties: {
1517
+ id: { type: 'string', description: 'Page ID (UUID or slug)' }
1518
+ },
1519
+ required: ['id']
1520
+ }
1521
+ },
1522
+ {
1523
+ name: 'brain_page_create',
1524
+ description: 'Create a new Brainpedia knowledge page (requires reputation >= 0.5)',
1525
+ inputSchema: {
1526
+ type: 'object',
1527
+ properties: {
1528
+ title: { type: 'string', description: 'Page title' },
1529
+ content: { type: 'string', description: 'Page content body' },
1530
+ category: { type: 'string', description: 'Category (pattern, solution, architecture, convention, security, performance, tooling)' },
1531
+ tags: { type: 'string', description: 'Comma-separated tags' },
1532
+ code_snippet: { type: 'string', description: 'Optional code snippet' }
1533
+ },
1534
+ required: ['title', 'content', 'category']
1535
+ }
1536
+ },
1537
+ {
1538
+ name: 'brain_page_update',
1539
+ description: 'Submit a delta (correction, extension, evidence) to a Brainpedia page',
1540
+ inputSchema: {
1541
+ type: 'object',
1542
+ properties: {
1543
+ page_id: { type: 'string', description: 'Page ID to update' },
1544
+ delta_type: { type: 'string', description: 'Delta type: correction, extension, evidence, deprecation' },
1545
+ content_diff: { type: 'string', description: 'Content diff or new content' },
1546
+ evidence_links: { type: 'string', description: 'JSON array of evidence links' }
1547
+ },
1548
+ required: ['page_id', 'content_diff']
1549
+ }
1550
+ },
1551
+ {
1552
+ name: 'brain_page_delete',
1553
+ description: 'Delete a Brainpedia page by ID',
1554
+ inputSchema: {
1555
+ type: 'object',
1556
+ properties: {
1557
+ id: { type: 'string', description: 'Page ID to delete' }
1558
+ },
1559
+ required: ['id']
1560
+ }
1561
+ },
1562
+ // ── WASM Node Tools (4) ── Executable compute node management ──
1563
+ {
1564
+ name: 'brain_node_list',
1565
+ description: 'List published WASM compute nodes',
1566
+ inputSchema: {
1567
+ type: 'object',
1568
+ properties: {
1569
+ limit: { type: 'number', description: 'Max nodes to return (default 20)' }
1570
+ }
1571
+ }
1572
+ },
1573
+ {
1574
+ name: 'brain_node_get',
1575
+ description: 'Get WASM compute node metadata and conformance vectors',
1576
+ inputSchema: {
1577
+ type: 'object',
1578
+ properties: {
1579
+ id: { type: 'string', description: 'Node ID' }
1580
+ },
1581
+ required: ['id']
1582
+ }
1583
+ },
1584
+ {
1585
+ name: 'brain_node_publish',
1586
+ description: 'Publish a WASM compute node to the shared brain network',
1587
+ inputSchema: {
1588
+ type: 'object',
1589
+ properties: {
1590
+ name: { type: 'string', description: 'Node name' },
1591
+ wasm_base64: { type: 'string', description: 'Base64-encoded WASM binary' },
1592
+ description: { type: 'string', description: 'Node description' },
1593
+ conformance_vectors: { type: 'string', description: 'JSON array of conformance test vectors' }
1594
+ },
1595
+ required: ['name', 'wasm_base64']
1596
+ }
1597
+ },
1598
+ {
1599
+ name: 'brain_node_revoke',
1600
+ description: 'Revoke a published WASM compute node',
1601
+ inputSchema: {
1602
+ type: 'object',
1603
+ properties: {
1604
+ id: { type: 'string', description: 'Node ID to revoke' }
1605
+ },
1606
+ required: ['id']
1607
+ }
1608
+ },
1495
1609
  // ── Midstream Tools (6) ── Real-time streaming analysis platform ──
1496
1610
  {
1497
1611
  name: 'midstream_status',
@@ -3416,20 +3530,127 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3416
3530
  fetchOpts.body = JSON.stringify({ source_domain: args.source_domain, target_domain: args.target_domain });
3417
3531
  break;
3418
3532
  }
3419
- case 'sync': url = `${brainUrl}/v1/lora/latest`; break;
3533
+ case 'sync': {
3534
+ const p = new URLSearchParams();
3535
+ if (args.direction) p.set('direction', args.direction);
3536
+ url = `${brainUrl}/v1/lora/latest${p.toString() ? '?' + p : ''}`;
3537
+ break;
3538
+ }
3420
3539
  }
3421
3540
  const resp = await proxyFetch(url, fetchOpts);
3422
3541
  if (!resp.ok) {
3423
3542
  const errText = await resp.text().catch(() => resp.statusText);
3424
3543
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3425
3544
  }
3426
- const result = await resp.json();
3545
+ const result = (resp.status === 204 || resp.headers.get('content-length') === '0') ? {} : await resp.json();
3427
3546
  return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3428
3547
  } catch (e) {
3429
3548
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3430
3549
  }
3431
3550
  }
3432
3551
 
3552
+ // ── Brainpedia Page Tool Handlers ────────────────────────────────────
3553
+ case 'brain_page_list':
3554
+ case 'brain_page_get':
3555
+ case 'brain_page_create':
3556
+ case 'brain_page_update':
3557
+ case 'brain_page_delete': {
3558
+ try {
3559
+ const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3560
+ const brainKey = process.env.PI;
3561
+ const hdrs = { 'Content-Type': 'application/json' };
3562
+ if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3563
+ let url, fetchOpts = { headers: hdrs, signal: AbortSignal.timeout(30000) };
3564
+ const subCmd = name.replace('brain_page_', '');
3565
+ switch (subCmd) {
3566
+ case 'list': {
3567
+ const p = new URLSearchParams();
3568
+ if (args.limit) p.set('limit', String(args.limit));
3569
+ if (args.offset) p.set('offset', String(args.offset));
3570
+ if (args.status) p.set('status', args.status);
3571
+ url = `${brainUrl}/v1/pages${p.toString() ? '?' + p : ''}`;
3572
+ break;
3573
+ }
3574
+ case 'get': url = `${brainUrl}/v1/pages/${args.id}`; break;
3575
+ case 'create': {
3576
+ url = `${brainUrl}/v1/pages`;
3577
+ fetchOpts.method = 'POST';
3578
+ fetchOpts.body = JSON.stringify({ title: args.title, content: args.content, category: args.category, tags: args.tags ? args.tags.split(',').map(t => t.trim()) : [], code_snippet: args.code_snippet, evidence_links: [], embedding: [], witness_hash: '' });
3579
+ break;
3580
+ }
3581
+ case 'update': {
3582
+ url = `${brainUrl}/v1/pages/${args.page_id}/deltas`;
3583
+ fetchOpts.method = 'POST';
3584
+ let evidence = [];
3585
+ try { if (args.evidence_links) evidence = JSON.parse(args.evidence_links); } catch {}
3586
+ fetchOpts.body = JSON.stringify({ delta_type: args.delta_type || 'extension', content_diff: args.content_diff, evidence_links: evidence, witness_hash: '' });
3587
+ break;
3588
+ }
3589
+ case 'delete': {
3590
+ url = `${brainUrl}/v1/pages/${args.id}`;
3591
+ fetchOpts.method = 'DELETE';
3592
+ break;
3593
+ }
3594
+ }
3595
+ const resp = await proxyFetch(url, fetchOpts);
3596
+ if (!resp.ok) {
3597
+ const errText = await resp.text().catch(() => resp.statusText);
3598
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3599
+ }
3600
+ const result = (resp.status === 204 || resp.headers.get('content-length') === '0') ? {} : await resp.json();
3601
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3602
+ } catch (e) {
3603
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3604
+ }
3605
+ }
3606
+
3607
+ // ── WASM Node Tool Handlers ────────────────────────────────────────────
3608
+ case 'brain_node_list':
3609
+ case 'brain_node_get':
3610
+ case 'brain_node_publish':
3611
+ case 'brain_node_revoke': {
3612
+ try {
3613
+ const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3614
+ const brainKey = process.env.PI;
3615
+ const hdrs = { 'Content-Type': 'application/json' };
3616
+ if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3617
+ let url, fetchOpts = { headers: hdrs, signal: AbortSignal.timeout(30000) };
3618
+ const subCmd = name.replace('brain_node_', '');
3619
+ switch (subCmd) {
3620
+ case 'list': {
3621
+ const p = new URLSearchParams();
3622
+ if (args.limit) p.set('limit', String(args.limit));
3623
+ url = `${brainUrl}/v1/nodes${p.toString() ? '?' + p : ''}`;
3624
+ break;
3625
+ }
3626
+ case 'get': url = `${brainUrl}/v1/nodes/${args.id}`; break;
3627
+ case 'publish': {
3628
+ url = `${brainUrl}/v1/nodes`;
3629
+ fetchOpts.method = 'POST';
3630
+ let vectors = [];
3631
+ try { if (args.conformance_vectors) vectors = JSON.parse(args.conformance_vectors); } catch {}
3632
+ fetchOpts.body = JSON.stringify({ name: args.name, wasm_base64: args.wasm_base64, description: args.description || '', conformance_vectors: vectors });
3633
+ break;
3634
+ }
3635
+ case 'revoke': {
3636
+ url = `${brainUrl}/v1/nodes/${args.id}/revoke`;
3637
+ fetchOpts.method = 'POST';
3638
+ fetchOpts.body = JSON.stringify({});
3639
+ break;
3640
+ }
3641
+ }
3642
+ const resp = await proxyFetch(url, fetchOpts);
3643
+ if (!resp.ok) {
3644
+ const errText = await resp.text().catch(() => resp.statusText);
3645
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3646
+ }
3647
+ const result = (resp.status === 204 || resp.headers.get('content-length') === '0') ? {} : await resp.json();
3648
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...(Array.isArray(result) ? { items: result, count: result.length } : result) }, null, 2) }] };
3649
+ } catch (e) {
3650
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3651
+ }
3652
+ }
3653
+
3433
3654
  // ── Brain AGI Tool Handlers ────────────────────────────────────────────
3434
3655
  case 'brain_agi_status':
3435
3656
  case 'brain_sona_stats':
@@ -3899,7 +4120,7 @@ async function main() {
3899
4120
  transport: 'sse',
3900
4121
  sessions: sessions.size,
3901
4122
  tools: 91,
3902
- version: '0.2.7'
4123
+ version: '0.2.9'
3903
4124
  }));
3904
4125
 
3905
4126
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvector",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "High-performance vector database for Node.js with automatic native/WASM fallback",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",