pi-chrome 0.15.23 → 0.15.25
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-facing changes to `pi-chrome`.
|
|
4
4
|
|
|
5
|
+
## 0.15.25 — 2026-05-16
|
|
6
|
+
|
|
7
|
+
- **Reload after older installs.** `/reload` now recovers from stale singleton state left by pi-chrome 0.15.19 and earlier instead of skipping the freshly loaded extension.
|
|
8
|
+
- **Test suite coverage.** Added gate buckets plus strict-CSP fallback, dynamic wait readiness, and explicit tab lifecycle challenges.
|
|
9
|
+
|
|
10
|
+
## 0.15.24 — 2026-05-16
|
|
11
|
+
|
|
12
|
+
- **Unload Chrome tools on lock.** `chrome_*` tools now deactivate when `/chrome revoke` runs or timed authorization expires, keeping the prompt/tool list small after Chrome control locks again.
|
|
13
|
+
|
|
5
14
|
## 0.15.23 — 2026-05-16
|
|
6
15
|
|
|
7
16
|
- **Attribution.** The 0.15.22 features below are pulled from Dani Bednarski's fork (`DaniBedz/pi-chrome`). Thank you, Dani.
|
|
@@ -441,6 +441,28 @@ class ChromeProfileBridge {
|
|
|
441
441
|
const tabActionValues = ["list", "new", "activate", "close", "version"] as const;
|
|
442
442
|
const imageFormatValues = ["png", "jpeg"] as const;
|
|
443
443
|
const waitForValues = ["selector", "expression"] as const;
|
|
444
|
+
const CHROME_TOOL_NAMES = [
|
|
445
|
+
"chrome_launch",
|
|
446
|
+
"chrome_tab",
|
|
447
|
+
"chrome_snapshot",
|
|
448
|
+
"chrome_navigate",
|
|
449
|
+
"chrome_evaluate",
|
|
450
|
+
"chrome_click",
|
|
451
|
+
"chrome_type",
|
|
452
|
+
"chrome_fill",
|
|
453
|
+
"chrome_key",
|
|
454
|
+
"chrome_wait_for",
|
|
455
|
+
"chrome_list_console_messages",
|
|
456
|
+
"chrome_list_network_requests",
|
|
457
|
+
"chrome_get_network_request",
|
|
458
|
+
"chrome_screenshot",
|
|
459
|
+
"chrome_hover",
|
|
460
|
+
"chrome_drag",
|
|
461
|
+
"chrome_tap",
|
|
462
|
+
"chrome_scroll",
|
|
463
|
+
"chrome_upload_file",
|
|
464
|
+
] as const;
|
|
465
|
+
const CHROME_TOOL_NAME_SET = new Set<string>(CHROME_TOOL_NAMES);
|
|
444
466
|
|
|
445
467
|
function StringEnum<T extends readonly [string, ...string[]]>(values: T) {
|
|
446
468
|
return Type.Union(values.map((value) => Type.Literal(value)) as [ReturnType<typeof Type.Literal>, ...ReturnType<typeof Type.Literal>[]]);
|
|
@@ -448,22 +470,50 @@ function StringEnum<T extends readonly [string, ...string[]]>(values: T) {
|
|
|
448
470
|
|
|
449
471
|
export default function (pi: ExtensionAPI): void {
|
|
450
472
|
const instanceToken = Symbol("pi-chrome-instance");
|
|
473
|
+
const currentRoot = extensionRoot();
|
|
451
474
|
const globalState = globalThis as typeof globalThis & {
|
|
452
|
-
[PI_CHROME_GLOBAL_KEY]?: { version: string; root: string; token
|
|
475
|
+
[PI_CHROME_GLOBAL_KEY]?: { version: string; root: string; token?: symbol };
|
|
453
476
|
};
|
|
454
477
|
const alreadyLoaded = globalState[PI_CHROME_GLOBAL_KEY];
|
|
455
|
-
if (alreadyLoaded) {
|
|
478
|
+
if (alreadyLoaded?.token || (alreadyLoaded && alreadyLoaded.root !== currentRoot)) {
|
|
456
479
|
console.warn(
|
|
457
|
-
`pi-chrome already loaded from ${alreadyLoaded.root} (v${alreadyLoaded.version}); skipping duplicate from ${
|
|
480
|
+
`pi-chrome already loaded from ${alreadyLoaded.root} (v${alreadyLoaded.version}); skipping duplicate from ${currentRoot}.`,
|
|
458
481
|
);
|
|
459
482
|
return;
|
|
460
483
|
}
|
|
461
|
-
|
|
484
|
+
// pi-chrome <=0.15.19 set the singleton flag but did not clear it on reload.
|
|
485
|
+
// If the stale flag points at this same extension root, replace it instead of
|
|
486
|
+
// skipping the freshly reloaded extension.
|
|
487
|
+
globalState[PI_CHROME_GLOBAL_KEY] = { version: PI_CHROME_VERSION, root: currentRoot, token: instanceToken };
|
|
462
488
|
|
|
463
489
|
const bridge = new ChromeProfileBridge(DEFAULT_HOST, DEFAULT_PORT);
|
|
464
490
|
let backgroundDefault = false;
|
|
465
491
|
let chromeAuthorizedUntil: number | "indefinite" | undefined;
|
|
466
492
|
let chromeToolsRegistered = false;
|
|
493
|
+
let authExpiryTimer: NodeJS.Timeout | undefined;
|
|
494
|
+
|
|
495
|
+
const clearAuthExpiryTimer = (): void => {
|
|
496
|
+
if (!authExpiryTimer) return;
|
|
497
|
+
clearTimeout(authExpiryTimer);
|
|
498
|
+
authExpiryTimer = undefined;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
const activeToolNamesWithoutChrome = (): string[] => pi.getActiveTools().filter((name) => !CHROME_TOOL_NAME_SET.has(name));
|
|
502
|
+
|
|
503
|
+
const activateChromeTools = (): void => {
|
|
504
|
+
registerChromeTools(pi);
|
|
505
|
+
pi.setActiveTools([...new Set([...pi.getActiveTools(), ...CHROME_TOOL_NAMES])]);
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
const deactivateChromeTools = (): void => {
|
|
509
|
+
pi.setActiveTools(activeToolNamesWithoutChrome());
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const lockChromeControl = (): void => {
|
|
513
|
+
clearAuthExpiryTimer();
|
|
514
|
+
chromeAuthorizedUntil = undefined;
|
|
515
|
+
deactivateChromeTools();
|
|
516
|
+
};
|
|
467
517
|
|
|
468
518
|
const authSummary = (): string => {
|
|
469
519
|
if (chromeAuthorizedUntil === "indefinite") return "authorized indefinitely";
|
|
@@ -477,7 +527,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
477
527
|
const chromeControlAuthorized = (): boolean => {
|
|
478
528
|
if (chromeAuthorizedUntil === "indefinite") return true;
|
|
479
529
|
if (typeof chromeAuthorizedUntil === "number" && chromeAuthorizedUntil > Date.now()) return true;
|
|
480
|
-
chromeAuthorizedUntil
|
|
530
|
+
if (chromeAuthorizedUntil !== undefined) lockChromeControl();
|
|
481
531
|
return false;
|
|
482
532
|
};
|
|
483
533
|
|
|
@@ -495,6 +545,21 @@ export default function (pi: ExtensionAPI): void {
|
|
|
495
545
|
}
|
|
496
546
|
};
|
|
497
547
|
|
|
548
|
+
const scheduleAuthExpiry = (ctx: ExtensionContext, until: number | "indefinite"): void => {
|
|
549
|
+
clearAuthExpiryTimer();
|
|
550
|
+
if (until === "indefinite") return;
|
|
551
|
+
authExpiryTimer = setTimeout(() => {
|
|
552
|
+
if (chromeAuthorizedUntil !== until) return;
|
|
553
|
+
try {
|
|
554
|
+
lockChromeControl();
|
|
555
|
+
ctx.ui.notify("Chrome control authorization expired. Run /chrome authorize to allow chrome_* tools again.", "info");
|
|
556
|
+
updateChromeStatus(ctx);
|
|
557
|
+
} catch (error) {
|
|
558
|
+
console.warn(`Failed to expire pi-chrome authorization cleanly: ${(error as Error).message}`);
|
|
559
|
+
}
|
|
560
|
+
}, Math.max(0, until - Date.now()));
|
|
561
|
+
};
|
|
562
|
+
|
|
498
563
|
const authorizedBridgeSend = (action: string, params: Record<string, unknown>, timeoutMs = DEFAULT_TIMEOUT_MS, signal?: AbortSignal): Promise<unknown> => {
|
|
499
564
|
requireChromeControlAuthorized();
|
|
500
565
|
return bridge.send(action, params, timeoutMs, signal);
|
|
@@ -520,6 +585,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
520
585
|
});
|
|
521
586
|
|
|
522
587
|
pi.on("session_shutdown", () => {
|
|
588
|
+
clearAuthExpiryTimer();
|
|
523
589
|
bridge.stop();
|
|
524
590
|
if (globalState[PI_CHROME_GLOBAL_KEY]?.token === instanceToken) {
|
|
525
591
|
delete globalState[PI_CHROME_GLOBAL_KEY];
|
|
@@ -653,8 +719,9 @@ Usage rules:
|
|
|
653
719
|
ctx.ui.notify("Chrome control remains locked.", "info");
|
|
654
720
|
return;
|
|
655
721
|
}
|
|
656
|
-
registerChromeTools(pi);
|
|
657
722
|
chromeAuthorizedUntil = until;
|
|
723
|
+
activateChromeTools();
|
|
724
|
+
scheduleAuthExpiry(ctx, until);
|
|
658
725
|
ctx.ui.notify(`Chrome control authorized for ${label}.`, "info");
|
|
659
726
|
updateChromeStatus(ctx);
|
|
660
727
|
};
|
|
@@ -677,7 +744,7 @@ Usage rules:
|
|
|
677
744
|
};
|
|
678
745
|
|
|
679
746
|
const revokeHandler = (ctx: ExtensionContext) => {
|
|
680
|
-
|
|
747
|
+
lockChromeControl();
|
|
681
748
|
ctx.ui.notify("Chrome control locked. Run /chrome authorize to allow chrome_* tools again.", "info");
|
|
682
749
|
updateChromeStatus(ctx);
|
|
683
750
|
};
|