open-agents-ai 0.184.49 → 0.184.50
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 +58 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5292,6 +5292,8 @@ var init_nexus = __esm({
|
|
|
5292
5292
|
import { NexusClient } from 'open-agents-nexus';
|
|
5293
5293
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, appendFileSync, watch as fsWatch } from 'node:fs';
|
|
5294
5294
|
import { join } from 'node:path';
|
|
5295
|
+
import { homedir, hostname } from 'node:os';
|
|
5296
|
+
import { createHash, createHmac } from 'node:crypto';
|
|
5295
5297
|
|
|
5296
5298
|
const nexusDir = process.argv[2];
|
|
5297
5299
|
const agentName = process.argv[3] || ('oa-node-' + process.pid);
|
|
@@ -5316,7 +5318,7 @@ writeFileSync(pidFile, String(process.pid));
|
|
|
5316
5318
|
|
|
5317
5319
|
// Use GLOBAL identity key so all OA instances on this machine share one peerId.
|
|
5318
5320
|
// Fallback to project-scoped key if global doesn't exist.
|
|
5319
|
-
const globalKeyDir = join(
|
|
5321
|
+
const globalKeyDir = join(homedir(), '.open-agents');
|
|
5320
5322
|
const globalKeyPath = join(globalKeyDir, 'identity.key');
|
|
5321
5323
|
const projectKeyPath = join(nexusDir, 'identity.key');
|
|
5322
5324
|
const keyPath = existsSync(globalKeyPath) ? globalKeyPath : projectKeyPath;
|
|
@@ -7248,19 +7250,33 @@ try {
|
|
|
7248
7250
|
try { process.stdout.on('error', function(e) { if (e.code !== 'EPIPE') throw e; }); } catch {}
|
|
7249
7251
|
try { process.stderr.on('error', function(e) { if (e.code !== 'EPIPE') throw e; }); } catch {}
|
|
7250
7252
|
|
|
7251
|
-
// Crash protection \u2014 prevent unhandled errors from killing the daemon
|
|
7253
|
+
// Crash protection \u2014 prevent unhandled errors from killing the daemon.
|
|
7254
|
+
// EPIPE/ECONNRESET/ETIMEDOUT are transient network errors \u2014 always swallow.
|
|
7255
|
+
// The daemon MUST stay alive through any non-fatal error.
|
|
7256
|
+
var TRANSIENT_CODES = ['EPIPE', 'ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EHOSTUNREACH', 'ENETUNREACH', 'EAI_AGAIN'];
|
|
7257
|
+
function isTransientError(err) {
|
|
7258
|
+
if (!err) return false;
|
|
7259
|
+
var code = err.code || '';
|
|
7260
|
+
var msg = err.message || String(err);
|
|
7261
|
+
return TRANSIENT_CODES.some(function(c) { return code === c || msg.includes(c); });
|
|
7262
|
+
}
|
|
7252
7263
|
process.on('uncaughtException', (err) => {
|
|
7264
|
+
if (isTransientError(err)) {
|
|
7265
|
+
dlog('Swallowed transient uncaughtException: ' + (err.code || err.message || err));
|
|
7266
|
+
return; // keep daemon alive
|
|
7267
|
+
}
|
|
7253
7268
|
var errMsg = err && err.message ? err.message : String(err);
|
|
7254
|
-
|
|
7255
|
-
if (err && err.code === 'EPIPE') return;
|
|
7256
|
-
if (errMsg.includes('EPIPE')) return;
|
|
7257
|
-
try { console.error('Nexus daemon uncaughtException:', errMsg); } catch {}
|
|
7269
|
+
try { dlog('uncaughtException (non-transient): ' + errMsg); } catch {}
|
|
7258
7270
|
writeStatus({ error: 'uncaughtException: ' + errMsg });
|
|
7271
|
+
// Do NOT exit \u2014 keep daemon alive even for non-transient errors
|
|
7259
7272
|
});
|
|
7260
7273
|
process.on('unhandledRejection', (reason) => {
|
|
7274
|
+
if (isTransientError(reason)) {
|
|
7275
|
+
dlog('Swallowed transient unhandledRejection: ' + (reason?.code || reason?.message || reason));
|
|
7276
|
+
return;
|
|
7277
|
+
}
|
|
7261
7278
|
var msg = reason instanceof Error ? reason.message : String(reason);
|
|
7262
|
-
|
|
7263
|
-
try { console.error('Nexus daemon unhandledRejection:', msg); } catch {}
|
|
7279
|
+
try { dlog('unhandledRejection (non-transient): ' + msg); } catch {}
|
|
7264
7280
|
// Do NOT exit \u2014 keep daemon alive
|
|
7265
7281
|
});
|
|
7266
7282
|
|
|
@@ -7705,7 +7721,7 @@ process.on('unhandledRejection', (reason) => {
|
|
|
7705
7721
|
const _cLatency = Date.now() - _cStart;
|
|
7706
7722
|
// Sign response for accountability (HMAC-SHA256 with peerId)
|
|
7707
7723
|
const _cSigData = _cData.queryId + ':' + _cContent.slice(0, 200) + ':' + _cModel + ':' + _cLatency;
|
|
7708
|
-
const _cSig =
|
|
7724
|
+
const _cSig = createHmac('sha256', nexus.peerId).update(_cSigData).digest('hex').slice(0, 32);
|
|
7709
7725
|
// Scan inbound query for leaked secrets (defense-in-depth)
|
|
7710
7726
|
const _cSecretPatterns = [/sk-[a-zA-Z0-9]{20,}/g, /ghp_[a-zA-Z0-9]{36,}/g, /AKIA[0-9A-Z]{16}/g];
|
|
7711
7727
|
for (const _cPat of _cSecretPatterns) {
|
|
@@ -7718,7 +7734,7 @@ process.on('unhandledRejection', (reason) => {
|
|
|
7718
7734
|
content: _cContent,
|
|
7719
7735
|
model: _cModel,
|
|
7720
7736
|
provider: nexus.peerId,
|
|
7721
|
-
agentName: nexus.agentName || ('oa-' +
|
|
7737
|
+
agentName: nexus.agentName || ('oa-' + hostname().slice(0, 12)),
|
|
7722
7738
|
latencyMs: _cLatency,
|
|
7723
7739
|
usage: _cResult.eval_count ? { inputTokens: _cResult.prompt_eval_count || 0, outputTokens: _cResult.eval_count || 0 } : undefined,
|
|
7724
7740
|
signature: _cSig,
|
|
@@ -8023,7 +8039,7 @@ process.on('unhandledRejection', (reason) => {
|
|
|
8023
8039
|
var _esTopIds = _esFiltered.map(function(m) { return m.id; }).sort().join(',');
|
|
8024
8040
|
|
|
8025
8041
|
// SHA-256 hash of top-10 IDs for compact comparison
|
|
8026
|
-
var _esHash =
|
|
8042
|
+
var _esHash = createHash('sha256').update(_esTopIds).digest('hex').slice(0, 16);
|
|
8027
8043
|
|
|
8028
8044
|
_epochCounter++;
|
|
8029
8045
|
var _esAnnouncement = {
|
|
@@ -8078,6 +8094,9 @@ process.on('unhandledRejection', (reason) => {
|
|
|
8078
8094
|
}
|
|
8079
8095
|
})();
|
|
8080
8096
|
|
|
8097
|
+
// Ignore SIGPIPE \u2014 broken pipe from network writes should NOT kill daemon
|
|
8098
|
+
try { process.on('SIGPIPE', function() { dlog('SIGPIPE received \u2014 ignored'); }); } catch {}
|
|
8099
|
+
|
|
8081
8100
|
// Graceful shutdown
|
|
8082
8101
|
process.on('SIGTERM', async () => {
|
|
8083
8102
|
for (const [, room] of rooms) { try { await room.leave(); } catch {} }
|
|
@@ -8511,8 +8530,36 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
|
|
|
8511
8530
|
}
|
|
8512
8531
|
return `Daemon did not respond within ${Math.round(timeoutMs / 1e3)}s. It may still be processing.`;
|
|
8513
8532
|
}
|
|
8533
|
+
/** Check if daemon process is alive. Returns PID if alive, 0 if dead. */
|
|
8534
|
+
isDaemonAlive() {
|
|
8535
|
+
const pid = this.getDaemonPid();
|
|
8536
|
+
if (!pid)
|
|
8537
|
+
return 0;
|
|
8538
|
+
try {
|
|
8539
|
+
process.kill(pid, 0);
|
|
8540
|
+
return pid;
|
|
8541
|
+
} catch {
|
|
8542
|
+
return 0;
|
|
8543
|
+
}
|
|
8544
|
+
}
|
|
8545
|
+
/** Auto-restart daemon if it crashed. Called before every command. */
|
|
8546
|
+
async ensureDaemonAlive() {
|
|
8547
|
+
if (this.isDaemonAlive())
|
|
8548
|
+
return;
|
|
8549
|
+
const statusFile = join14(this.nexusDir, "status.json");
|
|
8550
|
+
if (!existsSync11(statusFile))
|
|
8551
|
+
return;
|
|
8552
|
+
try {
|
|
8553
|
+
const status = JSON.parse(readFileSync8(statusFile, "utf8"));
|
|
8554
|
+
if (status.agentName) {
|
|
8555
|
+
await this.doConnect({ agent_name: status.agentName, agent_type: status.agentType || "general" });
|
|
8556
|
+
}
|
|
8557
|
+
} catch {
|
|
8558
|
+
}
|
|
8559
|
+
}
|
|
8514
8560
|
/** Public interface for daemon commands — used by NexusAgenticBackend */
|
|
8515
8561
|
async sendCommand(action, args, timeoutMs) {
|
|
8562
|
+
await this.ensureDaemonAlive();
|
|
8516
8563
|
return this.sendDaemonCmd(action, args, timeoutMs);
|
|
8517
8564
|
}
|
|
8518
8565
|
/** Get the nexus data directory path */
|
package/package.json
CHANGED