all repos — sz-chrome-remote-extension @ 745d1eee66cf86305934e3ad57f7019ccb249a45

pi-chrome, patched to drive a signed-in browser on another host through a broker, with an MCP server

0.15.31: per-session tab groups named 'Pi Session <name-or-id>'
Tianren Dong tianrendong.dtr@gmail.com
Sun, 31 May 2026 14:51:07 -0400
commit

745d1eee66cf86305934e3ad57f7019ccb249a45

parent

d57dc6583953fcb652db5bd222698d58d3090e20

M CHANGELOG.md → CHANGELOG.md

@@ -2,6 +2,12 @@ # Changelog

All notable user-facing changes to `pi-chrome`. +## 0.15.31 — 2026-05-31 + +Per-session tab groups. + +- **Each Pi session gets its own tab group.** Auto-grouped tabs are now named `Pi Session <name>` using the session's display name, falling back to the session id when unnamed. Multiple Pi sessions driving the same Chrome no longer share one group — each collects its tabs separately. Pass an explicit `groupTitle` to override, or `group:false` / `groupTitle:""` on `action=new` to opt out. + ## 0.15.30 — 2026-05-31 Tab grouping for `chrome_tab`.
M extensions/chrome-profile-bridge/browser-extension/manifest.json → extensions/chrome-profile-bridge/browser-extension/manifest.json

@@ -1,7 +1,7 @@

{ "manifest_version": 3, "name": "Pi Chrome Connector", - "version": "0.15.30", + "version": "0.15.31", "description": "Lets Pi control tabs in Chrome via a local connector at 127.0.0.1.", "permissions": [ "tabs",
M extensions/chrome-profile-bridge/index.ts → extensions/chrome-profile-bridge/index.ts

@@ -599,6 +599,14 @@ throw new Error("Chrome control locked. Ask the user to run /chrome authorize before using chrome_* tools.");

} }; + // Tab-group title for this Pi session: prefer the user-set display name, else the session id. + const sessionGroupTitle = (ctx: ExtensionContext): string => { + const sm = ctx.sessionManager; + const name = sm.getSessionName?.(); + const id = sm.getSessionId?.(); + return `Pi Session ${name || id || "unknown"}`; + }; + const updateChromeStatus = (ctx: ExtensionContext): void => { if (chromeControlAuthorized()) { ctx.ui.setStatus("chrome", ctx.ui.theme.fg("success", "●") + " Chrome Bridge");

@@ -1037,14 +1045,21 @@ url: Type.Optional(Type.String({ description: "URL for action=new." })),

targetId: Type.Optional(Type.String({ description: "Chrome tab id for activate/close/group/ungroup." })), urlIncludes: Type.Optional(Type.String({ description: "Match the target tab by URL substring for activate/close/group/ungroup." })), titleIncludes: Type.Optional(Type.String({ description: "Match the target tab by title substring for activate/close/group/ungroup." })), - group: Type.Optional(Type.Boolean({ description: "action=new only: pass false to open an ungrouped tab. By default every Pi-opened tab joins the window's 'Pi' tab group." })), - groupTitle: Type.Optional(Type.String({ description: "Tab group title for action=group (or action=new to open into a named group). Defaults to 'Pi'. Pass an empty string on action=new to opt out of grouping." })), + group: Type.Optional(Type.Boolean({ description: "action=new only: pass false to open an ungrouped tab. By default every Pi-opened tab joins this session's own tab group." })), + groupTitle: Type.Optional(Type.String({ description: "Tab group title for action=group/new. Defaults to this Pi session's group ('Pi Session <name-or-id>'). Pass an empty string on action=new to opt out of grouping." })), groupColor: Type.Optional(Type.String({ description: "Tab group color for action=group/new: grey, blue, red, yellow, green, pink, purple, cyan, or orange. Defaults to blue." })), host: Type.Optional(Type.String()), port: Type.Optional(Type.Number()), }), - async execute(_id, params, signal): Promise<ToolTextResult> { - const result = await authorizedBridgeSend(`tab.${params.action}`, params, DEFAULT_TIMEOUT_MS, signal); + async execute(_id, params, signal, _onUpdate, ctx): Promise<ToolTextResult> { + const forwarded = { ...params } as typeof params & { groupTitle?: string }; + // Default every Pi-opened/explicitly-grouped tab into this session's own group, + // named after the session display name (falling back to the session id), unless + // the caller specified a group title or opted out with group:false. + if ((params.action === "new" || params.action === "group") && params.groupTitle === undefined && params.group !== false) { + forwarded.groupTitle = sessionGroupTitle(ctx); + } + const result = await authorizedBridgeSend(`tab.${params.action}`, forwarded, DEFAULT_TIMEOUT_MS, signal); if (params.action === "list") { const tabs = result as Array<{ id: number; title: string; url: string; active: boolean; windowId: number; group?: { title?: string } | null }>; const text = tabs.map((tab) => `${tab.id}\t${tab.active ? "*" : " "}\t${tab.group?.title ? `[${tab.group.title}] ` : ""}${tab.title || "(untitled)"}\t${tab.url}`).join("\n") || "No tabs.";
M package.json → package.json

@@ -1,6 +1,6 @@

{ "name": "pi-chrome", - "version": "0.15.30", + "version": "0.15.31", "scripts": { "test": "node test-suite/unit/csp-eval.test.mjs", "version": "node scripts/sync-manifest-version.js",