pi-chrome 0.15.6 → 0.15.8
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,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-facing changes to `pi-chrome`.
|
|
4
4
|
|
|
5
|
+
## 0.15.8 — 2026-05-14
|
|
6
|
+
|
|
7
|
+
- **Simpler `/chrome` submenus.** Authorize menu now offers 15 minutes, 30 minutes, indefinite, and custom minutes only. Background menu now offers only foreground/background. Esc from a submenu returns to the main `/chrome` menu.
|
|
8
|
+
|
|
9
|
+
## 0.15.7 — 2026-05-14
|
|
10
|
+
|
|
11
|
+
- **Grouped `/chrome` menu.** Bare `/chrome` now opens a status dashboard with grouped actions: authorize, lock, status, doctor, background/watch mode, and onboard. Authorize/background open submenus instead of showing one flat command list.
|
|
12
|
+
|
|
5
13
|
## 0.15.6 — 2026-05-14
|
|
6
14
|
|
|
7
15
|
- **Bare `/chrome` is now a command menu.** Running `/chrome` shows interactive options for every `/chrome ...` command, including authorize/revoke/status/doctor/onboard/background variants.
|
package/README.md
CHANGED
|
@@ -191,9 +191,10 @@ Each tool is documented inline in Pi — agents see the parameters and gotchas (
|
|
|
191
191
|
Chrome control is locked by default. Before any agent can use `chrome_*` tools, explicitly authorize the current Pi session:
|
|
192
192
|
|
|
193
193
|
```text
|
|
194
|
-
/chrome authorize # authorize
|
|
195
|
-
/chrome authorize
|
|
196
|
-
/chrome authorize
|
|
194
|
+
/chrome authorize # default: authorize for 15 minutes
|
|
195
|
+
/chrome authorize 30m # authorize for 30 minutes
|
|
196
|
+
/chrome authorize 45 # custom minutes
|
|
197
|
+
/chrome authorize indefinite # authorize until revoked or Pi exits
|
|
197
198
|
/chrome revoke # lock again
|
|
198
199
|
/chrome status # shows connection + auth + background
|
|
199
200
|
```
|
|
@@ -216,7 +217,7 @@ Per-call `background: true` wins over the session setting.
|
|
|
216
217
|
|
|
217
218
|
- `/chrome doctor` — single command: connectivity, extension version, bridge owner, version drift, MAIN-world helper injection, `chrome_evaluate("1+1") === 2`, fingerprint flags.
|
|
218
219
|
- `/chrome onboard` — guided first-time setup.
|
|
219
|
-
- `/chrome
|
|
220
|
+
- `/chrome status` — current connection, authorization, and background state.
|
|
220
221
|
- `/chrome background status` — current watch/background setting.
|
|
221
222
|
|
|
222
223
|
If the loaded Chrome extension is older than `pi-chrome` on disk, `/chrome doctor` tells you to reload it from `chrome://extensions`.
|
|
@@ -434,26 +434,21 @@ export default function (pi: ExtensionAPI): void {
|
|
|
434
434
|
|
|
435
435
|
const bridge = new ChromeProfileBridge(DEFAULT_HOST, DEFAULT_PORT);
|
|
436
436
|
let backgroundDefault = false;
|
|
437
|
-
let chromeAuthorizedUntil: number | "
|
|
438
|
-
let chromeAuthorizedCalls: number | undefined;
|
|
437
|
+
let chromeAuthorizedUntil: number | "indefinite" | undefined;
|
|
439
438
|
|
|
440
439
|
const authSummary = (): string => {
|
|
441
|
-
if (chromeAuthorizedUntil === "
|
|
440
|
+
if (chromeAuthorizedUntil === "indefinite") return "authorized indefinitely";
|
|
442
441
|
if (typeof chromeAuthorizedUntil === "number") {
|
|
443
442
|
const remainingMs = chromeAuthorizedUntil - Date.now();
|
|
444
|
-
if (remainingMs > 0) {
|
|
445
|
-
const minutes = Math.ceil(remainingMs / 60_000);
|
|
446
|
-
return `authorized for ${chromeAuthorizedCalls === 1 ? "one Chrome command" : `~${minutes}m`}`;
|
|
447
|
-
}
|
|
443
|
+
if (remainingMs > 0) return `authorized for ~${Math.ceil(remainingMs / 60_000)}m`;
|
|
448
444
|
}
|
|
449
445
|
return "locked";
|
|
450
446
|
};
|
|
451
447
|
|
|
452
448
|
const chromeControlAuthorized = (): boolean => {
|
|
453
|
-
if (chromeAuthorizedUntil === "
|
|
449
|
+
if (chromeAuthorizedUntil === "indefinite") return true;
|
|
454
450
|
if (typeof chromeAuthorizedUntil === "number" && chromeAuthorizedUntil > Date.now()) return true;
|
|
455
451
|
chromeAuthorizedUntil = undefined;
|
|
456
|
-
chromeAuthorizedCalls = undefined;
|
|
457
452
|
return false;
|
|
458
453
|
};
|
|
459
454
|
|
|
@@ -461,13 +456,6 @@ export default function (pi: ExtensionAPI): void {
|
|
|
461
456
|
if (!chromeControlAuthorized()) {
|
|
462
457
|
throw new Error("Chrome control locked. Ask the user to run /chrome authorize before using chrome_* tools.");
|
|
463
458
|
}
|
|
464
|
-
if (chromeAuthorizedCalls !== undefined) {
|
|
465
|
-
chromeAuthorizedCalls -= 1;
|
|
466
|
-
if (chromeAuthorizedCalls <= 0) {
|
|
467
|
-
chromeAuthorizedUntil = undefined;
|
|
468
|
-
chromeAuthorizedCalls = undefined;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
459
|
};
|
|
472
460
|
|
|
473
461
|
const authorizedBridgeSend = (action: string, params: Record<string, unknown>, timeoutMs = DEFAULT_TIMEOUT_MS): Promise<unknown> => {
|
|
@@ -620,39 +608,38 @@ Usage rules:
|
|
|
620
608
|
ctx.ui.notify(`Run in background → ${nextLabel}. ${BACKGROUND_DESC[nextLabel]}`, "info");
|
|
621
609
|
};
|
|
622
610
|
|
|
623
|
-
const
|
|
624
|
-
const arg = (args || "").trim().toLowerCase() || "session";
|
|
625
|
-
if (arg === "status") {
|
|
626
|
-
ctx.ui.notify(`Chrome control auth: ${authSummary()}.`, "info");
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
const options: Record<string, { label: string; until: number | "session"; calls?: number }> = {
|
|
630
|
-
once: { label: "one Chrome command", until: Date.now() + 10 * 60_000, calls: 1 },
|
|
631
|
-
"15m": { label: "15 minutes", until: Date.now() + 15 * 60_000 },
|
|
632
|
-
"1h": { label: "1 hour", until: Date.now() + 60 * 60_000 },
|
|
633
|
-
session: { label: "this Pi session", until: "session" },
|
|
634
|
-
};
|
|
635
|
-
const grant = options[arg];
|
|
636
|
-
if (!grant) {
|
|
637
|
-
ctx.ui.notify("Unknown authorize duration. Pick one of: once | 15m | 1h | session | status.", "warning");
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
611
|
+
const authorizeFor = async (ctx: ExtensionContext, label: string, until: number | "indefinite") => {
|
|
640
612
|
const ok = await ctx.ui.confirm(
|
|
641
613
|
"Authorize pi-chrome control?",
|
|
642
|
-
`This Pi session will be allowed to inspect and control your existing Chrome profile for ${
|
|
614
|
+
`This Pi session will be allowed to inspect and control your existing Chrome profile for ${label}.\n\nChrome actions use your signed-in browser state and real input. Only approve if you trust the current agent/task.`,
|
|
643
615
|
);
|
|
644
616
|
if (!ok) {
|
|
645
617
|
ctx.ui.notify("Chrome control remains locked.", "info");
|
|
646
618
|
return;
|
|
647
619
|
}
|
|
648
|
-
chromeAuthorizedUntil =
|
|
649
|
-
|
|
650
|
-
|
|
620
|
+
chromeAuthorizedUntil = until;
|
|
621
|
+
ctx.ui.notify(`Chrome control authorized for ${label}.`, "info");
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const parseAuthorizeArg = (arg: string): { label: string; until: number | "indefinite" } | undefined => {
|
|
625
|
+
const normalized = arg.trim().toLowerCase() || "15m";
|
|
626
|
+
if (normalized === "indefinite" || normalized === "forever") return { label: "indefinitely", until: "indefinite" };
|
|
627
|
+
const minutes = normalized.endsWith("m") ? Number(normalized.slice(0, -1)) : Number(normalized);
|
|
628
|
+
if (!Number.isFinite(minutes) || minutes <= 0) return undefined;
|
|
629
|
+
return { label: `${minutes} minutes`, until: Date.now() + minutes * 60_000 };
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
const authorizeHandler = async (ctx: ExtensionContext, args: string) => {
|
|
633
|
+
const grant = parseAuthorizeArg(args);
|
|
634
|
+
if (!grant) {
|
|
635
|
+
ctx.ui.notify("Unknown authorize duration. Use minutes (15m, 30m, 45) or indefinite.", "warning");
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
return authorizeFor(ctx, grant.label, grant.until);
|
|
651
639
|
};
|
|
652
640
|
|
|
653
641
|
const revokeHandler = (ctx: ExtensionContext) => {
|
|
654
642
|
chromeAuthorizedUntil = undefined;
|
|
655
|
-
chromeAuthorizedCalls = undefined;
|
|
656
643
|
ctx.ui.notify("Chrome control locked. Run /chrome authorize to allow chrome_* tools again.", "info");
|
|
657
644
|
};
|
|
658
645
|
|
|
@@ -700,45 +687,65 @@ Usage rules:
|
|
|
700
687
|
ctx.ui.notify(await statusSummary(), "info");
|
|
701
688
|
};
|
|
702
689
|
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
"
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
690
|
+
const openAuthorizeMenu = async (ctx: ExtensionContext): Promise<void> => {
|
|
691
|
+
while (true) {
|
|
692
|
+
const choice = await ctx.ui.select("Authorize Chrome control", [
|
|
693
|
+
"15 minutes",
|
|
694
|
+
"30 minutes",
|
|
695
|
+
"Indefinite",
|
|
696
|
+
"Custom minutes",
|
|
697
|
+
]);
|
|
698
|
+
if (!choice) return;
|
|
699
|
+
switch (choice) {
|
|
700
|
+
case "15 minutes": return authorizeHandler(ctx, "15m");
|
|
701
|
+
case "30 minutes": return authorizeHandler(ctx, "30m");
|
|
702
|
+
case "Indefinite": return authorizeHandler(ctx, "indefinite");
|
|
703
|
+
case "Custom minutes": {
|
|
704
|
+
const value = await ctx.ui.input("Authorize for how many minutes?", "45");
|
|
705
|
+
if (!value) continue;
|
|
706
|
+
return authorizeHandler(ctx, value);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
const openBackgroundMenu = async (ctx: ExtensionContext): Promise<void> => {
|
|
713
|
+
const choice = await ctx.ui.select("Background / watch mode", [
|
|
714
|
+
"Use Chrome in background",
|
|
715
|
+
"Use Chrome in foreground",
|
|
719
716
|
]);
|
|
720
717
|
if (!choice) return;
|
|
721
718
|
switch (choice) {
|
|
722
|
-
case "
|
|
723
|
-
case "
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
719
|
+
case "Use Chrome in background": return backgroundHandler(ctx, "on");
|
|
720
|
+
case "Use Chrome in foreground": return backgroundHandler(ctx, "off");
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
const openCommandMenu = async (ctx: ExtensionContext): Promise<void> => {
|
|
725
|
+
while (true) {
|
|
726
|
+
const choice = await ctx.ui.select(`pi-chrome\n${await statusSummary()}`, [
|
|
727
|
+
"Authorize Chrome control…",
|
|
728
|
+
"Lock Chrome control",
|
|
729
|
+
"Connection status",
|
|
730
|
+
"Doctor / troubleshoot",
|
|
731
|
+
"Background / watch mode…",
|
|
732
|
+
"Install / onboard extension",
|
|
733
|
+
]);
|
|
734
|
+
if (!choice) return;
|
|
735
|
+
switch (choice) {
|
|
736
|
+
case "Authorize Chrome control…": await openAuthorizeMenu(ctx); continue;
|
|
737
|
+
case "Lock Chrome control": return revokeHandler(ctx);
|
|
738
|
+
case "Connection status": return statusHandler(ctx);
|
|
739
|
+
case "Doctor / troubleshoot": return doctorHandler(ctx);
|
|
740
|
+
case "Background / watch mode…": await openBackgroundMenu(ctx); continue;
|
|
741
|
+
case "Install / onboard extension": return onboardHandler(ctx);
|
|
742
|
+
}
|
|
736
743
|
}
|
|
737
744
|
};
|
|
738
745
|
|
|
739
746
|
pi.registerCommand("chrome", {
|
|
740
747
|
description:
|
|
741
|
-
"All pi-chrome controls in one place.\n /chrome authorize [
|
|
748
|
+
"All pi-chrome controls in one place.\n /chrome authorize [15m|30m|<minutes>|indefinite] — allow this Pi session to use chrome_* tools.\n /chrome revoke — lock Chrome control.\n /chrome status — one-line snapshot of connection, auth, and background setting.\n /chrome doctor — full health check.\n /chrome onboard — install the Chrome companion extension.\n /chrome background [on|off|status|toggle] — whether pi-chrome runs without focusing Chrome.\nRun with no arguments for an interactive picker that shows current state.",
|
|
742
749
|
getArgumentCompletions: (prefix) => {
|
|
743
750
|
const raw = prefix;
|
|
744
751
|
const trimmedRight = raw.replace(/\s+$/, "");
|
|
@@ -764,11 +771,9 @@ Usage rules:
|
|
|
764
771
|
];
|
|
765
772
|
} else if (path[0] === "authorize" && path.length === 1) {
|
|
766
773
|
candidates = [
|
|
767
|
-
{ fullValue: "authorize once", label: "once", description: "Authorize one Chrome command." },
|
|
768
774
|
{ fullValue: "authorize 15m", label: "15m", description: "Authorize Chrome control for 15 minutes." },
|
|
769
|
-
{ fullValue: "authorize
|
|
770
|
-
{ fullValue: "authorize
|
|
771
|
-
{ fullValue: "authorize status", label: "status", description: "Show Chrome control authorization state." },
|
|
775
|
+
{ fullValue: "authorize 30m", label: "30m", description: "Authorize Chrome control for 30 minutes." },
|
|
776
|
+
{ fullValue: "authorize indefinite", label: "indefinite", description: "Authorize Chrome control until revoked or Pi exits." },
|
|
772
777
|
];
|
|
773
778
|
} else if (path[0] === "background" && path.length === 1) {
|
|
774
779
|
candidates = [
|