open-agents-ai 0.187.364 → 0.187.366

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 +153 -52
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -544461,11 +544461,31 @@ async function handleSlashCommand(input, ctx3) {
544461
544461
  } else {
544462
544462
  ctx4.saveSettings({ oaAccess: val2 });
544463
544463
  }
544464
- const { stopDaemon: stopDaemon3, ensureDaemon: ensureDaemon3 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
544465
- stopDaemon3();
544466
- await new Promise((r2) => setTimeout(r2, 800));
544467
- const ok3 = await ensureDaemon3();
544468
- renderInfo2(ok3 ? "Daemon restarted to apply access policy." : "Failed to restart daemon.");
544464
+ const port2 = parseInt(process.env["OA_PORT"] || "11435", 10);
544465
+ let switched2 = false;
544466
+ try {
544467
+ const resp = await fetch(`http://127.0.0.1:${port2}/v1/admin/access`, {
544468
+ method: "POST",
544469
+ headers: { "Content-Type": "application/json" },
544470
+ body: JSON.stringify({ mode: val2 }),
544471
+ signal: AbortSignal.timeout(3e3)
544472
+ });
544473
+ switched2 = resp.ok;
544474
+ } catch {
544475
+ }
544476
+ if (switched2) {
544477
+ renderInfo2(`Access policy now '${val2}' (live, no restart). Persisted to ~/.open-agents/access.`);
544478
+ } else {
544479
+ const { stopDaemon: stopDaemon2, ensureDaemon: ensureDaemon2, isDaemonRunning: isDaemonRunning2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
544480
+ stopDaemon2();
544481
+ const deadline = Date.now() + 4e3;
544482
+ while (Date.now() < deadline) {
544483
+ if (!await isDaemonRunning2(port2)) break;
544484
+ await new Promise((r2) => setTimeout(r2, 200));
544485
+ }
544486
+ const ok2 = await ensureDaemon2();
544487
+ renderInfo2(ok2 ? "Daemon restarted to apply access policy." : "Failed to restart daemon.");
544488
+ }
544469
544489
  return "handled";
544470
544490
  }
544471
544491
  });
@@ -544506,11 +544526,36 @@ async function handleSlashCommand(input, ctx3) {
544506
544526
  } else {
544507
544527
  ctx3.saveSettings({ oaAccess: val });
544508
544528
  }
544509
- const { stopDaemon: stopDaemon2, ensureDaemon: ensureDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
544510
- stopDaemon2();
544511
- await new Promise((r2) => setTimeout(r2, 800));
544512
- const ok2 = await ensureDaemon2();
544513
- renderInfo2(ok2 ? "Daemon restarted to apply access policy." : "Failed to restart daemon.");
544529
+ const port = parseInt(process.env["OA_PORT"] || "11435", 10);
544530
+ let switched = false;
544531
+ try {
544532
+ const resp = await fetch(`http://127.0.0.1:${port}/v1/admin/access`, {
544533
+ method: "POST",
544534
+ headers: { "Content-Type": "application/json" },
544535
+ body: JSON.stringify({ mode: val }),
544536
+ signal: AbortSignal.timeout(3e3)
544537
+ });
544538
+ switched = resp.ok;
544539
+ if (!resp.ok) {
544540
+ const txt = await resp.text().catch(() => "");
544541
+ renderWarning2(`Live access switch HTTP ${resp.status}: ${txt.slice(0, 120)}`);
544542
+ }
544543
+ } catch (e2) {
544544
+ renderWarning2(`Live access switch failed (${e2 instanceof Error ? e2.message : String(e2)}); falling back to daemon restart.`);
544545
+ }
544546
+ if (switched) {
544547
+ renderInfo2(`Access policy now '${val}' (live, no restart). Persisted to ~/.open-agents/access.`);
544548
+ } else {
544549
+ const { stopDaemon: stopDaemon2, ensureDaemon: ensureDaemon2, isDaemonRunning: isDaemonRunning2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
544550
+ stopDaemon2();
544551
+ const deadline = Date.now() + 4e3;
544552
+ while (Date.now() < deadline) {
544553
+ if (!await isDaemonRunning2(port)) break;
544554
+ await new Promise((r2) => setTimeout(r2, 200));
544555
+ }
544556
+ const ok2 = await ensureDaemon2();
544557
+ renderInfo2(ok2 ? "Daemon restarted to apply access policy." : "Failed to restart daemon.");
544558
+ }
544514
544559
  return "handled";
544515
544560
  }
544516
544561
  case "host": {
@@ -568965,12 +569010,12 @@ async function loadScheduled() {
568965
569010
  const rows = tasks.map(t => {
568966
569011
  const enabled = !!t.enabled;
568967
569012
  const btn = enabled
568968
- ? '<button onclick="toggleScheduled('' + t.id + '',false)" style="background:#2a2a30;border:1px solid #5a2a2a;color:#b25f5f;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">disable</button>'
568969
- : '<button onclick="toggleScheduled('' + t.id + '',true)" style="background:#2a2a30;border:1px solid #2a3a2a;color:#4ec94e;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">enable</button>';
569013
+ ? '<button onclick="toggleScheduled(\\'' + t.id + '\\',false)" style="background:#2a2a30;border:1px solid #5a2a2a;color:#b25f5f;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">disable</button>'
569014
+ : '<button onclick="toggleScheduled(\\'' + t.id + '\\',true)" style="background:#2a2a30;border:1px solid #2a3a2a;color:#4ec94e;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">enable</button>';
568970
569015
  const color = enabled ? '#4ec94e' : '#5a2a2a';
568971
569016
  const procInfo = t.procs && t.procs.length ? (' (' + t.procs.length + ' proc)') : '';
568972
569017
  const up = t.procs && t.procs[0] && t.procs[0].uptime_s ? (' • up ' + Math.max(1, Math.round(t.procs[0].uptime_s/60)) + 'm') : '';
568973
- const killBtn = '<button onclick="killScheduledTask('' + t.id + '')" style="background:#2a2a30;border:1px solid #5a2a2a;color:#b25f5f;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">kill</button>';
569018
+ const killBtn = '<button onclick="killScheduledTask(\\'' + t.id + '\\')" style="background:#2a2a30;border:1px solid #5a2a2a;color:#b25f5f;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">kill</button>';
568974
569019
  const row = '<div style="background:#1e1e22;border-left:2px solid ' + color + ';padding:6px 10px;margin:4px 0;font-size:0.72rem">'
568975
569020
  + '<div style="color:#b0b0b0">' + (t.name || '(task)') + ' <span style="color:#555">' + (t.schedule || '') + '</span>' + procInfo + up + '</div>'
568976
569021
  + '<div style="color:#555;font-size:0.6rem">' + t.file + '#' + t.index + '</div>'
@@ -568990,14 +569035,14 @@ async function loadScheduled() {
568990
569035
  } catch {}
568991
569036
  }
568992
569037
 
568993
- (window as any).toggleScheduled = async function(id: string, enabled: boolean) {
569038
+ window.toggleScheduled = async function(id, enabled) {
568994
569039
  try {
568995
569040
  await fetch('/v1/scheduled/' + encodeURIComponent(id), { method:'POST', headers: headers(), body: JSON.stringify({enabled}) });
568996
569041
  loadScheduled();
568997
569042
  } catch {}
568998
569043
  }
568999
569044
 
569000
- (window as any).killScheduled = async function() {
569045
+ window.killScheduled = async function() {
569001
569046
  try {
569002
569047
  const r = await fetch('/v1/scheduled/kill', { method:'POST', headers: headers(), body: JSON.stringify({}) });
569003
569048
  const j = await r.json();
@@ -569008,18 +569053,15 @@ async function loadScheduled() {
569008
569053
  const rem = Array.isArray(j.procs_after) ? j.procs_after.length : 0;
569009
569054
  let msg = 'Killed ' + (kb + ka) + ' processes.';
569010
569055
  if (before && after) {
569011
- msg += '
569012
- GPU util: ' + before.gpu_pct + '% → ' + after.gpu_pct + '%, VRAM: ' + before.vram_used_gb + '/' + before.vram_total_gb + ' GB → ' + after.vram_used_gb + '/' + after.vram_total_gb + ' GB';
569056
+ msg += '\\nGPU util: ' + before.gpu_pct + '% → ' + after.gpu_pct + '%, VRAM: ' + before.vram_used_gb + '/' + before.vram_total_gb + ' GB → ' + after.vram_used_gb + '/' + after.vram_total_gb + ' GB';
569013
569057
  }
569014
- msg += rem > 0 ? ('
569015
- Remaining matched processes: ' + rem) : '
569016
- No remaining matched processes.';
569058
+ msg += rem > 0 ? ('\\nRemaining matched processes: ' + rem) : '\\nNo remaining matched processes.';
569017
569059
  alert(msg);
569018
569060
  loadScheduled();
569019
569061
  } catch (e) { alert('Kill failed: ' + (e && e.message || String(e))); }
569020
569062
  }
569021
569063
 
569022
- (window as any).disableAllScheduled = async function() {
569064
+ window.disableAllScheduled = async function() {
569023
569065
  try {
569024
569066
  const r = await fetch('/v1/scheduled', { headers: headers() });
569025
569067
  const d = await r.json();
@@ -569036,7 +569078,7 @@ No remaining matched processes.';
569036
569078
  } catch {}
569037
569079
  }
569038
569080
 
569039
- (window as any).enableAllScheduled = async function() {
569081
+ window.enableAllScheduled = async function() {
569040
569082
  try {
569041
569083
  const r = await fetch('/v1/scheduled', { headers: headers() });
569042
569084
  const d = await r.json();
@@ -569053,7 +569095,7 @@ No remaining matched processes.';
569053
569095
  } catch {}
569054
569096
  }
569055
569097
 
569056
- (window as any).adoptScheduled = async function() {
569098
+ window.adoptScheduled = async function() {
569057
569099
  try {
569058
569100
  const r = await fetch('/v1/scheduled/reconcile', { method:'POST', headers: headers(), body: JSON.stringify({ apply: true }) });
569059
569101
  const j = await r.json();
@@ -569065,7 +569107,7 @@ No remaining matched processes.';
569065
569107
  } catch (e) { alert('Adopt failed: ' + (e && e.message || String(e))); }
569066
569108
  }
569067
569109
 
569068
- (window as any).fixupScheduled = async function() {
569110
+ window.fixupScheduled = async function() {
569069
569111
  try {
569070
569112
  if (!confirm('Rewrite OA cron entries to canonical launcher?')) return;
569071
569113
  const r = await fetch('/v1/scheduled/fixup', { method:'POST', headers: headers(), body: JSON.stringify({ mode: 'cron', dryRun: false }) });
@@ -569075,7 +569117,7 @@ No remaining matched processes.';
569075
569117
  } catch (e) { alert('Fixup failed: ' + (e && e.message || String(e))); }
569076
569118
  }
569077
569119
 
569078
- (window as any).migrateScheduled = async function() {
569120
+ window.migrateScheduled = async function() {
569079
569121
  try {
569080
569122
  if (!confirm('Migrate OA cron entries to systemd user timers?')) return;
569081
569123
  const r = await fetch('/v1/scheduled/fixup', { method:'POST', headers: headers(), body: JSON.stringify({ mode: 'migrate', dryRun: false }) });
@@ -569085,7 +569127,7 @@ No remaining matched processes.';
569085
569127
  } catch (e) { alert('Migrate failed: ' + (e && e.message || String(e))); }
569086
569128
  }
569087
569129
 
569088
- (window as any).killScheduledTask = async function(id) {
569130
+ window.killScheduledTask = async function(id) {
569089
569131
  try {
569090
569132
  // Fetch task to derive a pattern (directory of tasks.json)
569091
569133
  const r = await fetch('/v1/scheduled/status', { headers: headers() });
@@ -569096,20 +569138,20 @@ No remaining matched processes.';
569096
569138
  const dir = (t.file || '').split('/').slice(0, -1).join('/') || t.file;
569097
569139
  // Escape for regex without using a character class that includes brace/dollar combo (parser quirk)
569098
569140
  const safe = dir
569099
- .replace(/\\/g, "\\\\")
569100
- .replace(/[/g, "\\[")
569101
- .replace(/]/g, "\\]")
569102
- .replace(/{/g, "\\{")
569103
- .replace(/}/g, "\\}")
569104
- .replace(/(/g, "\\(")
569105
- .replace(/)/g, "\\)")
569106
- .replace(/*/g, "\\*")
569107
- .replace(/+/g, "\\+")
569108
- .replace(/?/g, "\\?")
569109
- .replace(/^/g, "\\^")
569110
- .replace(/$/g, "\\$")
569111
- .replace(/|/g, "\\|")
569112
- .replace(/./g, "\\.");
569141
+ .replace(/\\\\/g, "\\\\\\\\")
569142
+ .replace(/\\[/g, "\\\\[")
569143
+ .replace(/\\]/g, "\\\\]")
569144
+ .replace(/\\{/g, "\\\\{")
569145
+ .replace(/\\}/g, "\\\\}")
569146
+ .replace(/\\(/g, "\\\\(")
569147
+ .replace(/\\)/g, "\\\\)")
569148
+ .replace(/\\*/g, "\\\\*")
569149
+ .replace(/\\+/g, "\\\\+")
569150
+ .replace(/\\?/g, "\\\\?")
569151
+ .replace(/\\^/g, "\\\\^")
569152
+ .replace(/\\$/g, "\\\\$")
569153
+ .replace(/\\|/g, "\\\\|")
569154
+ .replace(/\\./g, "\\\\.");
569113
569155
  const body = { pattern: safe };
569114
569156
  const resp = await fetch('/v1/scheduled/kill', { method:'POST', headers: headers(), body: JSON.stringify(body) });
569115
569157
  const j = await resp.json();
@@ -569119,11 +569161,8 @@ No remaining matched processes.';
569119
569161
  const after = j.gpu_after && j.gpu_after[0] ? j.gpu_after[0] : null;
569120
569162
  const rem = Array.isArray(j.procs_after) ? j.procs_after.length : 0;
569121
569163
  let msg = 'Killed ' + (kb + ka) + ' processes for task.';
569122
- if (before && after) msg += '
569123
- GPU util: ' + before.gpu_pct + '% ' + after.gpu_pct + '%';
569124
- msg += rem > 0 ? ('
569125
- Remaining matched processes: ' + rem) : '
569126
- No remaining matched processes.';
569164
+ if (before && after) msg += '\\nGPU util: ' + before.gpu_pct + '% → ' + after.gpu_pct + '%';
569165
+ msg += rem > 0 ? ('\\nRemaining matched processes: ' + rem) : '\\nNo remaining matched processes.';
569127
569166
  alert(msg);
569128
569167
  loadScheduled();
569129
569168
  } catch (e) { alert('Kill failed: ' + (e && e.message || String(e))); }
@@ -569138,8 +569177,8 @@ async function loadServices() {
569138
569177
  const svcs = Array.isArray(d.services) ? d.services : [];
569139
569178
  if (!svcs.length) { el.innerHTML = ''; return; }
569140
569179
  const rows = svcs.map(s => {
569141
- const stopBtn = '<button onclick="svcAction('' + s.name + '','stop')" style="background:#2a2a30;border:1px solid #5a2a2a;color:#b25f5f;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">stop</button>';
569142
- const disBtn = '<button onclick="svcAction('' + s.name + '','disable')" style="background:#2a2a30;border:1px solid #5a2a2a;color:#b25f5f;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">disable</button>';
569180
+ const stopBtn = '<button onclick="svcAction(\\'' + s.name + '\\',\\'stop\\')" style="background:#2a2a30;border:1px solid #5a2a2a;color:#b25f5f;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">stop</button>';
569181
+ const disBtn = '<button onclick="svcAction(\\'' + s.name + '\\',\\'disable\\')" style="background:#2a2a30;border:1px solid #5a2a2a;color:#b25f5f;padding:2px 6px;border-radius:3px;font-size:0.65rem;cursor:pointer">disable</button>';
569143
569182
  return '<div style="background:#1e1e22;border-left:2px solid #3a3a42;padding:6px 10px;margin:4px 0;font-size:0.72rem">'
569144
569183
  + '<div style="color:#b0b0b0">' + s.name + '</div>'
569145
569184
  + '<div style="color:#555;font-size:0.6rem">enabled: ' + s.enabled + ' • active: ' + s.active + '</div>'
@@ -569150,7 +569189,7 @@ async function loadServices() {
569150
569189
  } catch {}
569151
569190
  }
569152
569191
 
569153
- (window as any).svcAction = async function(name, action) {
569192
+ window.svcAction = async function(name, action) {
569154
569193
  try {
569155
569194
  await fetch('/v1/services/systemd/' + encodeURIComponent(name), { method:'POST', headers: headers(), body: JSON.stringify({ action }) });
569156
569195
  loadServices();
@@ -576838,23 +576877,85 @@ function startApiServer(options2 = {}) {
576838
576877
  process.exit(1);
576839
576878
  }
576840
576879
  }
576841
- const accessMode = resolveAccessMode(process.env["OA_ACCESS"], host);
576880
+ let runtimeAccessMode = resolveAccessMode(process.env["OA_ACCESS"], host);
576881
+ try {
576882
+ const accessFile = join104(homedir38(), ".open-agents", "access");
576883
+ if (existsSync87(accessFile)) {
576884
+ const persisted = readFileSync69(accessFile, "utf8").trim();
576885
+ const resolved = resolveAccessMode(persisted, host);
576886
+ if (resolved) runtimeAccessMode = resolved;
576887
+ }
576888
+ } catch {
576889
+ }
576842
576890
  let _accessRejected = 0;
576843
576891
  const handler = (req2, res) => {
576844
576892
  const rawIp = req2.socket?.remoteAddress ?? "";
576845
- if (!isAllowedIP(rawIp, accessMode)) {
576893
+ if (!isAllowedIP(rawIp, runtimeAccessMode)) {
576846
576894
  _accessRejected++;
576847
576895
  metrics.totalErrors++;
576848
576896
  try {
576849
576897
  res.writeHead(403, { "Content-Type": "application/json" });
576850
576898
  res.end(JSON.stringify({
576851
576899
  error: "access_denied",
576852
- message: `Address ${rawIp} is not in the allowed network (mode: ${accessMode}). Set OA_ACCESS=any to disable.`
576900
+ message: `Address ${rawIp} is not in the allowed network (mode: ${runtimeAccessMode}). Set OA_ACCESS=any to disable.`
576853
576901
  }));
576854
576902
  } catch {
576855
576903
  }
576856
576904
  return;
576857
576905
  }
576906
+ try {
576907
+ const url = new URL(req2.url || "", "http://local");
576908
+ if (url.pathname === "/v1/admin/access" && req2.method === "POST") {
576909
+ if (!isLoopbackIP(rawIp)) {
576910
+ res.writeHead(403, { "Content-Type": "application/json" });
576911
+ res.end(JSON.stringify({ error: "loopback_required", message: "POST /v1/admin/access requires a loopback origin." }));
576912
+ return;
576913
+ }
576914
+ let body = "";
576915
+ req2.on("data", (chunk) => {
576916
+ body += chunk;
576917
+ if (body.length > 4096) {
576918
+ try {
576919
+ req2.destroy();
576920
+ } catch {
576921
+ }
576922
+ }
576923
+ });
576924
+ req2.on("end", () => {
576925
+ try {
576926
+ const payload = JSON.parse(body || "{}");
576927
+ const requested = (payload?.mode || "").toLowerCase().trim();
576928
+ const allowed = /* @__PURE__ */ new Set(["loopback", "lan", "any"]);
576929
+ if (!allowed.has(requested)) {
576930
+ res.writeHead(400, { "Content-Type": "application/json" });
576931
+ res.end(JSON.stringify({ error: "invalid_mode", message: `mode must be one of: ${[...allowed].join(", ")}` }));
576932
+ return;
576933
+ }
576934
+ const previous = runtimeAccessMode;
576935
+ runtimeAccessMode = requested;
576936
+ try {
576937
+ const dir = join104(homedir38(), ".open-agents");
576938
+ mkdirSync54(dir, { recursive: true });
576939
+ writeFileSync47(join104(dir, "access"), `${runtimeAccessMode}
576940
+ `, "utf8");
576941
+ } catch {
576942
+ }
576943
+ res.writeHead(200, { "Content-Type": "application/json" });
576944
+ res.end(JSON.stringify({ ok: true, previous, current: runtimeAccessMode }));
576945
+ } catch (e2) {
576946
+ res.writeHead(400, { "Content-Type": "application/json" });
576947
+ res.end(JSON.stringify({ error: "bad_request", message: e2 instanceof Error ? e2.message : String(e2) }));
576948
+ }
576949
+ });
576950
+ return;
576951
+ }
576952
+ if (url.pathname === "/v1/admin/access" && req2.method === "GET") {
576953
+ res.writeHead(200, { "Content-Type": "application/json" });
576954
+ res.end(JSON.stringify({ mode: runtimeAccessMode, host }));
576955
+ return;
576956
+ }
576957
+ } catch {
576958
+ }
576858
576959
  try {
576859
576960
  const url = new URL(req2.url || "", "http://local");
576860
576961
  if (req2.method === "POST" && url.pathname === "/v1/vision/embed") {
@@ -576971,7 +577072,7 @@ function startApiServer(options2 = {}) {
576971
577072
  const proto = useTls ? "https" : "http";
576972
577073
  log22(` Listening on ${proto}://${host}:${port}
576973
577074
  `);
576974
- log22(` Access: ${accessMode} (${describeAccessMode(accessMode)})
577075
+ log22(` Access: ${runtimeAccessMode} (${describeAccessMode(runtimeAccessMode)})
576975
577076
  `);
576976
577077
  log22(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
576977
577078
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.364",
3
+ "version": "0.187.366",
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",