open-agents-ai 0.187.196 → 0.187.197
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 +322 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -316518,15 +316518,161 @@ async function pollMetrics() {
|
|
|
316518
316518
|
} catch {}
|
|
316519
316519
|
}
|
|
316520
316520
|
|
|
316521
|
-
|
|
316522
|
-
|
|
316523
|
-
|
|
316524
|
-
|
|
316525
|
-
|
|
316526
|
-
|
|
316527
|
-
|
|
316528
|
-
|
|
316521
|
+
// Self-update flow \u2014 POST /v1/update, then poll /version, then reconnect
|
|
316522
|
+
//
|
|
316523
|
+
// The button:
|
|
316524
|
+
// 1. POSTs /v1/update with {version: latest} \u2192 daemon detaches
|
|
316525
|
+
// 'npm install -g open-agents-ai@latest' which survives the daemon
|
|
316526
|
+
// restart (systemd/launchd auto-restarts it).
|
|
316527
|
+
// 2. Shows inline progress on the button itself (installing, restarting,
|
|
316528
|
+
// reconnecting, done).
|
|
316529
|
+
// 3. Polls GET /version every 2s. The new version appearing means the
|
|
316530
|
+
// daemon has come back with the upgraded bundle.
|
|
316531
|
+
// 4. Reconnects the SSE event stream (connectEventBus()) and refreshes
|
|
316532
|
+
// model list + health + sessions so the UI picks up any new surface.
|
|
316533
|
+
// 5. Flashes the status bar green on success.
|
|
316534
|
+
|
|
316535
|
+
async function doUpdate() {
|
|
316536
|
+
const btn = document.getElementById('update-btn');
|
|
316537
|
+
if (!btn) return;
|
|
316538
|
+
const latestVer = btn.title?.match(/Latest: v([\\d.]+)/)?.[1] || 'latest';
|
|
316539
|
+
const originalText = btn.textContent;
|
|
316540
|
+
const originalBg = btn.style.background;
|
|
316541
|
+
const originalBorder = btn.style.border;
|
|
316542
|
+
const originalColor = btn.style.color;
|
|
316543
|
+
|
|
316544
|
+
if (!confirm('Install open-agents-ai v' + latestVer + ' now?\\n\\n' +
|
|
316545
|
+
'The daemon will install the new version in the background ' +
|
|
316546
|
+
'and restart automatically. This UI will reconnect when the ' +
|
|
316547
|
+
'update completes (~30-60 seconds).')) {
|
|
316548
|
+
return;
|
|
316529
316549
|
}
|
|
316550
|
+
|
|
316551
|
+
// Visual: orange "installing..." state
|
|
316552
|
+
btn.style.background = '#3a2a10';
|
|
316553
|
+
btn.style.borderColor = '#c9944e';
|
|
316554
|
+
btn.style.color = '#c9944e';
|
|
316555
|
+
btn.style.pointerEvents = 'none';
|
|
316556
|
+
btn.textContent = 'installing...';
|
|
316557
|
+
|
|
316558
|
+
// Determine the current version BEFORE kicking off the update so we can
|
|
316559
|
+
// detect a successful version bump by comparison.
|
|
316560
|
+
let fromVersion = '0.0.0';
|
|
316561
|
+
try {
|
|
316562
|
+
const v = await fetch('/version').then(r => r.json());
|
|
316563
|
+
fromVersion = v.version || '0.0.0';
|
|
316564
|
+
} catch {}
|
|
316565
|
+
|
|
316566
|
+
// Kick off the install
|
|
316567
|
+
let updateStartOk = false;
|
|
316568
|
+
try {
|
|
316569
|
+
const r = await fetch('/v1/update', {
|
|
316570
|
+
method: 'POST',
|
|
316571
|
+
headers: headers(),
|
|
316572
|
+
body: JSON.stringify({ version: latestVer }),
|
|
316573
|
+
});
|
|
316574
|
+
if (r.ok || r.status === 202) {
|
|
316575
|
+
updateStartOk = true;
|
|
316576
|
+
} else {
|
|
316577
|
+
const txt = await r.text().catch(() => '');
|
|
316578
|
+
btn.textContent = 'error';
|
|
316579
|
+
btn.style.background = '#3a1a1a';
|
|
316580
|
+
btn.style.borderColor = '#c94e4e';
|
|
316581
|
+
btn.style.color = '#c94e4e';
|
|
316582
|
+
alert('Update failed to start: HTTP ' + r.status + '\\n\\n' + txt.slice(0, 300));
|
|
316583
|
+
setTimeout(() => {
|
|
316584
|
+
btn.textContent = originalText;
|
|
316585
|
+
btn.style.background = originalBg;
|
|
316586
|
+
btn.style.borderColor = originalBorder;
|
|
316587
|
+
btn.style.color = originalColor;
|
|
316588
|
+
btn.style.pointerEvents = '';
|
|
316589
|
+
}, 5000);
|
|
316590
|
+
return;
|
|
316591
|
+
}
|
|
316592
|
+
} catch (e) {
|
|
316593
|
+
alert('Update request failed: ' + (e.message || e));
|
|
316594
|
+
btn.textContent = originalText;
|
|
316595
|
+
btn.style.pointerEvents = '';
|
|
316596
|
+
return;
|
|
316597
|
+
}
|
|
316598
|
+
|
|
316599
|
+
if (!updateStartOk) return;
|
|
316600
|
+
|
|
316601
|
+
// Poll /version until the daemon reports a different (newer) version.
|
|
316602
|
+
// Timeouts: up to ~5 minutes total with 2s intervals = 150 attempts.
|
|
316603
|
+
// During the first ~10-15s the daemon usually restarts so fetch() will
|
|
316604
|
+
// throw. That's expected \u2014 we just keep polling.
|
|
316605
|
+
btn.textContent = 'restarting...';
|
|
316606
|
+
const startTs = Date.now();
|
|
316607
|
+
const maxWaitMs = 5 * 60 * 1000;
|
|
316608
|
+
let newVersion = null;
|
|
316609
|
+
let lastError = '';
|
|
316610
|
+
while (Date.now() - startTs < maxWaitMs) {
|
|
316611
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
316612
|
+
try {
|
|
316613
|
+
const r = await fetch('/version', { cache: 'no-store' });
|
|
316614
|
+
const j = await r.json();
|
|
316615
|
+
const v = j.version || '';
|
|
316616
|
+
if (v && v !== fromVersion) {
|
|
316617
|
+
newVersion = v;
|
|
316618
|
+
break;
|
|
316619
|
+
}
|
|
316620
|
+
} catch (e) {
|
|
316621
|
+
lastError = e.message || String(e);
|
|
316622
|
+
// Expected during daemon restart \u2014 keep polling.
|
|
316623
|
+
}
|
|
316624
|
+
}
|
|
316625
|
+
|
|
316626
|
+
if (!newVersion) {
|
|
316627
|
+
btn.textContent = 'timeout';
|
|
316628
|
+
btn.style.background = '#3a1a1a';
|
|
316629
|
+
btn.style.borderColor = '#c94e4e';
|
|
316630
|
+
btn.style.color = '#c94e4e';
|
|
316631
|
+
alert('Update did not complete within 5 minutes.\\n\\n' +
|
|
316632
|
+
'Last error: ' + lastError + '\\n\\n' +
|
|
316633
|
+
'Check logs: tail -f ~/.open-agents/update.log\\n' +
|
|
316634
|
+
'Or check daemon status: systemctl --user status open-agents-daemon');
|
|
316635
|
+
setTimeout(() => {
|
|
316636
|
+
btn.textContent = originalText;
|
|
316637
|
+
btn.style.background = originalBg;
|
|
316638
|
+
btn.style.borderColor = originalBorder;
|
|
316639
|
+
btn.style.color = originalColor;
|
|
316640
|
+
btn.style.pointerEvents = '';
|
|
316641
|
+
}, 10000);
|
|
316642
|
+
return;
|
|
316643
|
+
}
|
|
316644
|
+
|
|
316645
|
+
// Success \u2014 reconnect SSE and refresh UI state
|
|
316646
|
+
btn.textContent = 'reconnecting...';
|
|
316647
|
+
try { if (typeof procEventSource !== 'undefined' && procEventSource) procEventSource.close(); } catch {}
|
|
316648
|
+
try { connectEventBus(); } catch {}
|
|
316649
|
+
try { loadModels(); } catch {}
|
|
316650
|
+
try { checkHealth(); } catch {}
|
|
316651
|
+
try { pollMetrics(); } catch {}
|
|
316652
|
+
|
|
316653
|
+
btn.textContent = 'updated v' + newVersion;
|
|
316654
|
+
btn.style.background = '#1a3a1a';
|
|
316655
|
+
btn.style.borderColor = '#4ec94e';
|
|
316656
|
+
btn.style.color = '#4ec94e';
|
|
316657
|
+
|
|
316658
|
+
// Flash status bar
|
|
316659
|
+
const statusEl = document.getElementById('status');
|
|
316660
|
+
if (statusEl) {
|
|
316661
|
+
const origStatusColor = statusEl.style.color;
|
|
316662
|
+
statusEl.textContent = 'updated \u2192 v' + newVersion;
|
|
316663
|
+
statusEl.style.color = '#4ec94e';
|
|
316664
|
+
setTimeout(() => { statusEl.style.color = origStatusColor; }, 5000);
|
|
316665
|
+
}
|
|
316666
|
+
|
|
316667
|
+
// After 6s, hide the button (update is done, nothing more to do)
|
|
316668
|
+
setTimeout(() => {
|
|
316669
|
+
btn.style.display = 'none';
|
|
316670
|
+
btn.textContent = originalText;
|
|
316671
|
+
btn.style.background = originalBg;
|
|
316672
|
+
btn.style.borderColor = originalBorder;
|
|
316673
|
+
btn.style.color = originalColor;
|
|
316674
|
+
btn.style.pointerEvents = '';
|
|
316675
|
+
}, 6000);
|
|
316530
316676
|
}
|
|
316531
316677
|
|
|
316532
316678
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
@@ -319239,6 +319385,158 @@ ${task}` : task;
|
|
|
319239
319385
|
_oa: { tool_calls: toolCallCount, finish_reason: "stop", duration_ms: durationMs, request_id: requestId }
|
|
319240
319386
|
});
|
|
319241
319387
|
}
|
|
319388
|
+
function updateStateFile() {
|
|
319389
|
+
return join95(homedir32(), ".open-agents", "update-state.json");
|
|
319390
|
+
}
|
|
319391
|
+
function updateLogPath() {
|
|
319392
|
+
return join95(homedir32(), ".open-agents", "update.log");
|
|
319393
|
+
}
|
|
319394
|
+
function readUpdateState() {
|
|
319395
|
+
try {
|
|
319396
|
+
const p2 = updateStateFile();
|
|
319397
|
+
if (!existsSync78(p2)) return null;
|
|
319398
|
+
return JSON.parse(readFileSync61(p2, "utf-8"));
|
|
319399
|
+
} catch {
|
|
319400
|
+
return null;
|
|
319401
|
+
}
|
|
319402
|
+
}
|
|
319403
|
+
function writeUpdateState(state) {
|
|
319404
|
+
try {
|
|
319405
|
+
const dir = join95(homedir32(), ".open-agents");
|
|
319406
|
+
const fs4 = __require("node:fs");
|
|
319407
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
319408
|
+
const finalPath = updateStateFile();
|
|
319409
|
+
const tmpPath = `${finalPath}.tmp.${process.pid}`;
|
|
319410
|
+
fs4.writeFileSync(tmpPath, JSON.stringify(state, null, 2), "utf-8");
|
|
319411
|
+
fs4.renameSync(tmpPath, finalPath);
|
|
319412
|
+
} catch {
|
|
319413
|
+
}
|
|
319414
|
+
}
|
|
319415
|
+
async function handleV1Update(req2, res, requestId) {
|
|
319416
|
+
try {
|
|
319417
|
+
const body = await parseJsonBody(req2);
|
|
319418
|
+
const targetVersion = body?.version || "latest";
|
|
319419
|
+
const currentVersion = getVersion3();
|
|
319420
|
+
const existing = readUpdateState();
|
|
319421
|
+
if (existing?.status === "running") {
|
|
319422
|
+
if (existing.pid && existing.pid > 0) {
|
|
319423
|
+
try {
|
|
319424
|
+
process.kill(existing.pid, 0);
|
|
319425
|
+
jsonResponse(res, 409, {
|
|
319426
|
+
error: "Update already in progress",
|
|
319427
|
+
state: existing
|
|
319428
|
+
});
|
|
319429
|
+
return;
|
|
319430
|
+
} catch {
|
|
319431
|
+
}
|
|
319432
|
+
}
|
|
319433
|
+
}
|
|
319434
|
+
const logPath2 = updateLogPath();
|
|
319435
|
+
const startedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
319436
|
+
writeUpdateState({
|
|
319437
|
+
status: "running",
|
|
319438
|
+
target_version: targetVersion,
|
|
319439
|
+
from_version: currentVersion,
|
|
319440
|
+
started_at: startedAt2,
|
|
319441
|
+
log_path: logPath2
|
|
319442
|
+
});
|
|
319443
|
+
publishEvent("engine.state_changed", {
|
|
319444
|
+
action: "update_started",
|
|
319445
|
+
from: currentVersion,
|
|
319446
|
+
to: targetVersion
|
|
319447
|
+
}, { subject: req2._authUser ?? "anonymous" });
|
|
319448
|
+
const pkgSpec = `open-agents-ai@${targetVersion}`;
|
|
319449
|
+
const installCmd = `npm install -g ${pkgSpec} --no-audit --no-fund --no-progress > "${logPath2}" 2>&1; echo "__EXIT_CODE=$?" >> "${logPath2}"`;
|
|
319450
|
+
const fs4 = __require("node:fs");
|
|
319451
|
+
const dir = join95(homedir32(), ".open-agents");
|
|
319452
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
319453
|
+
const isWin2 = process.platform === "win32";
|
|
319454
|
+
let child;
|
|
319455
|
+
if (isWin2) {
|
|
319456
|
+
child = spawn25("cmd.exe", ["/c", installCmd], {
|
|
319457
|
+
detached: true,
|
|
319458
|
+
stdio: "ignore",
|
|
319459
|
+
windowsHide: true,
|
|
319460
|
+
env: { ...process.env, npm_config_yes: "true" }
|
|
319461
|
+
});
|
|
319462
|
+
} else {
|
|
319463
|
+
child = spawn25("bash", ["-lc", `nohup bash -c 'setsid bash -c ${JSON.stringify(installCmd)} &' >/dev/null 2>&1; exit 0`], {
|
|
319464
|
+
detached: true,
|
|
319465
|
+
stdio: "ignore",
|
|
319466
|
+
env: { ...process.env }
|
|
319467
|
+
});
|
|
319468
|
+
}
|
|
319469
|
+
child.unref();
|
|
319470
|
+
const initialPid = child.pid ?? 0;
|
|
319471
|
+
writeUpdateState({
|
|
319472
|
+
status: "running",
|
|
319473
|
+
target_version: targetVersion,
|
|
319474
|
+
from_version: currentVersion,
|
|
319475
|
+
started_at: startedAt2,
|
|
319476
|
+
pid: initialPid,
|
|
319477
|
+
log_path: logPath2
|
|
319478
|
+
});
|
|
319479
|
+
jsonResponse(res, 202, {
|
|
319480
|
+
status: "started",
|
|
319481
|
+
from_version: currentVersion,
|
|
319482
|
+
target_version: targetVersion,
|
|
319483
|
+
pid: initialPid,
|
|
319484
|
+
log_path: logPath2,
|
|
319485
|
+
poll_url: "/v1/update",
|
|
319486
|
+
version_url: "/version",
|
|
319487
|
+
note: "The daemon will restart automatically when the install completes. Poll /version until the new version appears, then reconnect /v1/events.",
|
|
319488
|
+
instance: requestId
|
|
319489
|
+
});
|
|
319490
|
+
} catch (err) {
|
|
319491
|
+
if (!res.headersSent) {
|
|
319492
|
+
res.setHeader("Content-Type", "application/problem+json; charset=utf-8");
|
|
319493
|
+
res.writeHead(500);
|
|
319494
|
+
res.end(JSON.stringify({
|
|
319495
|
+
type: "https://openagents.nexus/problems/internal-error",
|
|
319496
|
+
title: "Update failed to start",
|
|
319497
|
+
status: 500,
|
|
319498
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
319499
|
+
instance: requestId
|
|
319500
|
+
}));
|
|
319501
|
+
}
|
|
319502
|
+
}
|
|
319503
|
+
}
|
|
319504
|
+
function handleV1UpdateStatus(res) {
|
|
319505
|
+
const state = readUpdateState();
|
|
319506
|
+
const logPath2 = updateLogPath();
|
|
319507
|
+
let logTail = "";
|
|
319508
|
+
let exitCode = null;
|
|
319509
|
+
try {
|
|
319510
|
+
if (existsSync78(logPath2)) {
|
|
319511
|
+
const raw = readFileSync61(logPath2, "utf-8");
|
|
319512
|
+
const m2 = raw.match(/__EXIT_CODE=(\d+)/);
|
|
319513
|
+
if (m2) exitCode = parseInt(m2[1], 10);
|
|
319514
|
+
logTail = raw.slice(-2e3);
|
|
319515
|
+
}
|
|
319516
|
+
} catch {
|
|
319517
|
+
}
|
|
319518
|
+
if (state && state.status === "running" && exitCode !== null) {
|
|
319519
|
+
const newState = {
|
|
319520
|
+
...state,
|
|
319521
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
319522
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
319523
|
+
exit_code: exitCode
|
|
319524
|
+
};
|
|
319525
|
+
writeUpdateState(newState);
|
|
319526
|
+
jsonResponse(res, 200, {
|
|
319527
|
+
state: newState,
|
|
319528
|
+
log_tail: logTail,
|
|
319529
|
+
current_version: getVersion3()
|
|
319530
|
+
});
|
|
319531
|
+
return;
|
|
319532
|
+
}
|
|
319533
|
+
jsonResponse(res, 200, {
|
|
319534
|
+
state: state ?? { status: "idle" },
|
|
319535
|
+
log_tail: logTail,
|
|
319536
|
+
current_version: getVersion3(),
|
|
319537
|
+
exit_code: exitCode
|
|
319538
|
+
});
|
|
319539
|
+
}
|
|
319242
319540
|
async function handleV1Run(req2, res) {
|
|
319243
319541
|
const body = await parseJsonBody(req2);
|
|
319244
319542
|
if (!body || typeof body !== "object") {
|
|
@@ -320220,6 +320518,22 @@ ${historyLines}
|
|
|
320220
320518
|
await handleV1Generate(req2, res, ollamaUrl, requestId);
|
|
320221
320519
|
return;
|
|
320222
320520
|
}
|
|
320521
|
+
if (pathname === "/v1/update" && method === "POST") {
|
|
320522
|
+
if (!checkAuth(req2, res, "admin")) {
|
|
320523
|
+
status = 403;
|
|
320524
|
+
return;
|
|
320525
|
+
}
|
|
320526
|
+
await handleV1Update(req2, res, requestId);
|
|
320527
|
+
return;
|
|
320528
|
+
}
|
|
320529
|
+
if (pathname === "/v1/update" && method === "GET") {
|
|
320530
|
+
if (!checkAuth(req2, res, "read")) {
|
|
320531
|
+
status = 401;
|
|
320532
|
+
return;
|
|
320533
|
+
}
|
|
320534
|
+
handleV1UpdateStatus(res);
|
|
320535
|
+
return;
|
|
320536
|
+
}
|
|
320223
320537
|
if (pathname === "/v1/run" && method === "POST") {
|
|
320224
320538
|
await handleV1Run(req2, res);
|
|
320225
320539
|
return;
|
package/package.json
CHANGED