ruvector 0.2.8 → 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) {
@@ -8279,6 +8282,7 @@ async function fetchBrainEndpoint(config, endpoint) {
8279
8282
  if (config.key) headers['Authorization'] = `Bearer ${config.key}`;
8280
8283
  const resp = await proxyFetch(url, { headers, signal: AbortSignal.timeout(30000) });
8281
8284
  if (!resp.ok) throw new Error(`${resp.status} ${resp.statusText}`);
8285
+ if (resp.status === 204 || resp.headers.get('content-length') === '0') return {};
8282
8286
  return resp.json();
8283
8287
  }
8284
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.8',
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.8'
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.8",
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",