pi-chrome 0.4.3 → 0.5.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
|
@@ -13,7 +13,7 @@ Multiple Pi sessions can use Chrome at the same time. The first Pi session start
|
|
|
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
|
- **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`, or pass `foreground: true` on a single tool call.
|
|
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,14 +53,16 @@ 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
|
## Foreground control
|
|
@@ -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.5.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.5.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;
|
|
@@ -274,7 +274,24 @@ class ChromeProfileBridge {
|
|
|
274
274
|
if (request.method === "GET" && url.pathname === "/next") {
|
|
275
275
|
this.lastSeenAt = Date.now();
|
|
276
276
|
this.clientName = url.searchParams.get("name") ?? undefined;
|
|
277
|
-
|
|
277
|
+
let aborted = false;
|
|
278
|
+
let activeWaiter: ((command: BridgeCommand | undefined) => void) | undefined;
|
|
279
|
+
request.once("close", () => {
|
|
280
|
+
aborted = true;
|
|
281
|
+
if (activeWaiter) this.waiters = this.waiters.filter((entry) => entry !== activeWaiter);
|
|
282
|
+
});
|
|
283
|
+
let command = this.queue.shift();
|
|
284
|
+
if (!command) {
|
|
285
|
+
command = await this.waitForCommand(25_000, (waiter) => {
|
|
286
|
+
activeWaiter = waiter;
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (aborted) {
|
|
290
|
+
// Long-poll connection died before we could deliver. Requeue any command we pulled
|
|
291
|
+
// so the next live /next picks it up instead of dropping it on the floor.
|
|
292
|
+
if (command) this.queue.unshift(command);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
278
295
|
sendJson(response, 200, command ? { type: "command", command } : { type: "none" });
|
|
279
296
|
return;
|
|
280
297
|
}
|
|
@@ -296,16 +313,22 @@ class ChromeProfileBridge {
|
|
|
296
313
|
sendJson(response, 404, { error: "not found" });
|
|
297
314
|
}
|
|
298
315
|
|
|
299
|
-
private waitForCommand(
|
|
316
|
+
private waitForCommand(
|
|
317
|
+
timeoutMs: number,
|
|
318
|
+
registerWaiter?: (waiter: (command: BridgeCommand | undefined) => void) => void,
|
|
319
|
+
): Promise<BridgeCommand | undefined> {
|
|
300
320
|
return new Promise((resolveWait) => {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
this.waiters.push((command) => {
|
|
321
|
+
let settled = false;
|
|
322
|
+
const waiter = (command: BridgeCommand | undefined) => {
|
|
323
|
+
if (settled) return;
|
|
324
|
+
settled = true;
|
|
306
325
|
clearTimeout(timer);
|
|
326
|
+
this.waiters = this.waiters.filter((entry) => entry !== waiter);
|
|
307
327
|
resolveWait(command);
|
|
308
|
-
}
|
|
328
|
+
};
|
|
329
|
+
const timer = setTimeout(() => waiter(undefined), timeoutMs);
|
|
330
|
+
this.waiters.push(waiter);
|
|
331
|
+
registerWaiter?.(waiter);
|
|
309
332
|
});
|
|
310
333
|
}
|
|
311
334
|
}
|
|
@@ -347,31 +370,16 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
347
370
|
return { systemPrompt: event.systemPrompt + primer };
|
|
348
371
|
});
|
|
349
372
|
|
|
350
|
-
pi.registerCommand("chrome-status", {
|
|
351
|
-
description: "Run an explicit health check against the existing-profile Chrome companion extension",
|
|
352
|
-
handler: async (_args, ctx) => {
|
|
353
|
-
ctx.ui.notify("Performing Chrome bridge health check", "info");
|
|
354
|
-
try {
|
|
355
|
-
const version = (await bridge.send("tab.version", {}, 35_000)) as { extensionId?: string; extensionVersion?: string };
|
|
356
|
-
const suffix = [version.extensionId ? `ID: ${version.extensionId}` : null, version.extensionVersion ? `ext v${version.extensionVersion}` : null]
|
|
357
|
-
.filter(Boolean)
|
|
358
|
-
.join(", ");
|
|
359
|
-
ctx.ui.notify(suffix ? `Chrome profile bridge connected (${suffix})` : "Chrome profile bridge connected", "info");
|
|
360
|
-
} catch (error) {
|
|
361
|
-
ctx.ui.notify(`Chrome bridge health check failed: ${(error as Error).message}`, "warning");
|
|
362
|
-
}
|
|
363
|
-
},
|
|
364
|
-
});
|
|
365
|
-
|
|
366
373
|
pi.registerCommand("chrome-doctor", {
|
|
367
374
|
description:
|
|
368
|
-
"
|
|
375
|
+
"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).",
|
|
369
376
|
handler: async (_args, ctx) => {
|
|
377
|
+
ctx.ui.notify("Performing Chrome bridge health check", "info");
|
|
370
378
|
const lines: string[] = [`pi-chrome v${PI_CHROME_VERSION}`];
|
|
371
379
|
const status = bridge.status();
|
|
372
380
|
lines.push(`• Local bridge: mode=${status.mode}, url=${status.url}`);
|
|
373
381
|
try {
|
|
374
|
-
const version = (await bridge.send("tab.version", {},
|
|
382
|
+
const version = (await bridge.send("tab.version", {}, 35_000)) as {
|
|
375
383
|
extensionId?: string;
|
|
376
384
|
extensionVersion?: string;
|
|
377
385
|
};
|
|
@@ -440,7 +448,7 @@ If chrome_* tools time out, ask the user to run /chrome-onboard, then load the b
|
|
|
440
448
|
await pi.exec("sh", ["-lc", `printf %s ${JSON.stringify(extensionPath)} | pbcopy`], { cwd: workspaceCwd(ctx), timeout: 5_000 }).catch(() => undefined);
|
|
441
449
|
}
|
|
442
450
|
ctx.ui.notify(
|
|
443
|
-
"Chrome bridge setup opened. The extension path has been copied to your clipboard. After loading it, run /chrome-
|
|
451
|
+
"Chrome bridge setup opened. The extension path has been copied to your clipboard. After loading it, run /chrome-doctor.",
|
|
444
452
|
"info",
|
|
445
453
|
);
|
|
446
454
|
},
|