mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
ec8f6e5e03
* feat(browser): add copilot security contracts * fix(gateway): expose verified client identity to handlers * feat(browser): add secure per-tab copilot panel Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * refactor(browser): separate copilot gateway hint custody * fix(browser): preserve legacy pairing parse shape * fix(browser): harden copilot lifecycle custody Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): enforce copilot lifecycle boundaries Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * style(browser): format copilot sources Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): preserve copilot consent revocation Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * refactor(browser): split copilot custody owners Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(browser): normalize websocket array buffers Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * chore(protocol): regenerate Swift gateway models Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * refactor(browser): model copilot runtime entrypoints Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): honor extension build boundaries Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(gateway): assert targeted chat delivery Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(gateway): cover targeted delivery calls Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): declare copilot build dependencies Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(ci): clear browser copilot gate failures Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(ci): cover copilot lint exclusion Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * fix(browser): gate copilot on relay custody Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> * test(browser): bound copilot relay frames Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com> --------- Co-authored-by: Cameron Beeley <cameron.beeley@gmail.com>
90 lines
2.6 KiB
JavaScript
90 lines
2.6 KiB
JavaScript
/** Relay/run boundary owner for the tab-bound copilot. */
|
|
export function createCopilotRelayCustodyController({
|
|
appendGatewayRevocation,
|
|
broadcastStatus,
|
|
currentGatewayScope,
|
|
drainAborts,
|
|
getGatewayStatus,
|
|
invalidateGatewayEpoch,
|
|
markGatewayAbortError,
|
|
registry,
|
|
revokeActiveBindings,
|
|
runLifecycle,
|
|
}) {
|
|
let ready = false;
|
|
let label = "Connecting to browser relay";
|
|
let statusRevision = 0;
|
|
let reconciledStatusRevision = 0;
|
|
let pendingRevocation = Promise.resolve();
|
|
|
|
function isOperational() {
|
|
return ready && reconciledStatusRevision === statusRevision;
|
|
}
|
|
|
|
function currentPanelStatus() {
|
|
const gatewayStatus = getGatewayStatus();
|
|
return gatewayStatus.state === "ready" && !isOperational()
|
|
? { state: "connecting", label }
|
|
: gatewayStatus;
|
|
}
|
|
|
|
async function onStatus(status) {
|
|
const nextReady = status.ready === true;
|
|
const readinessChanged = ready !== nextReady;
|
|
ready = nextReady;
|
|
label = status.label || "Browser relay reconnecting";
|
|
if (!readinessChanged) {
|
|
broadcastStatus();
|
|
return;
|
|
}
|
|
// Relay availability is part of the run epoch. Reconcile debugger/run
|
|
// custody before a reconnected tool route can admit panel work again.
|
|
invalidateGatewayEpoch();
|
|
const revision = ++statusRevision;
|
|
if (nextReady) {
|
|
broadcastStatus();
|
|
await pendingRevocation;
|
|
await runLifecycle(async () => {
|
|
const gatewayScope = currentGatewayScope();
|
|
if (revision === statusRevision && ready && gatewayScope) {
|
|
await drainAborts(gatewayScope);
|
|
}
|
|
});
|
|
if (revision !== statusRevision || !ready) {
|
|
return;
|
|
}
|
|
reconciledStatusRevision = revision;
|
|
const gatewayScope = currentGatewayScope();
|
|
if (
|
|
gatewayScope &&
|
|
registry.pendingAborts(gatewayScope).length > 0 &&
|
|
getGatewayStatus().state === "ready"
|
|
) {
|
|
markGatewayAbortError();
|
|
broadcastStatus();
|
|
return;
|
|
}
|
|
broadcastStatus({ ensureSetup: true, hydrateHistory: true });
|
|
return;
|
|
}
|
|
reconciledStatusRevision = 0;
|
|
broadcastStatus();
|
|
const gatewayScope = currentGatewayScope();
|
|
if (!gatewayScope) {
|
|
return;
|
|
}
|
|
const revocation = revokeActiveBindings(gatewayScope);
|
|
appendGatewayRevocation(revocation);
|
|
const cleanup = runLifecycle(async () => {
|
|
await revocation;
|
|
if (gatewayScope === currentGatewayScope() && !ready) {
|
|
await drainAborts(gatewayScope);
|
|
}
|
|
});
|
|
pendingRevocation = cleanup.catch(() => undefined);
|
|
await pendingRevocation;
|
|
}
|
|
|
|
return { currentPanelStatus, isOperational, onStatus };
|
|
}
|