open-agents-ai 0.187.302 → 0.187.304

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 +212 -31
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -288212,7 +288212,8 @@ function matchRow(item, focused, isActive) {
288212
288212
  return ` ${marker} ${label}${detail}`;
288213
288213
  }
288214
288214
  function tuiSelect(opts) {
288215
- const { items, title, rl } = opts;
288215
+ const { items, rl } = opts;
288216
+ let currentTitle = opts.title;
288216
288217
  const renderRow = opts.renderRow ?? defaultRenderRow;
288217
288218
  const activeKey = opts.activeKey ?? null;
288218
288219
  const skipSet = new Set(opts.skipKeys ?? []);
@@ -288309,9 +288310,9 @@ function tuiSelect(opts) {
288309
288310
  lines.push(`
288310
288311
  ${selectColors.cyan("←")} ${trail}`);
288311
288312
  }
288312
- if (title) {
288313
+ if (currentTitle) {
288313
288314
  if (!hasBreadcrumbs) lines.push("");
288314
- lines.push(` ${selectColors.bold(title)}`);
288315
+ lines.push(` ${selectColors.bold(currentTitle)}`);
288315
288316
  }
288316
288317
  if (filter2) {
288317
288318
  const count = matchSet.size;
@@ -288383,7 +288384,12 @@ ${tuiBgSeq()}`);
288383
288384
  overlayWrite(tuiBgSeq() + output + "\x1B[K");
288384
288385
  lastRenderedLines = lines.length;
288385
288386
  }
288387
+ let externalCleanup = null;
288386
288388
  function cleanup() {
288389
+ try {
288390
+ externalCleanup?.();
288391
+ } catch {
288392
+ }
288387
288393
  stdin.removeListener("data", onData);
288388
288394
  process.stdout.removeListener("resize", onResize);
288389
288395
  overlayWrite("\x1B[?1003l\x1B[?1002l\x1B[?1000l\x1B[?1006l\x1B[?1049l\x1B[?25h");
@@ -288399,6 +288405,16 @@ ${tuiBgSeq()}`);
288399
288405
  rl.prompt?.(false);
288400
288406
  }
288401
288407
  }
288408
+ const updateItem = (index, updates) => {
288409
+ if (index >= 0 && index < items.length) {
288410
+ Object.assign(items[index], updates);
288411
+ render2();
288412
+ }
288413
+ };
288414
+ const setTitle = (title) => {
288415
+ currentTitle = title;
288416
+ render2();
288417
+ };
288402
288418
  function onData(chunk) {
288403
288419
  let seq = chunk.toString("utf8");
288404
288420
  const mouseRe = /\x1B\[<(\d+);(\d+);(\d+)([Mm])/g;
@@ -288626,10 +288642,7 @@ ${tuiBgSeq()}`);
288626
288642
  },
288627
288643
  getInput: (prompt, prefill) => getInputFromUser(prompt, prefill),
288628
288644
  render: () => render2(),
288629
- updateItem: (index, updates) => {
288630
- Object.assign(items[index], updates);
288631
- render2();
288632
- }
288645
+ updateItem
288633
288646
  });
288634
288647
  if (consumed) return;
288635
288648
  }
@@ -288765,6 +288778,20 @@ ${tuiBgSeq()}`);
288765
288778
  process.stdout.on("resize", onResize);
288766
288779
  stdin.on("data", onData);
288767
288780
  render2();
288781
+ if (opts.onInit) {
288782
+ const maybeCleanup = opts.onInit({
288783
+ render: () => render2(),
288784
+ updateItem,
288785
+ setTitle,
288786
+ resolve: (result) => {
288787
+ cleanup();
288788
+ resolve40(result);
288789
+ }
288790
+ });
288791
+ if (typeof maybeCleanup === "function") {
288792
+ externalCleanup = maybeCleanup;
288793
+ }
288794
+ }
288768
288795
  });
288769
288796
  }
288770
288797
  var isTTY2, selectColors;
@@ -297250,6 +297277,78 @@ async function handleSlashCommand(input, ctx3) {
297250
297277
  await showColorMenu(ctx3);
297251
297278
  return "handled";
297252
297279
  }
297280
+ case "apikey": {
297281
+ registerSlashCommand({
297282
+ name: "apikey",
297283
+ description: "Show/copy/rotate API key (show|copy|new)",
297284
+ handler: async ({ arg: arg2 }) => {
297285
+ const action = (arg2 || "show").trim().toLowerCase();
297286
+ let key = process.env["OA_API_KEY"] || "";
297287
+ if (!key) {
297288
+ try {
297289
+ const { homedir: homedir41 } = await import("node:os");
297290
+ const { readFileSync: readFileSync68, existsSync: existsSync87 } = await import("node:fs");
297291
+ const { join: join106 } = await import("node:path");
297292
+ const p2 = join106(homedir41(), ".open-agents", "api.key");
297293
+ if (existsSync87(p2)) key = readFileSync68(p2, "utf8").trim();
297294
+ } catch {
297295
+ }
297296
+ }
297297
+ if (action === "show") {
297298
+ if (!key) {
297299
+ renderWarning2("No API key set. Use /access any to generate one.");
297300
+ return "handled";
297301
+ }
297302
+ renderInfo2(`API key: ${c3.bold(c3.yellow(key))}`);
297303
+ renderInfo2("Use Authorization: Bearer <key> or paste in the Web UI key modal.");
297304
+ return "handled";
297305
+ }
297306
+ if (action === "copy") {
297307
+ if (!key) {
297308
+ renderWarning2("No API key set. Use /access any to generate one.");
297309
+ return "handled";
297310
+ }
297311
+ try {
297312
+ const { spawnSync: spawnSync6 } = await import("node:child_process");
297313
+ const tryCmd = (cmd2, args2) => spawnSync6(cmd2, args2, { input: key, encoding: "utf8" });
297314
+ let ok2 = false;
297315
+ if (process.platform === "darwin") {
297316
+ ok2 = tryCmd("pbcopy", []).status === 0;
297317
+ } else if (process.platform === "win32") {
297318
+ ok2 = tryCmd("clip", []).status === 0;
297319
+ } else {
297320
+ ok2 = tryCmd("wl-copy", []).status === 0 || tryCmd("xclip", ["-selection", "clipboard"]).status === 0;
297321
+ }
297322
+ renderInfo2(ok2 ? "Copied API key to clipboard." : "Copy failed — printed above, select to copy.");
297323
+ } catch {
297324
+ renderInfo2("Copy failed — printed above, select to copy.");
297325
+ }
297326
+ return "handled";
297327
+ }
297328
+ if (action === "new") {
297329
+ try {
297330
+ const { randomBytes: randomBytes23 } = await import("node:crypto");
297331
+ const { homedir: homedir41 } = await import("node:os");
297332
+ const { mkdirSync: mkdirSync55, writeFileSync: writeFileSync49 } = await import("node:fs");
297333
+ const { join: join106 } = await import("node:path");
297334
+ const newKey = randomBytes23(16).toString("hex");
297335
+ process.env["OA_API_KEY"] = newKey;
297336
+ const dir = join106(homedir41(), ".open-agents");
297337
+ mkdirSync55(dir, { recursive: true });
297338
+ writeFileSync49(join106(dir, "api.key"), newKey + "\n", "utf8");
297339
+ renderInfo2(`New API key: ${c3.bold(c3.yellow(newKey))}`);
297340
+ renderInfo2("Restart the daemon to apply if needed. Use /access any to restart quickly.");
297341
+ } catch (e2) {
297342
+ renderError2(`Failed to rotate key: ${e2 instanceof Error ? e2.message : String(e2)}`);
297343
+ }
297344
+ return "handled";
297345
+ }
297346
+ renderInfo2("Usage: /apikey [show|copy|new]");
297347
+ return "handled";
297348
+ }
297349
+ });
297350
+ return "handled";
297351
+ }
297253
297352
  case "sessions":
297254
297353
  case "session": {
297255
297354
  await showSessionsMenu(ctx3);
@@ -297413,6 +297512,15 @@ async function handleSlashCommand(input, ctx3) {
297413
297512
  process.env["OA_API_KEY"] = apiKey;
297414
297513
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
297415
297514
  renderInfo2("Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client.");
297515
+ try {
297516
+ const { homedir: homedir41 } = await import("node:os");
297517
+ const { mkdirSync: mkdirSync55, writeFileSync: writeFileSync49 } = await import("node:fs");
297518
+ const { join: join106 } = await import("node:path");
297519
+ const dir = join106(homedir41(), ".open-agents");
297520
+ mkdirSync55(dir, { recursive: true });
297521
+ writeFileSync49(join106(dir, "api.key"), apiKey + "\n", "utf8");
297522
+ } catch {
297523
+ }
297416
297524
  }
297417
297525
  if (hasLocal2) {
297418
297526
  ctx4.saveLocalSettings({ oaAccess: val2 });
@@ -297449,6 +297557,15 @@ async function handleSlashCommand(input, ctx3) {
297449
297557
  process.env["OA_API_KEY"] = apiKey;
297450
297558
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
297451
297559
  renderInfo2("Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client.");
297560
+ try {
297561
+ const { homedir: homedir41 } = await import("node:os");
297562
+ const { mkdirSync: mkdirSync55, writeFileSync: writeFileSync49 } = await import("node:fs");
297563
+ const { join: join106 } = await import("node:path");
297564
+ const dir = join106(homedir41(), ".open-agents");
297565
+ mkdirSync55(dir, { recursive: true });
297566
+ writeFileSync49(join106(dir, "api.key"), apiKey + "\n", "utf8");
297567
+ } catch {
297568
+ }
297452
297569
  }
297453
297570
  if (hasLocal) {
297454
297571
  ctx3.saveLocalSettings({ oaAccess: val });
@@ -320357,13 +320474,24 @@ function headers() {
320357
320474
  return h;
320358
320475
  }
320359
320476
 
320477
+ let seenVersion = '';
320478
+
320360
320479
  // Health check
320361
320480
  async function checkHealth() {
320362
320481
  try {
320363
- const r = await fetch('/health');
320482
+ const r = await fetch('/health', { cache: 'no-store' });
320364
320483
  const d = await r.json();
320365
320484
  statusEl.textContent = 'connected (' + d.version + ')';
320366
320485
  statusEl.className = 'status live';
320486
+ // Detect daemon version bump and refresh UI to new frontend bundle/state
320487
+ if (!seenVersion) {
320488
+ seenVersion = d.version || '';
320489
+ } else if (d.version && seenVersion && d.version !== seenVersion) {
320490
+ // Force a hard reload to pick up any UI changes
320491
+ try { localStorage.setItem('oa-last-version', d.version); } catch {}
320492
+ location.reload();
320493
+ return;
320494
+ }
320367
320495
  } catch {
320368
320496
  statusEl.textContent = 'disconnected';
320369
320497
  statusEl.className = 'status';
@@ -322476,6 +322604,7 @@ async function doUpdate() {
322476
322604
  btn.style.background = '#1a3a1a';
322477
322605
  btn.style.borderColor = '#4ec94e';
322478
322606
  btn.style.color = '#4ec94e';
322607
+ try { seenVersion = newVersion || seenVersion; } catch {}
322479
322608
 
322480
322609
  // Flash status bar
322481
322610
  const statusEl = document.getElementById('status');
@@ -322497,6 +322626,20 @@ async function doUpdate() {
322497
322626
  }, 6000);
322498
322627
  }
322499
322628
 
322629
+ // Poll for version bumps even if the update was triggered elsewhere (CLI)
322630
+ async function pollVersionBump() {
322631
+ try {
322632
+ const r = await fetch('/version', { cache: 'no-store' });
322633
+ const j = await r.json();
322634
+ const v = j.version || '';
322635
+ if (!seenVersion) seenVersion = v;
322636
+ if (v && seenVersion && v !== seenVersion) {
322637
+ try { localStorage.setItem('oa-last-version', v); } catch {}
322638
+ location.reload();
322639
+ }
322640
+ } catch {}
322641
+ }
322642
+
322500
322643
  // ═══════════════════════════════════════════════════════════════════
322501
322644
  // Live process dots — SSE subscription to /v1/events
322502
322645
  // ═══════════════════════════════════════════════════════════════════
@@ -323089,6 +323232,7 @@ updateAgentRunSelect(); // WO-TASK-02 — populate agent runs dropdown
323089
323232
  restoreChatSession(); // WO-CHAT-RESUME — rehydrate from server state
323090
323233
  setInterval(checkHealth, 30000);
323091
323234
  setInterval(pollMetrics, 10000);
323235
+ setInterval(pollVersionBump, 5000);
323092
323236
  input.focus();
323093
323237
  </script>
323094
323238
  </body>
@@ -326157,6 +326301,7 @@ async function handleV1Update(req2, res, requestId) {
326157
326301
  const fs4 = __require("node:fs");
326158
326302
  const nodeBin = process.execPath;
326159
326303
  const nodeDir = dirname29(nodeBin);
326304
+ const { execSync: es } = __require("node:child_process");
326160
326305
  const isWin2 = process.platform === "win32";
326161
326306
  let npmBin = "";
326162
326307
  for (const candidate of isWin2 ? [join99(nodeDir, "npm.cmd"), join99(nodeDir, "npm")] : [join99(nodeDir, "npm"), "/usr/local/bin/npm", "/usr/bin/npm"]) {
@@ -326171,12 +326316,40 @@ async function handleV1Update(req2, res, requestId) {
326171
326316
  fs4.mkdirSync(dir, { recursive: true });
326172
326317
  const logFd = fs4.openSync(logPath2, "w");
326173
326318
  const npmPrefix = dirname29(nodeDir);
326319
+ let globalBinDir = "";
326320
+ try {
326321
+ if (isWin2) {
326322
+ globalBinDir = es(`${npmBin} bin -g`, { encoding: "utf8", timeout: 5e3, stdio: "pipe" }).trim();
326323
+ } else {
326324
+ const npmCliCandidates = [
326325
+ join99(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
326326
+ join99(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
326327
+ ];
326328
+ let npmCli = "";
326329
+ for (const c7 of npmCliCandidates) {
326330
+ try {
326331
+ if (existsSync83(c7)) {
326332
+ npmCli = c7;
326333
+ break;
326334
+ }
326335
+ } catch {
326336
+ }
326337
+ }
326338
+ if (npmCli) {
326339
+ globalBinDir = es(`${JSON.stringify(nodeBin)} ${JSON.stringify(npmCli)} bin -g`, { encoding: "utf8", timeout: 5e3, stdio: "pipe" }).trim();
326340
+ } else {
326341
+ globalBinDir = es(`${npmBin} bin -g`, { encoding: "utf8", timeout: 5e3, stdio: "pipe" }).trim();
326342
+ }
326343
+ }
326344
+ } catch {
326345
+ }
326174
326346
  const cleanEnv = {};
326175
326347
  for (const [k, v] of Object.entries(process.env)) {
326176
326348
  if (k === "OA_DAEMON" || k === "OA_PORT") continue;
326177
326349
  if (typeof v === "string") cleanEnv[k] = v;
326178
326350
  }
326179
- cleanEnv.PATH = `${nodeDir}:${cleanEnv.PATH || ""}`;
326351
+ const pathParts = [globalBinDir, nodeDir, cleanEnv.PATH || ""].filter(Boolean);
326352
+ cleanEnv.PATH = pathParts.join(":");
326180
326353
  let child;
326181
326354
  if (isWin2) {
326182
326355
  child = spawn25(npmBin, ["install", "-g", pkgSpec, "--no-audit", "--no-fund", "--no-progress"], {
@@ -326223,13 +326396,22 @@ async function handleV1Update(req2, res, requestId) {
326223
326396
  stdio: "ignore"
326224
326397
  });
326225
326398
  follower.unref();
326399
+ let hasSystemdUnit = false;
326400
+ try {
326401
+ const out = es("systemctl --user is-active open-agents-daemon.service 2>/dev/null", { encoding: "utf8", timeout: 2e3, stdio: "pipe" }).trim();
326402
+ hasSystemdUnit = out === "active" || out === "activating";
326403
+ } catch {
326404
+ }
326405
+ let oaAbs = "oa";
326406
+ try {
326407
+ const which = es("command -v oa 2>/dev/null || which oa 2>/dev/null", { encoding: "utf8", timeout: 2e3, stdio: "pipe", env: cleanEnv }).trim();
326408
+ if (which) oaAbs = which;
326409
+ } catch {
326410
+ }
326226
326411
  const relaunchScript = [
326227
326412
  `while kill -0 ${installPid} 2>/dev/null; do sleep 1; done`,
326228
- // Short grace period before restart
326229
326413
  `sleep 1`,
326230
- // Use PATH to pick up the freshly installed oa; background and disown
326231
- `oa serve --quiet --daemon >/dev/null 2>&1 & disown`,
326232
- // Terminate current daemon so the new one can bind the port
326414
+ hasSystemdUnit ? `systemctl --user restart open-agents-daemon.service >/dev/null 2>&1 || true` : `${JSON.stringify(oaAbs)} serve --quiet --daemon >/dev/null 2>&1 & disown`,
326233
326415
  `kill -TERM ${process.pid} >/dev/null 2>&1 || true`
326234
326416
  ].join("; ");
326235
326417
  const relauncher = spawn25("bash", ["-c", relaunchScript], {
@@ -334436,19 +334618,6 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
334436
334618
  renderInfo2(`Last task: ${lastTask}${lastEntry.task && lastEntry.task.length > 80 ? "..." : ""}`);
334437
334619
  });
334438
334620
  let countdown = 10;
334439
- let selectDone = false;
334440
- const autoTimer = setInterval(() => {
334441
- if (selectDone) return;
334442
- countdown--;
334443
- if (countdown <= 0) {
334444
- selectDone = true;
334445
- clearInterval(autoTimer);
334446
- try {
334447
- rl.feed("\x1B");
334448
- } catch {
334449
- }
334450
- }
334451
- }, 1e3);
334452
334621
  const selectResult = await tuiSelect({
334453
334622
  items: [
334454
334623
  { key: "restore", label: `Restore previous context (auto in ${countdown}s)` },
@@ -334457,18 +334626,30 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
334457
334626
  activeKey: "restore",
334458
334627
  title: `Restore previous session?`,
334459
334628
  rl,
334460
- availableRows: statusBar.isActive ? statusBar.availableContentRows : void 0
334629
+ availableRows: statusBar.isActive ? statusBar.availableContentRows : void 0,
334630
+ onInit: ({ updateItem, resolve: resolve40 }) => {
334631
+ const timer = setInterval(() => {
334632
+ countdown -= 1;
334633
+ if (countdown >= 0) {
334634
+ updateItem(0, { label: `Restore previous context (auto in ${countdown}s)` });
334635
+ }
334636
+ if (countdown <= 0) {
334637
+ clearInterval(timer);
334638
+ resolve40({ confirmed: true, key: "restore", index: 0 });
334639
+ }
334640
+ }, 1e3);
334641
+ return () => clearInterval(timer);
334642
+ }
334461
334643
  });
334462
- clearInterval(autoTimer);
334463
- selectDone = true;
334464
- const doRestore = selectResult.confirmed ? selectResult.key === "restore" : countdown <= 0;
334644
+ const auto = countdown <= 0;
334645
+ const doRestore = selectResult.confirmed && selectResult.key === "restore";
334465
334646
  if (doRestore) {
334466
334647
  const prompt = buildContextRestorePrompt(repoRoot);
334467
334648
  if (prompt) {
334468
334649
  restoredSessionContext = prompt;
334469
334650
  const info = loadSessionContext(repoRoot);
334470
334651
  writeContent(() => renderInfo2(
334471
- countdown <= 0 ? `Context auto-restored from ${info?.entries.length ?? 0} session(s).` : `Context restored from ${info?.entries.length ?? 0} session(s).`
334652
+ auto ? `Context auto-restored from ${info?.entries.length ?? 0} session(s).` : `Context restored from ${info?.entries.length ?? 0} session(s).`
334472
334653
  ));
334473
334654
  } else {
334474
334655
  writeContent(() => renderInfo2("No context to restore. Starting fresh."));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.302",
3
+ "version": "0.187.304",
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",