ruvector 0.2.4 → 0.2.6

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
@@ -7851,19 +7851,9 @@ mcpCmd.command('test')
7851
7851
  });
7852
7852
 
7853
7853
  // ============================================================================
7854
- // Brain Commands — Shared intelligence via @ruvector/pi-brain (lazy-loaded)
7854
+ // Brain Commands — Shared intelligence via pi.ruv.io REST API (direct fetch)
7855
7855
  // ============================================================================
7856
7856
 
7857
- async function requirePiBrain() {
7858
- try {
7859
- return require('@ruvector/pi-brain');
7860
- } catch {
7861
- console.error(chalk.red('Brain commands require @ruvector/pi-brain'));
7862
- console.error(chalk.yellow(' npm install @ruvector/pi-brain'));
7863
- process.exit(1);
7864
- }
7865
- }
7866
-
7867
7857
  function getBrainConfig(opts) {
7868
7858
  return {
7869
7859
  url: opts.url || process.env.BRAIN_URL || 'https://pi.ruv.io',
@@ -7871,6 +7861,30 @@ function getBrainConfig(opts) {
7871
7861
  };
7872
7862
  }
7873
7863
 
7864
+ function brainHeaders(config) {
7865
+ const h = { 'Content-Type': 'application/json' };
7866
+ if (config.key) h['Authorization'] = `Bearer ${config.key}`;
7867
+ return h;
7868
+ }
7869
+
7870
+ async function brainFetch(config, endpoint, opts = {}) {
7871
+ const url = new URL(endpoint, config.url);
7872
+ if (opts.params) {
7873
+ for (const [k, v] of Object.entries(opts.params)) {
7874
+ if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
7875
+ }
7876
+ }
7877
+ const fetchOpts = { headers: brainHeaders(config), signal: AbortSignal.timeout(30000) };
7878
+ if (opts.method) fetchOpts.method = opts.method;
7879
+ if (opts.body) { fetchOpts.method = opts.method || 'POST'; fetchOpts.body = JSON.stringify(opts.body); }
7880
+ const resp = await fetch(url.toString(), fetchOpts);
7881
+ if (!resp.ok) {
7882
+ const errText = await resp.text().catch(() => resp.statusText);
7883
+ throw new Error(`${resp.status} ${errText}`);
7884
+ }
7885
+ return resp.json();
7886
+ }
7887
+
7874
7888
  const brainCmd = program.command('brain').description('Shared intelligence — search, share, and manage collective knowledge');
7875
7889
 
7876
7890
  brainCmd.command('search <query>')
@@ -7882,11 +7896,9 @@ brainCmd.command('search <query>')
7882
7896
  .option('--json', 'Output as JSON')
7883
7897
  .option('--verbose', 'Show detailed scoring and metadata per result')
7884
7898
  .action(async (query, opts) => {
7885
- const piBrain = await requirePiBrain();
7886
7899
  const config = getBrainConfig(opts);
7887
7900
  try {
7888
- const client = new piBrain.PiBrainClient(config);
7889
- const results = await client.search(query, { category: opts.category, limit: parseInt(opts.limit) });
7901
+ const results = await brainFetch(config, '/v1/memories/search', { params: { q: query, category: opts.category, limit: opts.limit } });
7890
7902
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(results, null, 2)); return; }
7891
7903
  console.log(chalk.bold.cyan(`\nBrain Search: "${query}"\n`));
7892
7904
  if (!results.length) { console.log(chalk.dim(' No results found.\n')); return; }
@@ -7916,11 +7928,9 @@ brainCmd.command('share <title>')
7916
7928
  .option('--url <url>', 'Brain server URL')
7917
7929
  .option('--key <key>', 'Pi key')
7918
7930
  .action(async (title, opts) => {
7919
- const piBrain = await requirePiBrain();
7920
7931
  const config = getBrainConfig(opts);
7921
7932
  try {
7922
- const client = new piBrain.PiBrainClient(config);
7923
- const result = await client.share({ title, content: opts.content || title, category: opts.category, tags: opts.tags ? opts.tags.split(',').map(t => t.trim()) : [], code_snippet: opts.code });
7933
+ 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 } });
7924
7934
  console.log(chalk.green(`Shared: ${result.id || 'OK'}`));
7925
7935
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
7926
7936
  });
@@ -7931,11 +7941,9 @@ brainCmd.command('get <id>')
7931
7941
  .option('--key <key>', 'Pi key')
7932
7942
  .option('--json', 'Output as JSON')
7933
7943
  .action(async (id, opts) => {
7934
- const piBrain = await requirePiBrain();
7935
7944
  const config = getBrainConfig(opts);
7936
7945
  try {
7937
- const client = new piBrain.PiBrainClient(config);
7938
- const result = await client.get(id);
7946
+ const result = await brainFetch(config, `/v1/memories/${id}`);
7939
7947
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(result, null, 2)); return; }
7940
7948
  console.log(chalk.bold.cyan(`\nMemory: ${id}\n`));
7941
7949
  if (result.title) console.log(` ${chalk.bold('Title:')} ${result.title}`);
@@ -7950,11 +7958,9 @@ brainCmd.command('vote <id> <direction>')
7950
7958
  .option('--url <url>', 'Brain server URL')
7951
7959
  .option('--key <key>', 'Pi key')
7952
7960
  .action(async (id, direction, opts) => {
7953
- const piBrain = await requirePiBrain();
7954
7961
  const config = getBrainConfig(opts);
7955
7962
  try {
7956
- const client = new piBrain.PiBrainClient(config);
7957
- await client.vote(id, direction);
7963
+ await brainFetch(config, `/v1/memories/${id}/vote`, { body: { direction } });
7958
7964
  console.log(chalk.green(`Voted ${direction} on ${id}`));
7959
7965
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
7960
7966
  });
@@ -7967,11 +7973,9 @@ brainCmd.command('list')
7967
7973
  .option('--key <key>', 'Pi key')
7968
7974
  .option('--json', 'Output as JSON')
7969
7975
  .action(async (opts) => {
7970
- const piBrain = await requirePiBrain();
7971
7976
  const config = getBrainConfig(opts);
7972
7977
  try {
7973
- const client = new piBrain.PiBrainClient(config);
7974
- const results = await client.list({ category: opts.category, limit: parseInt(opts.limit) });
7978
+ const results = await brainFetch(config, '/v1/memories/list', { params: { category: opts.category, limit: opts.limit } });
7975
7979
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(results, null, 2)); return; }
7976
7980
  console.log(chalk.bold.cyan('\nShared Brain Memories\n'));
7977
7981
  if (!results.length) { console.log(chalk.dim(' No memories found.\n')); return; }
@@ -7987,11 +7991,9 @@ brainCmd.command('delete <id>')
7987
7991
  .option('--url <url>', 'Brain server URL')
7988
7992
  .option('--key <key>', 'Pi key')
7989
7993
  .action(async (id, opts) => {
7990
- const piBrain = await requirePiBrain();
7991
7994
  const config = getBrainConfig(opts);
7992
7995
  try {
7993
- const client = new piBrain.PiBrainClient(config);
7994
- await client.delete(id);
7996
+ await brainFetch(config, `/v1/memories/${id}`, { method: 'DELETE' });
7995
7997
  console.log(chalk.green(`Deleted: ${id}`));
7996
7998
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
7997
7999
  });
@@ -8002,11 +8004,9 @@ brainCmd.command('status')
8002
8004
  .option('--key <key>', 'Pi key')
8003
8005
  .option('--json', 'Output as JSON')
8004
8006
  .action(async (opts) => {
8005
- const piBrain = await requirePiBrain();
8006
8007
  const config = getBrainConfig(opts);
8007
8008
  try {
8008
- const client = new piBrain.PiBrainClient(config);
8009
- const status = await client.status();
8009
+ const status = await brainFetch(config, '/v1/status');
8010
8010
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(status, null, 2)); return; }
8011
8011
  console.log(chalk.bold.cyan('\nBrain Status\n'));
8012
8012
  Object.entries(status).forEach(([k, v]) => {
@@ -8037,11 +8037,9 @@ brainCmd.command('drift')
8037
8037
  .option('--key <key>', 'Pi key')
8038
8038
  .option('--json', 'Output as JSON')
8039
8039
  .action(async (opts) => {
8040
- const piBrain = await requirePiBrain();
8041
8040
  const config = getBrainConfig(opts);
8042
8041
  try {
8043
- const client = new piBrain.PiBrainClient(config);
8044
- const report = await client.drift({ domain: opts.domain });
8042
+ const report = await brainFetch(config, '/v1/drift', { params: { domain: opts.domain } });
8045
8043
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(report, null, 2)); return; }
8046
8044
  console.log(chalk.bold.cyan('\nDrift Report\n'));
8047
8045
  console.log(` ${chalk.bold('Drifting:')} ${report.is_drifting ? chalk.red('Yes') : chalk.green('No')}`);
@@ -8058,11 +8056,9 @@ brainCmd.command('partition')
8058
8056
  .option('--key <key>', 'Pi key')
8059
8057
  .option('--json', 'Output as JSON')
8060
8058
  .action(async (opts) => {
8061
- const piBrain = await requirePiBrain();
8062
8059
  const config = getBrainConfig(opts);
8063
8060
  try {
8064
- const client = new piBrain.PiBrainClient(config);
8065
- const result = await client.partition({ domain: opts.domain, min_cluster_size: parseInt(opts.minSize) });
8061
+ const result = await brainFetch(config, '/v1/partition', { params: { domain: opts.domain, min_cluster_size: opts.minSize } });
8066
8062
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(result, null, 2)); return; }
8067
8063
  console.log(chalk.bold.cyan('\nKnowledge Partitions\n'));
8068
8064
  if (result.clusters) {
@@ -8080,11 +8076,9 @@ brainCmd.command('transfer <source> <target>')
8080
8076
  .option('--key <key>', 'Pi key')
8081
8077
  .option('--json', 'Output as JSON')
8082
8078
  .action(async (source, target, opts) => {
8083
- const piBrain = await requirePiBrain();
8084
8079
  const config = getBrainConfig(opts);
8085
8080
  try {
8086
- const client = new piBrain.PiBrainClient(config);
8087
- const result = await client.transfer(source, target);
8081
+ const result = await brainFetch(config, '/v1/transfer', { body: { source_domain: source, target_domain: target } });
8088
8082
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(result, null, 2)); return; }
8089
8083
  console.log(chalk.green(`Transfer ${source} -> ${target}: ${result.status || 'OK'}`));
8090
8084
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
@@ -8095,11 +8089,9 @@ brainCmd.command('sync [direction]')
8095
8089
  .option('--url <url>', 'Brain server URL')
8096
8090
  .option('--key <key>', 'Pi key')
8097
8091
  .action(async (direction, opts) => {
8098
- const piBrain = await requirePiBrain();
8099
8092
  const config = getBrainConfig(opts);
8100
8093
  try {
8101
- const client = new piBrain.PiBrainClient(config);
8102
- const result = await client.sync(direction || 'both');
8094
+ const result = await brainFetch(config, '/v1/lora/latest', { params: { direction: direction || 'both' } });
8103
8095
  console.log(chalk.green(`Sync ${direction || 'both'}: ${result.status || 'OK'}`));
8104
8096
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); process.exit(1); }
8105
8097
  });
@@ -8110,30 +8102,28 @@ brainCmd.command('page <action> [args...]')
8110
8102
  .option('--key <key>', 'Pi key')
8111
8103
  .option('--json', 'Output as JSON')
8112
8104
  .action(async (action, args, opts) => {
8113
- const piBrain = await requirePiBrain();
8114
8105
  const config = getBrainConfig(opts);
8115
8106
  try {
8116
- const client = new piBrain.PiBrainClient(config);
8117
8107
  let result;
8118
8108
  switch (action) {
8119
8109
  case 'list':
8120
- result = await client.listPages ? client.listPages({ limit: 20 }) : { pages: [], message: 'Brainpedia not yet available on this server' };
8110
+ result = await brainFetch(config, '/v1/pages', { params: { limit: 20 } }).catch(() => ({ pages: [], message: 'Brainpedia endpoint not available' }));
8121
8111
  break;
8122
8112
  case 'get':
8123
8113
  if (!args[0]) { console.error(chalk.red('Usage: brain page get <slug>')); process.exit(1); }
8124
- result = await client.getPage ? client.getPage(args[0]) : { error: 'Brainpedia not yet available' };
8114
+ result = await brainFetch(config, `/v1/pages/${args[0]}`);
8125
8115
  break;
8126
8116
  case 'create':
8127
8117
  if (!args[0]) { console.error(chalk.red('Usage: brain page create <title> [--content <text>]')); process.exit(1); }
8128
- result = await client.createPage ? client.createPage({ title: args[0], content: opts.content || '' }) : { error: 'Brainpedia not yet available' };
8118
+ result = await brainFetch(config, '/v1/pages', { body: { title: args[0], content: opts.content || '' } });
8129
8119
  break;
8130
8120
  case 'update':
8131
8121
  if (!args[0]) { console.error(chalk.red('Usage: brain page update <slug> [--content <text>]')); process.exit(1); }
8132
- result = await client.updatePage ? client.updatePage(args[0], { content: opts.content || '' }) : { error: 'Brainpedia not yet available' };
8122
+ result = await brainFetch(config, `/v1/pages/${args[0]}/deltas`, { body: { content: opts.content || '' } });
8133
8123
  break;
8134
8124
  case 'delete':
8135
8125
  if (!args[0]) { console.error(chalk.red('Usage: brain page delete <slug>')); process.exit(1); }
8136
- result = await client.deletePage ? client.deletePage(args[0]) : { error: 'Brainpedia not yet available' };
8126
+ result = await brainFetch(config, `/v1/pages/${args[0]}`, { method: 'DELETE' }).catch(() => ({ error: 'Delete not available' }));
8137
8127
  break;
8138
8128
  default:
8139
8129
  console.error(chalk.red(`Unknown page action: ${action}. Use: list, get, create, update, delete`));
@@ -8159,10 +8149,8 @@ brainCmd.command('node <action> [args...]')
8159
8149
  .option('--key <key>', 'Pi key')
8160
8150
  .option('--json', 'Output as JSON')
8161
8151
  .action(async (action, args, opts) => {
8162
- const piBrain = await requirePiBrain();
8163
8152
  const config = getBrainConfig(opts);
8164
8153
  try {
8165
- const client = new piBrain.PiBrainClient(config);
8166
8154
  let result;
8167
8155
  switch (action) {
8168
8156
  case 'publish':
@@ -8170,14 +8158,14 @@ brainCmd.command('node <action> [args...]')
8170
8158
  const wasmPath = path.resolve(args[0]);
8171
8159
  if (!fs.existsSync(wasmPath)) { console.error(chalk.red(`File not found: ${wasmPath}`)); process.exit(1); }
8172
8160
  const wasmBytes = fs.readFileSync(wasmPath);
8173
- result = await client.publishNode ? client.publishNode({ wasm: wasmBytes, name: path.basename(wasmPath, '.wasm') }) : { error: 'WASM node publish not yet available on this server' };
8161
+ result = await brainFetch(config, '/v1/nodes', { body: { name: path.basename(wasmPath, '.wasm'), wasm_base64: wasmBytes.toString('base64') } });
8174
8162
  break;
8175
8163
  case 'list':
8176
- result = await client.listNodes ? client.listNodes({ limit: 20 }) : { nodes: [], message: 'WASM node listing not yet available' };
8164
+ result = await brainFetch(config, '/v1/nodes', { params: { limit: 20 } }).catch(() => ({ nodes: [], message: 'WASM node listing not available' }));
8177
8165
  break;
8178
8166
  case 'status':
8179
8167
  if (!args[0]) { console.error(chalk.red('Usage: brain node status <node-id>')); process.exit(1); }
8180
- result = await client.nodeStatus ? client.nodeStatus(args[0]) : { error: 'WASM node status not yet available' };
8168
+ result = await brainFetch(config, `/v1/nodes/${args[0]}`);
8181
8169
  break;
8182
8170
  default:
8183
8171
  console.error(chalk.red(`Unknown node action: ${action}. Use: publish, list, status`));
@@ -8761,6 +8749,10 @@ function createSonaEngine(sona) {
8761
8749
  if (sona.SonaCoordinator) return new sona.SonaCoordinator(SONA_DEFAULT_DIM);
8762
8750
  throw new Error('No SONA engine class found');
8763
8751
  }
8752
+ function parseSonaResult(val) {
8753
+ if (typeof val === 'string') { try { return JSON.parse(val); } catch { return val; } }
8754
+ return val;
8755
+ }
8764
8756
 
8765
8757
  sonaCmd.command('status')
8766
8758
  .description('Show SONA learning engine status')
@@ -8769,7 +8761,7 @@ sonaCmd.command('status')
8769
8761
  const sona = loadSona();
8770
8762
  try {
8771
8763
  const engine = createSonaEngine(sona);
8772
- const status = engine.getStatus ? engine.getStatus() : { ready: true };
8764
+ const status = engine.getStatus ? parseSonaResult(engine.getStatus()) : { enabled: engine.isEnabled ? engine.isEnabled() : true, ...parseSonaResult(engine.getStats ? engine.getStats() : {}) };
8773
8765
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(status, null, 2)); return; }
8774
8766
  console.log(chalk.bold.cyan('\nSONA Status\n'));
8775
8767
  Object.entries(status).forEach(([k, v]) => console.log(` ${chalk.bold(k + ':')} ${v}`));
@@ -8814,7 +8806,7 @@ sonaCmd.command('export')
8814
8806
  const sona = loadSona();
8815
8807
  try {
8816
8808
  const engine = createSonaEngine(sona);
8817
- const weights = engine.exportWeights ? engine.exportWeights() : engine.export ? engine.export() : {};
8809
+ const weights = parseSonaResult(engine.exportWeights ? engine.exportWeights() : engine.getStats ? engine.getStats() : {});
8818
8810
  fs.writeFileSync(opts.output, JSON.stringify(weights, null, 2));
8819
8811
  console.log(chalk.green(`Exported to ${opts.output}`));
8820
8812
  } catch (e) { console.error(chalk.red(`Error: ${e.message}`)); }
@@ -8827,7 +8819,7 @@ sonaCmd.command('stats')
8827
8819
  const sona = loadSona();
8828
8820
  try {
8829
8821
  const engine = createSonaEngine(sona);
8830
- const stats = engine.getStats ? engine.getStats() : engine.stats ? engine.stats() : {};
8822
+ const stats = parseSonaResult(engine.getStats ? engine.getStats() : engine.stats ? engine.stats() : {});
8831
8823
  if (opts.json || !process.stdout.isTTY) { console.log(JSON.stringify(stats, null, 2)); return; }
8832
8824
  console.log(chalk.bold.cyan('\nSONA Statistics\n'));
8833
8825
  Object.entries(stats).forEach(([k, v]) => console.log(` ${chalk.bold(k + ':')} ${v}`));
package/bin/mcp-server.js CHANGED
@@ -363,7 +363,7 @@ class Intelligence {
363
363
  const server = new Server(
364
364
  {
365
365
  name: 'ruvector',
366
- version: '0.2.4',
366
+ version: '0.2.6',
367
367
  },
368
368
  {
369
369
  capabilities: {
@@ -3276,7 +3276,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3276
3276
  }
3277
3277
  }
3278
3278
 
3279
- // ── Brain Tool Handlers ──────────────────────────────────────────────
3279
+ // ── Brain Tool Handlers (direct fetch to pi.ruv.io) ─────────────────
3280
3280
  case 'brain_search':
3281
3281
  case 'brain_share':
3282
3282
  case 'brain_get':
@@ -3289,31 +3289,75 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3289
3289
  case 'brain_transfer':
3290
3290
  case 'brain_sync': {
3291
3291
  try {
3292
- const { PiBrainClient } = require('@ruvector/pi-brain');
3293
- const client = new PiBrainClient({
3294
- url: process.env.BRAIN_URL || 'https://pi.ruv.io',
3295
- key: process.env.PI
3296
- });
3292
+ const brainUrl = process.env.BRAIN_URL || 'https://pi.ruv.io';
3293
+ const brainKey = process.env.PI;
3294
+ const hdrs = { 'Content-Type': 'application/json' };
3295
+ if (brainKey) hdrs['Authorization'] = `Bearer ${brainKey}`;
3297
3296
  const subCmd = name.replace('brain_', '');
3298
- let result;
3297
+ let url, fetchOpts = { headers: hdrs, signal: AbortSignal.timeout(30000) };
3299
3298
  switch (subCmd) {
3300
- case 'search': result = await client.search(args.query, { category: args.category, limit: args.limit || 10 }); break;
3301
- case 'share': result = await client.share({ title: args.title, content: args.content, category: args.category, tags: args.tags ? args.tags.split(',').map(t => t.trim()) : [], code_snippet: args.code_snippet }); break;
3302
- case 'get': result = await client.get(args.id); break;
3303
- case 'vote': result = await client.vote(args.id, args.direction); break;
3304
- case 'list': result = await client.list({ category: args.category, limit: args.limit || 20 }); break;
3305
- case 'delete': result = await client.delete(args.id); break;
3306
- case 'status': result = await client.status(); break;
3307
- case 'drift': result = await client.drift({ domain: args.domain }); break;
3308
- case 'partition': result = await client.partition({ domain: args.domain, min_cluster_size: args.min_cluster_size }); break;
3309
- case 'transfer': result = await client.transfer(args.source_domain, args.target_domain); break;
3310
- case 'sync': result = await client.sync(args.direction || 'both'); break;
3299
+ case 'search': {
3300
+ const p = new URLSearchParams({ q: args.query || '' });
3301
+ if (args.category) p.set('category', args.category);
3302
+ if (args.limit) p.set('limit', String(args.limit));
3303
+ url = `${brainUrl}/v1/memories/search?${p}`;
3304
+ break;
3305
+ }
3306
+ case 'share': {
3307
+ url = `${brainUrl}/v1/memories`;
3308
+ fetchOpts.method = 'POST';
3309
+ 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 });
3310
+ break;
3311
+ }
3312
+ case 'get': url = `${brainUrl}/v1/memories/${args.id}`; break;
3313
+ case 'vote': {
3314
+ url = `${brainUrl}/v1/memories/${args.id}/vote`;
3315
+ fetchOpts.method = 'POST';
3316
+ fetchOpts.body = JSON.stringify({ direction: args.direction });
3317
+ break;
3318
+ }
3319
+ case 'list': {
3320
+ const p = new URLSearchParams();
3321
+ if (args.category) p.set('category', args.category);
3322
+ p.set('limit', String(args.limit || 20));
3323
+ url = `${brainUrl}/v1/memories/list?${p}`;
3324
+ break;
3325
+ }
3326
+ case 'delete': {
3327
+ url = `${brainUrl}/v1/memories/${args.id}`;
3328
+ fetchOpts.method = 'DELETE';
3329
+ break;
3330
+ }
3331
+ case 'status': url = `${brainUrl}/v1/status`; break;
3332
+ case 'drift': {
3333
+ const p = new URLSearchParams();
3334
+ if (args.domain) p.set('domain', args.domain);
3335
+ url = `${brainUrl}/v1/drift?${p}`;
3336
+ break;
3337
+ }
3338
+ case 'partition': {
3339
+ const p = new URLSearchParams();
3340
+ if (args.domain) p.set('domain', args.domain);
3341
+ if (args.min_cluster_size) p.set('min_cluster_size', String(args.min_cluster_size));
3342
+ url = `${brainUrl}/v1/partition?${p}`;
3343
+ break;
3344
+ }
3345
+ case 'transfer': {
3346
+ url = `${brainUrl}/v1/transfer`;
3347
+ fetchOpts.method = 'POST';
3348
+ fetchOpts.body = JSON.stringify({ source_domain: args.source_domain, target_domain: args.target_domain });
3349
+ break;
3350
+ }
3351
+ case 'sync': url = `${brainUrl}/v1/lora/latest`; break;
3311
3352
  }
3353
+ const resp = await fetch(url, fetchOpts);
3354
+ if (!resp.ok) {
3355
+ const errText = await resp.text().catch(() => resp.statusText);
3356
+ return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: `${resp.status} ${errText}` }, null, 2) }], isError: true };
3357
+ }
3358
+ const result = await resp.json();
3312
3359
  return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] };
3313
3360
  } catch (e) {
3314
- if (e.code === 'MODULE_NOT_FOUND') {
3315
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain. Install with: npm install @ruvector/pi-brain' }, null, 2) }], isError: true };
3316
- }
3317
3361
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true };
3318
3362
  }
3319
3363
  }
@@ -3787,7 +3831,7 @@ async function main() {
3787
3831
  transport: 'sse',
3788
3832
  sessions: sessions.size,
3789
3833
  tools: 91,
3790
- version: '0.2.4'
3834
+ version: '0.2.6'
3791
3835
  }));
3792
3836
 
3793
3837
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvector",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
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",
@@ -80,12 +80,10 @@
80
80
  "typescript": "^5.3.3"
81
81
  },
82
82
  "peerDependencies": {
83
- "@ruvector/pi-brain": ">=0.1.0",
84
83
  "@ruvector/ruvllm": ">=2.0.0",
85
84
  "@ruvector/router": ">=0.1.0"
86
85
  },
87
86
  "peerDependenciesMeta": {
88
- "@ruvector/pi-brain": { "optional": true },
89
87
  "@ruvector/ruvllm": { "optional": true },
90
88
  "@ruvector/router": { "optional": true }
91
89
  },