open-agents-ai 0.103.43 → 0.103.45
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 +128 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12601,6 +12601,18 @@ async function handleCmd(cmd) {
|
|
|
12601
12601
|
|
|
12602
12602
|
// \u2500\u2500 Metered Inference Exposure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
12603
12603
|
case 'expose': {
|
|
12604
|
+
// Clear old inference capabilities before registering new ones
|
|
12605
|
+
// (prevents stale local Ollama models when switching to passthrough, or vice versa)
|
|
12606
|
+
if (typeof nexus.getRegisteredCapabilities === 'function' && typeof nexus.unregisterCapability === 'function') {
|
|
12607
|
+
var oldCaps = nexus.getRegisteredCapabilities();
|
|
12608
|
+
for (var oci = 0; oci < oldCaps.length; oci++) {
|
|
12609
|
+
if (oldCaps[oci].startsWith('inference:') || oldCaps[oci] === 'system_metrics' || oldCaps[oci] === '__list_capabilities') {
|
|
12610
|
+
try { nexus.unregisterCapability(oldCaps[oci]); } catch {}
|
|
12611
|
+
}
|
|
12612
|
+
}
|
|
12613
|
+
dlog('expose: cleared ' + oldCaps.length + ' old capabilities');
|
|
12614
|
+
}
|
|
12615
|
+
|
|
12604
12616
|
// Auth key for gating inference access (passed from ExposeP2PGateway)
|
|
12605
12617
|
var exposeAuthKey = args.auth_key || '';
|
|
12606
12618
|
if (exposeAuthKey) {
|
|
@@ -12754,6 +12766,16 @@ async function handleCmd(cmd) {
|
|
|
12754
12766
|
const logFile = join(invocationsDir, Date.now() + '-' + capName + '.json');
|
|
12755
12767
|
try { writeFileSync(logFile, JSON.stringify(logEntry, null, 2)); } catch {}
|
|
12756
12768
|
|
|
12769
|
+
// Safe stream write \u2014 consumer may have timed out and closed the stream
|
|
12770
|
+
var streamClosed = false;
|
|
12771
|
+
async function swrite(msg) {
|
|
12772
|
+
if (streamClosed) return;
|
|
12773
|
+
try { await stream.write(msg); } catch (swErr) {
|
|
12774
|
+
dlog('stream.write failed (consumer likely timed out): ' + (swErr.message || swErr));
|
|
12775
|
+
streamClosed = true;
|
|
12776
|
+
}
|
|
12777
|
+
}
|
|
12778
|
+
|
|
12757
12779
|
// Collect input via stream data events
|
|
12758
12780
|
// NOTE: auth_key arrives in invoke.chunk data, NOT in invoke.open (request).
|
|
12759
12781
|
// We must accept + read data FIRST, then validate auth.
|
|
@@ -12771,7 +12793,7 @@ async function handleCmd(cmd) {
|
|
|
12771
12793
|
});
|
|
12772
12794
|
|
|
12773
12795
|
// Accept the invocation so the consumer sends data
|
|
12774
|
-
await
|
|
12796
|
+
await swrite({
|
|
12775
12797
|
type: 'invoke.accept', version: 1,
|
|
12776
12798
|
requestId: request.requestId, accepted: true,
|
|
12777
12799
|
});
|
|
@@ -12802,9 +12824,9 @@ async function handleCmd(cmd) {
|
|
|
12802
12824
|
}
|
|
12803
12825
|
if (reqAuthKey !== exposeAuthKey) {
|
|
12804
12826
|
dlog('expose: auth REJECTED for ' + capName + ' from ' + (request.from || 'unknown'));
|
|
12805
|
-
await
|
|
12806
|
-
await
|
|
12807
|
-
stream.close();
|
|
12827
|
+
await swrite({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: 0, event: 'error', data: 'Unauthorized \u2014 invalid or missing auth key' });
|
|
12828
|
+
await swrite({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: 0 } });
|
|
12829
|
+
try { stream.close(); } catch {}
|
|
12808
12830
|
return;
|
|
12809
12831
|
}
|
|
12810
12832
|
dlog('expose: auth OK for ' + capName);
|
|
@@ -12923,14 +12945,14 @@ async function handleCmd(cmd) {
|
|
|
12923
12945
|
}
|
|
12924
12946
|
|
|
12925
12947
|
// Stream result back
|
|
12926
|
-
await
|
|
12948
|
+
await swrite({
|
|
12927
12949
|
type: 'invoke.event', version: 1,
|
|
12928
12950
|
requestId: request.requestId, seq: 0,
|
|
12929
12951
|
event: 'result',
|
|
12930
12952
|
data: JSON.stringify(responsePayload),
|
|
12931
12953
|
});
|
|
12932
12954
|
|
|
12933
|
-
await
|
|
12955
|
+
await swrite({
|
|
12934
12956
|
type: 'invoke.done', version: 1,
|
|
12935
12957
|
requestId: request.requestId,
|
|
12936
12958
|
usage: {
|
|
@@ -12940,18 +12962,19 @@ async function handleCmd(cmd) {
|
|
|
12940
12962
|
},
|
|
12941
12963
|
});
|
|
12942
12964
|
} catch (e) {
|
|
12943
|
-
|
|
12965
|
+
dlog('expose: inference error: ' + (e.message || e));
|
|
12966
|
+
await swrite({
|
|
12944
12967
|
type: 'invoke.event', version: 1,
|
|
12945
12968
|
requestId: request.requestId, seq: 0,
|
|
12946
12969
|
event: 'error', data: 'Inference failed: ' + e.message,
|
|
12947
12970
|
});
|
|
12948
|
-
await
|
|
12971
|
+
await swrite({
|
|
12949
12972
|
type: 'invoke.done', version: 1,
|
|
12950
12973
|
requestId: request.requestId,
|
|
12951
12974
|
usage: { inputBytes: 0, outputBytes: 0 },
|
|
12952
12975
|
});
|
|
12953
12976
|
}
|
|
12954
|
-
stream.close();
|
|
12977
|
+
try { stream.close(); } catch {}
|
|
12955
12978
|
}, capOpts);
|
|
12956
12979
|
}
|
|
12957
12980
|
}
|
|
@@ -13350,7 +13373,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
|
|
|
13350
13373
|
},
|
|
13351
13374
|
margin: {
|
|
13352
13375
|
type: "string",
|
|
13353
|
-
description: "Price margin multiplier for expose (0.5 = 50% of market rate, 0 = free). Default: 0
|
|
13376
|
+
description: "Price margin multiplier for expose (0.5 = 50% of market rate, 0 = free). Default: 0 (free)"
|
|
13354
13377
|
},
|
|
13355
13378
|
daily_limit: {
|
|
13356
13379
|
type: "string",
|
|
@@ -13618,23 +13641,57 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
|
|
|
13618
13641
|
// =========================================================================
|
|
13619
13642
|
async doConnect(args) {
|
|
13620
13643
|
await this.ensureDir();
|
|
13644
|
+
const currentScriptHash = createHash("sha256").update(DAEMON_SCRIPT).digest("hex").slice(0, 16);
|
|
13621
13645
|
const existingPid = this.getDaemonPid();
|
|
13622
13646
|
if (existingPid) {
|
|
13623
|
-
const
|
|
13624
|
-
|
|
13647
|
+
const daemonPath2 = join30(this.nexusDir, "nexus-daemon.mjs");
|
|
13648
|
+
let needsRestart = false;
|
|
13649
|
+
if (existsSync23(daemonPath2)) {
|
|
13625
13650
|
try {
|
|
13626
|
-
const
|
|
13627
|
-
|
|
13628
|
-
|
|
13651
|
+
const onDisk = readFileSync16(daemonPath2, "utf8");
|
|
13652
|
+
const diskHash = createHash("sha256").update(onDisk).digest("hex").slice(0, 16);
|
|
13653
|
+
if (diskHash !== currentScriptHash) {
|
|
13654
|
+
needsRestart = true;
|
|
13629
13655
|
}
|
|
13630
13656
|
} catch {
|
|
13657
|
+
needsRestart = true;
|
|
13631
13658
|
}
|
|
13632
13659
|
}
|
|
13633
|
-
|
|
13634
|
-
|
|
13635
|
-
|
|
13660
|
+
if (needsRestart) {
|
|
13661
|
+
try {
|
|
13662
|
+
process.kill(existingPid, "SIGTERM");
|
|
13663
|
+
} catch {
|
|
13664
|
+
}
|
|
13665
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
13666
|
+
try {
|
|
13667
|
+
process.kill(existingPid, 0);
|
|
13668
|
+
process.kill(existingPid, "SIGKILL");
|
|
13669
|
+
} catch {
|
|
13670
|
+
}
|
|
13671
|
+
for (const f of ["daemon.pid", "status.json", "cmd.json", "resp.json"]) {
|
|
13672
|
+
const p = join30(this.nexusDir, f);
|
|
13673
|
+
if (existsSync23(p))
|
|
13674
|
+
await unlink(p).catch(() => {
|
|
13675
|
+
});
|
|
13676
|
+
}
|
|
13677
|
+
} else {
|
|
13678
|
+
const statusFile2 = join30(this.nexusDir, "status.json");
|
|
13679
|
+
if (existsSync23(statusFile2)) {
|
|
13680
|
+
try {
|
|
13681
|
+
const status = JSON.parse(await readFile13(statusFile2, "utf8"));
|
|
13682
|
+
if (status.connected && status.peerId) {
|
|
13683
|
+
await this.ensureWallet();
|
|
13684
|
+
return `Already connected (pid: ${existingPid}, peerId: ${status.peerId})`;
|
|
13685
|
+
}
|
|
13686
|
+
} catch {
|
|
13687
|
+
}
|
|
13688
|
+
}
|
|
13689
|
+
try {
|
|
13690
|
+
process.kill(existingPid, "SIGTERM");
|
|
13691
|
+
} catch {
|
|
13692
|
+
}
|
|
13693
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
13636
13694
|
}
|
|
13637
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
13638
13695
|
}
|
|
13639
13696
|
for (const f of ["daemon.pid", "status.json", "cmd.json", "resp.json"]) {
|
|
13640
13697
|
const p = join30(this.nexusDir, f);
|
|
@@ -13703,6 +13760,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
|
|
|
13703
13760
|
}
|
|
13704
13761
|
}
|
|
13705
13762
|
}
|
|
13763
|
+
await this.ensureWallet();
|
|
13706
13764
|
const daemonPath = join30(this.nexusDir, "nexus-daemon.mjs");
|
|
13707
13765
|
await writeFile12(daemonPath, DAEMON_SCRIPT);
|
|
13708
13766
|
const agentName = args.agent_name || "open-agents-node";
|
|
@@ -14019,6 +14077,57 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
|
|
|
14019
14077
|
`Free inference (margin=0) works without funding.`
|
|
14020
14078
|
].join("\n");
|
|
14021
14079
|
}
|
|
14080
|
+
/**
|
|
14081
|
+
* Auto-create wallet + x402-wallet.key if none exists.
|
|
14082
|
+
* Called from doConnect() before daemon spawn so the daemon starts x402-ready.
|
|
14083
|
+
* Silent — no user output, just ensures the files exist.
|
|
14084
|
+
*/
|
|
14085
|
+
async ensureWallet() {
|
|
14086
|
+
const walletPath = join30(this.nexusDir, "wallet.enc");
|
|
14087
|
+
if (existsSync23(walletPath))
|
|
14088
|
+
return;
|
|
14089
|
+
let address;
|
|
14090
|
+
let privKeyHex;
|
|
14091
|
+
try {
|
|
14092
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
14093
|
+
const privKey = randomBytes6(32);
|
|
14094
|
+
privKeyHex = privKey.toString("hex");
|
|
14095
|
+
const account = privateKeyToAccount(`0x${privKeyHex}`);
|
|
14096
|
+
address = account.address;
|
|
14097
|
+
privKey.fill(0);
|
|
14098
|
+
} catch {
|
|
14099
|
+
const privKey = randomBytes6(32);
|
|
14100
|
+
privKeyHex = privKey.toString("hex");
|
|
14101
|
+
address = "0x" + createHash("sha256").update(privKey).digest("hex").slice(0, 40);
|
|
14102
|
+
privKey.fill(0);
|
|
14103
|
+
}
|
|
14104
|
+
const salt = randomBytes6(32);
|
|
14105
|
+
const key = scryptSync(`${hostname()}:${userInfo().username}:nexus-wallet`, salt, 32, { N: 16384, r: 8, p: 1 });
|
|
14106
|
+
const iv = randomBytes6(16);
|
|
14107
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
14108
|
+
let enc = cipher.update(privKeyHex, "utf8", "hex");
|
|
14109
|
+
enc += cipher.final("hex");
|
|
14110
|
+
const x402KeyPath = join30(this.nexusDir, "x402-wallet.key");
|
|
14111
|
+
const x402Fh = await fsOpen(x402KeyPath, "w", 384);
|
|
14112
|
+
await x402Fh.writeFile("0x" + privKeyHex);
|
|
14113
|
+
await x402Fh.close();
|
|
14114
|
+
privKeyHex = "0".repeat(64);
|
|
14115
|
+
const w = {
|
|
14116
|
+
version: 2,
|
|
14117
|
+
address,
|
|
14118
|
+
encryptedKey: enc,
|
|
14119
|
+
iv: iv.toString("hex"),
|
|
14120
|
+
authTag: cipher.getAuthTag().toString("hex"),
|
|
14121
|
+
salt: salt.toString("hex"),
|
|
14122
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14123
|
+
network: "base",
|
|
14124
|
+
chainId: 8453
|
|
14125
|
+
};
|
|
14126
|
+
const walletFh = await fsOpen(walletPath, "w", 384);
|
|
14127
|
+
await walletFh.writeFile(JSON.stringify(w, null, 2));
|
|
14128
|
+
await walletFh.close();
|
|
14129
|
+
await this.ensureDefaultBudget();
|
|
14130
|
+
}
|
|
14022
14131
|
// ---------------------------------------------------------------------------
|
|
14023
14132
|
// Step 2: USDC Balance Query via Base RPC
|
|
14024
14133
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED