pi-chrome 0.4.4 → 0.6.0
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
|
@@ -11,9 +11,9 @@ Multiple Pi sessions can use Chrome at the same time. The first Pi session start
|
|
|
11
11
|
## Why try it?
|
|
12
12
|
|
|
13
13
|
- **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.
|
|
14
|
-
- **
|
|
14
|
+
- **Watch your authenticated Chrome work** — by default, `chrome_*` tool calls focus Chrome and activate the target tab so you can see the agent inspect, navigate, click, and type in real time. Switch to silent/background mode for the whole session with `/chrome-background`, or pass `background: true` on a single tool call when you want quiet.
|
|
15
15
|
- **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.
|
|
16
|
-
- **Built-in setup and agent guidance** — `/chrome-onboard` walks users through installing the companion extension, `/chrome-
|
|
16
|
+
- **Built-in setup and agent guidance** — `/chrome-onboard` walks users through installing the companion extension, `/chrome-doctor` checks connectivity and version drift, screenshots save to disk, and the prompt primer tells agents to inspect with `chrome_snapshot` before acting and avoid destructive actions unless explicitly requested.
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
@@ -53,38 +53,40 @@ Then in Chrome:
|
|
|
53
53
|
4. Return to Pi and run:
|
|
54
54
|
|
|
55
55
|
```text
|
|
56
|
-
/chrome-
|
|
56
|
+
/chrome-doctor
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Expected
|
|
59
|
+
Expected output:
|
|
60
60
|
|
|
61
61
|
```text
|
|
62
62
|
Performing Chrome bridge health check
|
|
63
|
-
|
|
63
|
+
pi-chrome v<version>
|
|
64
|
+
• Local bridge: mode=server, url=http://127.0.0.1:17318
|
|
65
|
+
✓ Companion Chrome extension responding (ID: <chrome-extension-id>, ext v<version>)
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
##
|
|
68
|
+
## Background mode
|
|
67
69
|
|
|
68
|
-
By default, `chrome_*` tools
|
|
70
|
+
By default, `chrome_*` tools focus Chrome and activate the target tab so you can watch the agent work — great for demos, pair-driving, debugging, and first-time confidence that things are happening.
|
|
69
71
|
|
|
70
|
-
When you want
|
|
72
|
+
When you want quiet (planner / audit / worker sessions running alongside your editor), turn background mode on for the whole Pi session:
|
|
71
73
|
|
|
72
74
|
```text
|
|
73
|
-
/chrome-
|
|
74
|
-
/chrome-
|
|
75
|
-
/chrome-
|
|
75
|
+
/chrome-background # toggle
|
|
76
|
+
/chrome-background on # explicit
|
|
77
|
+
/chrome-background off # explicit
|
|
76
78
|
```
|
|
77
79
|
|
|
78
|
-
For a single tool call, the agent can pass `
|
|
80
|
+
For a single tool call, the agent can pass `background: true` directly. The per-call value always wins over the session toggle.
|
|
79
81
|
|
|
80
82
|
## Quick demo prompts
|
|
81
83
|
|
|
82
84
|
After setup, try one of these in Pi:
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
Silent inspection (no Chrome interruption):
|
|
85
87
|
|
|
86
88
|
```text
|
|
87
|
-
Inspect my active GitHub tab
|
|
89
|
+
Inspect my active GitHub tab with chrome_snapshot using background:true and summarize the PR state without focusing Chrome.
|
|
88
90
|
```
|
|
89
91
|
|
|
90
92
|
Existing authenticated tab:
|
|
@@ -115,8 +117,7 @@ Screenshots save under `.pi/chrome-screenshots/` by default, which composes nice
|
|
|
115
117
|
|
|
116
118
|
## Diagnostics
|
|
117
119
|
|
|
118
|
-
- `/chrome-
|
|
119
|
-
- `/chrome-doctor` — deeper diagnosis with one-line fixes for common setup failures (extension not loaded, bridge owner stale after `pi update`, version mismatch between pi-chrome and the loaded Chrome extension).
|
|
120
|
+
- `/chrome-doctor` — single command that checks connectivity and reports the loaded Chrome extension ID + version, plus a one-line fix for common setup failures (extension not loaded, bridge owner stale after `pi update`, version mismatch between pi-chrome and the loaded Chrome extension).
|
|
120
121
|
|
|
121
122
|
If the Chrome extension you have loaded is older than `pi-chrome` on disk, `/chrome-doctor` will tell you to reload it from `chrome://extensions`.
|
|
122
123
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Pi Existing Chrome Profile Bridge",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"description": "Lets Pi control tabs in this existing Chrome profile via a local bridge at 127.0.0.1.",
|
|
6
6
|
"permissions": ["tabs", "scripting", "storage", "activeTab", "alarms"],
|
|
7
7
|
"host_permissions": ["<all_urls>", "http://127.0.0.1:17318/*"],
|
|
@@ -46,7 +46,7 @@ type BridgeResult = {
|
|
|
46
46
|
error?: string;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
const PI_CHROME_VERSION = "0.
|
|
49
|
+
const PI_CHROME_VERSION = "0.6.0";
|
|
50
50
|
const DEFAULT_HOST = process.env.PI_CHROME_BRIDGE_HOST ?? "127.0.0.1";
|
|
51
51
|
const DEFAULT_PORT = Number(process.env.PI_CHROME_BRIDGE_PORT ?? "17318");
|
|
52
52
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
@@ -339,10 +339,21 @@ const waitForValues = ["selector", "expression"] as const;
|
|
|
339
339
|
|
|
340
340
|
export default function (pi: ExtensionAPI): void {
|
|
341
341
|
const bridge = new ChromeProfileBridge(DEFAULT_HOST, DEFAULT_PORT);
|
|
342
|
-
let
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
let backgroundDefault = false;
|
|
343
|
+
|
|
344
|
+
// Translate the public `background` parameter (default false = visible/foreground) into the
|
|
345
|
+
// service worker's wire-level `foreground` flag, accepting legacy `foreground` as a fallback.
|
|
346
|
+
const withBackground = <T extends Record<string, unknown>>(params: T): T => {
|
|
347
|
+
const typed = params as { background?: boolean; foreground?: boolean };
|
|
348
|
+
const explicit =
|
|
349
|
+
typed.background !== undefined
|
|
350
|
+
? typed.background
|
|
351
|
+
: typed.foreground !== undefined
|
|
352
|
+
? !typed.foreground
|
|
353
|
+
: undefined;
|
|
354
|
+
const background = explicit ?? backgroundDefault;
|
|
355
|
+
return { ...params, foreground: !background } as T;
|
|
356
|
+
};
|
|
346
357
|
|
|
347
358
|
pi.on("session_start", async (_event, ctx) => {
|
|
348
359
|
await bridge.start();
|
|
@@ -365,36 +376,21 @@ export default function (pi: ExtensionAPI): void {
|
|
|
365
376
|
<chrome-profile-bridge>
|
|
366
377
|
Chrome control is available through the chrome_* tools via a companion Chrome extension installed in the user's normal Chrome profile.
|
|
367
378
|
This is not CDP: it can use the user's existing Chrome windows and authenticated sessions after the user loads the companion browser extension.
|
|
368
|
-
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
|
|
379
|
+
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 focus Chrome and activate the target tab so the user can watch the agent work. The user can switch to silent/background mode for the whole session via /chrome-background; you can also pass background=true on a single tool call when the user explicitly wants the action to be silent (for example, scraping while they keep working in another app).
|
|
369
380
|
</chrome-profile-bridge>`;
|
|
370
381
|
return { systemPrompt: event.systemPrompt + primer };
|
|
371
382
|
});
|
|
372
383
|
|
|
373
|
-
pi.registerCommand("chrome-status", {
|
|
374
|
-
description: "Run an explicit health check against the existing-profile Chrome companion extension",
|
|
375
|
-
handler: async (_args, ctx) => {
|
|
376
|
-
ctx.ui.notify("Performing Chrome bridge health check", "info");
|
|
377
|
-
try {
|
|
378
|
-
const version = (await bridge.send("tab.version", {}, 35_000)) as { extensionId?: string; extensionVersion?: string };
|
|
379
|
-
const suffix = [version.extensionId ? `ID: ${version.extensionId}` : null, version.extensionVersion ? `ext v${version.extensionVersion}` : null]
|
|
380
|
-
.filter(Boolean)
|
|
381
|
-
.join(", ");
|
|
382
|
-
ctx.ui.notify(suffix ? `Chrome profile bridge connected (${suffix})` : "Chrome profile bridge connected", "info");
|
|
383
|
-
} catch (error) {
|
|
384
|
-
ctx.ui.notify(`Chrome bridge health check failed: ${(error as Error).message}`, "warning");
|
|
385
|
-
}
|
|
386
|
-
},
|
|
387
|
-
});
|
|
388
|
-
|
|
389
384
|
pi.registerCommand("chrome-doctor", {
|
|
390
385
|
description:
|
|
391
|
-
"
|
|
386
|
+
"Check Chrome bridge connectivity and diagnose setup. Reports the local bridge, companion Chrome extension status (ID + version), and a one-line fix for common failures (extension not loaded, stale service worker, version drift).",
|
|
392
387
|
handler: async (_args, ctx) => {
|
|
388
|
+
ctx.ui.notify("Performing Chrome bridge health check", "info");
|
|
393
389
|
const lines: string[] = [`pi-chrome v${PI_CHROME_VERSION}`];
|
|
394
390
|
const status = bridge.status();
|
|
395
391
|
lines.push(`• Local bridge: mode=${status.mode}, url=${status.url}`);
|
|
396
392
|
try {
|
|
397
|
-
const version = (await bridge.send("tab.version", {},
|
|
393
|
+
const version = (await bridge.send("tab.version", {}, 35_000)) as {
|
|
398
394
|
extensionId?: string;
|
|
399
395
|
extensionVersion?: string;
|
|
400
396
|
};
|
|
@@ -419,13 +415,13 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
419
415
|
},
|
|
420
416
|
});
|
|
421
417
|
|
|
422
|
-
pi.registerCommand("chrome-
|
|
418
|
+
pi.registerCommand("chrome-background", {
|
|
423
419
|
description:
|
|
424
|
-
"Toggle
|
|
420
|
+
"Toggle silent/background mode for chrome_* tools. Background ON: chrome_* tools act silently — your editor/terminal keeps focus, Chrome does not pop up, your workflow is not interrupted. Background OFF (default): Chrome focuses and activates the target tab so you can watch the agent work, useful for demos, pair-driving, and debugging — tradeoff: Chrome pops up and steals focus. Pass `on` / `off` to set explicitly, or no argument to toggle.",
|
|
425
421
|
getArgumentCompletions: (prefix) => {
|
|
426
422
|
const items = [
|
|
427
|
-
{ value: "on", label: "on", description: "
|
|
428
|
-
{ value: "off", label: "off", description: "
|
|
423
|
+
{ value: "on", label: "on", description: "Run chrome_* actions silently without focusing Chrome" },
|
|
424
|
+
{ value: "off", label: "off", description: "Bring Chrome to the foreground for chrome_* actions (default)" },
|
|
429
425
|
];
|
|
430
426
|
const lowered = prefix.toLowerCase();
|
|
431
427
|
const matches = items.filter((item) => item.value.startsWith(lowered));
|
|
@@ -433,13 +429,13 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
433
429
|
},
|
|
434
430
|
handler: async (args, ctx) => {
|
|
435
431
|
const arg = (args || "").trim().toLowerCase();
|
|
436
|
-
if (arg === "on" || arg === "true" || arg === "1")
|
|
437
|
-
else if (arg === "off" || arg === "false" || arg === "0")
|
|
438
|
-
else
|
|
432
|
+
if (arg === "on" || arg === "true" || arg === "1") backgroundDefault = true;
|
|
433
|
+
else if (arg === "off" || arg === "false" || arg === "0") backgroundDefault = false;
|
|
434
|
+
else backgroundDefault = !backgroundDefault;
|
|
439
435
|
ctx.ui.notify(
|
|
440
|
-
|
|
441
|
-
? "Chrome
|
|
442
|
-
: "Chrome
|
|
436
|
+
backgroundDefault
|
|
437
|
+
? "Chrome background mode ON. chrome_* tools will run silently. Your current app keeps focus."
|
|
438
|
+
: "Chrome background mode OFF. chrome_* tools will focus Chrome and activate the target tab so you can watch the agent work.",
|
|
443
439
|
"info",
|
|
444
440
|
);
|
|
445
441
|
},
|
|
@@ -463,7 +459,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
463
459
|
await pi.exec("sh", ["-lc", `printf %s ${JSON.stringify(extensionPath)} | pbcopy`], { cwd: workspaceCwd(ctx), timeout: 5_000 }).catch(() => undefined);
|
|
464
460
|
}
|
|
465
461
|
ctx.ui.notify(
|
|
466
|
-
"Chrome bridge setup opened. The extension path has been copied to your clipboard. After loading it, run /chrome-
|
|
462
|
+
"Chrome bridge setup opened. The extension path has been copied to your clipboard. After loading it, run /chrome-doctor.",
|
|
467
463
|
"info",
|
|
468
464
|
);
|
|
469
465
|
},
|
|
@@ -533,15 +529,15 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
533
529
|
name: "chrome_snapshot",
|
|
534
530
|
label: "Chrome Snapshot",
|
|
535
531
|
description:
|
|
536
|
-
"Inspect a page in the user's existing Chrome profile: title, URL, visible body text, viewport, and clickable/focusable elements with CSS selectors.
|
|
532
|
+
"Inspect a page in the user's existing Chrome profile: title, URL, visible body text, viewport, and clickable/focusable elements with CSS selectors. Brings Chrome to the foreground by default so the user can watch; pass background=true to inspect silently.",
|
|
537
533
|
promptSnippet: "Inspect the current Chrome page and get CSS selectors for browser automation.",
|
|
538
534
|
parameters: Type.Object({
|
|
539
535
|
targetId: Type.Optional(Type.String()),
|
|
540
536
|
urlIncludes: Type.Optional(Type.String()),
|
|
541
537
|
titleIncludes: Type.Optional(Type.String()),
|
|
542
538
|
maxElements: Type.Optional(Type.Number({ default: MAX_ELEMENTS })),
|
|
543
|
-
|
|
544
|
-
Type.Boolean({ description: "If true,
|
|
539
|
+
background: Type.Optional(
|
|
540
|
+
Type.Boolean({ description: "If true, run silently in the background without focusing Chrome. Default false (Chrome focuses + tab activates so the user can watch)." }),
|
|
545
541
|
),
|
|
546
542
|
host: Type.Optional(Type.String()),
|
|
547
543
|
port: Type.Optional(Type.Number()),
|
|
@@ -549,7 +545,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
549
545
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
550
546
|
const snapshot = await bridge.send(
|
|
551
547
|
"page.snapshot",
|
|
552
|
-
|
|
548
|
+
withBackground({ ...params, maxElements: params.maxElements ?? MAX_ELEMENTS }),
|
|
553
549
|
DEFAULT_TIMEOUT_MS,
|
|
554
550
|
);
|
|
555
551
|
return { content: [{ type: "text", text: truncateText(safeJson(snapshot)) }], details: { snapshot } };
|
|
@@ -560,7 +556,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
560
556
|
name: "chrome_navigate",
|
|
561
557
|
label: "Chrome Navigate",
|
|
562
558
|
description:
|
|
563
|
-
"Navigate an existing Chrome tab to a URL via the companion extension.
|
|
559
|
+
"Navigate an existing Chrome tab to a URL via the companion extension. By default focuses Chrome and activates the tab so the user can watch; pass background=true to navigate silently. Optionally waits for load completion.",
|
|
564
560
|
promptSnippet: "Navigate a Chrome tab in the user's existing profile.",
|
|
565
561
|
parameters: Type.Object({
|
|
566
562
|
url: Type.String(),
|
|
@@ -569,14 +565,14 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
569
565
|
titleIncludes: Type.Optional(Type.String()),
|
|
570
566
|
waitUntilLoad: Type.Optional(Type.Boolean({ default: true })),
|
|
571
567
|
timeoutMs: Type.Optional(Type.Number({ default: 15_000 })),
|
|
572
|
-
|
|
573
|
-
Type.Boolean({ description: "If true,
|
|
568
|
+
background: Type.Optional(
|
|
569
|
+
Type.Boolean({ description: "If true, navigate silently without focusing Chrome. Default false." }),
|
|
574
570
|
),
|
|
575
571
|
host: Type.Optional(Type.String()),
|
|
576
572
|
port: Type.Optional(Type.Number()),
|
|
577
573
|
}),
|
|
578
574
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
579
|
-
const result = await bridge.send("page.navigate",
|
|
575
|
+
const result = await bridge.send("page.navigate", withBackground(params), params.timeoutMs ?? 15_000);
|
|
580
576
|
return { content: [{ type: "text", text: `Navigated to ${params.url}` }], details: { result: result as Json } };
|
|
581
577
|
},
|
|
582
578
|
});
|
|
@@ -585,7 +581,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
585
581
|
name: "chrome_evaluate",
|
|
586
582
|
label: "Chrome Evaluate",
|
|
587
583
|
description:
|
|
588
|
-
"Evaluate JavaScript in an existing Chrome tab through the companion extension. Runs in the page context
|
|
584
|
+
"Evaluate JavaScript in an existing Chrome tab through the companion extension. Runs in the page context and returns JSON-serializable values when possible. By default focuses Chrome and activates the tab; pass background=true to evaluate silently.",
|
|
589
585
|
promptSnippet: "Evaluate JavaScript in the active Chrome tab through the companion extension.",
|
|
590
586
|
parameters: Type.Object({
|
|
591
587
|
expression: Type.String(),
|
|
@@ -594,14 +590,14 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
594
590
|
targetId: Type.Optional(Type.String()),
|
|
595
591
|
urlIncludes: Type.Optional(Type.String()),
|
|
596
592
|
titleIncludes: Type.Optional(Type.String()),
|
|
597
|
-
|
|
598
|
-
Type.Boolean({ description: "If true,
|
|
593
|
+
background: Type.Optional(
|
|
594
|
+
Type.Boolean({ description: "If true, evaluate silently without focusing Chrome. Default false." }),
|
|
599
595
|
),
|
|
600
596
|
host: Type.Optional(Type.String()),
|
|
601
597
|
port: Type.Optional(Type.Number()),
|
|
602
598
|
}),
|
|
603
599
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
604
|
-
const value = await bridge.send("page.evaluate",
|
|
600
|
+
const value = await bridge.send("page.evaluate", withBackground(params), DEFAULT_TIMEOUT_MS);
|
|
605
601
|
return { content: [{ type: "text", text: truncateText(typeof value === "string" ? value : safeJson(value)) }], details: { value: value as Json } };
|
|
606
602
|
},
|
|
607
603
|
});
|
|
@@ -610,7 +606,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
610
606
|
name: "chrome_click",
|
|
611
607
|
label: "Chrome Click",
|
|
612
608
|
description:
|
|
613
|
-
"Click a CSS selector or viewport coordinate in an existing Chrome tab through the companion extension. The click is dispatched as a synthetic DOM event
|
|
609
|
+
"Click a CSS selector or viewport coordinate in an existing Chrome tab through the companion extension. The click is dispatched as a synthetic DOM event; by default Chrome is focused so the user can watch, pass background=true to click silently.",
|
|
614
610
|
promptSnippet: "Click page elements in Chrome by selector or viewport coordinate.",
|
|
615
611
|
parameters: Type.Object({
|
|
616
612
|
selector: Type.Optional(Type.String({ description: "CSS selector to click. Prefer selectors from chrome_snapshot." })),
|
|
@@ -619,14 +615,14 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
619
615
|
targetId: Type.Optional(Type.String()),
|
|
620
616
|
urlIncludes: Type.Optional(Type.String()),
|
|
621
617
|
titleIncludes: Type.Optional(Type.String()),
|
|
622
|
-
|
|
623
|
-
Type.Boolean({ description: "If true,
|
|
618
|
+
background: Type.Optional(
|
|
619
|
+
Type.Boolean({ description: "If true, click silently without focusing Chrome. Default false." }),
|
|
624
620
|
),
|
|
625
621
|
host: Type.Optional(Type.String()),
|
|
626
622
|
port: Type.Optional(Type.Number()),
|
|
627
623
|
}),
|
|
628
624
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
629
|
-
const result = await bridge.send("page.click",
|
|
625
|
+
const result = await bridge.send("page.click", withBackground(params), DEFAULT_TIMEOUT_MS);
|
|
630
626
|
return { content: [{ type: "text", text: `Clicked ${params.selector ?? `${params.x},${params.y}`}` }], details: { result: result as Json } };
|
|
631
627
|
},
|
|
632
628
|
});
|
|
@@ -635,7 +631,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
635
631
|
name: "chrome_type",
|
|
636
632
|
label: "Chrome Type",
|
|
637
633
|
description:
|
|
638
|
-
"Focus an optional CSS selector, then type text into an existing Chrome tab through the companion extension.
|
|
634
|
+
"Focus an optional CSS selector, then type text into an existing Chrome tab through the companion extension. By default focuses Chrome and activates the tab so the user can watch; pass background=true to type silently.",
|
|
639
635
|
promptSnippet: "Type text into Chrome, optionally focusing a selector first.",
|
|
640
636
|
parameters: Type.Object({
|
|
641
637
|
text: Type.String(),
|
|
@@ -644,14 +640,14 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
644
640
|
targetId: Type.Optional(Type.String()),
|
|
645
641
|
urlIncludes: Type.Optional(Type.String()),
|
|
646
642
|
titleIncludes: Type.Optional(Type.String()),
|
|
647
|
-
|
|
648
|
-
Type.Boolean({ description: "If true,
|
|
643
|
+
background: Type.Optional(
|
|
644
|
+
Type.Boolean({ description: "If true, type silently without focusing Chrome. Default false." }),
|
|
649
645
|
),
|
|
650
646
|
host: Type.Optional(Type.String()),
|
|
651
647
|
port: Type.Optional(Type.Number()),
|
|
652
648
|
}),
|
|
653
649
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
654
|
-
const result = await bridge.send("page.type",
|
|
650
|
+
const result = await bridge.send("page.type", withBackground(params), DEFAULT_TIMEOUT_MS);
|
|
655
651
|
return { content: [{ type: "text", text: `Typed ${params.text.length} character(s)${params.selector ? ` into ${params.selector}` : ""}.` }], details: { result: result as Json } };
|
|
656
652
|
},
|
|
657
653
|
});
|
|
@@ -660,21 +656,21 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
660
656
|
name: "chrome_key",
|
|
661
657
|
label: "Chrome Key",
|
|
662
658
|
description:
|
|
663
|
-
"Send a keyboard key to an existing Chrome tab (Enter, Escape, Tab, Backspace, Delete, ArrowUp/Down/Left/Right, or one character).
|
|
659
|
+
"Send a keyboard key to an existing Chrome tab (Enter, Escape, Tab, Backspace, Delete, ArrowUp/Down/Left/Right, or one character). By default focuses Chrome and activates the tab so the user can watch; pass background=true to send the key silently.",
|
|
664
660
|
promptSnippet: "Press keys in Chrome through the companion extension.",
|
|
665
661
|
parameters: Type.Object({
|
|
666
662
|
key: Type.String(),
|
|
667
663
|
targetId: Type.Optional(Type.String()),
|
|
668
664
|
urlIncludes: Type.Optional(Type.String()),
|
|
669
665
|
titleIncludes: Type.Optional(Type.String()),
|
|
670
|
-
|
|
671
|
-
Type.Boolean({ description: "If true,
|
|
666
|
+
background: Type.Optional(
|
|
667
|
+
Type.Boolean({ description: "If true, send the key silently without focusing Chrome. Default false." }),
|
|
672
668
|
),
|
|
673
669
|
host: Type.Optional(Type.String()),
|
|
674
670
|
port: Type.Optional(Type.Number()),
|
|
675
671
|
}),
|
|
676
672
|
async execute(_id, params): Promise<ToolTextResult> {
|
|
677
|
-
const result = await bridge.send("page.key",
|
|
673
|
+
const result = await bridge.send("page.key", withBackground(params), DEFAULT_TIMEOUT_MS);
|
|
678
674
|
return { content: [{ type: "text", text: `Pressed ${params.key}.` }], details: { result: result as Json } };
|
|
679
675
|
},
|
|
680
676
|
});
|
|
@@ -705,7 +701,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
705
701
|
name: "chrome_screenshot",
|
|
706
702
|
label: "Chrome Screenshot",
|
|
707
703
|
description:
|
|
708
|
-
"Capture a screenshot of an existing Chrome tab via the companion extension and save it to disk. Chrome's extension screenshot API requires the target tab to be the active tab in its window
|
|
704
|
+
"Capture a screenshot of an existing Chrome tab via the companion extension and save it to disk. Chrome's extension screenshot API requires the target tab to be the active tab in its window. By default Chrome is focused and the tab activates so the user can watch; pass background=true to capture silently (the tab is briefly activated within its window for the capture, then the previous active tab is restored).",
|
|
709
705
|
promptSnippet: "Capture Chrome screenshots and save them under .pi/chrome-screenshots by default.",
|
|
710
706
|
parameters: Type.Object({
|
|
711
707
|
path: Type.Optional(Type.String({ description: "Output path. Defaults to .pi/chrome-screenshots/<timestamp>.<format>." })),
|
|
@@ -715,8 +711,8 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
715
711
|
targetId: Type.Optional(Type.String()),
|
|
716
712
|
urlIncludes: Type.Optional(Type.String()),
|
|
717
713
|
titleIncludes: Type.Optional(Type.String()),
|
|
718
|
-
|
|
719
|
-
Type.Boolean({ description: "If true,
|
|
714
|
+
background: Type.Optional(
|
|
715
|
+
Type.Boolean({ description: "If true, capture silently without focusing the Chrome window (the target tab is briefly activated within its window for the capture, then restored). Default false." }),
|
|
720
716
|
),
|
|
721
717
|
host: Type.Optional(Type.String()),
|
|
722
718
|
port: Type.Optional(Type.Number()),
|
|
@@ -726,7 +722,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
726
722
|
const cwd = workspaceCwd(ctx);
|
|
727
723
|
const defaultPath = join(cwd, ".pi", "chrome-screenshots", `${new Date().toISOString().replace(/[:.]/g, "-")}.${format}`);
|
|
728
724
|
const outputPath = params.path ? resolve(cwd, params.path) : defaultPath;
|
|
729
|
-
const result = (await bridge.send("page.screenshot",
|
|
725
|
+
const result = (await bridge.send("page.screenshot", withBackground(params), DEFAULT_TIMEOUT_MS)) as { dataUrl: string; tab?: unknown };
|
|
730
726
|
const base64 = result.dataUrl.replace(/^data:image\/(?:png|jpeg);base64,/, "");
|
|
731
727
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
732
728
|
await writeFile(outputPath, Buffer.from(base64, "base64"));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-chrome",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Drive your existing logged-in Chrome from Pi \u2014 no re-login, no throwaway profile, background
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Drive your existing logged-in Chrome from Pi \u2014 no re-login, no throwaway profile, watch the agent work in real time (or toggle quiet background mode).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"pi-extension",
|