open-agents-ai 0.103.66 → 0.103.68
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 +246 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12126,6 +12126,11 @@ const rooms = new Map();
|
|
|
12126
12126
|
let connected = false;
|
|
12127
12127
|
const blockedPeers = [];
|
|
12128
12128
|
|
|
12129
|
+
// NATS invoke relay \u2014 for cross-NAT fallback when direct libp2p dial fails.
|
|
12130
|
+
// Both peers connect to wss://demo.nats.io:8443, so NATS request/reply bridges the gap.
|
|
12131
|
+
var _natsConn = null;
|
|
12132
|
+
var _natsCodec = null;
|
|
12133
|
+
|
|
12129
12134
|
function writeStatus(extra = {}) {
|
|
12130
12135
|
const caps = typeof nexus.getRegisteredCapabilities === 'function'
|
|
12131
12136
|
? nexus.getRegisteredCapabilities() : [];
|
|
@@ -12285,8 +12290,37 @@ async function handleCmd(cmd) {
|
|
|
12285
12290
|
dlog('query_peer_caps: SUCCESS');
|
|
12286
12291
|
writeResp(id, { ok: true, output: typeof qpcResult === 'string' ? qpcResult : JSON.stringify(qpcResult, null, 2) });
|
|
12287
12292
|
} catch (qpcErr) {
|
|
12288
|
-
|
|
12289
|
-
|
|
12293
|
+
var qpcErrMsg = qpcErr.message || String(qpcErr);
|
|
12294
|
+
dlog('query_peer_caps: ERROR ' + qpcErrMsg);
|
|
12295
|
+
// NATS fallback for peer cap discovery across NAT
|
|
12296
|
+
if (_natsConn && _natsCodec && qpcErrMsg.includes('multiaddrs failed')) {
|
|
12297
|
+
dlog('query_peer_caps: trying NATS relay fallback');
|
|
12298
|
+
try {
|
|
12299
|
+
var _qpcNatsSubject = 'nexus.invoke.' + String(qpcPeerId);
|
|
12300
|
+
var _qpcNatsPayload = JSON.stringify({
|
|
12301
|
+
capability: '__list_capabilities',
|
|
12302
|
+
input: qpcInput,
|
|
12303
|
+
from: nexus.peerId || '',
|
|
12304
|
+
});
|
|
12305
|
+
var _qpcNatsResp = await _natsConn.request(
|
|
12306
|
+
_qpcNatsSubject,
|
|
12307
|
+
_natsCodec.encode(_qpcNatsPayload),
|
|
12308
|
+
{ timeout: 15000 }
|
|
12309
|
+
);
|
|
12310
|
+
var _qpcNatsResult = JSON.parse(_natsCodec.decode(_qpcNatsResp.data));
|
|
12311
|
+
if (_qpcNatsResult && _qpcNatsResult.error) {
|
|
12312
|
+
writeResp(id, { ok: false, output: 'query_peer_caps (NATS): ' + _qpcNatsResult.error });
|
|
12313
|
+
} else {
|
|
12314
|
+
dlog('query_peer_caps: SUCCESS via NATS relay');
|
|
12315
|
+
writeResp(id, { ok: true, output: typeof _qpcNatsResult === 'string' ? _qpcNatsResult : JSON.stringify(_qpcNatsResult, null, 2) });
|
|
12316
|
+
}
|
|
12317
|
+
} catch (_qpcNatsErr) {
|
|
12318
|
+
dlog('query_peer_caps: NATS fallback also failed: ' + (_qpcNatsErr.message || _qpcNatsErr));
|
|
12319
|
+
writeResp(id, { ok: false, output: 'query_peer_caps error (libp2p + NATS): ' + qpcErrMsg });
|
|
12320
|
+
}
|
|
12321
|
+
} else {
|
|
12322
|
+
writeResp(id, { ok: false, output: 'query_peer_caps error: ' + qpcErrMsg });
|
|
12323
|
+
}
|
|
12290
12324
|
}
|
|
12291
12325
|
break;
|
|
12292
12326
|
}
|
|
@@ -12305,8 +12339,37 @@ async function handleCmd(cmd) {
|
|
|
12305
12339
|
dlog('invoke_capability: SUCCESS');
|
|
12306
12340
|
writeResp(id, { ok: true, output: JSON.stringify(result, null, 2) });
|
|
12307
12341
|
} catch (invokeErr) {
|
|
12308
|
-
|
|
12309
|
-
|
|
12342
|
+
var invokeErrMsg = invokeErr.message || String(invokeErr);
|
|
12343
|
+
dlog('invoke_capability: ERROR ' + invokeErrMsg);
|
|
12344
|
+
// NATS fallback \u2014 if direct dial failed (NAT), retry via NATS relay
|
|
12345
|
+
if (_natsConn && _natsCodec && invokeErrMsg.includes('multiaddrs failed')) {
|
|
12346
|
+
dlog('invoke_capability: trying NATS relay fallback for ' + peerId.slice(0, 20));
|
|
12347
|
+
try {
|
|
12348
|
+
var _icNatsSubject = 'nexus.invoke.' + peerId;
|
|
12349
|
+
var _icNatsPayload = JSON.stringify({
|
|
12350
|
+
capability: capability,
|
|
12351
|
+
input: typeof input === 'string' ? { prompt: input } : input,
|
|
12352
|
+
from: nexus.peerId || '',
|
|
12353
|
+
});
|
|
12354
|
+
var _icNatsResp = await _natsConn.request(
|
|
12355
|
+
_icNatsSubject,
|
|
12356
|
+
_natsCodec.encode(_icNatsPayload),
|
|
12357
|
+
{ timeout: 240000 }
|
|
12358
|
+
);
|
|
12359
|
+
var _icNatsResult = JSON.parse(_natsCodec.decode(_icNatsResp.data));
|
|
12360
|
+
if (_icNatsResult && _icNatsResult.error) {
|
|
12361
|
+
writeResp(id, { ok: false, output: 'NATS relay error: ' + _icNatsResult.error });
|
|
12362
|
+
} else {
|
|
12363
|
+
dlog('invoke_capability: SUCCESS via NATS relay');
|
|
12364
|
+
writeResp(id, { ok: true, output: typeof _icNatsResult === 'string' ? _icNatsResult : JSON.stringify(_icNatsResult, null, 2) });
|
|
12365
|
+
}
|
|
12366
|
+
} catch (_icNatsErr) {
|
|
12367
|
+
dlog('invoke_capability: NATS fallback also failed: ' + (_icNatsErr.message || _icNatsErr));
|
|
12368
|
+
writeResp(id, { ok: false, output: 'Invoke error (libp2p + NATS fallback failed): ' + invokeErrMsg });
|
|
12369
|
+
}
|
|
12370
|
+
} else {
|
|
12371
|
+
writeResp(id, { ok: false, output: 'Invoke error: ' + invokeErrMsg });
|
|
12372
|
+
}
|
|
12310
12373
|
}
|
|
12311
12374
|
break;
|
|
12312
12375
|
}
|
|
@@ -12355,9 +12418,39 @@ async function handleCmd(cmd) {
|
|
|
12355
12418
|
var riResult = await Promise.race([riInvokeP, riTimeoutP]);
|
|
12356
12419
|
dlog('remote_infer: SUCCESS (explicit peer)');
|
|
12357
12420
|
} catch (riErr) {
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12421
|
+
var riErrMsg = riErr.message || String(riErr);
|
|
12422
|
+
dlog('remote_infer: ERROR (explicit peer) ' + riErrMsg);
|
|
12423
|
+
// NATS fallback \u2014 if direct dial failed (NAT/firewall), retry via NATS relay
|
|
12424
|
+
if (_natsConn && _natsCodec && riErrMsg.includes('multiaddrs failed')) {
|
|
12425
|
+
dlog('remote_infer: trying NATS relay fallback for ' + riTargetPeer.slice(0, 20));
|
|
12426
|
+
try {
|
|
12427
|
+
var _riNatsSubject = 'nexus.invoke.' + riTargetPeer;
|
|
12428
|
+
var _riNatsPayload = JSON.stringify({
|
|
12429
|
+
capability: riCapName,
|
|
12430
|
+
input: riData,
|
|
12431
|
+
from: nexus.peerId || '',
|
|
12432
|
+
});
|
|
12433
|
+
var _riNatsResp = await _natsConn.request(
|
|
12434
|
+
_riNatsSubject,
|
|
12435
|
+
_natsCodec.encode(_riNatsPayload),
|
|
12436
|
+
{ timeout: 240000 }
|
|
12437
|
+
);
|
|
12438
|
+
var _riNatsResult = JSON.parse(_natsCodec.decode(_riNatsResp.data));
|
|
12439
|
+
if (_riNatsResult && _riNatsResult.error) {
|
|
12440
|
+
writeResp(id, { ok: false, output: 'Remote inference failed (NATS relay): ' + _riNatsResult.error });
|
|
12441
|
+
return;
|
|
12442
|
+
}
|
|
12443
|
+
riResult = _riNatsResult;
|
|
12444
|
+
dlog('remote_infer: SUCCESS via NATS relay');
|
|
12445
|
+
} catch (_riNatsErr) {
|
|
12446
|
+
dlog('remote_infer: NATS fallback also failed: ' + (_riNatsErr.message || _riNatsErr));
|
|
12447
|
+
writeResp(id, { ok: false, output: 'Remote inference failed (libp2p + NATS): ' + riErrMsg });
|
|
12448
|
+
return;
|
|
12449
|
+
}
|
|
12450
|
+
} else {
|
|
12451
|
+
writeResp(id, { ok: false, output: 'Remote inference failed: ' + riErrMsg });
|
|
12452
|
+
return;
|
|
12453
|
+
}
|
|
12361
12454
|
}
|
|
12362
12455
|
} else {
|
|
12363
12456
|
// Auto-discover: try speculative invoke on each 12D3KooW peer
|
|
@@ -12402,14 +12495,28 @@ async function handleCmd(cmd) {
|
|
|
12402
12495
|
}
|
|
12403
12496
|
|
|
12404
12497
|
// riResult and riTargetPeer are available from either path above
|
|
12405
|
-
// Check if the invoke returned an error message instead of real data
|
|
12498
|
+
// Check if the invoke returned an error message instead of real data.
|
|
12499
|
+
// IMPORTANT: Only flag short non-JSON strings as errors. Valid inference
|
|
12500
|
+
// responses are JSON with model/response/choices \u2014 the model's natural
|
|
12501
|
+
// language output often contains words like "error" or "not found" which
|
|
12502
|
+
// would cause false positives if we checked the entire JSON string.
|
|
12406
12503
|
var riIsError = false;
|
|
12407
12504
|
if (typeof riResult === 'string') {
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12505
|
+
// Try to parse as JSON \u2014 if it has model+response or choices, it's valid
|
|
12506
|
+
var riParsedCheck = null;
|
|
12507
|
+
try { riParsedCheck = JSON.parse(riResult); } catch {}
|
|
12508
|
+
if (riParsedCheck && typeof riParsedCheck === 'object' &&
|
|
12509
|
+
(riParsedCheck.model || riParsedCheck.choices || riParsedCheck.response !== undefined)) {
|
|
12510
|
+
// Valid inference response \u2014 NOT an error
|
|
12511
|
+
riIsError = false;
|
|
12512
|
+
} else {
|
|
12513
|
+
// Non-JSON or unstructured response \u2014 check for error keywords
|
|
12514
|
+
var riLower = riResult.toLowerCase();
|
|
12515
|
+
if (riLower.includes('unauthorized') || riLower.includes('forbidden') ||
|
|
12516
|
+
riLower.includes('not found') || riLower.includes('error') ||
|
|
12517
|
+
riLower.includes('payment') || riLower.includes('rejected')) {
|
|
12518
|
+
riIsError = true;
|
|
12519
|
+
}
|
|
12413
12520
|
}
|
|
12414
12521
|
}
|
|
12415
12522
|
if (riIsError) {
|
|
@@ -12993,22 +13100,51 @@ async function handleCmd(cmd) {
|
|
|
12993
13100
|
// Register system_metrics capability \u2014 returns CPU/GPU/memory utilization
|
|
12994
13101
|
if (typeof nexus.registerCapability === 'function') {
|
|
12995
13102
|
nexus.registerCapability('system_metrics', async (request, stream) => {
|
|
12996
|
-
//
|
|
13103
|
+
// Collect input via stream data events (auth_key arrives in invoke.chunk, NOT invoke.open)
|
|
13104
|
+
var smDataChunks = [];
|
|
13105
|
+
var smInputDone = false;
|
|
13106
|
+
stream.onData(function(msg) {
|
|
13107
|
+
if (msg.type === 'invoke.chunk') {
|
|
13108
|
+
smDataChunks.push(typeof msg.data === 'string' ? msg.data : JSON.stringify(msg.data));
|
|
13109
|
+
}
|
|
13110
|
+
if (msg.type === 'invoke.done' || msg.type === 'invoke.end') {
|
|
13111
|
+
smInputDone = true;
|
|
13112
|
+
}
|
|
13113
|
+
});
|
|
13114
|
+
|
|
13115
|
+
// Accept invocation so consumer sends data
|
|
13116
|
+
await stream.write({ type: 'invoke.accept', version: 1, requestId: request.requestId, accepted: true });
|
|
13117
|
+
|
|
13118
|
+
// Wait briefly for data (auth key arrives in chunk)
|
|
13119
|
+
var smWait = 0;
|
|
13120
|
+
while (!smInputDone && smDataChunks.length === 0 && smWait < 2000) {
|
|
13121
|
+
await new Promise(function(r) { setTimeout(r, 10); });
|
|
13122
|
+
smWait += 10;
|
|
13123
|
+
}
|
|
13124
|
+
|
|
13125
|
+
// Auth check \u2014 extract auth_key from chunk data
|
|
12997
13126
|
if (exposeAuthKey) {
|
|
12998
13127
|
var smAuthKey = '';
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13128
|
+
var smPayload = smDataChunks.join('');
|
|
13129
|
+
try {
|
|
13130
|
+
var smParsed = JSON.parse(smPayload);
|
|
13131
|
+
if (smParsed && typeof smParsed === 'object' && smParsed.auth_key) {
|
|
13132
|
+
smAuthKey = smParsed.auth_key;
|
|
13133
|
+
}
|
|
13134
|
+
} catch {}
|
|
13135
|
+
// Fallback: check invoke.open metadata (future-proofing)
|
|
13136
|
+
if (!smAuthKey && request.metadata && request.metadata.auth_key) {
|
|
13002
13137
|
smAuthKey = request.metadata.auth_key;
|
|
13003
13138
|
}
|
|
13004
13139
|
if (smAuthKey !== exposeAuthKey) {
|
|
13005
|
-
|
|
13140
|
+
dlog('system_metrics: auth REJECTED from ' + (request.from || 'unknown'));
|
|
13141
|
+
await stream.write({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: 0, event: 'error', data: 'Unauthorized' });
|
|
13006
13142
|
await stream.write({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: 0 } });
|
|
13007
13143
|
stream.close();
|
|
13008
13144
|
return;
|
|
13009
13145
|
}
|
|
13146
|
+
dlog('system_metrics: auth OK');
|
|
13010
13147
|
}
|
|
13011
|
-
await stream.write({ type: 'invoke.accept', version: 1, requestId: request.requestId, accepted: true });
|
|
13012
13148
|
try {
|
|
13013
13149
|
var os = require('os');
|
|
13014
13150
|
var loads = os.loadavg();
|
|
@@ -13359,6 +13495,97 @@ process.on('unhandledRejection', (reason) => {
|
|
|
13359
13495
|
dlog('NATS subscribe failed: ' + (natsSubErr.message || natsSubErr));
|
|
13360
13496
|
}
|
|
13361
13497
|
|
|
13498
|
+
// NATS invoke relay \u2014 subscribe to nexus.invoke.<myPeerId> for cross-NAT invoke fallback.
|
|
13499
|
+
// When a remote peer can't direct-dial us (all multiaddrs are private), they retry
|
|
13500
|
+
// via NATS request/reply. We dispatch to the registered capability handler using a
|
|
13501
|
+
// mock stream interface, then reply with the result.
|
|
13502
|
+
try {
|
|
13503
|
+
var _nc = nexus.nats && nexus.nats.nc ? nexus.nats.nc : null;
|
|
13504
|
+
if (_nc) {
|
|
13505
|
+
var _nws = await import('nats.ws');
|
|
13506
|
+
_natsConn = _nc;
|
|
13507
|
+
_natsCodec = _nws.StringCodec();
|
|
13508
|
+
var _natsInvSubject = 'nexus.invoke.' + nexus.peerId;
|
|
13509
|
+
var _natsInvSub = _nc.subscribe(_natsInvSubject);
|
|
13510
|
+
dlog('NATS invoke relay listening on ' + _natsInvSubject);
|
|
13511
|
+
|
|
13512
|
+
(async function _natsInvokeLoop() {
|
|
13513
|
+
for await (var _nm of _natsInvSub) {
|
|
13514
|
+
try {
|
|
13515
|
+
var _nReq = JSON.parse(_natsCodec.decode(_nm.data));
|
|
13516
|
+
var _nCap = _nReq.capability || '';
|
|
13517
|
+
var _nInput = _nReq.input || {};
|
|
13518
|
+
var _nInputStr = typeof _nInput === 'string' ? _nInput : JSON.stringify(_nInput);
|
|
13519
|
+
dlog('NATS relay invoke: cap=' + _nCap + ' from=' + (_nReq.from || 'unknown').slice(0, 20));
|
|
13520
|
+
|
|
13521
|
+
// Look up registered handler
|
|
13522
|
+
var _nHandler = null;
|
|
13523
|
+
if (nexus.capabilityHandlers && typeof nexus.capabilityHandlers.get === 'function') {
|
|
13524
|
+
_nHandler = nexus.capabilityHandlers.get(_nCap);
|
|
13525
|
+
}
|
|
13526
|
+
if (!_nHandler) {
|
|
13527
|
+
var _nCaps = typeof nexus.getRegisteredCapabilities === 'function'
|
|
13528
|
+
? nexus.getRegisteredCapabilities() : [];
|
|
13529
|
+
_nm.respond(_natsCodec.encode(JSON.stringify({
|
|
13530
|
+
error: 'Capability not found: ' + _nCap + '. Available: ' + _nCaps.join(', ')
|
|
13531
|
+
})));
|
|
13532
|
+
continue;
|
|
13533
|
+
}
|
|
13534
|
+
|
|
13535
|
+
// Create mock stream handle that captures the handler's output
|
|
13536
|
+
var _nResults = [];
|
|
13537
|
+
var _nHandleDone = false;
|
|
13538
|
+
var _nHandle = {
|
|
13539
|
+
write: async function(msg) {
|
|
13540
|
+
if (msg && msg.type === 'invoke.event') _nResults.push(msg);
|
|
13541
|
+
if (msg && msg.type === 'invoke.done') _nHandleDone = true;
|
|
13542
|
+
},
|
|
13543
|
+
onData: function(cb) {
|
|
13544
|
+
// Deliver input data asynchronously after handler registers its listener
|
|
13545
|
+
setTimeout(function() {
|
|
13546
|
+
try {
|
|
13547
|
+
cb({ type: 'invoke.chunk', data: _nInputStr, seq: 0 });
|
|
13548
|
+
cb({ type: 'invoke.done' });
|
|
13549
|
+
} catch {}
|
|
13550
|
+
}, 5);
|
|
13551
|
+
},
|
|
13552
|
+
close: function() {},
|
|
13553
|
+
};
|
|
13554
|
+
|
|
13555
|
+
var _nRequest = {
|
|
13556
|
+
type: 'invoke.open',
|
|
13557
|
+
requestId: 'nats-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8),
|
|
13558
|
+
capability: _nCap,
|
|
13559
|
+
from: _nReq.from || 'nats-peer',
|
|
13560
|
+
};
|
|
13561
|
+
|
|
13562
|
+
await _nHandler(_nRequest, _nHandle);
|
|
13563
|
+
|
|
13564
|
+
// Extract result from captured invoke.event messages
|
|
13565
|
+
var _nResult = null;
|
|
13566
|
+
if (_nResults.length === 1) {
|
|
13567
|
+
_nResult = _nResults[0].data;
|
|
13568
|
+
} else if (_nResults.length > 1) {
|
|
13569
|
+
_nResult = _nResults.map(function(r) { return r.data; });
|
|
13570
|
+
}
|
|
13571
|
+
|
|
13572
|
+
_nm.respond(_natsCodec.encode(JSON.stringify(_nResult !== null && _nResult !== undefined ? _nResult : { ok: true })));
|
|
13573
|
+
dlog('NATS relay invoke SUCCESS: ' + _nCap);
|
|
13574
|
+
} catch (_nErr) {
|
|
13575
|
+
dlog('NATS relay invoke error: ' + (_nErr.message || _nErr));
|
|
13576
|
+
try {
|
|
13577
|
+
_nm.respond(_natsCodec.encode(JSON.stringify({ error: String(_nErr.message || _nErr) })));
|
|
13578
|
+
} catch {}
|
|
13579
|
+
}
|
|
13580
|
+
}
|
|
13581
|
+
})().catch(function(e) { dlog('NATS invoke relay loop error: ' + (e.message || e)); });
|
|
13582
|
+
} else {
|
|
13583
|
+
dlog('NATS not connected \u2014 invoke relay disabled (cross-NAT invoke will not work)');
|
|
13584
|
+
}
|
|
13585
|
+
} catch (_nSetupErr) {
|
|
13586
|
+
dlog('NATS invoke relay setup failed: ' + (_nSetupErr.message || _nSetupErr));
|
|
13587
|
+
}
|
|
13588
|
+
|
|
13362
13589
|
writeStatus();
|
|
13363
13590
|
console.log('Nexus daemon connected as ' + nexus.peerId);
|
|
13364
13591
|
} catch (err) {
|
package/package.json
CHANGED