@bivy/bivy 0.3.0-staging.45 → 0.3.0-staging.46
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.
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: FSL-1.1-ALv2
|
|
2
|
+
// Copyright (c) 2026 Petter André Sjulstad
|
|
3
|
+
//
|
|
4
|
+
// Pure decision for sweeping stale browser-initiated OAuth logins (see the
|
|
5
|
+
// `oauthLogins` registry in src/server.ts). A login parks the node until the
|
|
6
|
+
// remote device pastes its code; an abandoned one must be reaped (aborting it
|
|
7
|
+
// closes the local callback http.Server) and a finished one dropped after a
|
|
8
|
+
// short grace so clients can still read its final status. Kept pure + isolated
|
|
9
|
+
// so the edge logic is unit-tested without importing the daemon.
|
|
10
|
+
export function isTerminalOAuthStatus(status) {
|
|
11
|
+
return status === "done" || status === "error";
|
|
12
|
+
}
|
|
13
|
+
export function decideOAuthLoginSweep(status, ageMs, opts) {
|
|
14
|
+
const terminal = isTerminalOAuthStatus(status);
|
|
15
|
+
const expired = terminal ? ageMs > opts.graceMs : ageMs > opts.ttlMs;
|
|
16
|
+
if (!expired)
|
|
17
|
+
return { drop: false, abort: false };
|
|
18
|
+
return { drop: true, abort: !terminal };
|
|
19
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -27,6 +27,7 @@ import { provisionAgentRun } from "./runtime/credential-provisioning.js";
|
|
|
27
27
|
import { ingestAgentCredentials } from "./runtime/credential-ingest.js";
|
|
28
28
|
import { suggestNameFromSelectedModel } from "./runtime/model-namer.js";
|
|
29
29
|
import { isNativeOAuthProvider, loginModelOAuth } from "./runtime/oauth/model-oauth.js";
|
|
30
|
+
import { decideOAuthLoginSweep } from "./runtime/oauth/oauth-login-sweep.js";
|
|
30
31
|
import { listCodexSessions, loadCodexTranscript, discoverCodexSessionForCwd } from "./runtime/codex-sessions.js";
|
|
31
32
|
import { dedupeSessionSummaries } from "./session-identity.js";
|
|
32
33
|
import { discoverPiSessionForCwd } from "./runtime/pi-session-discovery.js";
|
|
@@ -755,6 +756,40 @@ let relay;
|
|
|
755
756
|
const clients = new Set();
|
|
756
757
|
const commandProcesses = new Map();
|
|
757
758
|
const oauthLogins = new Map();
|
|
759
|
+
// A browser-initiated subscription login parks the node on `manualCodePromise`
|
|
760
|
+
// until the remote device pastes the code (`provider.oauth.code`). If the user
|
|
761
|
+
// abandons it, the entry — AND its local callback http.Server — would otherwise
|
|
762
|
+
// linger until the process exits. That matters especially on a short-lived
|
|
763
|
+
// ephemeral node. Sweep periodically: abort (which closes the callback server,
|
|
764
|
+
// see startCallbackServer) + drop any in-flight login past its TTL, and drop a
|
|
765
|
+
// finished one after a short grace so clients can still read the final status.
|
|
766
|
+
const OAUTH_LOGIN_TTL_MS = 10 * 60_000;
|
|
767
|
+
const OAUTH_LOGIN_DONE_GRACE_MS = 2 * 60_000;
|
|
768
|
+
function sweepOauthLogins(now = Date.now()) {
|
|
769
|
+
for (const [id, login] of oauthLogins.entries()) {
|
|
770
|
+
const { drop, abort } = decideOAuthLoginSweep(login.status, now - login.createdAt, {
|
|
771
|
+
ttlMs: OAUTH_LOGIN_TTL_MS,
|
|
772
|
+
graceMs: OAUTH_LOGIN_DONE_GRACE_MS,
|
|
773
|
+
});
|
|
774
|
+
if (!drop)
|
|
775
|
+
continue;
|
|
776
|
+
if (abort) {
|
|
777
|
+
login.cancelled = true;
|
|
778
|
+
try {
|
|
779
|
+
login.abort.abort();
|
|
780
|
+
}
|
|
781
|
+
catch { /* already settled */ }
|
|
782
|
+
}
|
|
783
|
+
oauthLogins.delete(id);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
let oauthLoginSweepTimer;
|
|
787
|
+
function startOAuthLoginSweeper() {
|
|
788
|
+
if (oauthLoginSweepTimer)
|
|
789
|
+
return;
|
|
790
|
+
oauthLoginSweepTimer = setInterval(() => sweepOauthLogins(), 60_000);
|
|
791
|
+
oauthLoginSweepTimer.unref?.();
|
|
792
|
+
}
|
|
758
793
|
const openSessions = new Map();
|
|
759
794
|
// Stage 2 (docs/agent-node-decoupling.md): sessionId -> agent-service address for
|
|
760
795
|
// live REMOTE sessions the agent service keeps running across an eviction/
|
|
@@ -5140,6 +5175,9 @@ async function startOAuthLogin(provider) {
|
|
|
5140
5175
|
// OpenAI's browser flow redirects to http://localhost:1455. Listen on IPv6
|
|
5141
5176
|
// wildcard so browsers resolving localhost to ::1 can reach the callback.
|
|
5142
5177
|
process.env.PI_OAUTH_CALLBACK_HOST ||= "::";
|
|
5178
|
+
// Opportunistically drop stale/abandoned logins whenever a new one starts, so a
|
|
5179
|
+
// long-lived node doesn't accumulate them between sweeps (and tests can drive it).
|
|
5180
|
+
sweepOauthLogins();
|
|
5143
5181
|
const id = randomUUID();
|
|
5144
5182
|
const abort = new AbortController();
|
|
5145
5183
|
const state = { id, provider, status: "starting", abort, createdAt: Date.now(), progress: [] };
|
|
@@ -9687,6 +9725,7 @@ const server = app.listen(port, host, async () => {
|
|
|
9687
9725
|
if (process.env.BIVY_RESTORE)
|
|
9688
9726
|
void restoreSessionFromSnapshot(String(process.env.BIVY_RESTORE));
|
|
9689
9727
|
startModelAuthWatcher();
|
|
9728
|
+
startOAuthLoginSweeper();
|
|
9690
9729
|
startGithubAppSyncWatcher();
|
|
9691
9730
|
await startGitHubTasksIfConfigured();
|
|
9692
9731
|
startControlPlaneTasksIfConfigured();
|
package/package.json
CHANGED