open-agents-ai 0.186.14 → 0.186.15
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/dist/index.js +126 -38
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5343,7 +5343,7 @@ var init_nexus = __esm({
|
|
|
5343
5343
|
* Usage: node nexus-daemon.mjs <nexus-dir> <agent-name> [agent-type]
|
|
5344
5344
|
*/
|
|
5345
5345
|
|
|
5346
|
-
import { NexusClient } from 'open-agents-nexus';
|
|
5346
|
+
import { NexusClient, buildNknEnvelope, parseNknEnvelope, deriveNknSeed } from 'open-agents-nexus';
|
|
5347
5347
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, appendFileSync, watch as fsWatch } from 'node:fs';
|
|
5348
5348
|
import { join } from 'node:path';
|
|
5349
5349
|
import { homedir, hostname } from 'node:os';
|
|
@@ -5639,12 +5639,33 @@ async function handleCmd(cmd) {
|
|
|
5639
5639
|
writeResp(id, { ok: false, output: 'NATS not connected \u2014 cannot announce sponsorship' });
|
|
5640
5640
|
return;
|
|
5641
5641
|
}
|
|
5642
|
+
// NX-07: Fetch per-model metadata from Ollama for capacity announcements
|
|
5643
|
+
var _saModels = args.models || [];
|
|
5644
|
+
var _saModelDetails = [];
|
|
5645
|
+
try {
|
|
5646
|
+
var _saOllamaUrl = process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
5647
|
+
var _saTagsResp = await fetch(_saOllamaUrl + '/api/tags');
|
|
5648
|
+
var _saTags = await _saTagsResp.json();
|
|
5649
|
+
for (var _saM of (_saTags.models || [])) {
|
|
5650
|
+
var _saName = _saM.name || '';
|
|
5651
|
+
if (_saModels.length > 0 && !_saModels.includes(_saName)) continue;
|
|
5652
|
+
_saModelDetails.push({
|
|
5653
|
+
name: _saName,
|
|
5654
|
+
sizeGB: Math.round((_saM.size || 0) / (1024*1024*1024) * 10) / 10,
|
|
5655
|
+
paramCount: _saM.details?.parameter_size || '',
|
|
5656
|
+
quantization: _saM.details?.quantization_level || '',
|
|
5657
|
+
family: _saM.details?.family || '',
|
|
5658
|
+
contextLength: 0, // would need /api/show per model \u2014 too slow for announce
|
|
5659
|
+
});
|
|
5660
|
+
}
|
|
5661
|
+
} catch {}
|
|
5642
5662
|
var sponsorData = {
|
|
5643
5663
|
type: 'sponsor.announce',
|
|
5644
5664
|
peerId: globalThis._daemonPeerId || 'unknown',
|
|
5645
5665
|
libp2pPeerId: globalThis._daemonPeerId || '',
|
|
5646
5666
|
name: args.name || 'Anonymous Sponsor',
|
|
5647
|
-
models:
|
|
5667
|
+
models: _saModels.length > 0 ? _saModels : _saModelDetails.map(function(m) { return m.name; }),
|
|
5668
|
+
modelDetails: _saModelDetails, // NX-07: per-model capacity
|
|
5648
5669
|
tunnelUrl: args.tunnel_url || null,
|
|
5649
5670
|
authKey: args.auth_key || '',
|
|
5650
5671
|
limits: {
|
|
@@ -5690,9 +5711,101 @@ async function handleCmd(cmd) {
|
|
|
5690
5711
|
await rooms.get('sponsors').send(JSON.stringify(sponsorData), { format: 'application/json' });
|
|
5691
5712
|
} catch {}
|
|
5692
5713
|
}
|
|
5693
|
-
|
|
5714
|
+
// NX-04: GossipSub meta topic announcement (multi-layer discovery)
|
|
5715
|
+
try {
|
|
5716
|
+
var _node = nexus.network ? nexus.network.node : nexus.node;
|
|
5717
|
+
if (_node && _node.services && _node.services.pubsub) {
|
|
5718
|
+
var _metaTopic = '/nexus/meta';
|
|
5719
|
+
var _metaMsg = JSON.stringify({
|
|
5720
|
+
version: 1,
|
|
5721
|
+
type: 'capability',
|
|
5722
|
+
id: Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
5723
|
+
timestamp: Date.now(),
|
|
5724
|
+
sender: nexus.peerId || 'unknown',
|
|
5725
|
+
topic: _metaTopic,
|
|
5726
|
+
payload: {
|
|
5727
|
+
capabilities: [{
|
|
5728
|
+
name: 'sponsor_inference',
|
|
5729
|
+
protocol: '/nexus/invoke/1.1.0',
|
|
5730
|
+
description: 'Sponsored inference via ' + sponsorData.name,
|
|
5731
|
+
pricing: 'free',
|
|
5732
|
+
rateLimit: String(sponsorData.limits.maxRequestsPerMinute) + '/min',
|
|
5733
|
+
}],
|
|
5734
|
+
sponsor: sponsorData,
|
|
5735
|
+
},
|
|
5736
|
+
});
|
|
5737
|
+
try { await _node.services.pubsub.subscribe(_metaTopic); } catch {}
|
|
5738
|
+
await _node.services.pubsub.publish(_metaTopic, new TextEncoder().encode(_metaMsg));
|
|
5739
|
+
dlog('sponsor_announce: published to GossipSub /nexus/meta');
|
|
5740
|
+
}
|
|
5741
|
+
} catch (gsErr) {
|
|
5742
|
+
dlog('sponsor_announce: GossipSub meta failed: ' + (gsErr.message || gsErr));
|
|
5743
|
+
}
|
|
5744
|
+
writeResp(id, { ok: true, output: 'Sponsor announced: ' + sponsorData.name + ' (' + sponsorData.models.length + ' models) [NATS+KV+Room+GossipSub]' });
|
|
5694
5745
|
break;
|
|
5695
5746
|
}
|
|
5747
|
+
// NX-05 + NX-06: Register sponsor_inference capability for P2P inference relay.
|
|
5748
|
+
// When a sponsor activates, this registers an invoke handler that proxies
|
|
5749
|
+
// chat completions to local Ollama \u2014 no tunnel needed for P2P inference.
|
|
5750
|
+
case 'register_sponsor_inference': {
|
|
5751
|
+
var _rsiOllamaUrl = args.ollama_url || process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
5752
|
+
var _rsiAllowedModels = args.models ? String(args.models).split(',') : null;
|
|
5753
|
+
if (typeof nexus.registerCapability !== 'function') {
|
|
5754
|
+
writeResp(id, { ok: false, output: 'registerCapability not available' });
|
|
5755
|
+
break;
|
|
5756
|
+
}
|
|
5757
|
+
nexus.registerCapability('sponsor_inference', async (request, stream) => {
|
|
5758
|
+
var _rsiInput = '';
|
|
5759
|
+
stream.onData(function(msg) {
|
|
5760
|
+
if (msg.type === 'invoke.chunk') {
|
|
5761
|
+
_rsiInput += (typeof msg.data === 'string' ? msg.data : JSON.stringify(msg.data));
|
|
5762
|
+
}
|
|
5763
|
+
});
|
|
5764
|
+
// Wait for input to finish (accept fires when sender signals done)
|
|
5765
|
+
await stream.accept();
|
|
5766
|
+
try {
|
|
5767
|
+
var _rsiReq = JSON.parse(_rsiInput);
|
|
5768
|
+
var _rsiModel = _rsiReq.model || '';
|
|
5769
|
+
// Check model allowlist
|
|
5770
|
+
if (_rsiAllowedModels && !_rsiAllowedModels.includes(_rsiModel)) {
|
|
5771
|
+
stream.write({ type: 'invoke.error', error: 'Model not allowed: ' + _rsiModel });
|
|
5772
|
+
return;
|
|
5773
|
+
}
|
|
5774
|
+
// Proxy to local Ollama /v1/chat/completions
|
|
5775
|
+
var _rsiBody = JSON.stringify({
|
|
5776
|
+
model: _rsiModel,
|
|
5777
|
+
messages: _rsiReq.messages || [],
|
|
5778
|
+
stream: true,
|
|
5779
|
+
});
|
|
5780
|
+
var _rsiResp = await fetch(_rsiOllamaUrl + '/v1/chat/completions', {
|
|
5781
|
+
method: 'POST',
|
|
5782
|
+
headers: { 'Content-Type': 'application/json' },
|
|
5783
|
+
body: _rsiBody,
|
|
5784
|
+
});
|
|
5785
|
+
if (!_rsiResp.ok) {
|
|
5786
|
+
stream.write({ type: 'invoke.error', error: 'Ollama returned HTTP ' + _rsiResp.status });
|
|
5787
|
+
return;
|
|
5788
|
+
}
|
|
5789
|
+
// Stream response back via invoke protocol
|
|
5790
|
+
var _rsiReader = _rsiResp.body?.getReader();
|
|
5791
|
+
if (_rsiReader) {
|
|
5792
|
+
var _rsiDecoder = new TextDecoder();
|
|
5793
|
+
while (true) {
|
|
5794
|
+
var _rsiChunk = await _rsiReader.read();
|
|
5795
|
+
if (_rsiChunk.done) break;
|
|
5796
|
+
stream.write({ type: 'invoke.event', data: _rsiDecoder.decode(_rsiChunk.value, { stream: true }) });
|
|
5797
|
+
}
|
|
5798
|
+
}
|
|
5799
|
+
stream.write({ type: 'invoke.done' });
|
|
5800
|
+
dlog('sponsor_inference: served ' + _rsiModel + ' to ' + (request.from || '?').slice(0, 16));
|
|
5801
|
+
} catch (rsiErr) {
|
|
5802
|
+
stream.write({ type: 'invoke.error', error: String(rsiErr.message || rsiErr) });
|
|
5803
|
+
}
|
|
5804
|
+
});
|
|
5805
|
+
writeResp(id, { ok: true, output: 'Registered sponsor_inference capability (P2P relay active)' });
|
|
5806
|
+
break;
|
|
5807
|
+
}
|
|
5808
|
+
|
|
5696
5809
|
case 'sponsor_discover': {
|
|
5697
5810
|
var foundSponsors = [];
|
|
5698
5811
|
var discoverTimeout = parseInt(args.timeout_ms || '5000', 10);
|
|
@@ -7328,32 +7441,29 @@ async function handleCmd(cmd) {
|
|
|
7328
7441
|
writeResp(id, { ok: false, output: 'Required: to (NKN address) and message' });
|
|
7329
7442
|
break;
|
|
7330
7443
|
}
|
|
7331
|
-
// Lazy NKN init
|
|
7444
|
+
// Lazy NKN init \u2014 uses open-agents-nexus API (deriveNknSeed, NknFallback)
|
|
7332
7445
|
if (!nexus.nkn || !nexus.nkn.isConnected) {
|
|
7333
7446
|
try {
|
|
7334
|
-
// Load or generate NKN seed from global identity
|
|
7335
7447
|
var _nsSeedFile = join(nexusDir, 'nkn-seed.hex');
|
|
7336
7448
|
var _nsSeed = undefined;
|
|
7337
7449
|
if (existsSync(_nsSeedFile)) {
|
|
7338
7450
|
_nsSeed = readFileSync(_nsSeedFile, 'utf8').trim();
|
|
7339
7451
|
} else {
|
|
7340
|
-
//
|
|
7452
|
+
// Use open-agents-nexus deriveNknSeed() for deterministic address
|
|
7341
7453
|
var _nsKeyData = existsSync(keyPath) ? readFileSync(keyPath) : Buffer.from(agentName);
|
|
7342
|
-
_nsSeed =
|
|
7454
|
+
_nsSeed = deriveNknSeed(_nsKeyData);
|
|
7343
7455
|
writeFileSync(_nsSeedFile, _nsSeed, { mode: 0o600 });
|
|
7344
7456
|
}
|
|
7345
|
-
// Connect NKN via nexus client if available
|
|
7346
7457
|
if (nexus.nkn && typeof nexus.nkn.connect === 'function') {
|
|
7347
7458
|
await nexus.nkn.connect(_nsSeed);
|
|
7348
7459
|
} else {
|
|
7349
|
-
// Direct NKN init
|
|
7460
|
+
// Direct NKN init via nkn-sdk
|
|
7350
7461
|
var nknSdk = await import('nkn-sdk');
|
|
7351
7462
|
var _nknMC = new nknSdk.default.MultiClient({ seed: _nsSeed, identifier: 'oa-' + agentName.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 20), numSubClients: 3 });
|
|
7352
7463
|
await new Promise(function(resolve, reject) {
|
|
7353
7464
|
_nknMC.onConnect(resolve);
|
|
7354
7465
|
setTimeout(function() { reject(new Error('NKN connect timeout (15s)')); }, 15000);
|
|
7355
7466
|
});
|
|
7356
|
-
// Store address
|
|
7357
7467
|
var _nsIdFile = join(nexusDir, 'nkn-identity.json');
|
|
7358
7468
|
writeFileSync(_nsIdFile, JSON.stringify({
|
|
7359
7469
|
address: _nknMC.addr,
|
|
@@ -7363,28 +7473,14 @@ async function handleCmd(cmd) {
|
|
|
7363
7473
|
createdAt: new Date().toISOString(),
|
|
7364
7474
|
}, null, 2), { mode: 0o600 });
|
|
7365
7475
|
dlog('NKN connected: ' + _nknMC.addr);
|
|
7366
|
-
// Receive handler \u2014
|
|
7476
|
+
// Receive handler \u2014 uses open-agents-nexus parseNknEnvelope() for HMAC verification
|
|
7367
7477
|
_nknMC.onMessage(function({ src, payload }) {
|
|
7368
7478
|
var _body = typeof payload === 'string' ? payload : new TextDecoder().decode(payload);
|
|
7369
7479
|
var _niDir2 = join(inboxDir, '_nkn');
|
|
7370
7480
|
mkdirSync(_niDir2, { recursive: true });
|
|
7371
|
-
var _entry =
|
|
7372
|
-
// Verify HMAC if present
|
|
7373
|
-
try {
|
|
7374
|
-
var _parsed = JSON.parse(_body);
|
|
7375
|
-
if (_parsed._hmac && _parsed._agentName && _parsed.content) {
|
|
7376
|
-
// Verify: HMAC(agentName + content + timestamp, shared_secret)
|
|
7377
|
-
var _sharedSecret = createHash('sha256').update(src).update(_nknMC.addr).update('oa-nkn-v1').digest('hex');
|
|
7378
|
-
var _expectedHmac = createHmac('sha256', _sharedSecret).update(_parsed._agentName + _parsed.content + String(_parsed.timestamp || 0)).digest('hex').slice(0, 16);
|
|
7379
|
-
_entry.verified = (_parsed._hmac === _expectedHmac);
|
|
7380
|
-
_entry.agentName = _parsed._agentName;
|
|
7381
|
-
_entry.content = _parsed.content;
|
|
7382
|
-
_entry.timestamp = _parsed.timestamp;
|
|
7383
|
-
}
|
|
7384
|
-
} catch { /* raw text message \u2014 not verified */ }
|
|
7481
|
+
var _entry = parseNknEnvelope(src, _nknMC.addr, _body);
|
|
7385
7482
|
try { writeFileSync(join(_niDir2, Date.now() + '.json'), JSON.stringify(_entry, null, 2)); } catch {}
|
|
7386
7483
|
});
|
|
7387
|
-
// Stash the client for reuse
|
|
7388
7484
|
nexus._nknDirect = _nknMC;
|
|
7389
7485
|
}
|
|
7390
7486
|
} catch (nknErr) {
|
|
@@ -7392,20 +7488,11 @@ async function handleCmd(cmd) {
|
|
|
7392
7488
|
break;
|
|
7393
7489
|
}
|
|
7394
7490
|
}
|
|
7395
|
-
// Build verified message
|
|
7491
|
+
// Build verified message using open-agents-nexus buildNknEnvelope()
|
|
7396
7492
|
var _nsClient = nexus._nknDirect || (nexus.nkn && nexus.nkn.client);
|
|
7397
7493
|
if (!_nsClient) { writeResp(id, { ok: false, output: 'NKN not connected' }); break; }
|
|
7398
7494
|
var _nsMyAddr = _nsClient.addr || (nexus.nkn ? nexus.nkn.address : '');
|
|
7399
|
-
var
|
|
7400
|
-
var _nsSharedSecret = createHash('sha256').update(_nsMyAddr).update(_nsTo).update('oa-nkn-v1').digest('hex');
|
|
7401
|
-
var _nsHmac = createHmac('sha256', _nsSharedSecret).update(agentName + _nsMsg + String(_nsTimestamp)).digest('hex').slice(0, 16);
|
|
7402
|
-
var _nsEnvelope = JSON.stringify({
|
|
7403
|
-
_agentName: agentName,
|
|
7404
|
-
content: _nsMsg,
|
|
7405
|
-
timestamp: _nsTimestamp,
|
|
7406
|
-
libp2pPeerId: nexus.peerId || null,
|
|
7407
|
-
_hmac: _nsHmac,
|
|
7408
|
-
});
|
|
7495
|
+
var _nsEnvelope = buildNknEnvelope(_nsMyAddr, _nsTo, agentName, _nsMsg, nexus.peerId || undefined);
|
|
7409
7496
|
try {
|
|
7410
7497
|
await _nsClient.send(_nsTo, _nsEnvelope, { msgHoldingSeconds: 3600 });
|
|
7411
7498
|
// Add to peer list if not already there
|
|
@@ -8396,7 +8483,8 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
|
|
|
8396
8483
|
"nkn_peers",
|
|
8397
8484
|
"nkn_inbox",
|
|
8398
8485
|
"nkn_send",
|
|
8399
|
-
"nkn_add_peer"
|
|
8486
|
+
"nkn_add_peer",
|
|
8487
|
+
"register_sponsor_inference"
|
|
8400
8488
|
],
|
|
8401
8489
|
description: "The nexus action. MUST call 'connect' first (spawns daemon). Then: join_room, send_message, discover_peers, expose, status, nkn_status (NKN backup comms), nkn_peers (address book), nkn_inbox (read messages), nkn_send (DM via NKN with HMAC verification), nkn_add_peer (add to address book), cohere_stats, ipfs_add, etc."
|
|
8402
8490
|
},
|
package/package.json
CHANGED