pi-chrome 0.3.0 → 0.3.1
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/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Multiple Pi sessions can use Chrome at the same time. The first Pi session start
|
|
|
10
10
|
|
|
11
11
|
- **Uses your existing Chrome profile** — works with the Chrome windows/tabs you are already using, including logged-in GitHub, admin dashboards, local apps, and internal tools.
|
|
12
12
|
- **Full browser automation toolkit for Pi** — list/create/activate/close tabs, snapshot pages with usable CSS selectors, navigate, evaluate JavaScript, click, type, press keys, wait for page state, and capture screenshots.
|
|
13
|
-
- **Background by default** — agents can inspect, navigate, click, type, and snapshot without bringing Chrome to the foreground or interrupting whatever you are doing.
|
|
13
|
+
- **Background by default** — agents can inspect, navigate, click, type, and snapshot without bringing Chrome to the foreground or interrupting whatever you are doing. Toggle for the whole session with `/chrome-foreground` (useful for demos, pair-driving, debugging) or pass `foreground: true` on a single tool call.
|
|
14
14
|
- **Built-in setup and agent guidance** — `/chrome-onboard` walks users through installing the companion extension, `/chrome-status` checks connectivity, screenshots save to disk, and the prompt primer tells agents to inspect with `chrome_snapshot` before acting and avoid destructive actions unless explicitly requested.
|
|
15
15
|
|
|
16
16
|
## Install
|
|
@@ -315,6 +315,10 @@ const waitForValues = ["selector", "expression"] as const;
|
|
|
315
315
|
|
|
316
316
|
export default function (pi: ExtensionAPI): void {
|
|
317
317
|
const bridge = new ChromeProfileBridge(DEFAULT_HOST, DEFAULT_PORT);
|
|
318
|
+
let foregroundDefault = false;
|
|
319
|
+
|
|
320
|
+
const withForeground = <T extends Record<string, unknown>>(params: T): T =>
|
|
321
|
+
({ ...params, foreground: ((params as { foreground?: boolean }).foreground) ?? foregroundDefault }) as T;
|
|
318
322
|
|
|
319
323
|
pi.on("session_start", async (_event, ctx) => {
|
|
320
324
|
await bridge.start();
|
|
@@ -337,7 +341,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
337
341
|
<chrome-profile-bridge>
|
|
338
342
|
Chrome control is available through the chrome_* tools via a companion Chrome extension installed in the user's normal Chrome profile.
|
|
339
343
|
This is not CDP: it can use the user's existing Chrome windows and authenticated sessions after the user loads the companion browser extension.
|
|
340
|
-
If chrome_* tools time out, ask the user to run /chrome-onboard, then load the bundled browser-extension folder in chrome://extensions. Prefer chrome_snapshot before clicking/typing. Avoid destructive actions unless explicitly requested. By default chrome_* tools run in the background and do not bring Chrome to the foreground;
|
|
344
|
+
If chrome_* tools time out, ask the user to run /chrome-onboard, then load the bundled browser-extension folder in chrome://extensions. Prefer chrome_snapshot before clicking/typing. Avoid destructive actions unless explicitly requested. By default chrome_* tools run in the background and do not bring Chrome to the foreground. The user can flip this for the whole session via /chrome-foreground; you can also pass foreground=true on a single tool call when you specifically need Chrome focused (for example, demoing a flow or capturing a screenshot the user should see).
|
|
341
345
|
</chrome-profile-bridge>`;
|
|
342
346
|
return { systemPrompt: event.systemPrompt + primer };
|
|
343
347
|
});
|
|
@@ -360,6 +364,32 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
360
364
|
},
|
|
361
365
|
});
|
|
362
366
|
|
|
367
|
+
pi.registerCommand("chrome-foreground", {
|
|
368
|
+
description:
|
|
369
|
+
"Toggle whether chrome_* tools bring Chrome to the foreground. Foreground ON: you can watch the agent work in your browser, useful for demos, pair-driving, and debugging — tradeoff: Chrome pops up and steals focus, interrupting whatever app you were using. Foreground OFF (default): chrome_* tools act silently in the background, so your editor/terminal keeps focus and your workflow is not interrupted. Pass `on` / `off` to set explicitly, or no argument to toggle.",
|
|
370
|
+
getArgumentCompletions: (prefix) => {
|
|
371
|
+
const items = [
|
|
372
|
+
{ value: "on", label: "on", description: "Bring Chrome to the foreground for chrome_* actions" },
|
|
373
|
+
{ value: "off", label: "off", description: "Run chrome_* actions silently in the background" },
|
|
374
|
+
];
|
|
375
|
+
const lowered = prefix.toLowerCase();
|
|
376
|
+
const matches = items.filter((item) => item.value.startsWith(lowered));
|
|
377
|
+
return matches.length > 0 ? matches : null;
|
|
378
|
+
},
|
|
379
|
+
handler: async (args, ctx) => {
|
|
380
|
+
const arg = (args || "").trim().toLowerCase();
|
|
381
|
+
if (arg === "on" || arg === "true" || arg === "1") foregroundDefault = true;
|
|
382
|
+
else if (arg === "off" || arg === "false" || arg === "0") foregroundDefault = false;
|
|
383
|
+
else foregroundDefault = !foregroundDefault;
|
|
384
|
+
ctx.ui.notify(
|
|
385
|
+
foregroundDefault
|
|
386
|
+
? "Chrome foreground ON. chrome_* tools will focus Chrome and activate the target tab. Useful for demos and debugging — but Chrome will pop up over whatever you're doing."
|
|
387
|
+
: "Chrome foreground OFF. chrome_* tools run silently in the background. Your current app keeps focus.",
|
|
388
|
+
"info",
|
|
389
|
+
);
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
|
|
363
393
|
pi.registerCommand("chrome-onboard", {
|
|
364
394
|
description: "Guide Chrome extension setup for the existing-profile bridge",
|
|
365
395
|
handler: async (_args, ctx) => {
|
|
@@ -462,7 +492,11 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
462
492
|
port: Type.Optional(Type.Number()),
|
|
463
493
|
}),
|
|
464
494
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
465
|
-
const snapshot = await bridge.send(
|
|
495
|
+
const snapshot = await bridge.send(
|
|
496
|
+
"page.snapshot",
|
|
497
|
+
withForeground({ ...params, maxElements: params.maxElements ?? MAX_ELEMENTS }),
|
|
498
|
+
DEFAULT_TIMEOUT_MS,
|
|
499
|
+
);
|
|
466
500
|
return { content: [{ type: "text", text: truncateText(safeJson(snapshot)) }], details: { snapshot } };
|
|
467
501
|
},
|
|
468
502
|
});
|
|
@@ -487,7 +521,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
487
521
|
port: Type.Optional(Type.Number()),
|
|
488
522
|
}),
|
|
489
523
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
490
|
-
const result = await bridge.send("page.navigate", params, params.timeoutMs ?? 15_000);
|
|
524
|
+
const result = await bridge.send("page.navigate", withForeground(params), params.timeoutMs ?? 15_000);
|
|
491
525
|
return { content: [{ type: "text", text: `Navigated to ${params.url}` }], details: { result: result as Json } };
|
|
492
526
|
},
|
|
493
527
|
});
|
|
@@ -512,7 +546,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
512
546
|
port: Type.Optional(Type.Number()),
|
|
513
547
|
}),
|
|
514
548
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
515
|
-
const value = await bridge.send("page.evaluate", params, DEFAULT_TIMEOUT_MS);
|
|
549
|
+
const value = await bridge.send("page.evaluate", withForeground(params), DEFAULT_TIMEOUT_MS);
|
|
516
550
|
return { content: [{ type: "text", text: truncateText(typeof value === "string" ? value : safeJson(value)) }], details: { value: value as Json } };
|
|
517
551
|
},
|
|
518
552
|
});
|
|
@@ -537,7 +571,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
537
571
|
port: Type.Optional(Type.Number()),
|
|
538
572
|
}),
|
|
539
573
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
540
|
-
const result = await bridge.send("page.click", params, DEFAULT_TIMEOUT_MS);
|
|
574
|
+
const result = await bridge.send("page.click", withForeground(params), DEFAULT_TIMEOUT_MS);
|
|
541
575
|
return { content: [{ type: "text", text: `Clicked ${params.selector ?? `${params.x},${params.y}`}` }], details: { result: result as Json } };
|
|
542
576
|
},
|
|
543
577
|
});
|
|
@@ -562,7 +596,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
562
596
|
port: Type.Optional(Type.Number()),
|
|
563
597
|
}),
|
|
564
598
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
565
|
-
const result = await bridge.send("page.type", params, DEFAULT_TIMEOUT_MS);
|
|
599
|
+
const result = await bridge.send("page.type", withForeground(params), DEFAULT_TIMEOUT_MS);
|
|
566
600
|
return { content: [{ type: "text", text: `Typed ${params.text.length} character(s)${params.selector ? ` into ${params.selector}` : ""}.` }], details: { result: result as Json } };
|
|
567
601
|
},
|
|
568
602
|
});
|
|
@@ -585,7 +619,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
585
619
|
port: Type.Optional(Type.Number()),
|
|
586
620
|
}),
|
|
587
621
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
588
|
-
const result = await bridge.send("page.key", params, DEFAULT_TIMEOUT_MS);
|
|
622
|
+
const result = await bridge.send("page.key", withForeground(params), DEFAULT_TIMEOUT_MS);
|
|
589
623
|
return { content: [{ type: "text", text: `Pressed ${params.key}.` }], details: { result: result as Json } };
|
|
590
624
|
},
|
|
591
625
|
});
|
|
@@ -637,7 +671,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
637
671
|
const cwd = workspaceCwd(ctx);
|
|
638
672
|
const defaultPath = join(cwd, ".pi", "chrome-screenshots", `${new Date().toISOString().replace(/[:.]/g, "-")}.${format}`);
|
|
639
673
|
const outputPath = params.path ? resolve(cwd, params.path) : defaultPath;
|
|
640
|
-
const result = (await bridge.send("page.screenshot", params, DEFAULT_TIMEOUT_MS)) as { dataUrl: string; tab?: unknown };
|
|
674
|
+
const result = (await bridge.send("page.screenshot", withForeground(params), DEFAULT_TIMEOUT_MS)) as { dataUrl: string; tab?: unknown };
|
|
641
675
|
const base64 = result.dataUrl.replace(/^data:image\/(?:png|jpeg);base64,/, "");
|
|
642
676
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
643
677
|
await writeFile(outputPath, Buffer.from(base64, "base64"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-chrome",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Control your existing authenticated Chrome profile from one or more Pi sessions with tabs, snapshots, clicks, typing, JS evaluation, waits, and screenshots. Background-by-default so Chrome stops popping up when agents act.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|