aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Soarez <igor@soarez.org>2026-11-11 13:15:51 +0200
committerIgor Soarez <igor@soarez.org>2026-11-11 13:15:51 +0200
commite18559a1240219b977ffc7da2076c09cd96d1dad (patch)
treefed77646ab6ba996538a34b9b6daa3728d235327
parentcf47a164991bf5588fd2b41ce244f55d900c1167 (diff)
extension: never show the bridge footer while PI_CHROME_AUTHORIZE is setHEADmaster
The footer was hidden through envGranted, a flag that depended on which grant a runtime ended up with. Any grant restored from globalThis, such as one an older build saved there before a /reload or a session switch, left the flag false and brought the footer back. Decide from the environment itself, on every status update: with a valid PI_CHROME_AUTHORIZE the footer never shows, whatever grant is active. A /reload is enough to clear the footer in a pi that has one today. The envGranted flag is gone.
-rw-r--r--extensions/chrome-profile-bridge/index.ts16
-rw-r--r--test-suite/unit/grant-scope.test.mjs30
2 files changed, 35 insertions, 11 deletions
diff --git a/extensions/chrome-profile-bridge/index.ts b/extensions/chrome-profile-bridge/index.ts
index 0338c5c..97b9f33 100644
--- a/extensions/chrome-profile-bridge/index.ts
+++ b/extensions/chrome-profile-bridge/index.ts
@@ -924,9 +924,6 @@ export default function (pi: ExtensionAPI): void {
const bridge = new ChromeProfileBridge(DEFAULT_HOST, DEFAULT_PORT);
let backgroundEnabled = true;
let chromeAuthorizedUntil: number | "indefinite" | undefined;
- // Whether the current grant stood in from PI_CHROME_AUTHORIZE rather than being given by
- // the user this session. The footer announces only the latter.
- let envGranted = false;
// Restore an authorization that survived a /reload. Drop it if it already expired.
const persistedAuth = globalState[PI_CHROME_AUTH_KEY];
if (persistedAuth) {
@@ -1026,7 +1023,6 @@ export default function (pi: ExtensionAPI): void {
chromeToolsUsable = false;
if (logAction && wasUsable) logChromeToolChange(logAction, { authorizedUntil: undefined });
chromeAuthorizedUntil = undefined;
- envGranted = false;
persistAuth();
// Revoking control ends pi-chrome's automation for this session; tidy up the target we own.
void cleanupAutomationTargetBestEffort();
@@ -1083,8 +1079,16 @@ export default function (pi: ExtensionAPI): void {
return typeof id === "string" && id ? `session:${id}` : undefined;
};
+ // FORK: with PI_CHROME_AUTHORIZE set, Chrome control is standing configuration, not session
+ // state, so the footer never shows. Read from the environment on every update, not from any
+ // saved state, so no grant restored across /reload or a session switch can bring it back.
+ const envAuthorizes = (): boolean => {
+ const value = process.env.PI_CHROME_AUTHORIZE?.trim();
+ return !!value && parseAuthorizeArg(value) !== undefined;
+ };
+
const updateChromeStatus = (ctx: ExtensionContext): void => {
- if (chromeControlAuthorized() && !envGranted) {
+ if (chromeControlAuthorized() && !envAuthorizes()) {
ctx.ui.setStatus("chrome", ctx.ui.theme.fg("success", "●") + " Chrome Bridge" + authCountdownLabel());
} else {
ctx.ui.setStatus("chrome", undefined);
@@ -1180,7 +1184,6 @@ export default function (pi: ExtensionAPI): void {
const grant = parseAuthorizeArg(autoGrant);
if (grant) {
chromeAuthorizedUntil = grant.until;
- envGranted = true;
} else {
ctx.ui.notify(`Ignoring PI_CHROME_AUTHORIZE="${autoGrant}": use minutes (15m, 30, 45) or indefinite.`, "warning");
}
@@ -1415,7 +1418,6 @@ Usage rules:
}
const wasUsable = chromeToolsUsable;
chromeAuthorizedUntil = until;
- envGranted = false;
persistAuth();
activateChromeTools();
chromeToolsUsable = true;
diff --git a/test-suite/unit/grant-scope.test.mjs b/test-suite/unit/grant-scope.test.mjs
index e7ce044..7717131 100644
--- a/test-suite/unit/grant-scope.test.mjs
+++ b/test-suite/unit/grant-scope.test.mjs
@@ -24,6 +24,8 @@ function piProcess(env) {
setTimeout, clearTimeout, setInterval, clearInterval,
});
let runtime;
+ // A grant an earlier runtime left on globalThis, e.g. a build that saved the env grant there.
+ const seedGrant = (until) => vm.runInContext(`globalThis.__piChromeProfileBridgeAuth__ = ${JSON.stringify({ until })}`, context);
// Evaluate the module again and run its factory, as pi does at startup, on /reload and on a
// session switch.
const load = () => {
@@ -65,7 +67,7 @@ function piProcess(env) {
return next;
};
// Stops the bridge and timers of the live runtime, so a failed assertion cannot hang the run.
- return { load, replace, end: () => runtime?.shutdown("quit") };
+ return { load, replace, seedGrant, end: () => runtime?.shutdown("quit") };
}
test("PI_CHROME_AUTHORIZE grants every runtime without announcing it", async (t) => {
@@ -104,18 +106,38 @@ test("/chrome authorize survives /reload and ends with its session", async (t) =
}
});
-test("/chrome authorize over PI_CHROME_AUTHORIZE is announced, and a switch falls back to the env grant", async (t) => {
+test("with PI_CHROME_AUTHORIZE set, /chrome authorize does not bring the footer, and a switch falls back to the env grant", async (t) => {
const pi = piProcess({ PI_CHROME_AUTHORIZE: "indefinite" });
t.after(() => pi.end());
const first = pi.load();
await first.start();
await first.chrome("authorize 15m");
- assert.match(first.footer(), /^● Chrome Bridge \(\d+m\)$/);
+ assert.equal(first.chromeTools(), true);
+ assert.equal(first.footer(), undefined);
const reloaded = await pi.replace("reload");
- assert.match(reloaded.footer(), /^● Chrome Bridge \(\d+m\)$/);
+ assert.equal(reloaded.footer(), undefined);
const next = await pi.replace("new");
assert.equal(next.chromeTools(), true);
assert.equal(next.footer(), undefined);
});
+
+test("with PI_CHROME_AUTHORIZE set, a grant left on globalThis by an older build never shows the footer", async (t) => {
+ for (const until of ["indefinite", Date.now() + 3_600_000]) {
+ const pi = piProcess({ PI_CHROME_AUTHORIZE: "indefinite" });
+ t.after(() => pi.end());
+ pi.seedGrant(until);
+ const first = pi.load();
+ await first.start();
+ assert.equal(first.chromeTools(), true);
+ assert.equal(first.footer(), undefined, `footer at start, saved until ${until}`);
+ for (const reason of ["reload", "new", "resume", "fork"]) {
+ pi.seedGrant(until);
+ const next = await pi.replace(reason);
+ assert.equal(next.chromeTools(), true, `tools after ${reason}`);
+ assert.equal(next.footer(), undefined, `footer after ${reason}, saved until ${until}`);
+ }
+ await pi.end();
+ }
+});