open-agents-ai 0.104.25 → 0.104.26

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.
Files changed (2) hide show
  1. package/dist/index.js +152 -170
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -38243,7 +38243,7 @@ async function handleSlashCommand(input, ctx) {
38243
38243
  renderWarning("No active expose gateway.");
38244
38244
  return "handled";
38245
38245
  }
38246
- await showExposeDashboard(gateway, ctx.rl);
38246
+ await showExposeDashboard(gateway, ctx.rl, ctx);
38247
38247
  return "handled";
38248
38248
  }
38249
38249
  if (arg === "config") {
@@ -38304,9 +38304,13 @@ async function handleSlashCommand(input, ctx) {
38304
38304
  try {
38305
38305
  const tunnelUrl = await ctx.exposeStart(kindOrUrl, exposeAuthKey, exposeTransport, exposeFullAccess);
38306
38306
  if (tunnelUrl) {
38307
- const info = ctx.getExposeInfo?.();
38308
- if (info) {
38309
- process.stdout.write("\n" + info.stats + "\n\n");
38307
+ const gw = ctx.getExposeGateway?.();
38308
+ if (gw) {
38309
+ await showExposeDashboard(gw, ctx.rl, ctx);
38310
+ } else {
38311
+ const info = ctx.getExposeInfo?.();
38312
+ if (info)
38313
+ process.stdout.write("\n" + info.stats + "\n\n");
38310
38314
  }
38311
38315
  } else {
38312
38316
  renderError("Failed to start expose gateway.");
@@ -40207,74 +40211,62 @@ function getLocalSystemMetrics() {
40207
40211
  }
40208
40212
  return result;
40209
40213
  }
40210
- async function showExposeDashboard(gateway, rl) {
40211
- const stdin = process.stdin;
40212
- const stdout = process.stdout;
40213
- if (!stdin.isTTY) {
40214
+ async function showExposeDashboard(gateway, rl, ctx) {
40215
+ if (!process.stdin.isTTY) {
40214
40216
  if (typeof gateway.formatStats === "function") {
40215
- stdout.write("\n" + gateway.formatStats() + "\n\n");
40217
+ process.stdout.write("\n" + gateway.formatStats() + "\n\n");
40216
40218
  }
40217
40219
  return;
40218
40220
  }
40219
- let lastRenderedLines = 0;
40220
- let refreshTimer = null;
40221
- const hadRawMode = stdin.isRaw;
40222
- let sysMetrics = getLocalSystemMetrics();
40223
- const savedListeners = rl ? stdin.listeners("data").slice() : [];
40224
- if (rl) {
40225
- rl.pause();
40226
- for (const fn of savedListeners)
40227
- stdin.removeListener("data", fn);
40228
- }
40229
- stdin.setRawMode(true);
40230
- stdin.resume();
40231
- stdout.write("\x1B[?25l");
40232
- function buildDashboard() {
40221
+ const peerLedColor = (peerIdx) => {
40222
+ const stops = [51, 81, 111, 141, 171, 201];
40223
+ const code = stops[Math.min(peerIdx, stops.length - 1)] ?? 51;
40224
+ return `\x1B[38;5;${code}m\u25CF\x1B[0m`;
40225
+ };
40226
+ while (true) {
40233
40227
  const s = gateway.stats ?? gateway._stats;
40234
- if (!s)
40235
- return [" No stats available.", "", " Press Escape or 'q' to close."];
40236
- const lines = [];
40237
40228
  const now = Date.now();
40238
- const uptime = fmtUptime(now - (s.startedAt || now));
40239
- const statusColor = s.status === "active" ? c2.green : s.status === "error" ? c2.red : c2.yellow;
40240
- lines.push("");
40241
- lines.push(` ${c2.bold("Expose Dashboard")} ${statusColor("\u25CF")} ${statusColor(s.status || "unknown")} ${c2.dim(`uptime ${uptime}`)}`);
40242
- lines.push(` ${c2.dim("\u2500".repeat(60))}`);
40243
- const activeConns = s.activeConnections || 0;
40244
- const totalReqs = s.totalRequests || 0;
40245
- const errors = s.errors || 0;
40246
- const tokIn = s.totalTokensIn || 0;
40247
- const tokOut = s.totalTokensOut || 0;
40248
- lines.push(` ${c2.cyan("Requests")} ${c2.bold(String(totalReqs))} ${c2.dim("|")} ${c2.cyan("Active")} ${activeConns > 0 ? c2.green(c2.bold(String(activeConns))) : c2.dim("0")} ${c2.dim("|")} ${c2.cyan("Errors")} ${errors > 0 ? c2.red(String(errors)) : c2.dim("0")}`);
40249
- lines.push(` ${c2.cyan("Tokens")} ${c2.dim("in:")}${c2.bold(fmtDashTokens(tokIn))} ${c2.dim("out:")}${c2.bold(fmtDashTokens(tokOut))} ${c2.dim("total:")}${fmtDashTokens(tokIn + tokOut)}`);
40250
- if (s.budgetTokensTotal > 0) {
40251
- const pct = Math.round(s.budgetTokensRemaining / s.budgetTokensTotal * 100);
40252
- const budgetColor = pct > 50 ? c2.green : pct > 20 ? c2.yellow : c2.red;
40253
- const barLen = 20;
40254
- const filledLen = Math.round(pct / 100 * barLen);
40255
- const bar = budgetColor("\u2588".repeat(filledLen)) + c2.dim("\u2591".repeat(barLen - filledLen));
40256
- lines.push(` ${c2.cyan("Budget")} ${bar} ${budgetColor(`${pct}%`)} ${c2.dim(`(${fmtDashTokens(s.budgetTokensRemaining)}/${fmtDashTokens(s.budgetTokensTotal)})`)}`);
40257
- }
40258
- const sys = sysMetrics;
40259
- lines.push("");
40260
- lines.push(` ${c2.bold("System")}`);
40261
- const cpuColor = sys.cpuUtil > 80 ? c2.red : sys.cpuUtil > 50 ? c2.yellow : c2.green;
40262
- const memColor = sys.memUtil > 80 ? c2.red : sys.memUtil > 50 ? c2.yellow : c2.green;
40263
- lines.push(` ${c2.cyan("CPU")} ${cpuColor(sys.cpuUtil + "%")} ${c2.dim(`(${sys.cpuCores} cores, ${sys.cpuModel.slice(0, 40)})`)}`);
40264
- lines.push(` ${c2.cyan("RAM")} ${memColor(sys.memUtil + "%")} ${c2.dim(`(${sys.memUsedGB}/${sys.memTotalGB} GB)`)}`);
40265
- if (sys.gpuAvailable) {
40266
- const gpuColor = sys.gpuUtil > 80 ? c2.red : sys.gpuUtil > 50 ? c2.yellow : c2.green;
40267
- const vramColor = sys.vramUtil > 80 ? c2.red : sys.vramUtil > 50 ? c2.yellow : c2.green;
40268
- lines.push(` ${c2.cyan("GPU")} ${gpuColor(sys.gpuUtil + "%")} ${c2.dim(`(${sys.gpuName.trim()})`)}`);
40269
- lines.push(` ${c2.cyan("VRAM")} ${vramColor(sys.vramUtil + "%")} ${c2.dim(`(${sys.vramUsedMB}/${sys.vramTotalMB} MB)`)}`);
40270
- }
40271
- const modelEntries = s.modelUsage ? Array.from(s.modelUsage.entries()).filter(([m]) => !DASH_INTERNAL.has(m)) : [];
40229
+ const uptime = s ? fmtUptime(now - (s.startedAt || now)) : "0s";
40230
+ const statusColor = !s ? c2.dim : s.status === "active" ? c2.green : s.status === "error" ? c2.red : c2.yellow;
40231
+ const statusLabel = s?.status || "unknown";
40232
+ const items = [];
40233
+ const skipKeys = [];
40234
+ items.push({ key: "hdr_status", label: selectColors.dim(`\u2500\u2500\u2500 ${statusColor("\u25CF")} ${statusLabel} uptime ${uptime} \u2500\u2500\u2500`), kind: "header" });
40235
+ skipKeys.push("hdr_status");
40236
+ const isTunnel = !!gateway.tunnelUrl;
40237
+ const isP2P = !!gateway.peerId;
40238
+ const endpointId = isTunnel ? gateway.tunnelUrl : isP2P ? gateway.peerId : null;
40239
+ const authKey = gateway.authKey ?? "";
40240
+ if (endpointId) {
40241
+ const endpointCmd = `/endpoint ${endpointId} --auth ${authKey}`;
40242
+ items.push({ key: "endpoint", label: "\u{1F517} Endpoint", detail: endpointCmd, kind: "action" });
40243
+ }
40244
+ if (s) {
40245
+ const totalReqs = s.totalRequests || 0;
40246
+ const activeConns = s.activeConnections || 0;
40247
+ const tokIn = s.totalTokensIn || 0;
40248
+ const tokOut = s.totalTokensOut || 0;
40249
+ const errors = s.errors || 0;
40250
+ const statsLine = `Req: ${totalReqs} Active: ${activeConns} Err: ${errors} Tok \u2191${fmtDashTokens(tokIn)} \u2193${fmtDashTokens(tokOut)}`;
40251
+ items.push({ key: "info_stats", label: c2.dim(statsLine), kind: "info" });
40252
+ skipKeys.push("info_stats");
40253
+ if (s.budgetTokensTotal > 0) {
40254
+ const pct = Math.round(s.budgetTokensRemaining / s.budgetTokensTotal * 100);
40255
+ const budgetColor = pct > 50 ? c2.green : pct > 20 ? c2.yellow : c2.red;
40256
+ const barLen = 20;
40257
+ const filledLen = Math.round(pct / 100 * barLen);
40258
+ const bar = budgetColor("\u2588".repeat(filledLen)) + c2.dim("\u2591".repeat(barLen - filledLen));
40259
+ items.push({ key: "info_budget", label: ` Budget ${bar} ${budgetColor(`${pct}%`)}`, kind: "info" });
40260
+ skipKeys.push("info_budget");
40261
+ }
40262
+ }
40263
+ const modelEntries = s?.modelUsage ? Array.from(s.modelUsage.entries()).filter(([m]) => !DASH_INTERNAL.has(m)) : [];
40272
40264
  if (modelEntries.length > 0) {
40273
- lines.push("");
40274
- lines.push(` ${c2.bold("Models")}`);
40265
+ items.push({ key: "hdr_models", label: selectColors.dim(`\u2500\u2500\u2500 Models (${modelEntries.length}) \u2500\u2500\u2500`), kind: "header" });
40266
+ skipKeys.push("hdr_models");
40275
40267
  for (const [model, count] of modelEntries) {
40276
40268
  let mIn = 0, mOut = 0;
40277
- if (s.users) {
40269
+ if (s?.users) {
40278
40270
  for (const user of s.users.values()) {
40279
40271
  const mm = user.models?.get?.(model);
40280
40272
  if (mm) {
@@ -40283,123 +40275,113 @@ async function showExposeDashboard(gateway, rl) {
40283
40275
  }
40284
40276
  }
40285
40277
  }
40286
- const bar = count > 0 ? c2.green("\u25AE".repeat(Math.min(count, 20))) : "";
40287
- lines.push(` ${c2.cyan(model.padEnd(35))} ${c2.bold(String(count).padStart(4))} req ${c2.dim(`in:${fmtDashTokens(mIn).padStart(6)} out:${fmtDashTokens(mOut).padStart(6)}`)} ${bar}`);
40278
+ const bar = count > 0 ? c2.green("\u25AE".repeat(Math.min(count, 10))) : "";
40279
+ items.push({
40280
+ key: `model_${model}`,
40281
+ label: `${model}`,
40282
+ detail: `${count} req \u2191${fmtDashTokens(mIn)} \u2193${fmtDashTokens(mOut)} ${bar}`,
40283
+ kind: "model"
40284
+ });
40288
40285
  }
40289
40286
  }
40290
- const users = s.users ? Array.from(s.users.values()) : [];
40287
+ const users = s?.users ? Array.from(s.users.values()) : [];
40291
40288
  if (users.length > 0) {
40292
- lines.push("");
40293
- lines.push(` ${c2.bold("Connected Peers")} (${users.length})`);
40294
- for (const user of users) {
40289
+ items.push({ key: "hdr_peers", label: selectColors.dim(`\u2500\u2500\u2500 Peers (${users.length}) \u2500\u2500\u2500`), kind: "header" });
40290
+ skipKeys.push("hdr_peers");
40291
+ for (let i = 0; i < users.length; i++) {
40292
+ const user = users[i];
40295
40293
  const ageSec = Math.floor((now - (user.firstSeen || now)) / 1e3);
40296
- const ageStr = ageSec < 60 ? `${ageSec}s` : ageSec < 3600 ? `${Math.floor(ageSec / 60)}m` : `${Math.floor(ageSec / 3600)}h${Math.floor(ageSec % 3600 / 60)}m`;
40294
+ const ageStr = ageSec < 60 ? `${ageSec}s` : ageSec < 3600 ? `${Math.floor(ageSec / 60)}m` : `${Math.floor(ageSec / 3600)}h`;
40297
40295
  const lastSec = Math.floor((now - (user.lastSeen || now)) / 1e3);
40298
40296
  const lastStr = lastSec < 5 ? c2.green("now") : lastSec < 60 ? `${lastSec}s ago` : `${Math.floor(lastSec / 60)}m ago`;
40299
- const activeStr = (user.activeRequests || 0) > 0 ? c2.green(` \u25CF ${user.activeRequests} active`) : "";
40300
- lines.push(` ${c2.bold(user.ip || "unknown")}${activeStr}`);
40301
- lines.push(` ${c2.dim("session")} ${ageStr} ${c2.dim("last")} ${lastStr} ${c2.dim("reqs")} ${user.requests || 0} ${c2.dim("tok")} ${c2.cyan("in:")}${fmtDashTokens(user.tokensIn || 0)} ${c2.cyan("out:")}${fmtDashTokens(user.tokensOut || 0)}`);
40302
- const userModels = user.models ? Array.from(user.models.entries()).filter(([m]) => !DASH_INTERNAL.has(m)) : [];
40303
- for (const [m, mm] of userModels) {
40304
- lines.push(` ${c2.dim("\u2514")} ${c2.cyan(m)} ${c2.dim(`${mm.requests || 0}req ${fmtDashTokens((mm.tokensIn || 0) + (mm.tokensOut || 0))}tok`)}`);
40305
- }
40306
- }
40307
- }
40308
- lines.push("");
40309
- lines.push(` ${c2.dim("Press Escape or 'q' to close \u2502 Live \u25CF updates every 2s")}`);
40310
- lines.push("");
40311
- return lines;
40312
- }
40313
- function render() {
40314
- sysMetrics = getLocalSystemMetrics();
40315
- const lines = buildDashboard();
40316
- if (lastRenderedLines > 0) {
40317
- stdout.write(`\x1B[${lastRenderedLines}A`);
40318
- for (let i = 0; i < lastRenderedLines; i++) {
40319
- stdout.write("\x1B[2K\n");
40320
- }
40321
- stdout.write(`\x1B[${lastRenderedLines}A`);
40322
- }
40323
- stdout.write(lines.join("\n") + "\n");
40324
- lastRenderedLines = lines.length;
40325
- }
40326
- function cleanup() {
40327
- if (refreshTimer) {
40328
- clearInterval(refreshTimer);
40329
- refreshTimer = null;
40330
- }
40331
- gateway.removeListener?.("stats", onStats);
40332
- if (lastRenderedLines > 0) {
40333
- stdout.write(`\x1B[${lastRenderedLines}A`);
40334
- for (let i = 0; i < lastRenderedLines; i++) {
40335
- stdout.write("\x1B[2K\n");
40297
+ const activeFlag = (user.activeRequests || 0) > 0 ? ` ${c2.green(`\u25CF ${user.activeRequests} active`)}` : "";
40298
+ const led = peerLedColor(i);
40299
+ const peerTok = `\u2191${fmtDashTokens(user.tokensIn || 0)} \u2193${fmtDashTokens(user.tokensOut || 0)}`;
40300
+ items.push({
40301
+ key: `peer_${user.ip || i}`,
40302
+ label: `${led} ${user.ip || "unknown"}${activeFlag}`,
40303
+ detail: `${user.requests || 0}req ${peerTok} ${c2.dim(ageStr)} last: ${lastStr}`,
40304
+ kind: "peer"
40305
+ });
40336
40306
  }
40337
- stdout.write(`\x1B[${lastRenderedLines}A`);
40338
- }
40339
- stdout.write("\x1B[?25h");
40340
- stdin.setRawMode(hadRawMode ?? false);
40341
- stdin.removeListener("data", onKey);
40342
- if (rl) {
40343
- for (const fn of savedListeners)
40344
- stdin.on("data", fn);
40345
- rl.resume();
40346
- }
40347
- }
40348
- function onStats() {
40349
- render();
40350
- }
40351
- function onKey(chunk) {
40352
- const key = chunk.toString();
40353
- if (key === "\x1B" || key === "q" || key === "Q") {
40354
- cleanup();
40355
- return;
40356
- }
40357
- if (key === "") {
40358
- cleanup();
40359
- return;
40360
40307
  }
40361
- }
40362
- if (typeof gateway.on === "function") {
40363
- gateway.on("stats", onStats);
40364
- }
40365
- refreshTimer = setInterval(render, 2e3);
40366
- render();
40367
- stdin.on("data", onKey);
40368
- return new Promise((resolve32) => {
40369
- const origCleanup = cleanup;
40370
- function wrappedCleanup() {
40371
- origCleanup();
40372
- resolve32();
40373
- }
40374
- stdin.removeListener("data", onKey);
40375
- function onKeyWrapped(chunk) {
40376
- const key = chunk.toString();
40377
- if (key === "\x1B" || key === "q" || key === "Q" || key === "") {
40378
- if (refreshTimer) {
40379
- clearInterval(refreshTimer);
40380
- refreshTimer = null;
40308
+ const sys = getLocalSystemMetrics();
40309
+ items.push({ key: "hdr_system", label: selectColors.dim("\u2500\u2500\u2500 System \u2500\u2500\u2500"), kind: "header" });
40310
+ skipKeys.push("hdr_system");
40311
+ const cpuColor = sys.cpuUtil > 80 ? c2.red : sys.cpuUtil > 50 ? c2.yellow : c2.green;
40312
+ const memColor = sys.memUtil > 80 ? c2.red : sys.memUtil > 50 ? c2.yellow : c2.green;
40313
+ let sysLine = `CPU ${cpuColor(sys.cpuUtil + "%")} (${sys.cpuCores}c) RAM ${memColor(sys.memUtil + "%")} (${sys.memUsedGB}/${sys.memTotalGB}GB)`;
40314
+ if (sys.gpuAvailable) {
40315
+ const gpuColor = sys.gpuUtil > 80 ? c2.red : sys.gpuUtil > 50 ? c2.yellow : c2.green;
40316
+ const vramColor = sys.vramUtil > 80 ? c2.red : sys.vramUtil > 50 ? c2.yellow : c2.green;
40317
+ sysLine += ` GPU ${gpuColor(sys.gpuUtil + "%")} VRAM ${vramColor(sys.vramUtil + "%")} (${sys.vramUsedMB}/${sys.vramTotalMB}MB)`;
40318
+ }
40319
+ items.push({ key: "info_system", label: c2.dim(sysLine), kind: "info" });
40320
+ skipKeys.push("info_system");
40321
+ items.push({ key: "hdr_actions", label: selectColors.dim("\u2500\u2500\u2500 Actions \u2500\u2500\u2500"), kind: "header" });
40322
+ skipKeys.push("hdr_actions");
40323
+ items.push({ key: "refresh", label: "\u{1F504} Refresh", detail: "Rebuild dashboard with latest data", kind: "action" });
40324
+ items.push({ key: "stop", label: "\u{1F6D1} Stop Gateway", detail: "Shut down the expose gateway", kind: "action" });
40325
+ const result = await tuiSelect({
40326
+ items,
40327
+ title: "Expose Dashboard",
40328
+ rl,
40329
+ skipKeys,
40330
+ availableRows: ctx?.availableContentRows?.()
40331
+ });
40332
+ if (!result.confirmed || !result.key)
40333
+ break;
40334
+ switch (result.key) {
40335
+ case "endpoint": {
40336
+ const id = isTunnel ? gateway.tunnelUrl : gateway.peerId;
40337
+ const cmd = `/endpoint ${id} --auth ${authKey}`;
40338
+ if (process.stdout.isTTY && id) {
40339
+ const b64 = Buffer.from(cmd).toString("base64");
40340
+ process.stdout.write(`\x1B]52;c;${b64}\x07`);
40381
40341
  }
40382
- gateway.removeListener?.("stats", onStats);
40383
- if (lastRenderedLines > 0) {
40384
- stdout.write(`\x1B[${lastRenderedLines}A`);
40385
- for (let i = 0; i < lastRenderedLines; i++) {
40386
- stdout.write("\x1B[2K\n");
40342
+ process.stdout.write(`
40343
+ ${c2.bold("Endpoint command")} ${c2.dim("(copied to clipboard)")}
40344
+ ${c2.cyan(cmd)}
40345
+
40346
+ `);
40347
+ await new Promise((resolve32) => {
40348
+ const onData = () => {
40349
+ process.stdin.removeListener("data", onData);
40350
+ resolve32();
40351
+ };
40352
+ process.stdin.once("data", onData);
40353
+ });
40354
+ continue;
40355
+ }
40356
+ case "refresh":
40357
+ continue;
40358
+ // Loop rebuilds items with fresh data
40359
+ case "stop":
40360
+ await ctx?.exposeStop?.();
40361
+ renderInfo("Expose gateway stopped.");
40362
+ return;
40363
+ default:
40364
+ if (result.key.startsWith("peer_")) {
40365
+ const peerId = result.key.slice(5);
40366
+ const user = users.find((u) => (u.ip || String(users.indexOf(u))) === peerId);
40367
+ if (user) {
40368
+ const userModels = user.models ? Array.from(user.models.entries()).filter(([m]) => !DASH_INTERNAL.has(m)) : [];
40369
+ if (userModels.length > 0) {
40370
+ process.stdout.write(`
40371
+ ${c2.bold(user.ip || "unknown")} \u2014 model usage:
40372
+ `);
40373
+ for (const [m, mm] of userModels) {
40374
+ process.stdout.write(` ${c2.cyan(m)} ${c2.dim(`${mm.requests}req \u2191${fmtDashTokens(mm.tokensIn)} \u2193${fmtDashTokens(mm.tokensOut)}`)}
40375
+ `);
40376
+ }
40377
+ process.stdout.write("\n");
40378
+ }
40387
40379
  }
40388
- stdout.write(`\x1B[${lastRenderedLines}A`);
40389
- }
40390
- stdout.write("\x1B[?25h");
40391
- stdin.setRawMode(hadRawMode ?? false);
40392
- stdin.removeListener("data", onKeyWrapped);
40393
- if (rl) {
40394
- for (const fn of savedListeners)
40395
- stdin.on("data", fn);
40396
- rl.resume();
40380
+ continue;
40397
40381
  }
40398
- resolve32();
40399
- }
40382
+ continue;
40400
40383
  }
40401
- stdin.on("data", onKeyWrapped);
40402
- });
40384
+ }
40403
40385
  }
40404
40386
  var DASH_INTERNAL;
40405
40387
  var init_commands = __esm({
@@ -48380,8 +48362,8 @@ var init_status_bar = __esm({
48380
48362
  const compactOrder = [];
48381
48363
  let modelSectionIdx = -1;
48382
48364
  let versionSectionIdx = -1;
48383
- if (this._exposeFlashOn) {
48384
- const ledColor = this._aliveLedColor();
48365
+ if (this._expose) {
48366
+ const ledColor = this._exposeFlashOn ? this._aliveLedColor() : 240;
48385
48367
  const dot = `\x1B[38;5;${ledColor}m\u2B24\x1B[0m`;
48386
48368
  sections.push({ expanded: dot, compact: dot, expandedW: 1, compactW: 1, empty: false });
48387
48369
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.104.25",
3
+ "version": "0.104.26",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",