open-agents-ai 0.103.9 → 0.103.11
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
CHANGED
|
@@ -12839,7 +12839,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
|
|
|
12839
12839
|
];
|
|
12840
12840
|
NexusTool = class {
|
|
12841
12841
|
name = "nexus";
|
|
12842
|
-
description = "
|
|
12842
|
+
description = "P2P agent mesh networking. IMPORTANT: You MUST call action='connect' FIRST \u2014 it spawns the daemon. Nothing works without it. After connect: join_room, send_message, discover_peers, expose (share models), remote_infer (use remote models). Quick start: connect \u2192 join_room \u2192 send_message. Also: wallet_create, wallet_status, ledger_status, budget_set, spend, invoke_capability, store_content.";
|
|
12843
12843
|
parameters = {
|
|
12844
12844
|
type: "object",
|
|
12845
12845
|
properties: {
|
|
@@ -12878,7 +12878,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
|
|
|
12878
12878
|
"spend",
|
|
12879
12879
|
"remote_infer"
|
|
12880
12880
|
],
|
|
12881
|
-
description: "The nexus action
|
|
12881
|
+
description: "The nexus action. MUST call 'connect' first (spawns daemon). Then: join_room, send_message, discover_peers, expose, status, etc."
|
|
12882
12882
|
},
|
|
12883
12883
|
room_id: {
|
|
12884
12884
|
type: "string",
|
|
@@ -17464,7 +17464,9 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
|
|
|
17464
17464
|
} catch (reqErr) {
|
|
17465
17465
|
const recovered = await this.retryOnTransient(reqErr, chatRequest, turn);
|
|
17466
17466
|
if (!recovered) {
|
|
17467
|
-
|
|
17467
|
+
const errMsg = reqErr instanceof Error ? reqErr.message : String(reqErr);
|
|
17468
|
+
const cause = reqErr instanceof Error && reqErr.cause ? ` (${reqErr.cause.message ?? ""} ${reqErr.cause?.code ?? ""})` : "";
|
|
17469
|
+
this.emit({ type: "error", content: `Backend error: ${errMsg}${cause}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
17468
17470
|
messages.push({ role: "user", content: "[System: backend request failed, retrying on next turn. The previous request was lost.]" });
|
|
17469
17471
|
continue;
|
|
17470
17472
|
}
|
|
@@ -17974,7 +17976,9 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
17974
17976
|
} catch (reqErr) {
|
|
17975
17977
|
const recovered = await this.retryOnTransient(reqErr, chatRequest, turn);
|
|
17976
17978
|
if (!recovered) {
|
|
17977
|
-
|
|
17979
|
+
const errMsg2 = reqErr instanceof Error ? reqErr.message : String(reqErr);
|
|
17980
|
+
const cause2 = reqErr instanceof Error && reqErr.cause ? ` (${reqErr.cause.message ?? ""} ${reqErr.cause?.code ?? ""})` : "";
|
|
17981
|
+
this.emit({ type: "error", content: `Backend error: ${errMsg2}${cause2}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
17978
17982
|
messages.push({ role: "user", content: "[System: backend request failed, retrying on next turn. The previous request was lost.]" });
|
|
17979
17983
|
continue;
|
|
17980
17984
|
}
|
|
@@ -20358,9 +20362,9 @@ function ensureTranscribeCliBackground() {
|
|
|
20358
20362
|
} catch {
|
|
20359
20363
|
}
|
|
20360
20364
|
try {
|
|
20361
|
-
const { exec } = await import("node:child_process");
|
|
20365
|
+
const { exec: exec2 } = await import("node:child_process");
|
|
20362
20366
|
return new Promise((resolve31) => {
|
|
20363
|
-
|
|
20367
|
+
exec2("npm i -g transcribe-cli", { timeout: 18e4 }, (err) => {
|
|
20364
20368
|
resolve31(!err);
|
|
20365
20369
|
});
|
|
20366
20370
|
});
|
|
@@ -26194,12 +26198,30 @@ var init_voice_session = __esm({
|
|
|
26194
26198
|
|
|
26195
26199
|
// packages/cli/dist/tui/expose.js
|
|
26196
26200
|
import { createServer as createServer2, request as httpRequest } from "node:http";
|
|
26197
|
-
import { spawn as spawn14,
|
|
26201
|
+
import { spawn as spawn14, exec } from "node:child_process";
|
|
26198
26202
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
26199
26203
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
26200
26204
|
import { URL as URL2 } from "node:url";
|
|
26201
26205
|
import { loadavg, cpus, totalmem, freemem } from "node:os";
|
|
26202
|
-
function
|
|
26206
|
+
function cleanForwardHeaders(raw, targetHost) {
|
|
26207
|
+
const out = {};
|
|
26208
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
26209
|
+
if (HOP_BY_HOP_HEADERS.has(key))
|
|
26210
|
+
continue;
|
|
26211
|
+
if (CF_HEADERS_PREFIX.some((p) => key.startsWith(p)))
|
|
26212
|
+
continue;
|
|
26213
|
+
if (key === "authorization")
|
|
26214
|
+
continue;
|
|
26215
|
+
if (key.startsWith("x-forwarded-"))
|
|
26216
|
+
continue;
|
|
26217
|
+
if (key === "sec-fetch-mode")
|
|
26218
|
+
continue;
|
|
26219
|
+
out[key] = value;
|
|
26220
|
+
}
|
|
26221
|
+
out.host = targetHost;
|
|
26222
|
+
return out;
|
|
26223
|
+
}
|
|
26224
|
+
async function collectSystemMetricsAsync() {
|
|
26203
26225
|
const [l1, l5, l15] = loadavg();
|
|
26204
26226
|
const cores = cpus().length;
|
|
26205
26227
|
const totalMem = totalmem();
|
|
@@ -26214,7 +26236,9 @@ function collectSystemMetrics() {
|
|
|
26214
26236
|
vramUtilization: 0
|
|
26215
26237
|
};
|
|
26216
26238
|
try {
|
|
26217
|
-
const smi =
|
|
26239
|
+
const smi = await new Promise((resolve31, reject) => {
|
|
26240
|
+
exec("nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 3e3 }, (err, stdout) => err ? reject(err) : resolve31(stdout));
|
|
26241
|
+
});
|
|
26218
26242
|
const line = smi.trim().split("\n")[0];
|
|
26219
26243
|
if (line) {
|
|
26220
26244
|
const parts = line.split(",").map((s) => s.trim());
|
|
@@ -26245,11 +26269,22 @@ function collectSystemMetrics() {
|
|
|
26245
26269
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
26246
26270
|
};
|
|
26247
26271
|
}
|
|
26248
|
-
var DEFAULT_TARGETS, ExposeGateway;
|
|
26272
|
+
var HOP_BY_HOP_HEADERS, CF_HEADERS_PREFIX, DEFAULT_TARGETS, ExposeGateway;
|
|
26249
26273
|
var init_expose = __esm({
|
|
26250
26274
|
"packages/cli/dist/tui/expose.js"() {
|
|
26251
26275
|
"use strict";
|
|
26252
26276
|
init_render();
|
|
26277
|
+
HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
26278
|
+
"connection",
|
|
26279
|
+
"keep-alive",
|
|
26280
|
+
"proxy-authenticate",
|
|
26281
|
+
"proxy-authorization",
|
|
26282
|
+
"te",
|
|
26283
|
+
"trailers",
|
|
26284
|
+
"transfer-encoding",
|
|
26285
|
+
"upgrade"
|
|
26286
|
+
]);
|
|
26287
|
+
CF_HEADERS_PREFIX = ["cf-", "cdn-"];
|
|
26253
26288
|
DEFAULT_TARGETS = {
|
|
26254
26289
|
ollama: "http://127.0.0.1:11434",
|
|
26255
26290
|
vllm: "http://127.0.0.1:8000",
|
|
@@ -26331,100 +26366,168 @@ var init_expose = __esm({
|
|
|
26331
26366
|
// ── Proxy server ────────────────────────────────────────────────────────
|
|
26332
26367
|
createProxyServer(localPort) {
|
|
26333
26368
|
const target = new URL2(this._targetUrl);
|
|
26334
|
-
|
|
26369
|
+
const server = createServer2((req, res) => {
|
|
26370
|
+
res.on("error", () => {
|
|
26371
|
+
});
|
|
26335
26372
|
const authHeader = req.headers.authorization;
|
|
26336
26373
|
const url = new URL2(req.url ?? "/", `http://127.0.0.1:${localPort}`);
|
|
26337
26374
|
const queryKey = url.searchParams.get("key");
|
|
26338
26375
|
const providedKey = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : queryKey;
|
|
26339
26376
|
if (providedKey !== this._authKey) {
|
|
26340
|
-
this._stats.errors++;
|
|
26341
26377
|
res.writeHead(401, { "Content-Type": "application/json" });
|
|
26342
26378
|
res.end(JSON.stringify({ error: "Unauthorized \u2014 provide Bearer token or ?key= parameter" }));
|
|
26343
|
-
this.emitStats();
|
|
26344
26379
|
return;
|
|
26345
26380
|
}
|
|
26346
26381
|
if (url.pathname === "/v1/system/metrics" && req.method === "GET") {
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
|
|
26382
|
+
collectSystemMetricsAsync().then((metrics) => {
|
|
26383
|
+
const gatewayStats = {
|
|
26384
|
+
totalRequests: this._stats.totalRequests,
|
|
26385
|
+
activeConnections: this._stats.activeConnections,
|
|
26386
|
+
errors: this._stats.errors,
|
|
26387
|
+
modelUsage: Object.fromEntries(this._stats.modelUsage)
|
|
26388
|
+
};
|
|
26389
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
26390
|
+
res.end(JSON.stringify({ ...metrics, gateway: gatewayStats }));
|
|
26391
|
+
}).catch(() => {
|
|
26392
|
+
res.writeHead(500);
|
|
26393
|
+
res.end("metrics error");
|
|
26394
|
+
});
|
|
26356
26395
|
return;
|
|
26357
26396
|
}
|
|
26358
26397
|
this._stats.totalRequests++;
|
|
26359
26398
|
this._stats.activeConnections++;
|
|
26360
26399
|
this.emitStats();
|
|
26361
|
-
this.extractModelFromRequest(req);
|
|
26362
26400
|
url.searchParams.delete("key");
|
|
26363
26401
|
const forwardPath = url.pathname + url.search;
|
|
26364
|
-
const
|
|
26365
|
-
|
|
26366
|
-
|
|
26367
|
-
|
|
26368
|
-
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
|
|
26372
|
-
|
|
26373
|
-
|
|
26374
|
-
|
|
26375
|
-
|
|
26402
|
+
const bodyChunks = [];
|
|
26403
|
+
req.on("data", (chunk) => bodyChunks.push(chunk));
|
|
26404
|
+
req.on("end", () => {
|
|
26405
|
+
const body = Buffer.concat(bodyChunks);
|
|
26406
|
+
let isStreaming = false;
|
|
26407
|
+
if (req.method === "POST" && body.length > 0) {
|
|
26408
|
+
try {
|
|
26409
|
+
const text = body.toString("utf8", 0, Math.min(body.length, 1024));
|
|
26410
|
+
const modelMatch = text.match(/"model"\s*:\s*"([^"]+)"/);
|
|
26411
|
+
if (modelMatch) {
|
|
26412
|
+
const model = modelMatch[1];
|
|
26413
|
+
const count = this._stats.modelUsage.get(model) ?? 0;
|
|
26414
|
+
this._stats.modelUsage.set(model, count + 1);
|
|
26415
|
+
}
|
|
26416
|
+
isStreaming = /"stream"\s*:\s*true/.test(text);
|
|
26417
|
+
} catch {
|
|
26418
|
+
}
|
|
26419
|
+
}
|
|
26420
|
+
const forwardHeaders = cleanForwardHeaders(req.headers, target.host);
|
|
26421
|
+
if (body.length > 0) {
|
|
26422
|
+
forwardHeaders["content-length"] = String(body.length);
|
|
26423
|
+
}
|
|
26424
|
+
let heartbeatTimer = null;
|
|
26425
|
+
if (isStreaming) {
|
|
26426
|
+
res.writeHead(200, {
|
|
26427
|
+
"Content-Type": "text/event-stream",
|
|
26428
|
+
"Cache-Control": "no-cache",
|
|
26429
|
+
"X-Accel-Buffering": "no"
|
|
26430
|
+
// tells nginx/CF not to buffer
|
|
26431
|
+
});
|
|
26432
|
+
heartbeatTimer = setInterval(() => {
|
|
26433
|
+
try {
|
|
26434
|
+
res.write(": heartbeat\n\n");
|
|
26435
|
+
} catch {
|
|
26436
|
+
}
|
|
26437
|
+
}, 15e3);
|
|
26438
|
+
}
|
|
26439
|
+
const cleanupHeartbeat = () => {
|
|
26440
|
+
if (heartbeatTimer) {
|
|
26441
|
+
clearInterval(heartbeatTimer);
|
|
26442
|
+
heartbeatTimer = null;
|
|
26443
|
+
}
|
|
26444
|
+
};
|
|
26445
|
+
const proxyReq = httpRequest({
|
|
26446
|
+
hostname: target.hostname,
|
|
26447
|
+
port: target.port || (target.protocol === "https:" ? 443 : 80),
|
|
26448
|
+
path: forwardPath,
|
|
26449
|
+
method: req.method,
|
|
26450
|
+
headers: forwardHeaders
|
|
26451
|
+
}, (proxyRes) => {
|
|
26452
|
+
cleanupHeartbeat();
|
|
26453
|
+
proxyRes.on("error", () => {
|
|
26454
|
+
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26455
|
+
this.emitStats();
|
|
26456
|
+
});
|
|
26457
|
+
if (isStreaming) {
|
|
26458
|
+
if (proxyRes.statusCode !== 200) {
|
|
26459
|
+
let errBody = "";
|
|
26460
|
+
proxyRes.on("data", (chunk) => {
|
|
26461
|
+
errBody += chunk.toString();
|
|
26462
|
+
});
|
|
26463
|
+
proxyRes.on("end", () => {
|
|
26464
|
+
try {
|
|
26465
|
+
res.write(`data: ${JSON.stringify({ error: `Backend ${proxyRes.statusCode}: ${errBody.slice(0, 200)}` })}
|
|
26466
|
+
|
|
26467
|
+
`);
|
|
26468
|
+
} catch {
|
|
26469
|
+
}
|
|
26470
|
+
try {
|
|
26471
|
+
res.end();
|
|
26472
|
+
} catch {
|
|
26473
|
+
}
|
|
26474
|
+
this._stats.errors++;
|
|
26475
|
+
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26476
|
+
this.emitStats();
|
|
26477
|
+
});
|
|
26478
|
+
return;
|
|
26479
|
+
}
|
|
26480
|
+
proxyRes.pipe(res);
|
|
26481
|
+
} else {
|
|
26482
|
+
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
|
|
26483
|
+
proxyRes.pipe(res);
|
|
26484
|
+
}
|
|
26485
|
+
proxyRes.on("end", () => {
|
|
26486
|
+
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26487
|
+
this.emitStats();
|
|
26488
|
+
});
|
|
26489
|
+
});
|
|
26490
|
+
proxyReq.on("error", (err) => {
|
|
26491
|
+
cleanupHeartbeat();
|
|
26492
|
+
this._stats.errors++;
|
|
26376
26493
|
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26494
|
+
if (!res.headersSent) {
|
|
26495
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
26496
|
+
res.end(JSON.stringify({ error: `Backend unreachable: ${err.message}` }));
|
|
26497
|
+
} else {
|
|
26498
|
+
try {
|
|
26499
|
+
res.write(`data: ${JSON.stringify({ error: err.message })}
|
|
26500
|
+
|
|
26501
|
+
`);
|
|
26502
|
+
} catch {
|
|
26503
|
+
}
|
|
26504
|
+
try {
|
|
26505
|
+
res.end();
|
|
26506
|
+
} catch {
|
|
26507
|
+
}
|
|
26508
|
+
}
|
|
26377
26509
|
this.emitStats();
|
|
26378
26510
|
});
|
|
26511
|
+
if (body.length > 0) {
|
|
26512
|
+
proxyReq.write(body);
|
|
26513
|
+
}
|
|
26514
|
+
proxyReq.end();
|
|
26379
26515
|
});
|
|
26380
|
-
|
|
26381
|
-
this._stats.errors++;
|
|
26516
|
+
req.on("error", () => {
|
|
26382
26517
|
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26383
|
-
if (!res.headersSent) {
|
|
26384
|
-
res.writeHead(502, { "Content-Type": "application/json" });
|
|
26385
|
-
res.end(JSON.stringify({ error: `Backend unreachable: ${err.message}` }));
|
|
26386
|
-
}
|
|
26387
|
-
this._stats.status = "error";
|
|
26388
26518
|
this.emitStats();
|
|
26389
|
-
setTimeout(() => {
|
|
26390
|
-
if (this.server && this._stats.status === "error") {
|
|
26391
|
-
this._stats.status = "active";
|
|
26392
|
-
this.emitStats();
|
|
26393
|
-
}
|
|
26394
|
-
}, 5e3);
|
|
26395
26519
|
});
|
|
26396
|
-
req.pipe(proxyReq);
|
|
26397
26520
|
});
|
|
26398
|
-
|
|
26399
|
-
|
|
26400
|
-
|
|
26401
|
-
|
|
26402
|
-
return;
|
|
26403
|
-
const chunks = [];
|
|
26404
|
-
const origEmit = req.emit.bind(req);
|
|
26405
|
-
let peeked = false;
|
|
26406
|
-
const stats = this._stats;
|
|
26407
|
-
req.emit = function(event, ...args) {
|
|
26408
|
-
if (event === "data" && !peeked) {
|
|
26409
|
-
peeked = true;
|
|
26410
|
-
try {
|
|
26411
|
-
const chunk = args[0];
|
|
26412
|
-
chunks.push(chunk);
|
|
26413
|
-
const text = Buffer.concat(chunks).toString();
|
|
26414
|
-
const modelMatch = text.match(/"model"\s*:\s*"([^"]+)"/);
|
|
26415
|
-
if (modelMatch) {
|
|
26416
|
-
const model = modelMatch[1];
|
|
26417
|
-
const count = stats.modelUsage.get(model) ?? 0;
|
|
26418
|
-
stats.modelUsage.set(model, count + 1);
|
|
26419
|
-
}
|
|
26420
|
-
} catch {
|
|
26421
|
-
}
|
|
26422
|
-
}
|
|
26423
|
-
return origEmit(event, ...args);
|
|
26424
|
-
};
|
|
26521
|
+
server.on("error", (err) => {
|
|
26522
|
+
this.options.onError?.(`Proxy server error: ${err.message}`);
|
|
26523
|
+
});
|
|
26524
|
+
return server;
|
|
26425
26525
|
}
|
|
26426
26526
|
// ── Cloudflared ─────────────────────────────────────────────────────────
|
|
26527
|
+
/** Port the proxy is listening on (needed for cloudflared restart) */
|
|
26528
|
+
_proxyPort = 0;
|
|
26427
26529
|
startCloudflared(port) {
|
|
26530
|
+
this._proxyPort = port;
|
|
26428
26531
|
return new Promise((resolve31, reject) => {
|
|
26429
26532
|
const timeout = setTimeout(() => {
|
|
26430
26533
|
reject(new Error("Cloudflared tunnel start timeout (30s)"));
|
|
@@ -26432,7 +26535,9 @@ var init_expose = __esm({
|
|
|
26432
26535
|
this.cloudflaredProcess = spawn14("cloudflared", [
|
|
26433
26536
|
"tunnel",
|
|
26434
26537
|
"--url",
|
|
26435
|
-
`http://127.0.0.1:${port}
|
|
26538
|
+
`http://127.0.0.1:${port}`,
|
|
26539
|
+
"--proxy-keepalive-timeout",
|
|
26540
|
+
"300s"
|
|
26436
26541
|
], {
|
|
26437
26542
|
stdio: ["ignore", "pipe", "pipe"]
|
|
26438
26543
|
});
|
|
@@ -26458,12 +26563,27 @@ var init_expose = __esm({
|
|
|
26458
26563
|
reject(new Error(`Cloudflared exited with code ${code} before providing URL`));
|
|
26459
26564
|
}
|
|
26460
26565
|
if (this._stats.status === "active") {
|
|
26566
|
+
this.options.onInfo?.("Cloudflared tunnel died \u2014 restarting...");
|
|
26461
26567
|
this._stats.status = "error";
|
|
26462
26568
|
this.emitStats();
|
|
26569
|
+
this.restartCloudflared();
|
|
26463
26570
|
}
|
|
26464
26571
|
});
|
|
26465
26572
|
});
|
|
26466
26573
|
}
|
|
26574
|
+
/** Auto-restart cloudflared after unexpected exit (new URL assigned) */
|
|
26575
|
+
async restartCloudflared() {
|
|
26576
|
+
if (!this.server || this._proxyPort === 0)
|
|
26577
|
+
return;
|
|
26578
|
+
try {
|
|
26579
|
+
this._tunnelUrl = await this.startCloudflared(this._proxyPort);
|
|
26580
|
+
this._stats.status = "active";
|
|
26581
|
+
this.emitStats();
|
|
26582
|
+
this.options.onInfo?.(`Tunnel restarted: ${this._tunnelUrl}`);
|
|
26583
|
+
} catch (err) {
|
|
26584
|
+
this.options.onError?.(`Tunnel restart failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26585
|
+
}
|
|
26586
|
+
}
|
|
26467
26587
|
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
26468
26588
|
findFreePort() {
|
|
26469
26589
|
return new Promise((resolve31, reject) => {
|
|
@@ -28618,7 +28738,7 @@ var init_oa_directory = __esm({
|
|
|
28618
28738
|
|
|
28619
28739
|
// packages/cli/dist/tui/setup.js
|
|
28620
28740
|
import * as readline from "node:readline";
|
|
28621
|
-
import { execSync as
|
|
28741
|
+
import { execSync as execSync25, spawn as spawn15 } from "node:child_process";
|
|
28622
28742
|
import { existsSync as existsSync29, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10 } from "node:fs";
|
|
28623
28743
|
import { join as join39 } from "node:path";
|
|
28624
28744
|
import { homedir as homedir10, platform } from "node:os";
|
|
@@ -28628,7 +28748,7 @@ function detectSystemSpecs() {
|
|
|
28628
28748
|
let gpuVramGB = 0;
|
|
28629
28749
|
let gpuName = "";
|
|
28630
28750
|
try {
|
|
28631
|
-
const memInfo =
|
|
28751
|
+
const memInfo = execSync25("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
|
|
28632
28752
|
encoding: "utf8",
|
|
28633
28753
|
timeout: 5e3
|
|
28634
28754
|
});
|
|
@@ -28648,7 +28768,7 @@ function detectSystemSpecs() {
|
|
|
28648
28768
|
} catch {
|
|
28649
28769
|
}
|
|
28650
28770
|
try {
|
|
28651
|
-
const nvidiaSmi =
|
|
28771
|
+
const nvidiaSmi = execSync25("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
|
|
28652
28772
|
const lines = nvidiaSmi.trim().split("\n");
|
|
28653
28773
|
if (lines.length > 0) {
|
|
28654
28774
|
for (const line of lines) {
|
|
@@ -28773,7 +28893,7 @@ function ensureCurl() {
|
|
|
28773
28893
|
for (const s of strategies) {
|
|
28774
28894
|
if (hasCmd(s.check)) {
|
|
28775
28895
|
try {
|
|
28776
|
-
|
|
28896
|
+
execSync25(s.install, { stdio: "inherit", timeout: 12e4 });
|
|
28777
28897
|
if (hasCmd("curl")) {
|
|
28778
28898
|
process.stdout.write(` ${c2.green("\u2714")} curl installed via ${s.label}.
|
|
28779
28899
|
`);
|
|
@@ -28787,7 +28907,7 @@ function ensureCurl() {
|
|
|
28787
28907
|
}
|
|
28788
28908
|
if (plat === "darwin") {
|
|
28789
28909
|
try {
|
|
28790
|
-
|
|
28910
|
+
execSync25("xcode-select --install", { stdio: "inherit", timeout: 3e5 });
|
|
28791
28911
|
if (hasCmd("curl"))
|
|
28792
28912
|
return true;
|
|
28793
28913
|
} catch {
|
|
@@ -28822,7 +28942,7 @@ function installOllamaLinux() {
|
|
|
28822
28942
|
|
|
28823
28943
|
`);
|
|
28824
28944
|
try {
|
|
28825
|
-
|
|
28945
|
+
execSync25("curl -fsSL https://ollama.com/install.sh | sh", {
|
|
28826
28946
|
stdio: "inherit",
|
|
28827
28947
|
timeout: 3e5
|
|
28828
28948
|
});
|
|
@@ -28850,7 +28970,7 @@ async function installOllamaMac(_rl) {
|
|
|
28850
28970
|
|
|
28851
28971
|
`);
|
|
28852
28972
|
try {
|
|
28853
|
-
|
|
28973
|
+
execSync25('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit", timeout: 6e5 });
|
|
28854
28974
|
if (!hasCmd("brew")) {
|
|
28855
28975
|
try {
|
|
28856
28976
|
const brewPrefix = existsSync29("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local";
|
|
@@ -28883,7 +29003,7 @@ async function installOllamaMac(_rl) {
|
|
|
28883
29003
|
|
|
28884
29004
|
`);
|
|
28885
29005
|
try {
|
|
28886
|
-
|
|
29006
|
+
execSync25("brew install ollama", {
|
|
28887
29007
|
stdio: "inherit",
|
|
28888
29008
|
timeout: 3e5
|
|
28889
29009
|
});
|
|
@@ -28910,7 +29030,7 @@ function installOllamaWindows() {
|
|
|
28910
29030
|
|
|
28911
29031
|
`);
|
|
28912
29032
|
try {
|
|
28913
|
-
|
|
29033
|
+
execSync25('powershell -Command "irm https://ollama.com/install.ps1 | iex"', {
|
|
28914
29034
|
stdio: "inherit",
|
|
28915
29035
|
timeout: 3e5
|
|
28916
29036
|
});
|
|
@@ -28991,7 +29111,7 @@ async function ensureOllamaRunning(backendUrl, rl) {
|
|
|
28991
29111
|
}
|
|
28992
29112
|
function pullModelWithAutoUpdate(tag) {
|
|
28993
29113
|
try {
|
|
28994
|
-
|
|
29114
|
+
execSync25(`ollama pull ${tag}`, {
|
|
28995
29115
|
stdio: "inherit",
|
|
28996
29116
|
timeout: 36e5
|
|
28997
29117
|
// 1 hour max
|
|
@@ -29011,7 +29131,7 @@ function pullModelWithAutoUpdate(tag) {
|
|
|
29011
29131
|
|
|
29012
29132
|
`);
|
|
29013
29133
|
try {
|
|
29014
|
-
|
|
29134
|
+
execSync25("curl -fsSL https://ollama.com/install.sh | sh", {
|
|
29015
29135
|
stdio: "inherit",
|
|
29016
29136
|
timeout: 3e5
|
|
29017
29137
|
// 5 min max for install
|
|
@@ -29022,7 +29142,7 @@ function pullModelWithAutoUpdate(tag) {
|
|
|
29022
29142
|
process.stdout.write(` ${c2.cyan("\u25CF")} Retrying pull of ${c2.bold(tag)}...
|
|
29023
29143
|
|
|
29024
29144
|
`);
|
|
29025
|
-
|
|
29145
|
+
execSync25(`ollama pull ${tag}`, {
|
|
29026
29146
|
stdio: "inherit",
|
|
29027
29147
|
timeout: 36e5
|
|
29028
29148
|
});
|
|
@@ -29124,7 +29244,7 @@ function ensurePython3() {
|
|
|
29124
29244
|
if (plat === "darwin") {
|
|
29125
29245
|
if (hasCmd("brew")) {
|
|
29126
29246
|
try {
|
|
29127
|
-
|
|
29247
|
+
execSync25("brew install python3", { stdio: "inherit", timeout: 3e5 });
|
|
29128
29248
|
if (hasCmd("python3")) {
|
|
29129
29249
|
process.stdout.write(` ${c2.green("\u2714")} Python3 installed via Homebrew.
|
|
29130
29250
|
`);
|
|
@@ -29137,7 +29257,7 @@ function ensurePython3() {
|
|
|
29137
29257
|
for (const s of strategies) {
|
|
29138
29258
|
if (hasCmd(s.check)) {
|
|
29139
29259
|
try {
|
|
29140
|
-
|
|
29260
|
+
execSync25(s.install, { stdio: "inherit", timeout: 12e4 });
|
|
29141
29261
|
if (hasCmd("python3") || hasCmd("python")) {
|
|
29142
29262
|
process.stdout.write(` ${c2.green("\u2714")} Python3 installed via ${s.label}.
|
|
29143
29263
|
`);
|
|
@@ -29153,11 +29273,11 @@ function ensurePython3() {
|
|
|
29153
29273
|
}
|
|
29154
29274
|
function checkPythonVenv() {
|
|
29155
29275
|
try {
|
|
29156
|
-
|
|
29276
|
+
execSync25("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
|
|
29157
29277
|
return true;
|
|
29158
29278
|
} catch {
|
|
29159
29279
|
try {
|
|
29160
|
-
|
|
29280
|
+
execSync25("python -m venv --help", { stdio: "pipe", timeout: 5e3 });
|
|
29161
29281
|
return true;
|
|
29162
29282
|
} catch {
|
|
29163
29283
|
return false;
|
|
@@ -29176,7 +29296,7 @@ function ensurePythonVenv() {
|
|
|
29176
29296
|
for (const s of strategies) {
|
|
29177
29297
|
if (hasCmd(s.check)) {
|
|
29178
29298
|
try {
|
|
29179
|
-
|
|
29299
|
+
execSync25(s.install, { stdio: "inherit", timeout: 12e4 });
|
|
29180
29300
|
if (checkPythonVenv()) {
|
|
29181
29301
|
process.stdout.write(` ${c2.green("\u2714")} python3-venv installed via ${s.label}.
|
|
29182
29302
|
`);
|
|
@@ -29612,7 +29732,7 @@ async function doSetup(config, rl) {
|
|
|
29612
29732
|
const modelfilePath = join39(modelDir2, `Modelfile.${customName}`);
|
|
29613
29733
|
writeFileSync10(modelfilePath, modelfileContent + "\n", "utf8");
|
|
29614
29734
|
process.stdout.write(` ${c2.dim("Creating model...")} `);
|
|
29615
|
-
|
|
29735
|
+
execSync25(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
29616
29736
|
stdio: "pipe",
|
|
29617
29737
|
timeout: 12e4
|
|
29618
29738
|
});
|
|
@@ -29663,7 +29783,7 @@ function isFirstRun() {
|
|
|
29663
29783
|
function hasCmd(cmd) {
|
|
29664
29784
|
try {
|
|
29665
29785
|
const whichCmd = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
29666
|
-
|
|
29786
|
+
execSync25(whichCmd, { stdio: "pipe", timeout: 3e3 });
|
|
29667
29787
|
return true;
|
|
29668
29788
|
} catch {
|
|
29669
29789
|
return false;
|
|
@@ -29696,7 +29816,7 @@ function getVenvDir() {
|
|
|
29696
29816
|
}
|
|
29697
29817
|
function hasVenvModule() {
|
|
29698
29818
|
try {
|
|
29699
|
-
|
|
29819
|
+
execSync25("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
|
|
29700
29820
|
return true;
|
|
29701
29821
|
} catch {
|
|
29702
29822
|
return false;
|
|
@@ -29718,8 +29838,8 @@ function ensureVenv(log) {
|
|
|
29718
29838
|
}
|
|
29719
29839
|
try {
|
|
29720
29840
|
mkdirSync10(join39(homedir10(), ".open-agents"), { recursive: true });
|
|
29721
|
-
|
|
29722
|
-
|
|
29841
|
+
execSync25(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
|
|
29842
|
+
execSync25(`"${join39(venvDir, "bin", "pip")}" install --upgrade pip`, {
|
|
29723
29843
|
stdio: "pipe",
|
|
29724
29844
|
timeout: 6e4
|
|
29725
29845
|
});
|
|
@@ -29732,7 +29852,7 @@ function ensureVenv(log) {
|
|
|
29732
29852
|
}
|
|
29733
29853
|
function trySudoPasswordless(cmd, timeoutMs = 12e4) {
|
|
29734
29854
|
try {
|
|
29735
|
-
|
|
29855
|
+
execSync25(`sudo -n ${cmd}`, {
|
|
29736
29856
|
stdio: "pipe",
|
|
29737
29857
|
timeout: timeoutMs,
|
|
29738
29858
|
env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
|
|
@@ -29744,7 +29864,8 @@ function trySudoPasswordless(cmd, timeoutMs = 12e4) {
|
|
|
29744
29864
|
}
|
|
29745
29865
|
function runWithSudo(cmd, password, timeoutMs = 12e4) {
|
|
29746
29866
|
try {
|
|
29747
|
-
|
|
29867
|
+
const escaped = cmd.replace(/'/g, "'\\''");
|
|
29868
|
+
execSync25(`sudo -S bash -c '${escaped}'`, {
|
|
29748
29869
|
input: password + "\n",
|
|
29749
29870
|
stdio: ["pipe", "pipe", "pipe"],
|
|
29750
29871
|
timeout: timeoutMs,
|
|
@@ -29851,7 +29972,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29851
29972
|
ok = await sudoInstall(batchCmd, getPassword, log, cachedPasswordRef, 18e4);
|
|
29852
29973
|
} else {
|
|
29853
29974
|
try {
|
|
29854
|
-
|
|
29975
|
+
execSync25(batchCmd, { stdio: "pipe", timeout: 18e4 });
|
|
29855
29976
|
ok = true;
|
|
29856
29977
|
} catch {
|
|
29857
29978
|
ok = false;
|
|
@@ -29888,7 +30009,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29888
30009
|
const venvCmds = {
|
|
29889
30010
|
apt: () => {
|
|
29890
30011
|
try {
|
|
29891
|
-
const pyVer =
|
|
30012
|
+
const pyVer = execSync25(`python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).trim();
|
|
29892
30013
|
return `apt-get install -y python3-venv python${pyVer}-venv`;
|
|
29893
30014
|
} catch {
|
|
29894
30015
|
return "apt-get install -y python3-venv";
|
|
@@ -29916,12 +30037,12 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29916
30037
|
const venvPip = join39(venvBin, "pip");
|
|
29917
30038
|
log("Installing moondream-station in ~/.open-agents/venv...");
|
|
29918
30039
|
try {
|
|
29919
|
-
|
|
30040
|
+
execSync25(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
|
|
29920
30041
|
if (existsSync29(venvMoondream)) {
|
|
29921
30042
|
log("moondream-station installed successfully.");
|
|
29922
30043
|
} else {
|
|
29923
30044
|
try {
|
|
29924
|
-
const check =
|
|
30045
|
+
const check = execSync25(`"${venvPip}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
|
|
29925
30046
|
if (check.includes("moondream")) {
|
|
29926
30047
|
log("moondream-station package installed.");
|
|
29927
30048
|
}
|
|
@@ -29938,7 +30059,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29938
30059
|
const venvPip2 = join39(venvBin, "pip");
|
|
29939
30060
|
let ocrStackInstalled = false;
|
|
29940
30061
|
try {
|
|
29941
|
-
|
|
30062
|
+
execSync25(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
|
|
29942
30063
|
ocrStackInstalled = true;
|
|
29943
30064
|
} catch {
|
|
29944
30065
|
}
|
|
@@ -29946,9 +30067,9 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29946
30067
|
const ocrPackages = "pytesseract Pillow opencv-python-headless numpy";
|
|
29947
30068
|
log("Installing OCR Python stack (pytesseract, OpenCV, Pillow, numpy)...");
|
|
29948
30069
|
try {
|
|
29949
|
-
|
|
30070
|
+
execSync25(`"${venvPip2}" install ${ocrPackages}`, { stdio: "pipe", timeout: 3e5 });
|
|
29950
30071
|
try {
|
|
29951
|
-
|
|
30072
|
+
execSync25(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
|
|
29952
30073
|
log("OCR Python stack installed successfully.");
|
|
29953
30074
|
} catch {
|
|
29954
30075
|
log("OCR Python stack install completed but import verification failed.");
|
|
@@ -29982,7 +30103,7 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
29982
30103
|
const archMap = { x64: "amd64", arm64: "arm64", arm: "arm" };
|
|
29983
30104
|
const cfArch = archMap[arch] ?? "amd64";
|
|
29984
30105
|
try {
|
|
29985
|
-
|
|
30106
|
+
execSync25(`curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cfArch}" -o /tmp/cloudflared && chmod +x /tmp/cloudflared && mkdir -p "${homedir10()}/.local/bin" && mv /tmp/cloudflared "${homedir10()}/.local/bin/cloudflared"`, { stdio: "pipe", timeout: 6e4 });
|
|
29986
30107
|
if (!process.env.PATH?.includes(`${homedir10()}/.local/bin`)) {
|
|
29987
30108
|
process.env.PATH = `${homedir10()}/.local/bin:${process.env.PATH}`;
|
|
29988
30109
|
}
|
|
@@ -29993,7 +30114,7 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
29993
30114
|
} catch {
|
|
29994
30115
|
}
|
|
29995
30116
|
try {
|
|
29996
|
-
|
|
30117
|
+
execSync25(`curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cfArch}" -o /tmp/cloudflared && chmod +x /tmp/cloudflared && sudo mv /tmp/cloudflared /usr/local/bin/cloudflared 2>/dev/null`, { stdio: "pipe", timeout: 6e4 });
|
|
29997
30118
|
if (hasCmd("cloudflared")) {
|
|
29998
30119
|
log("cloudflared installed.");
|
|
29999
30120
|
return true;
|
|
@@ -30002,7 +30123,7 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
30002
30123
|
}
|
|
30003
30124
|
} else if (os === "darwin") {
|
|
30004
30125
|
try {
|
|
30005
|
-
|
|
30126
|
+
execSync25("brew install cloudflared", { stdio: "pipe", timeout: 12e4 });
|
|
30006
30127
|
if (hasCmd("cloudflared")) {
|
|
30007
30128
|
log("cloudflared installed via Homebrew.");
|
|
30008
30129
|
return true;
|
|
@@ -30052,7 +30173,7 @@ function createExpandedVariant(baseModel, specs, sizeGB) {
|
|
|
30052
30173
|
mkdirSync10(modelDir2, { recursive: true });
|
|
30053
30174
|
const modelfilePath = join39(modelDir2, `Modelfile.${customName}`);
|
|
30054
30175
|
writeFileSync10(modelfilePath, modelfileContent + "\n", "utf8");
|
|
30055
|
-
|
|
30176
|
+
execSync25(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
30056
30177
|
stdio: "pipe",
|
|
30057
30178
|
timeout: 12e4
|
|
30058
30179
|
});
|
|
@@ -31802,10 +31923,38 @@ async function handleEndpoint(arg, ctx, local = false) {
|
|
|
31802
31923
|
process.stdout.write(` ${c2.dim(" /endpoint https://api.together.xyz --auth ... Together AI")}
|
|
31803
31924
|
`);
|
|
31804
31925
|
process.stdout.write(` ${c2.dim(" /endpoint http://localhost:8000 vLLM")}
|
|
31926
|
+
`);
|
|
31927
|
+
process.stdout.write(` ${c2.dim(" /endpoint stop Reset to local Ollama")}
|
|
31805
31928
|
|
|
31806
31929
|
`);
|
|
31807
31930
|
return;
|
|
31808
31931
|
}
|
|
31932
|
+
if (arg === "stop" || arg === "off" || arg.startsWith("stop ") || arg.startsWith("off ")) {
|
|
31933
|
+
if (ctx.isExposeActive?.()) {
|
|
31934
|
+
await ctx.exposeStop?.();
|
|
31935
|
+
renderInfo("Expose gateway stopped.");
|
|
31936
|
+
}
|
|
31937
|
+
if (ctx.hasActiveTask?.()) {
|
|
31938
|
+
ctx.abortActiveTask?.();
|
|
31939
|
+
}
|
|
31940
|
+
const defaultUrl = "http://127.0.0.1:11434";
|
|
31941
|
+
ctx.setEndpoint(defaultUrl, "ollama", void 0);
|
|
31942
|
+
const defaultSettings = { backendUrl: defaultUrl, backendType: "ollama", apiKey: void 0 };
|
|
31943
|
+
if (local) {
|
|
31944
|
+
ctx.saveLocalSettings(defaultSettings);
|
|
31945
|
+
} else {
|
|
31946
|
+
setConfigValue("backendUrl", defaultUrl);
|
|
31947
|
+
setConfigValue("backendType", "ollama");
|
|
31948
|
+
ctx.saveSettings(defaultSettings);
|
|
31949
|
+
}
|
|
31950
|
+
process.stdout.write(`
|
|
31951
|
+
${c2.green("\u2714")} Endpoint reset to local Ollama (${defaultUrl})
|
|
31952
|
+
`);
|
|
31953
|
+
if (ctx.hasActiveTask?.()) {
|
|
31954
|
+
renderWarning("Active task aborted \u2014 send a new prompt to continue.");
|
|
31955
|
+
}
|
|
31956
|
+
return;
|
|
31957
|
+
}
|
|
31809
31958
|
const parts = arg.split(/\s+/);
|
|
31810
31959
|
const url = parts[0];
|
|
31811
31960
|
let apiKey;
|
|
@@ -31906,6 +32055,10 @@ async function handleEndpoint(arg, ctx, local = false) {
|
|
|
31906
32055
|
`);
|
|
31907
32056
|
}
|
|
31908
32057
|
process.stdout.write("\n");
|
|
32058
|
+
if (ctx.hasActiveTask?.()) {
|
|
32059
|
+
ctx.abortActiveTask?.();
|
|
32060
|
+
renderWarning("Active task aborted \u2014 endpoint changed. Send a new prompt to continue.");
|
|
32061
|
+
}
|
|
31909
32062
|
try {
|
|
31910
32063
|
const newModels = await fetchModels(normalizedUrl, apiKey);
|
|
31911
32064
|
if (newModels.length > 0) {
|
|
@@ -31998,7 +32151,7 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
31998
32151
|
return;
|
|
31999
32152
|
}
|
|
32000
32153
|
checkSpinner.stop(`Update available: v${info.currentVersion} \u2192 v${c2.bold(c2.green(info.latestVersion))}`);
|
|
32001
|
-
const { exec, execSync: es2 } = await import("node:child_process");
|
|
32154
|
+
const { exec: exec2, execSync: es2 } = await import("node:child_process");
|
|
32002
32155
|
let needsSudo = false;
|
|
32003
32156
|
try {
|
|
32004
32157
|
const prefix = es2("npm prefix -g", { encoding: "utf8", timeout: 5e3 }).trim();
|
|
@@ -32027,7 +32180,7 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
32027
32180
|
let installOk = false;
|
|
32028
32181
|
let installError = "";
|
|
32029
32182
|
const runInstall2 = (cmd) => new Promise((resolve31) => {
|
|
32030
|
-
const child =
|
|
32183
|
+
const child = exec2(cmd, { timeout: 18e4 }, (err, _stdout, stderr) => {
|
|
32031
32184
|
if (err)
|
|
32032
32185
|
installError = (stderr || err.message || "").trim();
|
|
32033
32186
|
resolve31(!err);
|
|
@@ -32065,7 +32218,7 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
32065
32218
|
installSpinner.stop(`Update installed (v${info.latestVersion}).`);
|
|
32066
32219
|
const rebuildSpinner = startInlineSpinner("Rebuilding native modules");
|
|
32067
32220
|
const rebuildOk = await new Promise((resolve31) => {
|
|
32068
|
-
const child =
|
|
32221
|
+
const child = exec2(`${sudoPrefix}npm rebuild -g open-agents-ai 2>/dev/null || true`, { timeout: 12e4 }, () => resolve31(true));
|
|
32069
32222
|
child.stdout?.resume();
|
|
32070
32223
|
child.stderr?.resume();
|
|
32071
32224
|
});
|
|
@@ -32098,7 +32251,7 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
32098
32251
|
if (fsExists(venvPip)) {
|
|
32099
32252
|
const pySpinner = startInlineSpinner("Upgrading Python venv packages");
|
|
32100
32253
|
const pyOk = await new Promise((resolve31) => {
|
|
32101
|
-
const child =
|
|
32254
|
+
const child = exec2(`"${venvPip}" install --upgrade moondream-station pytesseract Pillow opencv-python-headless numpy 2>/dev/null || true`, { timeout: 3e5 }, (err) => resolve31(!err));
|
|
32102
32255
|
child.stdout?.resume();
|
|
32103
32256
|
child.stderr?.resume();
|
|
32104
32257
|
});
|
|
@@ -32249,7 +32402,7 @@ var init_commands = __esm({
|
|
|
32249
32402
|
// packages/cli/dist/tui/project-context.js
|
|
32250
32403
|
import { existsSync as existsSync30, readFileSync as readFileSync21, readdirSync as readdirSync8 } from "node:fs";
|
|
32251
32404
|
import { join as join40, basename as basename10 } from "node:path";
|
|
32252
|
-
import { execSync as
|
|
32405
|
+
import { execSync as execSync26 } from "node:child_process";
|
|
32253
32406
|
import { homedir as homedir11, platform as platform2, release } from "node:os";
|
|
32254
32407
|
function getModelTier(modelName) {
|
|
32255
32408
|
const m = modelName.toLowerCase();
|
|
@@ -32295,19 +32448,19 @@ function loadProjectMap(repoRoot) {
|
|
|
32295
32448
|
}
|
|
32296
32449
|
function getGitInfo(repoRoot) {
|
|
32297
32450
|
try {
|
|
32298
|
-
|
|
32451
|
+
execSync26("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
|
|
32299
32452
|
} catch {
|
|
32300
32453
|
return "";
|
|
32301
32454
|
}
|
|
32302
32455
|
const lines = [];
|
|
32303
32456
|
try {
|
|
32304
|
-
const branch =
|
|
32457
|
+
const branch = execSync26("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
32305
32458
|
if (branch)
|
|
32306
32459
|
lines.push(`Branch: ${branch}`);
|
|
32307
32460
|
} catch {
|
|
32308
32461
|
}
|
|
32309
32462
|
try {
|
|
32310
|
-
const status =
|
|
32463
|
+
const status = execSync26("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
32311
32464
|
if (status) {
|
|
32312
32465
|
const changed = status.split("\n").length;
|
|
32313
32466
|
lines.push(`Working tree: ${changed} changed file(s)`);
|
|
@@ -32317,7 +32470,7 @@ function getGitInfo(repoRoot) {
|
|
|
32317
32470
|
} catch {
|
|
32318
32471
|
}
|
|
32319
32472
|
try {
|
|
32320
|
-
const log =
|
|
32473
|
+
const log = execSync26("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
32321
32474
|
if (log)
|
|
32322
32475
|
lines.push(`Recent commits:
|
|
32323
32476
|
${log}`);
|
|
@@ -33734,7 +33887,7 @@ var init_carousel_descriptors = __esm({
|
|
|
33734
33887
|
import { existsSync as existsSync32, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12, readFileSync as readFileSync23, unlinkSync as unlinkSync4 } from "node:fs";
|
|
33735
33888
|
import { join as join42 } from "node:path";
|
|
33736
33889
|
import { homedir as homedir12, tmpdir as tmpdir6, platform as platform3 } from "node:os";
|
|
33737
|
-
import { execSync as
|
|
33890
|
+
import { execSync as execSync27, spawn as nodeSpawn } from "node:child_process";
|
|
33738
33891
|
import { createRequire } from "node:module";
|
|
33739
33892
|
function voiceDir() {
|
|
33740
33893
|
return join42(homedir12(), ".open-agents", "voice");
|
|
@@ -34791,7 +34944,7 @@ var init_voice = __esm({
|
|
|
34791
34944
|
}
|
|
34792
34945
|
for (const player of ["paplay", "pw-play", "aplay"]) {
|
|
34793
34946
|
try {
|
|
34794
|
-
|
|
34947
|
+
execSync27(`which ${player}`, { stdio: "pipe" });
|
|
34795
34948
|
return [player, path];
|
|
34796
34949
|
} catch {
|
|
34797
34950
|
}
|
|
@@ -34820,7 +34973,7 @@ var init_voice = __esm({
|
|
|
34820
34973
|
return this.python3Path;
|
|
34821
34974
|
for (const bin of ["python3", "python"]) {
|
|
34822
34975
|
try {
|
|
34823
|
-
const path =
|
|
34976
|
+
const path = execSync27(`which ${bin}`, { stdio: "pipe", timeout: 5e3 }).toString().trim();
|
|
34824
34977
|
if (path) {
|
|
34825
34978
|
this.python3Path = path;
|
|
34826
34979
|
return path;
|
|
@@ -34840,7 +34993,7 @@ var init_voice = __esm({
|
|
|
34840
34993
|
return false;
|
|
34841
34994
|
}
|
|
34842
34995
|
try {
|
|
34843
|
-
|
|
34996
|
+
execSync27(`${py} -c "import mlx_audio"`, { stdio: "pipe", timeout: 1e4 });
|
|
34844
34997
|
this.mlxInstalled = true;
|
|
34845
34998
|
return true;
|
|
34846
34999
|
} catch {
|
|
@@ -34864,7 +35017,7 @@ var init_voice = __esm({
|
|
|
34864
35017
|
return;
|
|
34865
35018
|
renderInfo("Installing MLX Audio for voice synthesis (first time setup)...");
|
|
34866
35019
|
try {
|
|
34867
|
-
|
|
35020
|
+
execSync27(`${py} -m pip install mlx-audio --quiet`, {
|
|
34868
35021
|
stdio: "pipe",
|
|
34869
35022
|
timeout: 3e5
|
|
34870
35023
|
// 5 min — may need to compile
|
|
@@ -34872,7 +35025,7 @@ var init_voice = __esm({
|
|
|
34872
35025
|
this.mlxInstalled = true;
|
|
34873
35026
|
} catch (err) {
|
|
34874
35027
|
try {
|
|
34875
|
-
|
|
35028
|
+
execSync27(`${py} -m pip install mlx-audio --user --quiet`, {
|
|
34876
35029
|
stdio: "pipe",
|
|
34877
35030
|
timeout: 3e5
|
|
34878
35031
|
});
|
|
@@ -34908,11 +35061,11 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
34908
35061
|
`tts_gen.main(["--model", ${JSON.stringify(mlxModelId)}, "--text", text, "--voice", ${JSON.stringify(mlxVoice)}, "--lang_code", ${JSON.stringify(mlxLangCode)}, "--audio_path", ${JSON.stringify(wavPath)}])`
|
|
34909
35062
|
].join("; ");
|
|
34910
35063
|
try {
|
|
34911
|
-
|
|
35064
|
+
execSync27(`${py} -c ${JSON.stringify(pyScript)} ${JSON.stringify(JSON.stringify(cleaned))}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir6() });
|
|
34912
35065
|
} catch (err) {
|
|
34913
35066
|
try {
|
|
34914
35067
|
const safeText = cleaned.replace(/'/g, "'\\''");
|
|
34915
|
-
|
|
35068
|
+
execSync27(`${py} -m mlx_audio.tts.generate --model ${mlxModelId} --text '${safeText}' --voice ${mlxVoice} --lang_code ${mlxLangCode} --audio_path ${JSON.stringify(wavPath)}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir6() });
|
|
34916
35069
|
} catch (err2) {
|
|
34917
35070
|
return;
|
|
34918
35071
|
}
|
|
@@ -34976,11 +35129,11 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
34976
35129
|
`tts_gen.main(["--model", ${JSON.stringify(mlxModelId)}, "--text", text, "--voice", ${JSON.stringify(mlxVoice)}, "--lang_code", ${JSON.stringify(mlxLangCode)}, "--audio_path", ${JSON.stringify(wavPath)}])`
|
|
34977
35130
|
].join("; ");
|
|
34978
35131
|
try {
|
|
34979
|
-
|
|
35132
|
+
execSync27(`${py} -c ${JSON.stringify(pyScript)} ${JSON.stringify(JSON.stringify(cleaned))}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir6() });
|
|
34980
35133
|
} catch {
|
|
34981
35134
|
try {
|
|
34982
35135
|
const safeText = cleaned.replace(/'/g, "'\\''");
|
|
34983
|
-
|
|
35136
|
+
execSync27(`${py} -m mlx_audio.tts.generate --model ${mlxModelId} --text '${safeText}' --voice ${mlxVoice} --lang_code ${mlxLangCode} --audio_path ${JSON.stringify(wavPath)}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir6() });
|
|
34984
35137
|
} catch {
|
|
34985
35138
|
return null;
|
|
34986
35139
|
}
|
|
@@ -35028,7 +35181,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
35028
35181
|
const voiceRequire = createRequire(join42(voiceDir(), "index.js"));
|
|
35029
35182
|
const probeOnnx = () => {
|
|
35030
35183
|
try {
|
|
35031
|
-
const result =
|
|
35184
|
+
const result = execSync27(`node -e "try { require('onnxruntime-node'); console.log('OK'); } catch(e) { console.log('FAIL:' + e.message); }"`, { cwd: voiceDir(), stdio: "pipe", timeout: 15e3, env: { ...process.env, NODE_PATH: join42(voiceDir(), "node_modules") } });
|
|
35032
35185
|
const output = result.toString().trim();
|
|
35033
35186
|
return output === "OK";
|
|
35034
35187
|
} catch {
|
|
@@ -35045,7 +35198,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
35045
35198
|
} catch {
|
|
35046
35199
|
renderInfo("Installing ONNX runtime for voice synthesis...");
|
|
35047
35200
|
try {
|
|
35048
|
-
|
|
35201
|
+
execSync27("npm install --no-audit --no-fund", {
|
|
35049
35202
|
cwd: voiceDir(),
|
|
35050
35203
|
stdio: "pipe",
|
|
35051
35204
|
timeout: 12e4
|
|
@@ -35070,7 +35223,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
35070
35223
|
} catch {
|
|
35071
35224
|
renderInfo("Installing phonemizer for voice synthesis...");
|
|
35072
35225
|
try {
|
|
35073
|
-
|
|
35226
|
+
execSync27("npm install --no-audit --no-fund", {
|
|
35074
35227
|
cwd: voiceDir(),
|
|
35075
35228
|
stdio: "pipe",
|
|
35076
35229
|
timeout: 12e4
|
|
@@ -36154,7 +36307,7 @@ var init_promptLoader3 = __esm({
|
|
|
36154
36307
|
// packages/cli/dist/tui/dream-engine.js
|
|
36155
36308
|
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync13, readFileSync as readFileSync25, existsSync as existsSync34, cpSync, rmSync, readdirSync as readdirSync10 } from "node:fs";
|
|
36156
36309
|
import { join as join45, basename as basename12 } from "node:path";
|
|
36157
|
-
import { execSync as
|
|
36310
|
+
import { execSync as execSync28 } from "node:child_process";
|
|
36158
36311
|
function loadAutoresearchMemory(repoRoot) {
|
|
36159
36312
|
const memoryPath = join45(repoRoot, ".oa", "memory", "autoresearch.json");
|
|
36160
36313
|
if (!existsSync34(memoryPath))
|
|
@@ -36518,7 +36671,7 @@ var init_dream_engine = __esm({
|
|
|
36518
36671
|
}
|
|
36519
36672
|
}
|
|
36520
36673
|
try {
|
|
36521
|
-
const output =
|
|
36674
|
+
const output = execSync28(cmd, {
|
|
36522
36675
|
cwd: this.repoRoot,
|
|
36523
36676
|
timeout: 3e4,
|
|
36524
36677
|
encoding: "utf-8",
|
|
@@ -37295,17 +37448,17 @@ ${summaryResult}
|
|
|
37295
37448
|
try {
|
|
37296
37449
|
mkdirSync14(checkpointDir, { recursive: true });
|
|
37297
37450
|
try {
|
|
37298
|
-
const gitStatus =
|
|
37451
|
+
const gitStatus = execSync28("git status --porcelain", {
|
|
37299
37452
|
cwd: this.repoRoot,
|
|
37300
37453
|
encoding: "utf-8",
|
|
37301
37454
|
timeout: 1e4
|
|
37302
37455
|
});
|
|
37303
|
-
const gitDiff =
|
|
37456
|
+
const gitDiff = execSync28("git diff", {
|
|
37304
37457
|
cwd: this.repoRoot,
|
|
37305
37458
|
encoding: "utf-8",
|
|
37306
37459
|
timeout: 1e4
|
|
37307
37460
|
});
|
|
37308
|
-
const gitHash =
|
|
37461
|
+
const gitHash = execSync28("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
|
|
37309
37462
|
cwd: this.repoRoot,
|
|
37310
37463
|
encoding: "utf-8",
|
|
37311
37464
|
timeout: 5e3
|
|
@@ -44000,6 +44153,11 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
44000
44153
|
},
|
|
44001
44154
|
refreshModelCache,
|
|
44002
44155
|
hasActiveTask: () => activeTask !== null,
|
|
44156
|
+
abortActiveTask() {
|
|
44157
|
+
if (!activeTask)
|
|
44158
|
+
return;
|
|
44159
|
+
activeTask.runner.abort();
|
|
44160
|
+
},
|
|
44003
44161
|
requestCompaction(strategy) {
|
|
44004
44162
|
if (!activeTask)
|
|
44005
44163
|
return false;
|
|
@@ -44635,8 +44793,8 @@ NEW TASK: ${fullInput}`;
|
|
|
44635
44793
|
try {
|
|
44636
44794
|
const updateInfo = await checkForUpdate(version);
|
|
44637
44795
|
if (updateInfo) {
|
|
44638
|
-
const { exec } = await import("node:child_process");
|
|
44639
|
-
|
|
44796
|
+
const { exec: exec2 } = await import("node:child_process");
|
|
44797
|
+
exec2(`npm install -g open-agents-ai@latest --prefer-online`, { timeout: 18e4 }, (err) => {
|
|
44640
44798
|
if (!err) {
|
|
44641
44799
|
writeContent(() => renderInfo(`Updated to v${updateInfo.latestVersion} in background. Takes effect next session.`));
|
|
44642
44800
|
}
|
package/package.json
CHANGED
|
@@ -238,11 +238,13 @@ If you notice you're performing the SAME multi-step sequence for the 3rd time or
|
|
|
238
238
|
|
|
239
239
|
## Nexus P2P Networking (v1.5.6) — Decentralized Agent Communication + x402 Payments
|
|
240
240
|
|
|
241
|
-
You HAVE the nexus tool.
|
|
241
|
+
You HAVE the nexus tool. USE IT when asked about connecting, messaging, or networking with other agents.
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
**CRITICAL: ALWAYS call nexus(action='connect') FIRST.** It spawns the daemon process. No other action works without it.
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
Auto-installs open-agents-nexus on first use. Requires Node >= 22.
|
|
246
|
+
|
|
247
|
+
### Quick Start (3 steps — connect MUST be first)
|
|
246
248
|
nexus(action='connect', agent_name='MyAgent')
|
|
247
249
|
nexus(action='join_room', room_id='general')
|
|
248
250
|
nexus(action='send_message', room_id='general', message='Hello from MyAgent!')
|
|
@@ -21,7 +21,7 @@ NEVER say "I can't do that". ALWAYS attempt the task using your tools. If a tool
|
|
|
21
21
|
- web_search: Search the web
|
|
22
22
|
- web_fetch: Fetch a web page's text
|
|
23
23
|
- memory_read / memory_write: Persistent memory across sessions
|
|
24
|
-
- nexus: P2P agent
|
|
24
|
+
- nexus: P2P agent mesh. ALWAYS call connect FIRST (spawns daemon). Then: join_room, send_message, discover_peers, expose, etc.
|
|
25
25
|
- task_complete: Signal completion with a summary
|
|
26
26
|
- background_run / task_status / task_output / task_stop: Background tasks
|
|
27
27
|
- sub_agent: Delegate a subtask to an independent agent (use background=true for parallel work)
|
|
@@ -67,7 +67,7 @@ You are **Open Agent** (open-agents-ai), an autonomous AI coding agent running o
|
|
|
67
67
|
- Web: search documentation and fetch web pages
|
|
68
68
|
- Memory: persistent cross-session knowledge (memory_read/memory_write)
|
|
69
69
|
- Skills: 250+ behavioral skills (skill_list), build new ones (skill_build)
|
|
70
|
-
- P2P:
|
|
70
|
+
- P2P: nexus agent mesh — ALWAYS call nexus(action='connect') FIRST, then join_room/send_message/discover_peers/expose
|
|
71
71
|
- Background tasks: run long commands in background, check status later
|
|
72
72
|
- Voice/TTS: text-to-speech via ONNX (cross-platform) or MLX (Apple Silicon) — use /voice to enable
|
|
73
73
|
- Desktop/Vision: screenshot, click UI, OCR (via explore_tools)
|
|
@@ -20,5 +20,5 @@ Rules:
|
|
|
20
20
|
- Directory entries are RELATIVE. If you list "parent/" and see "child", the path is "parent/child" — NOT ".child".
|
|
21
21
|
- Use list_directory for directories, NOT file_read. Prefer list_directory over shell ls.
|
|
22
22
|
- You are **Open Agent** (open-agents-ai) — an AI coding agent running locally via Ollama/vLLM. No cloud APIs.
|
|
23
|
-
- Core: code editing, shell commands, web search, memory, 250+ skills (skill_list), P2P
|
|
23
|
+
- Core: code editing, shell commands, web search, memory, 250+ skills (skill_list), P2P mesh (nexus — call connect FIRST), background tasks.
|
|
24
24
|
- When asked "what can you do?", use explore_tools() and skill_list() to discover and report your actual capabilities. Do NOT hallucinate.
|