open-agents-ai 0.187.533 → 0.187.534
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 +421 -6
- package/npm-shrinkwrap.json +11 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -528897,6 +528897,8 @@ TASK: ${task}` : task;
|
|
|
528897
528897
|
try {
|
|
528898
528898
|
if (this.options.subAgent)
|
|
528899
528899
|
throw "skip-handoff-subagent";
|
|
528900
|
+
if (process.env["OA_FRESH_SESSION"] === "1")
|
|
528901
|
+
throw "skip-handoff-fresh";
|
|
528900
528902
|
const oaDir = this._workingDirectory ? _pathJoin(this._workingDirectory, ".oa") : _pathJoin(process.cwd(), ".oa");
|
|
528901
528903
|
const chainPairs = loadMessagePairsFromLog(oaDir, { currentTask: cleanedTask });
|
|
528902
528904
|
if (chainPairs.length > 0) {
|
|
@@ -585554,6 +585556,7 @@ async function tryRouteV1(ctx3) {
|
|
|
585554
585556
|
const m2 = /^\/v1\/keys\/([^/]+)$/.exec(pathname);
|
|
585555
585557
|
if (m2 && method === "DELETE") return handleRevokeKey(ctx3, decodeURIComponent(m2[1]));
|
|
585556
585558
|
}
|
|
585559
|
+
if (pathname === "/v1/share/generate" && method === "POST") return handleGenerateShare(ctx3);
|
|
585557
585560
|
if (pathname === "/v1/tools" && method === "GET") {
|
|
585558
585561
|
return handleListTools(ctx3);
|
|
585559
585562
|
}
|
|
@@ -586695,6 +586698,75 @@ async function handleMintKey(ctx3) {
|
|
|
586695
586698
|
}
|
|
586696
586699
|
return true;
|
|
586697
586700
|
}
|
|
586701
|
+
async function handleGenerateShare(ctx3) {
|
|
586702
|
+
const { req: req2, res, requestId } = ctx3;
|
|
586703
|
+
const reqAuth = req2;
|
|
586704
|
+
if (reqAuth._authScope !== "admin") {
|
|
586705
|
+
sendProblem(res, problemDetails({
|
|
586706
|
+
type: P.forbidden,
|
|
586707
|
+
status: 403,
|
|
586708
|
+
title: "Admin scope required",
|
|
586709
|
+
detail: "Generating a share URL mints a runtime key, which requires 'admin' scope.",
|
|
586710
|
+
instance: requestId
|
|
586711
|
+
}));
|
|
586712
|
+
return true;
|
|
586713
|
+
}
|
|
586714
|
+
try {
|
|
586715
|
+
const body = await parseJsonBodyStrict(req2).catch(() => null) ?? {};
|
|
586716
|
+
const label = typeof body["label"] === "string" && body["label"] ? String(body["label"]).slice(0, 100) : `share-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
586717
|
+
const scope = typeof body["scope"] === "string" && ["read", "run", "admin"].includes(body["scope"]) ? body["scope"] : "run";
|
|
586718
|
+
const owner = `share:${label}`;
|
|
586719
|
+
const { mintKey: mintKey2 } = await Promise.resolve().then(() => (init_runtime_keys(), runtime_keys_exports));
|
|
586720
|
+
const rec = mintKey2({
|
|
586721
|
+
scope,
|
|
586722
|
+
owner,
|
|
586723
|
+
profile: null
|
|
586724
|
+
});
|
|
586725
|
+
const hostHeader = String(req2.headers["host"] || "").trim();
|
|
586726
|
+
const fallbackHost = process.env["OA_HOST"] || "127.0.0.1:11435";
|
|
586727
|
+
let hostPort = hostHeader || fallbackHost;
|
|
586728
|
+
hostPort = hostPort.replace(/^https?:\/\//i, "");
|
|
586729
|
+
const colonIdx = hostPort.lastIndexOf(":");
|
|
586730
|
+
const host = colonIdx > 0 ? hostPort.slice(0, colonIdx) : hostPort;
|
|
586731
|
+
const portStr = colonIdx > 0 ? hostPort.slice(colonIdx + 1) : "11435";
|
|
586732
|
+
const port = parseInt(portStr, 10) || 11435;
|
|
586733
|
+
const fullKey = rec.key || "";
|
|
586734
|
+
const keyPrefix2 = fullKey.slice(0, 12);
|
|
586735
|
+
if (!fullKey) {
|
|
586736
|
+
sendProblem(res, problemDetails({
|
|
586737
|
+
type: P.internalError,
|
|
586738
|
+
status: 500,
|
|
586739
|
+
title: "Key generation succeeded but no secret returned",
|
|
586740
|
+
detail: "mintKey() did not include the full secret in its response.",
|
|
586741
|
+
instance: requestId
|
|
586742
|
+
}));
|
|
586743
|
+
return true;
|
|
586744
|
+
}
|
|
586745
|
+
const scheme = String(req2.headers["x-forwarded-proto"] || (req2.socket?.encrypted ? "https" : "http"));
|
|
586746
|
+
const shareUrl = `oa-share://${hostPort}#${fullKey}`;
|
|
586747
|
+
const plainUrl = `${scheme}://${hostPort}/?oa-key=${encodeURIComponent(fullKey)}&oa-share-label=${encodeURIComponent(label)}`;
|
|
586748
|
+
sendJson(res, 201, {
|
|
586749
|
+
shareUrl,
|
|
586750
|
+
plainUrl,
|
|
586751
|
+
host,
|
|
586752
|
+
port,
|
|
586753
|
+
key: fullKey,
|
|
586754
|
+
keyPrefix: keyPrefix2,
|
|
586755
|
+
label,
|
|
586756
|
+
issuedAt: rec.created || (/* @__PURE__ */ new Date()).toISOString(),
|
|
586757
|
+
_note: "This is the ONLY response that contains the full key. Hand off the URL now."
|
|
586758
|
+
});
|
|
586759
|
+
} catch (err) {
|
|
586760
|
+
sendProblem(res, problemDetails({
|
|
586761
|
+
type: P.internalError,
|
|
586762
|
+
status: 500,
|
|
586763
|
+
title: "Share URL generation failed",
|
|
586764
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
586765
|
+
instance: requestId
|
|
586766
|
+
}));
|
|
586767
|
+
}
|
|
586768
|
+
return true;
|
|
586769
|
+
}
|
|
586698
586770
|
async function handleRevokeKey(ctx3, prefix) {
|
|
586699
586771
|
const { req: req2, res, requestId } = ctx3;
|
|
586700
586772
|
const reqAuth = req2;
|
|
@@ -589011,7 +589083,7 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
|
|
|
589011
589083
|
<span id="sidebar-status-dot" style="width:8px;height:8px;border-radius:50%;background:var(--color-fg-faint);flex-shrink:0" title="Backend connection"></span>
|
|
589012
589084
|
<span id="sidebar-status-text" style="flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">connecting...</span>
|
|
589013
589085
|
<button id="sidebar-update-btn" onclick="doUpdate()" style="display:none;background:var(--color-warning);border:none;color:#000;padding:2px 6px;border-radius:var(--radius-sm);font-size:0.6rem;cursor:pointer;font-weight:600">update</button>
|
|
589014
|
-
<button id="sidebar-key-btn" onclick="
|
|
589086
|
+
<button id="sidebar-key-btn" onclick="openKeyModal()" title="Set API key / share access" style="background:transparent;border:1px solid var(--color-border);color:var(--color-fg-muted);padding:2px 6px;border-radius:var(--radius-sm);font-size:0.6rem;cursor:pointer">key</button>
|
|
589015
589087
|
</div>
|
|
589016
589088
|
|
|
589017
589089
|
<!-- Resize handle -->
|
|
@@ -589343,14 +589415,21 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
|
|
|
589343
589415
|
</div>
|
|
589344
589416
|
|
|
589345
589417
|
<div id="key-modal">
|
|
589346
|
-
<form class="modal" onsubmit="event.preventDefault(); saveKey();" autocomplete="off">
|
|
589347
|
-
<h3>API Key</h3>
|
|
589348
|
-
<
|
|
589349
|
-
|
|
589418
|
+
<form class="modal" onsubmit="event.preventDefault(); saveKey();" autocomplete="off" style="min-width:480px">
|
|
589419
|
+
<h3>API Key & Sharing</h3>
|
|
589420
|
+
<div style="position:relative">
|
|
589421
|
+
<input id="key-input" type="password" placeholder="Bearer token (leave empty if auth disabled, or paste an oa-share:// URL to connect remote)" autocomplete="new-password" oninput="onKeyInputChange(this.value)" onfocus="showRecentKeysDropdown()" onblur="setTimeout(hideRecentKeysDropdown, 150)">
|
|
589422
|
+
<div id="key-recent-dropdown" style="display:none;position:absolute;top:100%;left:0;right:0;background:var(--color-bg-elevated);border:1px solid var(--color-border);border-radius:var(--radius-sm);max-height:180px;overflow-y:auto;z-index:10;font-size:0.78rem;margin-top:2px"></div>
|
|
589423
|
+
</div>
|
|
589424
|
+
<p id="key-input-hint" style="font-size:0.7rem;color:var(--color-fg-muted);margin:4px 0 8px">Tip: paste an <code>oa-share://host:port#key</code> URL or <code>http://host:port/?oa-key=…</code> to connect to a remote OA instance.</p>
|
|
589425
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
|
589350
589426
|
<button type="submit">save</button>
|
|
589351
589427
|
<button type="button" onclick="clearKey()">clear</button>
|
|
589428
|
+
<button type="button" onclick="generateShareUrl()" title="Generate a share URL that lets a remote OA instance connect to this one">share access</button>
|
|
589352
589429
|
<button type="button" onclick="closeKeyModal()">cancel</button>
|
|
589353
589430
|
</div>
|
|
589431
|
+
<div id="share-result" style="display:none;margin-top:14px;padding:10px;background:var(--color-bg-elevated);border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:0.78rem"></div>
|
|
589432
|
+
<div id="remote-state" style="display:none;margin-top:14px;padding:10px;background:var(--color-bg-elevated);border:1px solid var(--color-accent);border-radius:var(--radius-sm);font-size:0.78rem"></div>
|
|
589354
589433
|
</form>
|
|
589355
589434
|
</div>
|
|
589356
589435
|
|
|
@@ -590472,6 +590551,15 @@ async function sendMessage() {
|
|
|
590472
590551
|
messageWithContext = filesBlock + text;
|
|
590473
590552
|
}
|
|
590474
590553
|
|
|
590554
|
+
// FRESH-SESSION: pull the one-shot fresh flag the newChatSession()
|
|
590555
|
+
// handler set. Consumed (cleared) here so subsequent sends within
|
|
590556
|
+
// this same session don't keep claiming "fresh" and miss legitimate
|
|
590557
|
+
// mid-session handoff.
|
|
590558
|
+
let _freshPending = false;
|
|
590559
|
+
try {
|
|
590560
|
+
_freshPending = localStorage.getItem('oa.freshSessionPending') === '1';
|
|
590561
|
+
if (_freshPending) localStorage.removeItem('oa.freshSessionPending');
|
|
590562
|
+
} catch {}
|
|
590475
590563
|
const body = {
|
|
590476
590564
|
session_id: chatSessionId,
|
|
590477
590565
|
model: modelSelect.value,
|
|
@@ -590481,6 +590569,10 @@ async function sendMessage() {
|
|
|
590481
590569
|
// Pass the user-selected workspace as working_directory so the
|
|
590482
590570
|
// agent subprocess operates in the right cwd.
|
|
590483
590571
|
...(chatWorkingDir ? { working_directory: chatWorkingDir } : {}),
|
|
590572
|
+
// FRESH-SESSION: tell the daemon to skip cross-task handoff
|
|
590573
|
+
// injection on this turn. Daemon plumbs it to OA_FRESH_SESSION=1
|
|
590574
|
+
// for the agent subprocess; runner's handoff block respects it.
|
|
590575
|
+
...(_freshPending ? { fresh: true } : {}),
|
|
590484
590576
|
};
|
|
590485
590577
|
|
|
590486
590578
|
const response = await fetch('/v1/chat', {
|
|
@@ -590788,8 +590880,33 @@ document.getElementById('key-btn').onclick = () => {
|
|
|
590788
590880
|
document.getElementById('key-input').value = apiKey;
|
|
590789
590881
|
};
|
|
590790
590882
|
function saveKey() {
|
|
590791
|
-
|
|
590883
|
+
const raw = document.getElementById('key-input').value || '';
|
|
590884
|
+
// SHARE: detect oa-share://host:port#key OR http(s)://host:port/?oa-key=...
|
|
590885
|
+
const parsed = parseShareInput(raw);
|
|
590886
|
+
if (parsed) {
|
|
590887
|
+
// Pasting a share URL → switch into REMOTE mode and reload page
|
|
590888
|
+
// against the remote origin with the key in localStorage.
|
|
590889
|
+
saveRecentKey({ key: parsed.key, host: parsed.host, label: parsed.label || ('remote ' + parsed.host) });
|
|
590890
|
+
try {
|
|
590891
|
+
localStorage.setItem('oa.remoteHost', parsed.host);
|
|
590892
|
+
localStorage.setItem('oa.remoteScheme', parsed.scheme || 'http');
|
|
590893
|
+
localStorage.setItem('oa-api-key', parsed.key);
|
|
590894
|
+
} catch {}
|
|
590895
|
+
// Redirect to the remote host's UI with the key carried in localStorage
|
|
590896
|
+
// (the remote origin uses its own localStorage so we set on first
|
|
590897
|
+
// load there). We open the remote UI in a new tab to preserve the
|
|
590898
|
+
// local session.
|
|
590899
|
+
const remoteUrl = (parsed.scheme || 'http') + '://' + parsed.host + '/?oa-key=' + encodeURIComponent(parsed.key) + '&oa-share-label=' + encodeURIComponent(parsed.label || '');
|
|
590900
|
+
window.open(remoteUrl, '_blank');
|
|
590901
|
+
closeKeyModal();
|
|
590902
|
+
return;
|
|
590903
|
+
}
|
|
590904
|
+
apiKey = raw;
|
|
590792
590905
|
localStorage.setItem('oa-api-key', apiKey);
|
|
590906
|
+
// Track this key in the recent-keys list so future paste autocompletes it.
|
|
590907
|
+
if (apiKey) {
|
|
590908
|
+
saveRecentKey({ key: apiKey, host: location.host, label: 'local ' + location.host });
|
|
590909
|
+
}
|
|
590793
590910
|
closeKeyModal();
|
|
590794
590911
|
loadModels();
|
|
590795
590912
|
}
|
|
@@ -590802,8 +590919,296 @@ function clearKey() {
|
|
|
590802
590919
|
}
|
|
590803
590920
|
function closeKeyModal() {
|
|
590804
590921
|
document.getElementById('key-modal').classList.remove('visible');
|
|
590922
|
+
const sr = document.getElementById('share-result'); if (sr) sr.style.display = 'none';
|
|
590923
|
+
}
|
|
590924
|
+
function openKeyModal() {
|
|
590925
|
+
document.getElementById('key-modal').classList.add('visible');
|
|
590926
|
+
document.getElementById('key-input').value = apiKey;
|
|
590927
|
+
// Refresh the remote-state hint based on current localStorage.
|
|
590928
|
+
refreshKeyModalRemoteState();
|
|
590929
|
+
}
|
|
590930
|
+
window.openKeyModal = openKeyModal;
|
|
590931
|
+
|
|
590932
|
+
// ─── Share URL parsing ─────────────────────────────────────────────────
|
|
590933
|
+
// Accepts BOTH:
|
|
590934
|
+
// oa-share://[peerId@]host:port#key
|
|
590935
|
+
// http(s)://host:port/?oa-key=KEY[&oa-share-label=LABEL]
|
|
590936
|
+
// Returns { host, key, scheme?, label? } or null when the input is a plain key.
|
|
590937
|
+
function parseShareInput(raw) {
|
|
590938
|
+
const v = String(raw || '').trim();
|
|
590939
|
+
if (!v) return null;
|
|
590940
|
+
// oa-share scheme — use a custom parser since URL() doesn't always honor it.
|
|
590941
|
+
if (v.toLowerCase().startsWith('oa-share://')) {
|
|
590942
|
+
const after = v.slice('oa-share://'.length);
|
|
590943
|
+
const hashIdx = after.indexOf('#');
|
|
590944
|
+
if (hashIdx < 0) return null;
|
|
590945
|
+
const hostPart = after.slice(0, hashIdx);
|
|
590946
|
+
const key = after.slice(hashIdx + 1);
|
|
590947
|
+
if (!hostPart || !key) return null;
|
|
590948
|
+
// Strip optional peerId@ prefix (forward-compat for libp2p tunneling).
|
|
590949
|
+
const atIdx = hostPart.indexOf('@');
|
|
590950
|
+
const host = atIdx >= 0 ? hostPart.slice(atIdx + 1) : hostPart;
|
|
590951
|
+
return { host, key, scheme: 'http' };
|
|
590952
|
+
}
|
|
590953
|
+
// http(s) URL with ?oa-key=...
|
|
590954
|
+
if (/^https?:\\/\\//i.test(v)) {
|
|
590955
|
+
try {
|
|
590956
|
+
const u = new URL(v);
|
|
590957
|
+
const k = u.searchParams.get('oa-key');
|
|
590958
|
+
if (!k) return null;
|
|
590959
|
+
const label = u.searchParams.get('oa-share-label') || '';
|
|
590960
|
+
return { host: u.host, key: k, scheme: u.protocol.replace(':', ''), label };
|
|
590961
|
+
} catch { return null; }
|
|
590962
|
+
}
|
|
590963
|
+
return null;
|
|
590964
|
+
}
|
|
590965
|
+
|
|
590966
|
+
function onKeyInputChange(v) {
|
|
590967
|
+
const hint = document.getElementById('key-input-hint');
|
|
590968
|
+
if (!hint) return;
|
|
590969
|
+
const parsed = parseShareInput(v);
|
|
590970
|
+
if (parsed) {
|
|
590971
|
+
hint.innerHTML = '✓ detected share URL — host <code>' + escapeHtml(parsed.host) + '</code>; clicking save will open the remote UI with this key';
|
|
590972
|
+
hint.style.color = 'var(--color-success)';
|
|
590973
|
+
} else {
|
|
590974
|
+
hint.innerHTML = 'Tip: paste an <code>oa-share://host:port#key</code> URL or <code>http://host:port/?oa-key=…</code> to connect to a remote OA instance.';
|
|
590975
|
+
hint.style.color = 'var(--color-fg-muted)';
|
|
590976
|
+
}
|
|
590977
|
+
showRecentKeysDropdown();
|
|
590805
590978
|
}
|
|
590806
590979
|
|
|
590980
|
+
// Tiny HTML escape — same as the one used in connections settings.
|
|
590981
|
+
function escapeHtml(s) {
|
|
590982
|
+
return String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
590983
|
+
}
|
|
590984
|
+
|
|
590985
|
+
// ─── Recent keys (localStorage history with autocomplete dropdown) ──
|
|
590986
|
+
const _OA_RECENT_KEYS_LS = 'oa.recentKeys';
|
|
590987
|
+
const _OA_RECENT_KEYS_MAX = 10;
|
|
590988
|
+
function loadRecentKeys() {
|
|
590989
|
+
try {
|
|
590990
|
+
const raw = localStorage.getItem(_OA_RECENT_KEYS_LS);
|
|
590991
|
+
if (!raw) return [];
|
|
590992
|
+
const parsed = JSON.parse(raw);
|
|
590993
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
590994
|
+
} catch { return []; }
|
|
590995
|
+
}
|
|
590996
|
+
function saveRecentKey(rec) {
|
|
590997
|
+
if (!rec || !rec.key) return;
|
|
590998
|
+
let list = loadRecentKeys();
|
|
590999
|
+
// Move existing entry with same key to top; otherwise prepend.
|
|
591000
|
+
list = list.filter(r => r.key !== rec.key);
|
|
591001
|
+
list.unshift({ ...rec, lastUsed: new Date().toISOString() });
|
|
591002
|
+
if (list.length > _OA_RECENT_KEYS_MAX) list = list.slice(0, _OA_RECENT_KEYS_MAX);
|
|
591003
|
+
try { localStorage.setItem(_OA_RECENT_KEYS_LS, JSON.stringify(list)); } catch {}
|
|
591004
|
+
}
|
|
591005
|
+
function deleteRecentKey(key) {
|
|
591006
|
+
let list = loadRecentKeys().filter(r => r.key !== key);
|
|
591007
|
+
try { localStorage.setItem(_OA_RECENT_KEYS_LS, JSON.stringify(list)); } catch {}
|
|
591008
|
+
showRecentKeysDropdown();
|
|
591009
|
+
}
|
|
591010
|
+
window.deleteRecentKey = deleteRecentKey;
|
|
591011
|
+
|
|
591012
|
+
function showRecentKeysDropdown() {
|
|
591013
|
+
const dd = document.getElementById('key-recent-dropdown');
|
|
591014
|
+
if (!dd) return;
|
|
591015
|
+
const recents = loadRecentKeys();
|
|
591016
|
+
const inp = document.getElementById('key-input');
|
|
591017
|
+
const filter = (inp && inp.value || '').toLowerCase();
|
|
591018
|
+
const matches = recents.filter(r => {
|
|
591019
|
+
if (!filter) return true;
|
|
591020
|
+
return (r.key || '').toLowerCase().includes(filter)
|
|
591021
|
+
|| (r.host || '').toLowerCase().includes(filter)
|
|
591022
|
+
|| (r.label || '').toLowerCase().includes(filter);
|
|
591023
|
+
});
|
|
591024
|
+
if (matches.length === 0) { dd.style.display = 'none'; return; }
|
|
591025
|
+
dd.innerHTML = matches.map(r => {
|
|
591026
|
+
const k = String(r.key || '');
|
|
591027
|
+
const masked = k.length > 12 ? k.slice(0, 4) + '…' + k.slice(-4) : k;
|
|
591028
|
+
const host = escapeHtml(r.host || '');
|
|
591029
|
+
const label = escapeHtml(r.label || '');
|
|
591030
|
+
const keySafe = k.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, "\\\\'");
|
|
591031
|
+
return '<div class="oa-rec-key" style="display:flex;align-items:center;gap:8px;padding:6px 10px;cursor:pointer;border-bottom:1px solid var(--color-border)" '
|
|
591032
|
+
+ 'onclick="useRecentKey(\\'' + keySafe + '\\')" '
|
|
591033
|
+
+ 'onmouseover="this.style.background=\\'var(--color-bg-hover)\\'" '
|
|
591034
|
+
+ 'onmouseout="this.style.background=\\'transparent\\'">'
|
|
591035
|
+
+ '<span style="flex:1;font-family:var(--font-mono);font-size:0.78rem"><span style="color:var(--color-fg-muted)">' + (host || 'local') + '</span> · <code>' + masked + '</code></span>'
|
|
591036
|
+
+ '<span style="font-size:0.7rem;color:var(--color-fg-muted)">' + label + '</span>'
|
|
591037
|
+
+ '<button type="button" onclick="event.stopPropagation();deleteRecentKey(\\'' + keySafe + '\\')" '
|
|
591038
|
+
+ 'title="Forget this key" '
|
|
591039
|
+
+ 'style="background:transparent;border:none;color:var(--color-fg-muted);cursor:pointer;font-size:1rem;padding:0 4px">×</button>'
|
|
591040
|
+
+ '</div>';
|
|
591041
|
+
}).join('');
|
|
591042
|
+
dd.style.display = 'block';
|
|
591043
|
+
}
|
|
591044
|
+
function hideRecentKeysDropdown() {
|
|
591045
|
+
const dd = document.getElementById('key-recent-dropdown');
|
|
591046
|
+
if (dd) dd.style.display = 'none';
|
|
591047
|
+
}
|
|
591048
|
+
function useRecentKey(k) {
|
|
591049
|
+
const inp = document.getElementById('key-input');
|
|
591050
|
+
if (inp) {
|
|
591051
|
+
inp.value = k;
|
|
591052
|
+
inp.focus();
|
|
591053
|
+
onKeyInputChange(k);
|
|
591054
|
+
}
|
|
591055
|
+
hideRecentKeysDropdown();
|
|
591056
|
+
}
|
|
591057
|
+
window.useRecentKey = useRecentKey;
|
|
591058
|
+
|
|
591059
|
+
// ─── Generate share URL ────────────────────────────────────────────────
|
|
591060
|
+
async function generateShareUrl() {
|
|
591061
|
+
const out = document.getElementById('share-result');
|
|
591062
|
+
if (!out) return;
|
|
591063
|
+
out.style.display = 'block';
|
|
591064
|
+
out.innerHTML = '<div style="color:var(--color-fg-muted)">generating…</div>';
|
|
591065
|
+
try {
|
|
591066
|
+
const r = await fetch('/v1/share/generate', {
|
|
591067
|
+
method: 'POST',
|
|
591068
|
+
headers: { ...headers(), 'Content-Type': 'application/json' },
|
|
591069
|
+
body: JSON.stringify({ scope: 'run' }),
|
|
591070
|
+
});
|
|
591071
|
+
if (r.status === 403) {
|
|
591072
|
+
out.innerHTML = '<div style="color:var(--color-error)">✗ admin scope required — your current key does not have permission to mint share URLs. Set an admin-scope key first.</div>';
|
|
591073
|
+
return;
|
|
591074
|
+
}
|
|
591075
|
+
if (r.status >= 400) {
|
|
591076
|
+
const err = await r.json().catch(() => ({}));
|
|
591077
|
+
out.innerHTML = '<div style="color:var(--color-error)">✗ HTTP ' + r.status + (err.detail ? ' — ' + escapeHtml(err.detail) : '') + '</div>';
|
|
591078
|
+
return;
|
|
591079
|
+
}
|
|
591080
|
+
const j = await r.json();
|
|
591081
|
+
const safeShare = escapeHtml(j.shareUrl || '');
|
|
591082
|
+
const safePlain = escapeHtml(j.plainUrl || '');
|
|
591083
|
+
out.innerHTML =
|
|
591084
|
+
'<div style="font-weight:500;margin-bottom:6px;color:var(--color-success)">✓ Share URL generated — hand off to remote</div>' +
|
|
591085
|
+
'<div style="margin:6px 0">' +
|
|
591086
|
+
'<label style="display:block;font-size:0.7rem;color:var(--color-fg-muted);margin-bottom:2px">oa-share URL (compact, recommended for OA-to-OA)</label>' +
|
|
591087
|
+
'<div style="display:flex;gap:6px;align-items:center">' +
|
|
591088
|
+
'<input readonly value="' + safeShare + '" style="flex:1;background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:5px 8px;border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:0.74rem">' +
|
|
591089
|
+
'<button type="button" onclick="copyShareUrl(\\'oa-share\\')" style="font-size:0.74rem">copy</button>' +
|
|
591090
|
+
'</div>' +
|
|
591091
|
+
'</div>' +
|
|
591092
|
+
'<div style="margin:6px 0">' +
|
|
591093
|
+
'<label style="display:block;font-size:0.7rem;color:var(--color-fg-muted);margin-bottom:2px">plain URL (works in any browser, no scheme handler needed)</label>' +
|
|
591094
|
+
'<div style="display:flex;gap:6px;align-items:center">' +
|
|
591095
|
+
'<input readonly value="' + safePlain + '" style="flex:1;background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:5px 8px;border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:0.74rem">' +
|
|
591096
|
+
'<button type="button" onclick="copyShareUrl(\\'plain\\')" style="font-size:0.74rem">copy</button>' +
|
|
591097
|
+
'</div>' +
|
|
591098
|
+
'</div>' +
|
|
591099
|
+
'<p style="font-size:0.68rem;color:var(--color-fg-muted);margin:6px 0 0">Note: this URL contains the full secret. Anyone with it can use this OA. To revoke: open Advanced settings → keys → revoke the prefix <code>' + escapeHtml(j.keyPrefix || '') + '</code>.</p>';
|
|
591100
|
+
// Stash for the copy buttons + add to recent.
|
|
591101
|
+
window.__oaLastShareUrl = j.shareUrl;
|
|
591102
|
+
window.__oaLastPlainUrl = j.plainUrl;
|
|
591103
|
+
saveRecentKey({ key: j.key, host: j.host + ':' + j.port, label: j.label || ('shared ' + j.host) });
|
|
591104
|
+
} catch (e) {
|
|
591105
|
+
out.innerHTML = '<div style="color:var(--color-error)">✗ ' + escapeHtml(e && e.message ? e.message : String(e)) + '</div>';
|
|
591106
|
+
}
|
|
591107
|
+
}
|
|
591108
|
+
function copyShareUrl(which) {
|
|
591109
|
+
const v = which === 'plain' ? window.__oaLastPlainUrl : window.__oaLastShareUrl;
|
|
591110
|
+
if (!v) return;
|
|
591111
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
591112
|
+
navigator.clipboard.writeText(v).then(
|
|
591113
|
+
() => { /* ok */ },
|
|
591114
|
+
() => { fallbackCopy(v); }
|
|
591115
|
+
);
|
|
591116
|
+
} else {
|
|
591117
|
+
fallbackCopy(v);
|
|
591118
|
+
}
|
|
591119
|
+
}
|
|
591120
|
+
function fallbackCopy(v) {
|
|
591121
|
+
const ta = document.createElement('textarea');
|
|
591122
|
+
ta.value = v; document.body.appendChild(ta); ta.select();
|
|
591123
|
+
try { document.execCommand('copy'); } catch {}
|
|
591124
|
+
document.body.removeChild(ta);
|
|
591125
|
+
}
|
|
591126
|
+
window.generateShareUrl = generateShareUrl;
|
|
591127
|
+
window.copyShareUrl = copyShareUrl;
|
|
591128
|
+
|
|
591129
|
+
// ─── Remote-state helpers (visual indicator on the key button) ───────
|
|
591130
|
+
// When localStorage carries oa.remoteHost we are in REMOTE mode: the
|
|
591131
|
+
// key was loaded from an oa-share URL or via on-load ?oa-key=. The key
|
|
591132
|
+
// button shows a "remote" badge and clicking expands to show host+key
|
|
591133
|
+
// inline with a "close connection" button that severs and shuffles the
|
|
591134
|
+
// key into recents.
|
|
591135
|
+
function refreshKeyModalRemoteState() {
|
|
591136
|
+
const remote = (function() {
|
|
591137
|
+
try { return localStorage.getItem('oa.remoteHost') || ''; } catch { return ''; }
|
|
591138
|
+
})();
|
|
591139
|
+
const stateBox = document.getElementById('remote-state');
|
|
591140
|
+
const btn = document.getElementById('sidebar-key-btn');
|
|
591141
|
+
if (remote && btn) {
|
|
591142
|
+
btn.textContent = 'remote';
|
|
591143
|
+
btn.style.background = 'var(--color-accent)';
|
|
591144
|
+
btn.style.color = '#fff';
|
|
591145
|
+
btn.style.borderColor = 'var(--color-accent)';
|
|
591146
|
+
btn.title = 'connected to remote ' + remote + ' — click to view / disconnect';
|
|
591147
|
+
} else if (btn) {
|
|
591148
|
+
btn.textContent = 'key';
|
|
591149
|
+
btn.style.background = 'transparent';
|
|
591150
|
+
btn.style.color = 'var(--color-fg-muted)';
|
|
591151
|
+
btn.style.borderColor = 'var(--color-border)';
|
|
591152
|
+
btn.title = 'set API key / share access';
|
|
591153
|
+
}
|
|
591154
|
+
if (!stateBox) return;
|
|
591155
|
+
if (remote) {
|
|
591156
|
+
const safeRemote = escapeHtml(remote);
|
|
591157
|
+
stateBox.style.display = 'block';
|
|
591158
|
+
stateBox.innerHTML =
|
|
591159
|
+
'<div style="font-weight:500;color:var(--color-accent)">REMOTE connection active</div>' +
|
|
591160
|
+
'<div style="margin-top:4px;font-size:0.74rem">host <code>' + safeRemote + '</code></div>' +
|
|
591161
|
+
'<div style="margin-top:8px"><button type="button" onclick="closeRemoteConnection()" style="background:var(--color-error);color:#fff;border:none;padding:4px 10px;border-radius:var(--radius-sm);cursor:pointer;font-size:0.74rem">close connection</button></div>';
|
|
591162
|
+
} else {
|
|
591163
|
+
stateBox.style.display = 'none';
|
|
591164
|
+
}
|
|
591165
|
+
}
|
|
591166
|
+
function closeRemoteConnection() {
|
|
591167
|
+
// Move current remote key into recents BEFORE clearing.
|
|
591168
|
+
let savedKey = '';
|
|
591169
|
+
let savedHost = '';
|
|
591170
|
+
try {
|
|
591171
|
+
savedKey = localStorage.getItem('oa-api-key') || '';
|
|
591172
|
+
savedHost = localStorage.getItem('oa.remoteHost') || '';
|
|
591173
|
+
} catch {}
|
|
591174
|
+
if (savedKey) {
|
|
591175
|
+
saveRecentKey({ key: savedKey, host: savedHost, label: 'recent remote ' + savedHost });
|
|
591176
|
+
}
|
|
591177
|
+
try {
|
|
591178
|
+
localStorage.removeItem('oa.remoteHost');
|
|
591179
|
+
localStorage.removeItem('oa.remoteScheme');
|
|
591180
|
+
localStorage.removeItem('oa-api-key');
|
|
591181
|
+
} catch {}
|
|
591182
|
+
apiKey = '';
|
|
591183
|
+
refreshKeyModalRemoteState();
|
|
591184
|
+
// Reload to drop any cached state from the remote target.
|
|
591185
|
+
location.reload();
|
|
591186
|
+
}
|
|
591187
|
+
window.closeRemoteConnection = closeRemoteConnection;
|
|
591188
|
+
|
|
591189
|
+
// ─── On-load: ?oa-key=... pickup ───────────────────────────────────────
|
|
591190
|
+
// When the page loads with an oa-key query param (i.e. someone clicked
|
|
591191
|
+
// a share URL), capture the key into localStorage, mark this as a
|
|
591192
|
+
// remote session (host = this origin), and clean the URL.
|
|
591193
|
+
(function pickupShareKeyFromUrl() {
|
|
591194
|
+
try {
|
|
591195
|
+
const u = new URL(location.href);
|
|
591196
|
+
const k = u.searchParams.get('oa-key');
|
|
591197
|
+
if (!k) return;
|
|
591198
|
+
const label = u.searchParams.get('oa-share-label') || '';
|
|
591199
|
+
localStorage.setItem('oa-api-key', k);
|
|
591200
|
+
// Origin-based remote: when the page loaded from a different host
|
|
591201
|
+
// than the user's "home" OA, that's a remote session by definition.
|
|
591202
|
+
localStorage.setItem('oa.remoteHost', location.host);
|
|
591203
|
+
localStorage.setItem('oa.remoteScheme', location.protocol.replace(':', ''));
|
|
591204
|
+
saveRecentKey({ key: k, host: location.host, label: label || ('remote ' + location.host) });
|
|
591205
|
+
// Strip the key from the URL so it isn't visible / browser-history'd.
|
|
591206
|
+
u.searchParams.delete('oa-key');
|
|
591207
|
+
u.searchParams.delete('oa-share-label');
|
|
591208
|
+
history.replaceState({}, '', u.pathname + (u.search || '') + (u.hash || ''));
|
|
591209
|
+
} catch {}
|
|
591210
|
+
})();
|
|
591211
|
+
|
|
590807
591212
|
// Tab switching
|
|
590808
591213
|
const allPanels = ['chat-container','agent-panel','jobs-panel','config-panel','activity-panel','projects-panel','voice-panel'];
|
|
590809
591214
|
function switchTab(tab) {
|
|
@@ -591536,6 +591941,11 @@ function newChatSession() {
|
|
|
591536
591941
|
switchSession('');
|
|
591537
591942
|
const sel = document.getElementById('chat-session-select');
|
|
591538
591943
|
if (sel) sel.value = '';
|
|
591944
|
+
// FRESH-SESSION: mark the next chat send as fresh so the daemon skips
|
|
591945
|
+
// cross-task handoff injection (which previously bled prior session
|
|
591946
|
+
// context into the new chat regardless of the browser's reset).
|
|
591947
|
+
// Cleared inside the chat send handler after one use.
|
|
591948
|
+
try { localStorage.setItem('oa.freshSessionPending', '1'); } catch {}
|
|
591539
591949
|
}
|
|
591540
591950
|
function deleteChatSession() {
|
|
591541
591951
|
if (!chatSessionId) return;
|
|
@@ -592399,6 +592809,7 @@ async function doUpdate() {
|
|
|
592399
592809
|
try { pollMetrics(); } catch {}
|
|
592400
592810
|
try { loadScheduled(); } catch {}
|
|
592401
592811
|
try { loadServices(); } catch {}
|
|
592812
|
+
try { refreshKeyModalRemoteState(); } catch {}
|
|
592402
592813
|
|
|
592403
592814
|
btn.textContent = 'updated v' + newVersion;
|
|
592404
592815
|
btn.style.background = '#1a3a1a';
|
|
@@ -593383,6 +593794,7 @@ $currentProject.subscribe((proj) => {
|
|
|
593383
593794
|
try { if (typeof updateSessionSelect === 'function') updateSessionSelect(); } catch {}
|
|
593384
593795
|
try { if (typeof updateAgentRunSelect === 'function') updateAgentRunSelect(); } catch {}
|
|
593385
593796
|
try { if (typeof restoreChatSession === 'function') restoreChatSession(); } catch {}
|
|
593797
|
+
try { if (typeof refreshKeyModalRemoteState === 'function') refreshKeyModalRemoteState(); } catch {}
|
|
593386
593798
|
});
|
|
593387
593799
|
|
|
593388
593800
|
// $selectedModel changes update the visible <select> if it's not
|
|
@@ -600994,6 +601406,9 @@ ${historyLines}
|
|
|
600994
601406
|
runEnv["OA_RUN_USER"] = req2._authUser || "anonymous";
|
|
600995
601407
|
runEnv["OA_RUN_SCOPE"] = req2._authScope || "admin";
|
|
600996
601408
|
runEnv["OA_SESSION_ID"] = session.id;
|
|
601409
|
+
if (chatBody["fresh"] === true) {
|
|
601410
|
+
runEnv["OA_FRESH_SESSION"] = "1";
|
|
601411
|
+
}
|
|
600997
601412
|
runEnv["OLLAMA_HOST"] = currentCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434";
|
|
600998
601413
|
if (currentCfg.apiKey) runEnv["OA_API_KEY_INHERIT"] = currentCfg.apiKey;
|
|
600999
601414
|
const child = spawn25(process.execPath, [oaBin, ...args], {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-agents-ai",
|
|
3
|
-
"version": "0.187.
|
|
3
|
+
"version": "0.187.534",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "open-agents-ai",
|
|
9
|
-
"version": "0.187.
|
|
9
|
+
"version": "0.187.534",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "CC-BY-NC-4.0",
|
|
12
12
|
"dependencies": {
|
|
@@ -2192,9 +2192,9 @@
|
|
|
2192
2192
|
}
|
|
2193
2193
|
},
|
|
2194
2194
|
"node_modules/bare-url": {
|
|
2195
|
-
"version": "2.4.
|
|
2196
|
-
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.
|
|
2197
|
-
"integrity": "sha512
|
|
2195
|
+
"version": "2.4.3",
|
|
2196
|
+
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.3.tgz",
|
|
2197
|
+
"integrity": "sha512-Kccpc7ACfXaxfeInfqKcZtW4pT5YBn1mesc4sCsun6sRwtbJ4h+sNOaksUpYEJUKfN65YWC6Bw2OJEFiKxq8nQ==",
|
|
2198
2198
|
"license": "Apache-2.0",
|
|
2199
2199
|
"optional": true,
|
|
2200
2200
|
"dependencies": {
|
|
@@ -3179,9 +3179,9 @@
|
|
|
3179
3179
|
}
|
|
3180
3180
|
},
|
|
3181
3181
|
"node_modules/fast-uri": {
|
|
3182
|
-
"version": "3.1.
|
|
3183
|
-
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.
|
|
3184
|
-
"integrity": "sha512-
|
|
3182
|
+
"version": "3.1.2",
|
|
3183
|
+
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
|
|
3184
|
+
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
|
|
3185
3185
|
"funding": [
|
|
3186
3186
|
{
|
|
3187
3187
|
"type": "github",
|
|
@@ -3638,9 +3638,9 @@
|
|
|
3638
3638
|
}
|
|
3639
3639
|
},
|
|
3640
3640
|
"node_modules/hono": {
|
|
3641
|
-
"version": "4.12.
|
|
3642
|
-
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.
|
|
3643
|
-
"integrity": "sha512-
|
|
3641
|
+
"version": "4.12.17",
|
|
3642
|
+
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.17.tgz",
|
|
3643
|
+
"integrity": "sha512-FbJJNb/XgX7YW0hX/V8w5oYLztKEsRLykCMZWt1WdLtsfjzMvmoqWBA4H4t5norinq8/rh20oiZYr+WSl4UzAQ==",
|
|
3644
3644
|
"license": "MIT",
|
|
3645
3645
|
"engines": {
|
|
3646
3646
|
"node": ">=16.9.0"
|
package/package.json
CHANGED