open-agents-ai 0.103.8 → 0.103.10
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",
|
|
@@ -25086,6 +25086,13 @@ function renderSlashHelp() {
|
|
|
25086
25086
|
["/nexus", "Show nexus P2P network status (or connect/disconnect/rooms)"],
|
|
25087
25087
|
["/nexus connect", "Connect to the agent mesh network"],
|
|
25088
25088
|
["/nexus wallet", "Show wallet address and x402 payment status"],
|
|
25089
|
+
["/expose <backend>", "Share local inference via cloudflared tunnel"],
|
|
25090
|
+
["/expose <backend> --key", "Share with auto-generated auth key"],
|
|
25091
|
+
["/expose status", "Show gateway stats (requests, models, errors)"],
|
|
25092
|
+
["/expose stop", "Stop the expose gateway"],
|
|
25093
|
+
["/p2p start", "Join the P2P agent mesh network"],
|
|
25094
|
+
["/p2p status", "Show mesh peers and rooms"],
|
|
25095
|
+
["/p2p stop", "Leave the mesh network"],
|
|
25089
25096
|
["/style", "Show current response style"],
|
|
25090
25097
|
["/style <preset>", "Set style: concise, balanced, verbose, pedagogical"],
|
|
25091
25098
|
["/commands auto", "Allow agent to invoke slash commands as tools"],
|
|
@@ -25148,17 +25155,6 @@ function renderModelSwitch(oldModel, newModel) {
|
|
|
25148
25155
|
|
|
25149
25156
|
`);
|
|
25150
25157
|
}
|
|
25151
|
-
function renderConfig(config) {
|
|
25152
|
-
process.stdout.write(`
|
|
25153
|
-
${c2.bold("Configuration:")}
|
|
25154
|
-
|
|
25155
|
-
`);
|
|
25156
|
-
for (const [key, value] of Object.entries(config)) {
|
|
25157
|
-
process.stdout.write(` ${c2.cyan(key.padEnd(20))} ${value}
|
|
25158
|
-
`);
|
|
25159
|
-
}
|
|
25160
|
-
process.stdout.write("\n");
|
|
25161
|
-
}
|
|
25162
25158
|
function formatToolArgs(toolName, args, verbose) {
|
|
25163
25159
|
const maxArg = verbose ? 1e4 : Math.max(40, getTermWidth() - 20);
|
|
25164
25160
|
switch (toolName) {
|
|
@@ -26198,10 +26194,57 @@ var init_voice_session = __esm({
|
|
|
26198
26194
|
|
|
26199
26195
|
// packages/cli/dist/tui/expose.js
|
|
26200
26196
|
import { createServer as createServer2, request as httpRequest } from "node:http";
|
|
26201
|
-
import { spawn as spawn14 } from "node:child_process";
|
|
26197
|
+
import { spawn as spawn14, execSync as execSync25 } from "node:child_process";
|
|
26202
26198
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
26203
26199
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
26204
26200
|
import { URL as URL2 } from "node:url";
|
|
26201
|
+
import { loadavg, cpus, totalmem, freemem } from "node:os";
|
|
26202
|
+
function collectSystemMetrics() {
|
|
26203
|
+
const [l1, l5, l15] = loadavg();
|
|
26204
|
+
const cores = cpus().length;
|
|
26205
|
+
const totalMem = totalmem();
|
|
26206
|
+
const freeMem = freemem();
|
|
26207
|
+
const usedMem = totalMem - freeMem;
|
|
26208
|
+
const gpu = {
|
|
26209
|
+
available: false,
|
|
26210
|
+
name: "",
|
|
26211
|
+
utilization: 0,
|
|
26212
|
+
vramUsedMB: 0,
|
|
26213
|
+
vramTotalMB: 0,
|
|
26214
|
+
vramUtilization: 0
|
|
26215
|
+
};
|
|
26216
|
+
try {
|
|
26217
|
+
const smi = execSync25("nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 3e3 });
|
|
26218
|
+
const line = smi.trim().split("\n")[0];
|
|
26219
|
+
if (line) {
|
|
26220
|
+
const parts = line.split(",").map((s) => s.trim());
|
|
26221
|
+
gpu.available = true;
|
|
26222
|
+
gpu.utilization = parseInt(parts[0] ?? "0", 10) || 0;
|
|
26223
|
+
gpu.vramUsedMB = parseInt(parts[1] ?? "0", 10) || 0;
|
|
26224
|
+
gpu.vramTotalMB = parseInt(parts[2] ?? "0", 10) || 0;
|
|
26225
|
+
gpu.name = parts[3] ?? "";
|
|
26226
|
+
gpu.vramUtilization = gpu.vramTotalMB > 0 ? Math.round(gpu.vramUsedMB / gpu.vramTotalMB * 100) : 0;
|
|
26227
|
+
}
|
|
26228
|
+
} catch {
|
|
26229
|
+
}
|
|
26230
|
+
return {
|
|
26231
|
+
cpu: {
|
|
26232
|
+
loadAvg1m: Math.round(l1 * 100) / 100,
|
|
26233
|
+
loadAvg5m: Math.round(l5 * 100) / 100,
|
|
26234
|
+
loadAvg15m: Math.round(l15 * 100) / 100,
|
|
26235
|
+
cores,
|
|
26236
|
+
utilization: Math.min(100, Math.round(l1 / cores * 100))
|
|
26237
|
+
},
|
|
26238
|
+
memory: {
|
|
26239
|
+
totalGB: Math.round(totalMem / 1024 ** 3 * 10) / 10,
|
|
26240
|
+
freeGB: Math.round(freeMem / 1024 ** 3 * 10) / 10,
|
|
26241
|
+
usedGB: Math.round(usedMem / 1024 ** 3 * 10) / 10,
|
|
26242
|
+
utilization: Math.round(usedMem / totalMem * 100)
|
|
26243
|
+
},
|
|
26244
|
+
gpu,
|
|
26245
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
26246
|
+
};
|
|
26247
|
+
}
|
|
26205
26248
|
var DEFAULT_TARGETS, ExposeGateway;
|
|
26206
26249
|
var init_expose = __esm({
|
|
26207
26250
|
"packages/cli/dist/tui/expose.js"() {
|
|
@@ -26288,85 +26331,102 @@ var init_expose = __esm({
|
|
|
26288
26331
|
// ── Proxy server ────────────────────────────────────────────────────────
|
|
26289
26332
|
createProxyServer(localPort) {
|
|
26290
26333
|
const target = new URL2(this._targetUrl);
|
|
26291
|
-
|
|
26334
|
+
const server = createServer2((req, res) => {
|
|
26335
|
+
res.on("error", () => {
|
|
26336
|
+
});
|
|
26292
26337
|
const authHeader = req.headers.authorization;
|
|
26293
26338
|
const url = new URL2(req.url ?? "/", `http://127.0.0.1:${localPort}`);
|
|
26294
26339
|
const queryKey = url.searchParams.get("key");
|
|
26295
26340
|
const providedKey = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : queryKey;
|
|
26296
26341
|
if (providedKey !== this._authKey) {
|
|
26297
|
-
this._stats.errors++;
|
|
26298
26342
|
res.writeHead(401, { "Content-Type": "application/json" });
|
|
26299
26343
|
res.end(JSON.stringify({ error: "Unauthorized \u2014 provide Bearer token or ?key= parameter" }));
|
|
26300
|
-
|
|
26344
|
+
return;
|
|
26345
|
+
}
|
|
26346
|
+
if (url.pathname === "/v1/system/metrics" && req.method === "GET") {
|
|
26347
|
+
const metrics = collectSystemMetrics();
|
|
26348
|
+
const gatewayStats = {
|
|
26349
|
+
totalRequests: this._stats.totalRequests,
|
|
26350
|
+
activeConnections: this._stats.activeConnections,
|
|
26351
|
+
errors: this._stats.errors,
|
|
26352
|
+
modelUsage: Object.fromEntries(this._stats.modelUsage)
|
|
26353
|
+
};
|
|
26354
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
26355
|
+
res.end(JSON.stringify({ ...metrics, gateway: gatewayStats }));
|
|
26301
26356
|
return;
|
|
26302
26357
|
}
|
|
26303
26358
|
this._stats.totalRequests++;
|
|
26304
26359
|
this._stats.activeConnections++;
|
|
26305
26360
|
this.emitStats();
|
|
26306
|
-
this.extractModelFromRequest(req);
|
|
26307
26361
|
url.searchParams.delete("key");
|
|
26308
26362
|
const forwardPath = url.pathname + url.search;
|
|
26309
|
-
const
|
|
26310
|
-
|
|
26311
|
-
|
|
26312
|
-
|
|
26313
|
-
|
|
26314
|
-
|
|
26315
|
-
|
|
26316
|
-
|
|
26317
|
-
|
|
26318
|
-
|
|
26319
|
-
|
|
26320
|
-
|
|
26363
|
+
const bodyChunks = [];
|
|
26364
|
+
req.on("data", (chunk) => bodyChunks.push(chunk));
|
|
26365
|
+
req.on("end", () => {
|
|
26366
|
+
const body = Buffer.concat(bodyChunks);
|
|
26367
|
+
if (req.method === "POST" && body.length > 0) {
|
|
26368
|
+
try {
|
|
26369
|
+
const text = body.toString("utf8", 0, Math.min(body.length, 1024));
|
|
26370
|
+
const modelMatch = text.match(/"model"\s*:\s*"([^"]+)"/);
|
|
26371
|
+
if (modelMatch) {
|
|
26372
|
+
const model = modelMatch[1];
|
|
26373
|
+
const count = this._stats.modelUsage.get(model) ?? 0;
|
|
26374
|
+
this._stats.modelUsage.set(model, count + 1);
|
|
26375
|
+
}
|
|
26376
|
+
} catch {
|
|
26377
|
+
}
|
|
26378
|
+
}
|
|
26379
|
+
const forwardHeaders = { ...req.headers, host: target.host };
|
|
26380
|
+
delete forwardHeaders.authorization;
|
|
26381
|
+
if (body.length > 0) {
|
|
26382
|
+
forwardHeaders["content-length"] = String(body.length);
|
|
26383
|
+
}
|
|
26384
|
+
const proxyReq = httpRequest({
|
|
26385
|
+
hostname: target.hostname,
|
|
26386
|
+
port: target.port || (target.protocol === "https:" ? 443 : 80),
|
|
26387
|
+
path: forwardPath,
|
|
26388
|
+
method: req.method,
|
|
26389
|
+
headers: forwardHeaders
|
|
26390
|
+
}, (proxyRes) => {
|
|
26391
|
+
proxyRes.on("error", () => {
|
|
26392
|
+
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26393
|
+
this.emitStats();
|
|
26394
|
+
});
|
|
26395
|
+
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
|
|
26396
|
+
proxyRes.pipe(res);
|
|
26397
|
+
proxyRes.on("end", () => {
|
|
26398
|
+
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26399
|
+
this.emitStats();
|
|
26400
|
+
});
|
|
26401
|
+
});
|
|
26402
|
+
proxyReq.on("error", (err) => {
|
|
26403
|
+
this._stats.errors++;
|
|
26321
26404
|
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26405
|
+
if (!res.headersSent) {
|
|
26406
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
26407
|
+
res.end(JSON.stringify({ error: `Backend unreachable: ${err.message}` }));
|
|
26408
|
+
} else {
|
|
26409
|
+
try {
|
|
26410
|
+
res.end();
|
|
26411
|
+
} catch {
|
|
26412
|
+
}
|
|
26413
|
+
}
|
|
26322
26414
|
this.emitStats();
|
|
26323
26415
|
});
|
|
26416
|
+
if (body.length > 0) {
|
|
26417
|
+
proxyReq.write(body);
|
|
26418
|
+
}
|
|
26419
|
+
proxyReq.end();
|
|
26324
26420
|
});
|
|
26325
|
-
|
|
26326
|
-
this._stats.errors++;
|
|
26421
|
+
req.on("error", () => {
|
|
26327
26422
|
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
26328
|
-
if (!res.headersSent) {
|
|
26329
|
-
res.writeHead(502, { "Content-Type": "application/json" });
|
|
26330
|
-
res.end(JSON.stringify({ error: `Backend unreachable: ${err.message}` }));
|
|
26331
|
-
}
|
|
26332
|
-
this._stats.status = "error";
|
|
26333
26423
|
this.emitStats();
|
|
26334
|
-
setTimeout(() => {
|
|
26335
|
-
if (this.server && this._stats.status === "error") {
|
|
26336
|
-
this._stats.status = "active";
|
|
26337
|
-
this.emitStats();
|
|
26338
|
-
}
|
|
26339
|
-
}, 5e3);
|
|
26340
26424
|
});
|
|
26341
|
-
req.pipe(proxyReq);
|
|
26342
26425
|
});
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
|
|
26346
|
-
|
|
26347
|
-
return;
|
|
26348
|
-
const chunks = [];
|
|
26349
|
-
const origEmit = req.emit.bind(req);
|
|
26350
|
-
let peeked = false;
|
|
26351
|
-
const stats = this._stats;
|
|
26352
|
-
req.emit = function(event, ...args) {
|
|
26353
|
-
if (event === "data" && !peeked) {
|
|
26354
|
-
peeked = true;
|
|
26355
|
-
try {
|
|
26356
|
-
const chunk = args[0];
|
|
26357
|
-
chunks.push(chunk);
|
|
26358
|
-
const text = Buffer.concat(chunks).toString();
|
|
26359
|
-
const modelMatch = text.match(/"model"\s*:\s*"([^"]+)"/);
|
|
26360
|
-
if (modelMatch) {
|
|
26361
|
-
const model = modelMatch[1];
|
|
26362
|
-
const count = stats.modelUsage.get(model) ?? 0;
|
|
26363
|
-
stats.modelUsage.set(model, count + 1);
|
|
26364
|
-
}
|
|
26365
|
-
} catch {
|
|
26366
|
-
}
|
|
26367
|
-
}
|
|
26368
|
-
return origEmit(event, ...args);
|
|
26369
|
-
};
|
|
26426
|
+
server.on("error", (err) => {
|
|
26427
|
+
this.options.onError?.(`Proxy server error: ${err.message}`);
|
|
26428
|
+
});
|
|
26429
|
+
return server;
|
|
26370
26430
|
}
|
|
26371
26431
|
// ── Cloudflared ─────────────────────────────────────────────────────────
|
|
26372
26432
|
startCloudflared(port) {
|
|
@@ -28563,7 +28623,7 @@ var init_oa_directory = __esm({
|
|
|
28563
28623
|
|
|
28564
28624
|
// packages/cli/dist/tui/setup.js
|
|
28565
28625
|
import * as readline from "node:readline";
|
|
28566
|
-
import { execSync as
|
|
28626
|
+
import { execSync as execSync26, spawn as spawn15 } from "node:child_process";
|
|
28567
28627
|
import { existsSync as existsSync29, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10 } from "node:fs";
|
|
28568
28628
|
import { join as join39 } from "node:path";
|
|
28569
28629
|
import { homedir as homedir10, platform } from "node:os";
|
|
@@ -28573,7 +28633,7 @@ function detectSystemSpecs() {
|
|
|
28573
28633
|
let gpuVramGB = 0;
|
|
28574
28634
|
let gpuName = "";
|
|
28575
28635
|
try {
|
|
28576
|
-
const memInfo =
|
|
28636
|
+
const memInfo = execSync26("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
|
|
28577
28637
|
encoding: "utf8",
|
|
28578
28638
|
timeout: 5e3
|
|
28579
28639
|
});
|
|
@@ -28593,7 +28653,7 @@ function detectSystemSpecs() {
|
|
|
28593
28653
|
} catch {
|
|
28594
28654
|
}
|
|
28595
28655
|
try {
|
|
28596
|
-
const nvidiaSmi =
|
|
28656
|
+
const nvidiaSmi = execSync26("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
|
|
28597
28657
|
const lines = nvidiaSmi.trim().split("\n");
|
|
28598
28658
|
if (lines.length > 0) {
|
|
28599
28659
|
for (const line of lines) {
|
|
@@ -28718,7 +28778,7 @@ function ensureCurl() {
|
|
|
28718
28778
|
for (const s of strategies) {
|
|
28719
28779
|
if (hasCmd(s.check)) {
|
|
28720
28780
|
try {
|
|
28721
|
-
|
|
28781
|
+
execSync26(s.install, { stdio: "inherit", timeout: 12e4 });
|
|
28722
28782
|
if (hasCmd("curl")) {
|
|
28723
28783
|
process.stdout.write(` ${c2.green("\u2714")} curl installed via ${s.label}.
|
|
28724
28784
|
`);
|
|
@@ -28732,7 +28792,7 @@ function ensureCurl() {
|
|
|
28732
28792
|
}
|
|
28733
28793
|
if (plat === "darwin") {
|
|
28734
28794
|
try {
|
|
28735
|
-
|
|
28795
|
+
execSync26("xcode-select --install", { stdio: "inherit", timeout: 3e5 });
|
|
28736
28796
|
if (hasCmd("curl"))
|
|
28737
28797
|
return true;
|
|
28738
28798
|
} catch {
|
|
@@ -28767,7 +28827,7 @@ function installOllamaLinux() {
|
|
|
28767
28827
|
|
|
28768
28828
|
`);
|
|
28769
28829
|
try {
|
|
28770
|
-
|
|
28830
|
+
execSync26("curl -fsSL https://ollama.com/install.sh | sh", {
|
|
28771
28831
|
stdio: "inherit",
|
|
28772
28832
|
timeout: 3e5
|
|
28773
28833
|
});
|
|
@@ -28795,7 +28855,7 @@ async function installOllamaMac(_rl) {
|
|
|
28795
28855
|
|
|
28796
28856
|
`);
|
|
28797
28857
|
try {
|
|
28798
|
-
|
|
28858
|
+
execSync26('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit", timeout: 6e5 });
|
|
28799
28859
|
if (!hasCmd("brew")) {
|
|
28800
28860
|
try {
|
|
28801
28861
|
const brewPrefix = existsSync29("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local";
|
|
@@ -28828,7 +28888,7 @@ async function installOllamaMac(_rl) {
|
|
|
28828
28888
|
|
|
28829
28889
|
`);
|
|
28830
28890
|
try {
|
|
28831
|
-
|
|
28891
|
+
execSync26("brew install ollama", {
|
|
28832
28892
|
stdio: "inherit",
|
|
28833
28893
|
timeout: 3e5
|
|
28834
28894
|
});
|
|
@@ -28855,7 +28915,7 @@ function installOllamaWindows() {
|
|
|
28855
28915
|
|
|
28856
28916
|
`);
|
|
28857
28917
|
try {
|
|
28858
|
-
|
|
28918
|
+
execSync26('powershell -Command "irm https://ollama.com/install.ps1 | iex"', {
|
|
28859
28919
|
stdio: "inherit",
|
|
28860
28920
|
timeout: 3e5
|
|
28861
28921
|
});
|
|
@@ -28936,7 +28996,7 @@ async function ensureOllamaRunning(backendUrl, rl) {
|
|
|
28936
28996
|
}
|
|
28937
28997
|
function pullModelWithAutoUpdate(tag) {
|
|
28938
28998
|
try {
|
|
28939
|
-
|
|
28999
|
+
execSync26(`ollama pull ${tag}`, {
|
|
28940
29000
|
stdio: "inherit",
|
|
28941
29001
|
timeout: 36e5
|
|
28942
29002
|
// 1 hour max
|
|
@@ -28956,7 +29016,7 @@ function pullModelWithAutoUpdate(tag) {
|
|
|
28956
29016
|
|
|
28957
29017
|
`);
|
|
28958
29018
|
try {
|
|
28959
|
-
|
|
29019
|
+
execSync26("curl -fsSL https://ollama.com/install.sh | sh", {
|
|
28960
29020
|
stdio: "inherit",
|
|
28961
29021
|
timeout: 3e5
|
|
28962
29022
|
// 5 min max for install
|
|
@@ -28967,7 +29027,7 @@ function pullModelWithAutoUpdate(tag) {
|
|
|
28967
29027
|
process.stdout.write(` ${c2.cyan("\u25CF")} Retrying pull of ${c2.bold(tag)}...
|
|
28968
29028
|
|
|
28969
29029
|
`);
|
|
28970
|
-
|
|
29030
|
+
execSync26(`ollama pull ${tag}`, {
|
|
28971
29031
|
stdio: "inherit",
|
|
28972
29032
|
timeout: 36e5
|
|
28973
29033
|
});
|
|
@@ -29069,7 +29129,7 @@ function ensurePython3() {
|
|
|
29069
29129
|
if (plat === "darwin") {
|
|
29070
29130
|
if (hasCmd("brew")) {
|
|
29071
29131
|
try {
|
|
29072
|
-
|
|
29132
|
+
execSync26("brew install python3", { stdio: "inherit", timeout: 3e5 });
|
|
29073
29133
|
if (hasCmd("python3")) {
|
|
29074
29134
|
process.stdout.write(` ${c2.green("\u2714")} Python3 installed via Homebrew.
|
|
29075
29135
|
`);
|
|
@@ -29082,7 +29142,7 @@ function ensurePython3() {
|
|
|
29082
29142
|
for (const s of strategies) {
|
|
29083
29143
|
if (hasCmd(s.check)) {
|
|
29084
29144
|
try {
|
|
29085
|
-
|
|
29145
|
+
execSync26(s.install, { stdio: "inherit", timeout: 12e4 });
|
|
29086
29146
|
if (hasCmd("python3") || hasCmd("python")) {
|
|
29087
29147
|
process.stdout.write(` ${c2.green("\u2714")} Python3 installed via ${s.label}.
|
|
29088
29148
|
`);
|
|
@@ -29098,11 +29158,11 @@ function ensurePython3() {
|
|
|
29098
29158
|
}
|
|
29099
29159
|
function checkPythonVenv() {
|
|
29100
29160
|
try {
|
|
29101
|
-
|
|
29161
|
+
execSync26("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
|
|
29102
29162
|
return true;
|
|
29103
29163
|
} catch {
|
|
29104
29164
|
try {
|
|
29105
|
-
|
|
29165
|
+
execSync26("python -m venv --help", { stdio: "pipe", timeout: 5e3 });
|
|
29106
29166
|
return true;
|
|
29107
29167
|
} catch {
|
|
29108
29168
|
return false;
|
|
@@ -29121,7 +29181,7 @@ function ensurePythonVenv() {
|
|
|
29121
29181
|
for (const s of strategies) {
|
|
29122
29182
|
if (hasCmd(s.check)) {
|
|
29123
29183
|
try {
|
|
29124
|
-
|
|
29184
|
+
execSync26(s.install, { stdio: "inherit", timeout: 12e4 });
|
|
29125
29185
|
if (checkPythonVenv()) {
|
|
29126
29186
|
process.stdout.write(` ${c2.green("\u2714")} python3-venv installed via ${s.label}.
|
|
29127
29187
|
`);
|
|
@@ -29557,7 +29617,7 @@ async function doSetup(config, rl) {
|
|
|
29557
29617
|
const modelfilePath = join39(modelDir2, `Modelfile.${customName}`);
|
|
29558
29618
|
writeFileSync10(modelfilePath, modelfileContent + "\n", "utf8");
|
|
29559
29619
|
process.stdout.write(` ${c2.dim("Creating model...")} `);
|
|
29560
|
-
|
|
29620
|
+
execSync26(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
29561
29621
|
stdio: "pipe",
|
|
29562
29622
|
timeout: 12e4
|
|
29563
29623
|
});
|
|
@@ -29608,7 +29668,7 @@ function isFirstRun() {
|
|
|
29608
29668
|
function hasCmd(cmd) {
|
|
29609
29669
|
try {
|
|
29610
29670
|
const whichCmd = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
29611
|
-
|
|
29671
|
+
execSync26(whichCmd, { stdio: "pipe", timeout: 3e3 });
|
|
29612
29672
|
return true;
|
|
29613
29673
|
} catch {
|
|
29614
29674
|
return false;
|
|
@@ -29641,7 +29701,7 @@ function getVenvDir() {
|
|
|
29641
29701
|
}
|
|
29642
29702
|
function hasVenvModule() {
|
|
29643
29703
|
try {
|
|
29644
|
-
|
|
29704
|
+
execSync26("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
|
|
29645
29705
|
return true;
|
|
29646
29706
|
} catch {
|
|
29647
29707
|
return false;
|
|
@@ -29663,8 +29723,8 @@ function ensureVenv(log) {
|
|
|
29663
29723
|
}
|
|
29664
29724
|
try {
|
|
29665
29725
|
mkdirSync10(join39(homedir10(), ".open-agents"), { recursive: true });
|
|
29666
|
-
|
|
29667
|
-
|
|
29726
|
+
execSync26(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
|
|
29727
|
+
execSync26(`"${join39(venvDir, "bin", "pip")}" install --upgrade pip`, {
|
|
29668
29728
|
stdio: "pipe",
|
|
29669
29729
|
timeout: 6e4
|
|
29670
29730
|
});
|
|
@@ -29677,7 +29737,7 @@ function ensureVenv(log) {
|
|
|
29677
29737
|
}
|
|
29678
29738
|
function trySudoPasswordless(cmd, timeoutMs = 12e4) {
|
|
29679
29739
|
try {
|
|
29680
|
-
|
|
29740
|
+
execSync26(`sudo -n ${cmd}`, {
|
|
29681
29741
|
stdio: "pipe",
|
|
29682
29742
|
timeout: timeoutMs,
|
|
29683
29743
|
env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
|
|
@@ -29689,7 +29749,8 @@ function trySudoPasswordless(cmd, timeoutMs = 12e4) {
|
|
|
29689
29749
|
}
|
|
29690
29750
|
function runWithSudo(cmd, password, timeoutMs = 12e4) {
|
|
29691
29751
|
try {
|
|
29692
|
-
|
|
29752
|
+
const escaped = cmd.replace(/'/g, "'\\''");
|
|
29753
|
+
execSync26(`sudo -S bash -c '${escaped}'`, {
|
|
29693
29754
|
input: password + "\n",
|
|
29694
29755
|
stdio: ["pipe", "pipe", "pipe"],
|
|
29695
29756
|
timeout: timeoutMs,
|
|
@@ -29796,7 +29857,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29796
29857
|
ok = await sudoInstall(batchCmd, getPassword, log, cachedPasswordRef, 18e4);
|
|
29797
29858
|
} else {
|
|
29798
29859
|
try {
|
|
29799
|
-
|
|
29860
|
+
execSync26(batchCmd, { stdio: "pipe", timeout: 18e4 });
|
|
29800
29861
|
ok = true;
|
|
29801
29862
|
} catch {
|
|
29802
29863
|
ok = false;
|
|
@@ -29833,7 +29894,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29833
29894
|
const venvCmds = {
|
|
29834
29895
|
apt: () => {
|
|
29835
29896
|
try {
|
|
29836
|
-
const pyVer =
|
|
29897
|
+
const pyVer = execSync26(`python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).trim();
|
|
29837
29898
|
return `apt-get install -y python3-venv python${pyVer}-venv`;
|
|
29838
29899
|
} catch {
|
|
29839
29900
|
return "apt-get install -y python3-venv";
|
|
@@ -29861,12 +29922,12 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29861
29922
|
const venvPip = join39(venvBin, "pip");
|
|
29862
29923
|
log("Installing moondream-station in ~/.open-agents/venv...");
|
|
29863
29924
|
try {
|
|
29864
|
-
|
|
29925
|
+
execSync26(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
|
|
29865
29926
|
if (existsSync29(venvMoondream)) {
|
|
29866
29927
|
log("moondream-station installed successfully.");
|
|
29867
29928
|
} else {
|
|
29868
29929
|
try {
|
|
29869
|
-
const check =
|
|
29930
|
+
const check = execSync26(`"${venvPip}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
|
|
29870
29931
|
if (check.includes("moondream")) {
|
|
29871
29932
|
log("moondream-station package installed.");
|
|
29872
29933
|
}
|
|
@@ -29883,7 +29944,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29883
29944
|
const venvPip2 = join39(venvBin, "pip");
|
|
29884
29945
|
let ocrStackInstalled = false;
|
|
29885
29946
|
try {
|
|
29886
|
-
|
|
29947
|
+
execSync26(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
|
|
29887
29948
|
ocrStackInstalled = true;
|
|
29888
29949
|
} catch {
|
|
29889
29950
|
}
|
|
@@ -29891,9 +29952,9 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29891
29952
|
const ocrPackages = "pytesseract Pillow opencv-python-headless numpy";
|
|
29892
29953
|
log("Installing OCR Python stack (pytesseract, OpenCV, Pillow, numpy)...");
|
|
29893
29954
|
try {
|
|
29894
|
-
|
|
29955
|
+
execSync26(`"${venvPip2}" install ${ocrPackages}`, { stdio: "pipe", timeout: 3e5 });
|
|
29895
29956
|
try {
|
|
29896
|
-
|
|
29957
|
+
execSync26(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
|
|
29897
29958
|
log("OCR Python stack installed successfully.");
|
|
29898
29959
|
} catch {
|
|
29899
29960
|
log("OCR Python stack install completed but import verification failed.");
|
|
@@ -29927,7 +29988,7 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
29927
29988
|
const archMap = { x64: "amd64", arm64: "arm64", arm: "arm" };
|
|
29928
29989
|
const cfArch = archMap[arch] ?? "amd64";
|
|
29929
29990
|
try {
|
|
29930
|
-
|
|
29991
|
+
execSync26(`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 });
|
|
29931
29992
|
if (!process.env.PATH?.includes(`${homedir10()}/.local/bin`)) {
|
|
29932
29993
|
process.env.PATH = `${homedir10()}/.local/bin:${process.env.PATH}`;
|
|
29933
29994
|
}
|
|
@@ -29938,7 +29999,7 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
29938
29999
|
} catch {
|
|
29939
30000
|
}
|
|
29940
30001
|
try {
|
|
29941
|
-
|
|
30002
|
+
execSync26(`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 });
|
|
29942
30003
|
if (hasCmd("cloudflared")) {
|
|
29943
30004
|
log("cloudflared installed.");
|
|
29944
30005
|
return true;
|
|
@@ -29947,7 +30008,7 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
29947
30008
|
}
|
|
29948
30009
|
} else if (os === "darwin") {
|
|
29949
30010
|
try {
|
|
29950
|
-
|
|
30011
|
+
execSync26("brew install cloudflared", { stdio: "pipe", timeout: 12e4 });
|
|
29951
30012
|
if (hasCmd("cloudflared")) {
|
|
29952
30013
|
log("cloudflared installed via Homebrew.");
|
|
29953
30014
|
return true;
|
|
@@ -29997,7 +30058,7 @@ function createExpandedVariant(baseModel, specs, sizeGB) {
|
|
|
29997
30058
|
mkdirSync10(modelDir2, { recursive: true });
|
|
29998
30059
|
const modelfilePath = join39(modelDir2, `Modelfile.${customName}`);
|
|
29999
30060
|
writeFileSync10(modelfilePath, modelfileContent + "\n", "utf8");
|
|
30000
|
-
|
|
30061
|
+
execSync26(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
30001
30062
|
stdio: "pipe",
|
|
30002
30063
|
timeout: 12e4
|
|
30003
30064
|
});
|
|
@@ -30250,7 +30311,8 @@ function tuiSelect(opts) {
|
|
|
30250
30311
|
lines.push(` ${selectColors.dim(` \u25BC ${remaining} more`)}`);
|
|
30251
30312
|
}
|
|
30252
30313
|
lines.push("");
|
|
30253
|
-
|
|
30314
|
+
const actionHint = opts.onAction ? " \u2190/\u2192/Space toggle" : "";
|
|
30315
|
+
lines.push(` ${selectColors.dim("\u2191/\u2193 navigate Enter select" + actionHint + " Esc " + (filter ? "clear filter" : "cancel") + " Type to filter")}`);
|
|
30254
30316
|
lines.push("");
|
|
30255
30317
|
const output = lines.join("\n");
|
|
30256
30318
|
process.stdout.write(output);
|
|
@@ -30320,6 +30382,21 @@ function tuiSelect(opts) {
|
|
|
30320
30382
|
cursor = last;
|
|
30321
30383
|
render();
|
|
30322
30384
|
}
|
|
30385
|
+
} else if (seq === "\x1B[D" && opts.onAction) {
|
|
30386
|
+
if (!isSkippable(cursor) && matchSet.has(cursor)) {
|
|
30387
|
+
if (opts.onAction(items[cursor], "left"))
|
|
30388
|
+
render();
|
|
30389
|
+
}
|
|
30390
|
+
} else if (seq === "\x1B[C" && opts.onAction) {
|
|
30391
|
+
if (!isSkippable(cursor) && matchSet.has(cursor)) {
|
|
30392
|
+
if (opts.onAction(items[cursor], "right"))
|
|
30393
|
+
render();
|
|
30394
|
+
}
|
|
30395
|
+
} else if (seq === " " && opts.onAction) {
|
|
30396
|
+
if (!isSkippable(cursor) && matchSet.has(cursor)) {
|
|
30397
|
+
if (opts.onAction(items[cursor], "space"))
|
|
30398
|
+
render();
|
|
30399
|
+
}
|
|
30323
30400
|
} else if (seq === "\r" || seq === "\n") {
|
|
30324
30401
|
if (!isSkippable(cursor) && matchSet.has(cursor)) {
|
|
30325
30402
|
cleanup();
|
|
@@ -30426,38 +30503,7 @@ async function handleSlashCommand(input, ctx) {
|
|
|
30426
30503
|
return "handled";
|
|
30427
30504
|
case "config":
|
|
30428
30505
|
case "cfg": {
|
|
30429
|
-
|
|
30430
|
-
const globalS = loadGlobalSettings();
|
|
30431
|
-
const merged = { ...globalS, ...resolved };
|
|
30432
|
-
renderConfig({
|
|
30433
|
-
// -- Core inference --
|
|
30434
|
-
model: ctx.config.model,
|
|
30435
|
-
backendType: ctx.config.backendType,
|
|
30436
|
-
backendUrl: ctx.config.backendUrl,
|
|
30437
|
-
apiKey: ctx.config.apiKey ? "[set]" : "[not set]",
|
|
30438
|
-
maxRetries: String(ctx.config.maxRetries),
|
|
30439
|
-
timeoutMs: String(ctx.config.timeoutMs),
|
|
30440
|
-
dbPath: ctx.config.dbPath,
|
|
30441
|
-
// -- Behaviour --
|
|
30442
|
-
dryRun: String(ctx.config.dryRun),
|
|
30443
|
-
verbose: String(ctx.config.verbose),
|
|
30444
|
-
stream: String(merged.stream ?? false),
|
|
30445
|
-
bruteforce: String(merged.bruteforce ?? false),
|
|
30446
|
-
deepContext: String(merged.deepContext ?? false),
|
|
30447
|
-
// -- Presentation --
|
|
30448
|
-
emojis: String(ctx.getEmojis?.() ?? merged.emojis ?? true),
|
|
30449
|
-
colors: String(ctx.getColors?.() ?? merged.colors ?? true),
|
|
30450
|
-
style: String(ctx.getStyle?.() ?? merged.style ?? "balanced"),
|
|
30451
|
-
// -- Voice --
|
|
30452
|
-
voice: String(merged.voice ?? false),
|
|
30453
|
-
voiceModel: String(merged.voiceModel ?? "glados"),
|
|
30454
|
-
// -- Autonomy --
|
|
30455
|
-
commandsMode: String(ctx.getCommandsMode?.() ?? merged.commandsMode ?? "manual"),
|
|
30456
|
-
updateMode: String(merged.updateMode ?? "auto"),
|
|
30457
|
-
// -- Integrations --
|
|
30458
|
-
telegramKey: merged.telegramKey ? "[set]" : "[not set]",
|
|
30459
|
-
telegramAdmin: String(merged.telegramAdmin ?? "[not set]")
|
|
30460
|
-
});
|
|
30506
|
+
await showConfigEditor(ctx);
|
|
30461
30507
|
return "handled";
|
|
30462
30508
|
}
|
|
30463
30509
|
case "cost":
|
|
@@ -30871,25 +30917,26 @@ async function handleSlashCommand(input, ctx) {
|
|
|
30871
30917
|
}
|
|
30872
30918
|
case "commands":
|
|
30873
30919
|
case "cmds": {
|
|
30874
|
-
if (!ctx.getCommandsMode || !ctx.setCommandsMode) {
|
|
30875
|
-
renderWarning("Commands mode not available in this context.");
|
|
30876
|
-
return "handled";
|
|
30877
|
-
}
|
|
30878
30920
|
if (arg === "auto") {
|
|
30921
|
+
if (!ctx.setCommandsMode) {
|
|
30922
|
+
renderWarning("Commands mode not available.");
|
|
30923
|
+
return "handled";
|
|
30924
|
+
}
|
|
30879
30925
|
ctx.setCommandsMode("auto");
|
|
30880
30926
|
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
30881
30927
|
save({ commandsMode: "auto" });
|
|
30882
30928
|
renderInfo("Commands mode: auto \u2014 the agent can now invoke slash commands as tools.");
|
|
30883
30929
|
} else if (arg === "manual") {
|
|
30930
|
+
if (!ctx.setCommandsMode) {
|
|
30931
|
+
renderWarning("Commands mode not available.");
|
|
30932
|
+
return "handled";
|
|
30933
|
+
}
|
|
30884
30934
|
ctx.setCommandsMode("manual");
|
|
30885
30935
|
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
30886
30936
|
save({ commandsMode: "manual" });
|
|
30887
30937
|
renderInfo("Commands mode: manual \u2014 slash commands are user-only.");
|
|
30888
30938
|
} else {
|
|
30889
|
-
|
|
30890
|
-
renderInfo(`Commands mode: ${c2.bold(current)}`);
|
|
30891
|
-
renderInfo(" /commands auto \u2014 agent can invoke slash commands as tools");
|
|
30892
|
-
renderInfo(" /commands manual \u2014 slash commands are user-only (default)");
|
|
30939
|
+
renderSlashHelp();
|
|
30893
30940
|
}
|
|
30894
30941
|
return "handled";
|
|
30895
30942
|
}
|
|
@@ -31496,6 +31543,162 @@ async function handleSlashCommand(input, ctx) {
|
|
|
31496
31543
|
}
|
|
31497
31544
|
}
|
|
31498
31545
|
}
|
|
31546
|
+
async function showConfigEditor(ctx) {
|
|
31547
|
+
const merged = { ...loadGlobalSettings(), ...loadProjectSettings(ctx.repoRoot) };
|
|
31548
|
+
const pendingChanges = {};
|
|
31549
|
+
let hasChanges = false;
|
|
31550
|
+
const entries = [
|
|
31551
|
+
// -- Headers & categories --
|
|
31552
|
+
{ key: "__h_core__", label: c2.dim("\u2500\u2500 Core Inference \u2500\u2500"), detail: "", kind: "header", value: "" },
|
|
31553
|
+
{ key: "model", label: "model", kind: "readonly", value: ctx.config.model, detail: ctx.config.model },
|
|
31554
|
+
{ key: "backendUrl", label: "backendUrl", kind: "readonly", value: ctx.config.backendUrl, detail: ctx.config.backendUrl },
|
|
31555
|
+
{ key: "backendType", label: "backendType", kind: "readonly", value: ctx.config.backendType, detail: ctx.config.backendType },
|
|
31556
|
+
{ key: "apiKey", label: "apiKey", kind: "readonly", value: ctx.config.apiKey ? "[set]" : "[not set]", detail: ctx.config.apiKey ? "[set]" : "[not set]" },
|
|
31557
|
+
{ key: "maxRetries", label: "maxRetries", kind: "string", value: String(ctx.config.maxRetries), detail: String(ctx.config.maxRetries), settingsKey: "maxRetries" },
|
|
31558
|
+
{ key: "timeoutMs", label: "timeoutMs", kind: "string", value: String(ctx.config.timeoutMs), detail: String(ctx.config.timeoutMs), settingsKey: "timeoutMs" },
|
|
31559
|
+
{ key: "__h_behav__", label: c2.dim("\u2500\u2500 Behaviour \u2500\u2500"), detail: "", kind: "header", value: "" },
|
|
31560
|
+
{ key: "verbose", label: "verbose", kind: "boolean", value: String(ctx.config.verbose), detail: String(ctx.config.verbose), settingsKey: "verbose" },
|
|
31561
|
+
{ key: "stream", label: "stream", kind: "boolean", value: String(merged.stream ?? false), detail: String(merged.stream ?? false), settingsKey: "stream" },
|
|
31562
|
+
{ key: "bruteforce", label: "bruteforce", kind: "boolean", value: String(merged.bruteforce ?? false), detail: String(merged.bruteforce ?? false), settingsKey: "bruteforce" },
|
|
31563
|
+
{ key: "deepContext", label: "deepContext", kind: "boolean", value: String(merged.deepContext ?? false), detail: String(merged.deepContext ?? false), settingsKey: "deepContext" },
|
|
31564
|
+
{ key: "dryRun", label: "dryRun", kind: "boolean", value: String(ctx.config.dryRun), detail: String(ctx.config.dryRun), settingsKey: "dryRun" },
|
|
31565
|
+
{ key: "__h_pres__", label: c2.dim("\u2500\u2500 Presentation \u2500\u2500"), detail: "", kind: "header", value: "" },
|
|
31566
|
+
{ key: "emojis", label: "emojis", kind: "boolean", value: String(ctx.getEmojis?.() ?? merged.emojis ?? true), detail: String(ctx.getEmojis?.() ?? merged.emojis ?? true), settingsKey: "emojis" },
|
|
31567
|
+
{ key: "colors", label: "colors", kind: "boolean", value: String(ctx.getColors?.() ?? merged.colors ?? true), detail: String(ctx.getColors?.() ?? merged.colors ?? true), settingsKey: "colors" },
|
|
31568
|
+
{ key: "style", label: "style", kind: "enum", value: String(ctx.getStyle?.() ?? merged.style ?? "balanced"), detail: String(ctx.getStyle?.() ?? merged.style ?? "balanced"), options: [...PRESET_NAMES], settingsKey: "style" },
|
|
31569
|
+
{ key: "__h_voice__", label: c2.dim("\u2500\u2500 Voice \u2500\u2500"), detail: "", kind: "header", value: "" },
|
|
31570
|
+
{ key: "voice", label: "voice", kind: "boolean", value: String(merged.voice ?? false), detail: String(merged.voice ?? false), settingsKey: "voice" },
|
|
31571
|
+
{ key: "voiceModel", label: "voiceModel", kind: "enum", value: String(merged.voiceModel ?? "glados"), detail: String(merged.voiceModel ?? "glados"), options: ["glados", "overwatch"], settingsKey: "voiceModel" },
|
|
31572
|
+
{ key: "__h_auto__", label: c2.dim("\u2500\u2500 Autonomy \u2500\u2500"), detail: "", kind: "header", value: "" },
|
|
31573
|
+
{ key: "commandsMode", label: "commandsMode", kind: "enum", value: String(ctx.getCommandsMode?.() ?? merged.commandsMode ?? "manual"), detail: String(ctx.getCommandsMode?.() ?? merged.commandsMode ?? "manual"), options: ["manual", "auto"], settingsKey: "commandsMode" },
|
|
31574
|
+
{ key: "updateMode", label: "updateMode", kind: "enum", value: String(merged.updateMode ?? "auto"), detail: String(merged.updateMode ?? "auto"), options: ["auto", "manual"], settingsKey: "updateMode" },
|
|
31575
|
+
{ key: "__h_int__", label: c2.dim("\u2500\u2500 Integrations \u2500\u2500"), detail: "", kind: "header", value: "" },
|
|
31576
|
+
{ key: "telegramKey", label: "telegramKey", kind: "readonly", value: merged.telegramKey ? "[set]" : "[not set]", detail: merged.telegramKey ? "[set]" : "[not set]" },
|
|
31577
|
+
{ key: "telegramAdmin", label: "telegramAdmin", kind: "readonly", value: String(merged.telegramAdmin ?? "[not set]"), detail: String(merged.telegramAdmin ?? "[not set]") },
|
|
31578
|
+
// -- Actions --
|
|
31579
|
+
{ key: "__h_actions__", label: c2.dim("\u2500\u2500 Actions \u2500\u2500"), detail: "", kind: "header", value: "" },
|
|
31580
|
+
{ key: "__save_global__", label: c2.bold("Save to global settings"), detail: "~/.open-agents/settings.json", kind: "action", value: "save_global" },
|
|
31581
|
+
{ key: "__save_local__", label: c2.bold("Save to project settings"), detail: ".oa/settings.json", kind: "action", value: "save_local" }
|
|
31582
|
+
];
|
|
31583
|
+
function renderConfigRow(item, focused, _isActive) {
|
|
31584
|
+
if (item.kind === "header") {
|
|
31585
|
+
return ` ${item.label}`;
|
|
31586
|
+
}
|
|
31587
|
+
const marker = focused ? selectColors.orange("\u25CF") : selectColors.dim("\u25CB");
|
|
31588
|
+
const nameStr = focused ? selectColors.orange(selectColors.bold(item.label)) : item.label;
|
|
31589
|
+
if (item.kind === "action") {
|
|
31590
|
+
return ` ${marker} ${nameStr} ${selectColors.dim(item.detail ?? "")}`;
|
|
31591
|
+
}
|
|
31592
|
+
let valueStr;
|
|
31593
|
+
if (item.kind === "boolean") {
|
|
31594
|
+
const val = item.value === "true";
|
|
31595
|
+
valueStr = val ? selectColors.green("true") : c2.red("false");
|
|
31596
|
+
if (focused)
|
|
31597
|
+
valueStr += selectColors.dim(" \u2190/\u2192/Space toggle");
|
|
31598
|
+
} else if (item.kind === "enum") {
|
|
31599
|
+
valueStr = selectColors.cyan(item.value);
|
|
31600
|
+
if (focused && item.options) {
|
|
31601
|
+
const idx = item.options.indexOf(item.value);
|
|
31602
|
+
const prev = item.options[(idx - 1 + item.options.length) % item.options.length];
|
|
31603
|
+
const next = item.options[(idx + 1) % item.options.length];
|
|
31604
|
+
valueStr += selectColors.dim(` \u2190 ${prev} | ${next} \u2192`);
|
|
31605
|
+
}
|
|
31606
|
+
} else if (item.kind === "readonly") {
|
|
31607
|
+
valueStr = selectColors.dim(item.value);
|
|
31608
|
+
} else {
|
|
31609
|
+
valueStr = item.value;
|
|
31610
|
+
if (focused)
|
|
31611
|
+
valueStr += selectColors.dim(" Enter to edit");
|
|
31612
|
+
}
|
|
31613
|
+
return ` ${marker} ${nameStr.padEnd(focused ? 20 : 20)} ${valueStr}`;
|
|
31614
|
+
}
|
|
31615
|
+
function onAction(item, action) {
|
|
31616
|
+
if (item.kind === "boolean") {
|
|
31617
|
+
const newVal = item.value === "true" ? "false" : "true";
|
|
31618
|
+
item.value = newVal;
|
|
31619
|
+
item.detail = newVal;
|
|
31620
|
+
if (item.settingsKey) {
|
|
31621
|
+
pendingChanges[item.settingsKey] = newVal === "true";
|
|
31622
|
+
}
|
|
31623
|
+
hasChanges = true;
|
|
31624
|
+
return true;
|
|
31625
|
+
}
|
|
31626
|
+
if (item.kind === "enum" && item.options) {
|
|
31627
|
+
const idx = item.options.indexOf(item.value);
|
|
31628
|
+
let newIdx;
|
|
31629
|
+
if (action === "left") {
|
|
31630
|
+
newIdx = (idx - 1 + item.options.length) % item.options.length;
|
|
31631
|
+
} else {
|
|
31632
|
+
newIdx = (idx + 1) % item.options.length;
|
|
31633
|
+
}
|
|
31634
|
+
item.value = item.options[newIdx];
|
|
31635
|
+
item.detail = item.value;
|
|
31636
|
+
if (item.settingsKey) {
|
|
31637
|
+
pendingChanges[item.settingsKey] = item.value;
|
|
31638
|
+
}
|
|
31639
|
+
hasChanges = true;
|
|
31640
|
+
return true;
|
|
31641
|
+
}
|
|
31642
|
+
return false;
|
|
31643
|
+
}
|
|
31644
|
+
const skipKeys = entries.filter((e) => e.kind === "header").map((e) => e.key);
|
|
31645
|
+
const result = await tuiSelect({
|
|
31646
|
+
items: entries,
|
|
31647
|
+
title: "Configuration",
|
|
31648
|
+
rl: ctx.rl,
|
|
31649
|
+
skipKeys,
|
|
31650
|
+
renderRow: renderConfigRow,
|
|
31651
|
+
onAction
|
|
31652
|
+
});
|
|
31653
|
+
if (result.confirmed && result.key) {
|
|
31654
|
+
const entry = entries.find((e) => e.key === result.key);
|
|
31655
|
+
if (entry?.kind === "action") {
|
|
31656
|
+
if (entry.value === "save_global") {
|
|
31657
|
+
applyConfigChanges(ctx, pendingChanges);
|
|
31658
|
+
ctx.saveSettings(pendingChanges);
|
|
31659
|
+
renderInfo("Settings saved globally.");
|
|
31660
|
+
hasChanges = false;
|
|
31661
|
+
return;
|
|
31662
|
+
}
|
|
31663
|
+
if (entry.value === "save_local") {
|
|
31664
|
+
applyConfigChanges(ctx, pendingChanges);
|
|
31665
|
+
ctx.saveLocalSettings(pendingChanges);
|
|
31666
|
+
renderInfo("Settings saved to project.");
|
|
31667
|
+
hasChanges = false;
|
|
31668
|
+
return;
|
|
31669
|
+
}
|
|
31670
|
+
}
|
|
31671
|
+
if (entry?.kind === "boolean" || entry?.kind === "enum") {
|
|
31672
|
+
onAction(entry, "right");
|
|
31673
|
+
applyConfigChanges(ctx, pendingChanges);
|
|
31674
|
+
ctx.saveSettings(pendingChanges);
|
|
31675
|
+
renderInfo(`${entry.key} set to ${c2.bold(entry.value)}`);
|
|
31676
|
+
return;
|
|
31677
|
+
}
|
|
31678
|
+
if (entry?.kind === "readonly") {
|
|
31679
|
+
renderInfo(`${entry.key} is read-only. Use the dedicated command (e.g. /model, /endpoint).`);
|
|
31680
|
+
return;
|
|
31681
|
+
}
|
|
31682
|
+
}
|
|
31683
|
+
if (hasChanges) {
|
|
31684
|
+
renderWarning("Unsaved config changes. Saving to global settings...");
|
|
31685
|
+
applyConfigChanges(ctx, pendingChanges);
|
|
31686
|
+
ctx.saveSettings(pendingChanges);
|
|
31687
|
+
renderInfo("Changes saved.");
|
|
31688
|
+
}
|
|
31689
|
+
}
|
|
31690
|
+
function applyConfigChanges(ctx, changes) {
|
|
31691
|
+
if (changes.verbose !== void 0)
|
|
31692
|
+
ctx.setVerbose(changes.verbose);
|
|
31693
|
+
if (changes.emojis !== void 0)
|
|
31694
|
+
ctx.setEmojis?.(changes.emojis);
|
|
31695
|
+
if (changes.colors !== void 0)
|
|
31696
|
+
ctx.setColors?.(changes.colors);
|
|
31697
|
+
if (changes.style !== void 0)
|
|
31698
|
+
ctx.setStyle?.(changes.style);
|
|
31699
|
+
if (changes.commandsMode !== void 0)
|
|
31700
|
+
ctx.setCommandsMode?.(changes.commandsMode);
|
|
31701
|
+
}
|
|
31499
31702
|
async function listModels(ctx) {
|
|
31500
31703
|
try {
|
|
31501
31704
|
const models = await fetchModels(ctx.config.backendUrl, ctx.config.apiKey);
|
|
@@ -31709,6 +31912,10 @@ async function handleEndpoint(arg, ctx, local = false) {
|
|
|
31709
31912
|
`);
|
|
31710
31913
|
}
|
|
31711
31914
|
process.stdout.write("\n");
|
|
31915
|
+
if (ctx.hasActiveTask?.()) {
|
|
31916
|
+
ctx.abortActiveTask?.();
|
|
31917
|
+
renderWarning("Active task aborted \u2014 endpoint changed. Send a new prompt to continue.");
|
|
31918
|
+
}
|
|
31712
31919
|
try {
|
|
31713
31920
|
const newModels = await fetchModels(normalizedUrl, apiKey);
|
|
31714
31921
|
if (newModels.length > 0) {
|
|
@@ -32052,7 +32259,7 @@ var init_commands = __esm({
|
|
|
32052
32259
|
// packages/cli/dist/tui/project-context.js
|
|
32053
32260
|
import { existsSync as existsSync30, readFileSync as readFileSync21, readdirSync as readdirSync8 } from "node:fs";
|
|
32054
32261
|
import { join as join40, basename as basename10 } from "node:path";
|
|
32055
|
-
import { execSync as
|
|
32262
|
+
import { execSync as execSync27 } from "node:child_process";
|
|
32056
32263
|
import { homedir as homedir11, platform as platform2, release } from "node:os";
|
|
32057
32264
|
function getModelTier(modelName) {
|
|
32058
32265
|
const m = modelName.toLowerCase();
|
|
@@ -32098,19 +32305,19 @@ function loadProjectMap(repoRoot) {
|
|
|
32098
32305
|
}
|
|
32099
32306
|
function getGitInfo(repoRoot) {
|
|
32100
32307
|
try {
|
|
32101
|
-
|
|
32308
|
+
execSync27("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
|
|
32102
32309
|
} catch {
|
|
32103
32310
|
return "";
|
|
32104
32311
|
}
|
|
32105
32312
|
const lines = [];
|
|
32106
32313
|
try {
|
|
32107
|
-
const branch =
|
|
32314
|
+
const branch = execSync27("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
32108
32315
|
if (branch)
|
|
32109
32316
|
lines.push(`Branch: ${branch}`);
|
|
32110
32317
|
} catch {
|
|
32111
32318
|
}
|
|
32112
32319
|
try {
|
|
32113
|
-
const status =
|
|
32320
|
+
const status = execSync27("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
32114
32321
|
if (status) {
|
|
32115
32322
|
const changed = status.split("\n").length;
|
|
32116
32323
|
lines.push(`Working tree: ${changed} changed file(s)`);
|
|
@@ -32120,7 +32327,7 @@ function getGitInfo(repoRoot) {
|
|
|
32120
32327
|
} catch {
|
|
32121
32328
|
}
|
|
32122
32329
|
try {
|
|
32123
|
-
const log =
|
|
32330
|
+
const log = execSync27("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
32124
32331
|
if (log)
|
|
32125
32332
|
lines.push(`Recent commits:
|
|
32126
32333
|
${log}`);
|
|
@@ -33537,7 +33744,7 @@ var init_carousel_descriptors = __esm({
|
|
|
33537
33744
|
import { existsSync as existsSync32, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12, readFileSync as readFileSync23, unlinkSync as unlinkSync4 } from "node:fs";
|
|
33538
33745
|
import { join as join42 } from "node:path";
|
|
33539
33746
|
import { homedir as homedir12, tmpdir as tmpdir6, platform as platform3 } from "node:os";
|
|
33540
|
-
import { execSync as
|
|
33747
|
+
import { execSync as execSync28, spawn as nodeSpawn } from "node:child_process";
|
|
33541
33748
|
import { createRequire } from "node:module";
|
|
33542
33749
|
function voiceDir() {
|
|
33543
33750
|
return join42(homedir12(), ".open-agents", "voice");
|
|
@@ -34594,7 +34801,7 @@ var init_voice = __esm({
|
|
|
34594
34801
|
}
|
|
34595
34802
|
for (const player of ["paplay", "pw-play", "aplay"]) {
|
|
34596
34803
|
try {
|
|
34597
|
-
|
|
34804
|
+
execSync28(`which ${player}`, { stdio: "pipe" });
|
|
34598
34805
|
return [player, path];
|
|
34599
34806
|
} catch {
|
|
34600
34807
|
}
|
|
@@ -34623,7 +34830,7 @@ var init_voice = __esm({
|
|
|
34623
34830
|
return this.python3Path;
|
|
34624
34831
|
for (const bin of ["python3", "python"]) {
|
|
34625
34832
|
try {
|
|
34626
|
-
const path =
|
|
34833
|
+
const path = execSync28(`which ${bin}`, { stdio: "pipe", timeout: 5e3 }).toString().trim();
|
|
34627
34834
|
if (path) {
|
|
34628
34835
|
this.python3Path = path;
|
|
34629
34836
|
return path;
|
|
@@ -34643,7 +34850,7 @@ var init_voice = __esm({
|
|
|
34643
34850
|
return false;
|
|
34644
34851
|
}
|
|
34645
34852
|
try {
|
|
34646
|
-
|
|
34853
|
+
execSync28(`${py} -c "import mlx_audio"`, { stdio: "pipe", timeout: 1e4 });
|
|
34647
34854
|
this.mlxInstalled = true;
|
|
34648
34855
|
return true;
|
|
34649
34856
|
} catch {
|
|
@@ -34667,7 +34874,7 @@ var init_voice = __esm({
|
|
|
34667
34874
|
return;
|
|
34668
34875
|
renderInfo("Installing MLX Audio for voice synthesis (first time setup)...");
|
|
34669
34876
|
try {
|
|
34670
|
-
|
|
34877
|
+
execSync28(`${py} -m pip install mlx-audio --quiet`, {
|
|
34671
34878
|
stdio: "pipe",
|
|
34672
34879
|
timeout: 3e5
|
|
34673
34880
|
// 5 min — may need to compile
|
|
@@ -34675,7 +34882,7 @@ var init_voice = __esm({
|
|
|
34675
34882
|
this.mlxInstalled = true;
|
|
34676
34883
|
} catch (err) {
|
|
34677
34884
|
try {
|
|
34678
|
-
|
|
34885
|
+
execSync28(`${py} -m pip install mlx-audio --user --quiet`, {
|
|
34679
34886
|
stdio: "pipe",
|
|
34680
34887
|
timeout: 3e5
|
|
34681
34888
|
});
|
|
@@ -34711,11 +34918,11 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
34711
34918
|
`tts_gen.main(["--model", ${JSON.stringify(mlxModelId)}, "--text", text, "--voice", ${JSON.stringify(mlxVoice)}, "--lang_code", ${JSON.stringify(mlxLangCode)}, "--audio_path", ${JSON.stringify(wavPath)}])`
|
|
34712
34919
|
].join("; ");
|
|
34713
34920
|
try {
|
|
34714
|
-
|
|
34921
|
+
execSync28(`${py} -c ${JSON.stringify(pyScript)} ${JSON.stringify(JSON.stringify(cleaned))}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir6() });
|
|
34715
34922
|
} catch (err) {
|
|
34716
34923
|
try {
|
|
34717
34924
|
const safeText = cleaned.replace(/'/g, "'\\''");
|
|
34718
|
-
|
|
34925
|
+
execSync28(`${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() });
|
|
34719
34926
|
} catch (err2) {
|
|
34720
34927
|
return;
|
|
34721
34928
|
}
|
|
@@ -34779,11 +34986,11 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
34779
34986
|
`tts_gen.main(["--model", ${JSON.stringify(mlxModelId)}, "--text", text, "--voice", ${JSON.stringify(mlxVoice)}, "--lang_code", ${JSON.stringify(mlxLangCode)}, "--audio_path", ${JSON.stringify(wavPath)}])`
|
|
34780
34987
|
].join("; ");
|
|
34781
34988
|
try {
|
|
34782
|
-
|
|
34989
|
+
execSync28(`${py} -c ${JSON.stringify(pyScript)} ${JSON.stringify(JSON.stringify(cleaned))}`, { stdio: "pipe", timeout: 6e4, cwd: tmpdir6() });
|
|
34783
34990
|
} catch {
|
|
34784
34991
|
try {
|
|
34785
34992
|
const safeText = cleaned.replace(/'/g, "'\\''");
|
|
34786
|
-
|
|
34993
|
+
execSync28(`${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() });
|
|
34787
34994
|
} catch {
|
|
34788
34995
|
return null;
|
|
34789
34996
|
}
|
|
@@ -34831,7 +35038,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
34831
35038
|
const voiceRequire = createRequire(join42(voiceDir(), "index.js"));
|
|
34832
35039
|
const probeOnnx = () => {
|
|
34833
35040
|
try {
|
|
34834
|
-
const result =
|
|
35041
|
+
const result = execSync28(`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") } });
|
|
34835
35042
|
const output = result.toString().trim();
|
|
34836
35043
|
return output === "OK";
|
|
34837
35044
|
} catch {
|
|
@@ -34848,7 +35055,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
34848
35055
|
} catch {
|
|
34849
35056
|
renderInfo("Installing ONNX runtime for voice synthesis...");
|
|
34850
35057
|
try {
|
|
34851
|
-
|
|
35058
|
+
execSync28("npm install --no-audit --no-fund", {
|
|
34852
35059
|
cwd: voiceDir(),
|
|
34853
35060
|
stdio: "pipe",
|
|
34854
35061
|
timeout: 12e4
|
|
@@ -34873,7 +35080,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
34873
35080
|
} catch {
|
|
34874
35081
|
renderInfo("Installing phonemizer for voice synthesis...");
|
|
34875
35082
|
try {
|
|
34876
|
-
|
|
35083
|
+
execSync28("npm install --no-audit --no-fund", {
|
|
34877
35084
|
cwd: voiceDir(),
|
|
34878
35085
|
stdio: "pipe",
|
|
34879
35086
|
timeout: 12e4
|
|
@@ -35957,7 +36164,7 @@ var init_promptLoader3 = __esm({
|
|
|
35957
36164
|
// packages/cli/dist/tui/dream-engine.js
|
|
35958
36165
|
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync13, readFileSync as readFileSync25, existsSync as existsSync34, cpSync, rmSync, readdirSync as readdirSync10 } from "node:fs";
|
|
35959
36166
|
import { join as join45, basename as basename12 } from "node:path";
|
|
35960
|
-
import { execSync as
|
|
36167
|
+
import { execSync as execSync29 } from "node:child_process";
|
|
35961
36168
|
function loadAutoresearchMemory(repoRoot) {
|
|
35962
36169
|
const memoryPath = join45(repoRoot, ".oa", "memory", "autoresearch.json");
|
|
35963
36170
|
if (!existsSync34(memoryPath))
|
|
@@ -36321,7 +36528,7 @@ var init_dream_engine = __esm({
|
|
|
36321
36528
|
}
|
|
36322
36529
|
}
|
|
36323
36530
|
try {
|
|
36324
|
-
const output =
|
|
36531
|
+
const output = execSync29(cmd, {
|
|
36325
36532
|
cwd: this.repoRoot,
|
|
36326
36533
|
timeout: 3e4,
|
|
36327
36534
|
encoding: "utf-8",
|
|
@@ -37098,17 +37305,17 @@ ${summaryResult}
|
|
|
37098
37305
|
try {
|
|
37099
37306
|
mkdirSync14(checkpointDir, { recursive: true });
|
|
37100
37307
|
try {
|
|
37101
|
-
const gitStatus =
|
|
37308
|
+
const gitStatus = execSync29("git status --porcelain", {
|
|
37102
37309
|
cwd: this.repoRoot,
|
|
37103
37310
|
encoding: "utf-8",
|
|
37104
37311
|
timeout: 1e4
|
|
37105
37312
|
});
|
|
37106
|
-
const gitDiff =
|
|
37313
|
+
const gitDiff = execSync29("git diff", {
|
|
37107
37314
|
cwd: this.repoRoot,
|
|
37108
37315
|
encoding: "utf-8",
|
|
37109
37316
|
timeout: 1e4
|
|
37110
37317
|
});
|
|
37111
|
-
const gitHash =
|
|
37318
|
+
const gitHash = execSync29("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
|
|
37112
37319
|
cwd: this.repoRoot,
|
|
37113
37320
|
encoding: "utf-8",
|
|
37114
37321
|
timeout: 5e3
|
|
@@ -40962,6 +41169,62 @@ var init_status_bar = __esm({
|
|
|
40962
41169
|
if (this.active)
|
|
40963
41170
|
this.renderFooterPreserveCursor();
|
|
40964
41171
|
}
|
|
41172
|
+
/** Remote host metrics (when connected to a remote endpoint) */
|
|
41173
|
+
_remoteMetrics = null;
|
|
41174
|
+
_remoteMetricsTimer = null;
|
|
41175
|
+
/** Update remote host system metrics (from polling /v1/system/metrics) */
|
|
41176
|
+
setRemoteMetrics(metrics) {
|
|
41177
|
+
this._remoteMetrics = metrics;
|
|
41178
|
+
if (this.active)
|
|
41179
|
+
this.renderFooterPreserveCursor();
|
|
41180
|
+
}
|
|
41181
|
+
/** Clear remote metrics (e.g. when switching to local endpoint) */
|
|
41182
|
+
clearRemoteMetrics() {
|
|
41183
|
+
this._remoteMetrics = null;
|
|
41184
|
+
if (this._remoteMetricsTimer) {
|
|
41185
|
+
clearInterval(this._remoteMetricsTimer);
|
|
41186
|
+
this._remoteMetricsTimer = null;
|
|
41187
|
+
}
|
|
41188
|
+
if (this.active)
|
|
41189
|
+
this.renderFooterPreserveCursor();
|
|
41190
|
+
}
|
|
41191
|
+
/**
|
|
41192
|
+
* Start polling a remote endpoint for system metrics.
|
|
41193
|
+
* Call when /endpoint is set to a remote URL that has /v1/system/metrics.
|
|
41194
|
+
*/
|
|
41195
|
+
startRemoteMetricsPolling(endpointUrl, authKey) {
|
|
41196
|
+
this.stopRemoteMetricsPolling();
|
|
41197
|
+
const poll = async () => {
|
|
41198
|
+
try {
|
|
41199
|
+
const url = new URL("/v1/system/metrics", endpointUrl);
|
|
41200
|
+
const headers = {};
|
|
41201
|
+
if (authKey)
|
|
41202
|
+
headers["Authorization"] = `Bearer ${authKey}`;
|
|
41203
|
+
const resp = await fetch(url.toString(), { headers, signal: AbortSignal.timeout(5e3) });
|
|
41204
|
+
if (resp.ok) {
|
|
41205
|
+
const data = await resp.json();
|
|
41206
|
+
this.setRemoteMetrics({
|
|
41207
|
+
cpuUtil: data.cpu?.utilization ?? 0,
|
|
41208
|
+
gpuUtil: data.gpu?.available ? data.gpu.utilization ?? 0 : -1,
|
|
41209
|
+
gpuName: data.gpu?.name ?? "",
|
|
41210
|
+
vramUtil: data.gpu?.available ? data.gpu.vramUtilization ?? 0 : -1,
|
|
41211
|
+
memUtil: data.memory?.utilization ?? 0
|
|
41212
|
+
});
|
|
41213
|
+
}
|
|
41214
|
+
} catch {
|
|
41215
|
+
}
|
|
41216
|
+
};
|
|
41217
|
+
poll();
|
|
41218
|
+
this._remoteMetricsTimer = setInterval(poll, 1e4);
|
|
41219
|
+
}
|
|
41220
|
+
/** Stop polling remote metrics */
|
|
41221
|
+
stopRemoteMetricsPolling() {
|
|
41222
|
+
if (this._remoteMetricsTimer) {
|
|
41223
|
+
clearInterval(this._remoteMetricsTimer);
|
|
41224
|
+
this._remoteMetricsTimer = null;
|
|
41225
|
+
}
|
|
41226
|
+
this._remoteMetrics = null;
|
|
41227
|
+
}
|
|
40965
41228
|
/** Update token metrics from a token_usage event */
|
|
40966
41229
|
updateMetrics(update) {
|
|
40967
41230
|
if (update.promptTokens !== void 0)
|
|
@@ -41325,6 +41588,35 @@ var init_status_bar = __esm({
|
|
|
41325
41588
|
empty: false
|
|
41326
41589
|
});
|
|
41327
41590
|
}
|
|
41591
|
+
if (this._remoteMetrics) {
|
|
41592
|
+
const rm2 = this._remoteMetrics;
|
|
41593
|
+
const cpuColor = rm2.cpuUtil > 80 ? c2.red : rm2.cpuUtil > 50 ? c2.yellow : c2.green;
|
|
41594
|
+
const cpuStr = `CPU:${cpuColor(rm2.cpuUtil + "%")}`;
|
|
41595
|
+
const cpuW = 4 + `${rm2.cpuUtil}%`.length;
|
|
41596
|
+
let gpuStr = "";
|
|
41597
|
+
let gpuW = 0;
|
|
41598
|
+
if (rm2.gpuUtil >= 0) {
|
|
41599
|
+
const gpuColor = rm2.gpuUtil > 80 ? c2.red : rm2.gpuUtil > 50 ? c2.yellow : c2.green;
|
|
41600
|
+
gpuStr = ` GPU:${gpuColor(rm2.gpuUtil + "%")}`;
|
|
41601
|
+
gpuW = 5 + `${rm2.gpuUtil}%`.length;
|
|
41602
|
+
}
|
|
41603
|
+
let vramStr = "";
|
|
41604
|
+
let vramW = 0;
|
|
41605
|
+
if (rm2.vramUtil >= 0) {
|
|
41606
|
+
const vramColor = rm2.vramUtil > 80 ? c2.red : rm2.vramUtil > 50 ? c2.yellow : c2.green;
|
|
41607
|
+
vramStr = ` VRAM:${vramColor(rm2.vramUtil + "%")}`;
|
|
41608
|
+
vramW = 6 + `${rm2.vramUtil}%`.length;
|
|
41609
|
+
}
|
|
41610
|
+
const remoteExpanded = pastel2(117, "\u{1F4E1}") + " " + cpuStr + gpuStr + vramStr;
|
|
41611
|
+
const remoteCompact = pastel2(117, "\u{1F4E1}") + " " + cpuStr + gpuStr;
|
|
41612
|
+
sections.push({
|
|
41613
|
+
expanded: remoteExpanded,
|
|
41614
|
+
compact: remoteCompact,
|
|
41615
|
+
expandedW: 3 + cpuW + gpuW + vramW,
|
|
41616
|
+
compactW: 3 + cpuW + gpuW,
|
|
41617
|
+
empty: false
|
|
41618
|
+
});
|
|
41619
|
+
}
|
|
41328
41620
|
if (this._recording) {
|
|
41329
41621
|
const dot = this._recBlink ? pastel2(210, "\u25CF") : " ";
|
|
41330
41622
|
const countdown = this._countdown > 0 ? c2.dim(` ${this._countdown}s`) : "";
|
|
@@ -43020,6 +43312,12 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
43020
43312
|
};
|
|
43021
43313
|
const newProvider = detectProvider(url);
|
|
43022
43314
|
costTracker.setProvider(newProvider.id);
|
|
43315
|
+
const isRemote = !url.includes("127.0.0.1") && !url.includes("localhost");
|
|
43316
|
+
if (isRemote) {
|
|
43317
|
+
statusBar.startRemoteMetricsPolling(url, apiKey);
|
|
43318
|
+
} else {
|
|
43319
|
+
statusBar.stopRemoteMetricsPolling();
|
|
43320
|
+
}
|
|
43023
43321
|
},
|
|
43024
43322
|
clearScreen() {
|
|
43025
43323
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
@@ -43712,6 +44010,11 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
43712
44010
|
},
|
|
43713
44011
|
refreshModelCache,
|
|
43714
44012
|
hasActiveTask: () => activeTask !== null,
|
|
44013
|
+
abortActiveTask() {
|
|
44014
|
+
if (!activeTask)
|
|
44015
|
+
return;
|
|
44016
|
+
activeTask.runner.abort();
|
|
44017
|
+
},
|
|
43715
44018
|
requestCompaction(strategy) {
|
|
43716
44019
|
if (!activeTask)
|
|
43717
44020
|
return false;
|
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.
|