rechrome 1.24.0 → 1.25.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
@@ -40,33 +40,69 @@ bun link
40
40
 
41
41
  Now `rechrome` (or `rech`) is available globally.
42
42
 
43
+ `rech profile` and `rech profiles` are aliases that list installed Chrome profiles and registered managed test profiles together.
44
+ Both also accept `ls` or `list`, for example `rechrome profiles ls`.
45
+ The connection column checks each profile's default session for the current worktree without
46
+ opening a browser: **Connected**, **Registered / idle**, **Registered / unknown** (the check
47
+ could not complete), or **Not set up**. Sessions opened with a custom `-s` or from another
48
+ worktree are outside this check. Managed test profiles are labeled separately from real Chrome.
49
+
43
50
  ## Quick start
44
51
 
52
+ Agents setting up Chrome on macOS can use the [rechrome setup skill](skills/rechrome-setup/SKILL.md), which covers native extension installation with Computer Use and CLI connection verification.
53
+ `rech setup` detects Codex and Claude Code environment hints and prints agent-specific guidance before the setup prompts. Set `RECH_SETUP_AGENT=codex`, `claude`, or `none` to override detection. Detection changes guidance only; desktop automation still follows the available tools and approval rules.
54
+
45
55
  ### 0. One-command setup (recommended)
46
56
 
47
57
  `rech setup` configures the daemon, Chrome extension, and connection URL in one pass:
48
58
 
59
+ If no supported daemon manager is available, setup asks before installing `oxmgr` globally
60
+ (default: No). It uses `bun i -g oxmgr` when launched with bunx and `npm i -g oxmgr`
61
+ when launched with npx. Pass `--yes` to approve this installation without prompting,
62
+ for example `bunx rechrome setup --profile Default --yes`.
63
+
49
64
  ```bash
50
- rech setup # interactive: pick a profile, follow the prompts
65
+ rech setup # choose a network, then a Chrome profile
51
66
  rech setup --profile you@email.com # non-interactive profile selection
67
+ rech setup --listen tailscale --profile you@email.com
52
68
  ```
53
69
 
54
70
  What it does per Chrome profile:
55
71
 
56
- 1. **Daemon** — installs/starts the `serve` daemon (skipped if already healthy).
57
- 2. **Extension** — if the multi-tab extension isn't installed in the chosen profile, it opens an
72
+ 1. **Network** — choose where this profile should be accessible: Local (default),
73
+ LAN, Tailscale (only when detected), or another detected interface. One daemon
74
+ serves multiple addresses; changing this profile's exposure preserves other
75
+ profiles. Listener changes reload without restarting the daemon or Chrome.
76
+ 2. **Profile** — choose a Chrome profile; automatically select it when only one exists.
77
+ 3. **Extension** — if the multi-tab extension isn't installed in the chosen profile, it opens an
58
78
  install guide **in that exact profile**; load it once via `chrome://extensions → Load unpacked`
59
79
  (a one-time manual click — Chrome only allows unpacked extensions through the GUI).
60
- 3. **Token** — once the extension is present, the auth token is **read automatically** from the
80
+ 4. **Token and connection** — once the extension is present, the auth token is **read automatically** from the
61
81
  profile's `localStorage` (no copy-paste). For headless/agent runs you can also pass it
62
82
  explicitly:
63
83
 
64
84
  ```bash
65
- rech setup --profile 18 --token <PLAYWRIGHT_MCP_EXTENSION_TOKEN> # or RECH_TOKEN=… rech setup …
85
+ rech setup --profile "Profile 18" --token <PLAYWRIGHT_MCP_EXTENSION_TOKEN> # or RECH_TOKEN=… rech setup …
66
86
  ```
67
87
 
68
- `setup` is agent-friendly: it never blocks on a TTY, opens the guide in the right profile, and
69
- auto-reads the token, so a non-interactive run completes end-to-end once the extension is loaded.
88
+ For unattended runs, `--listen local`, `--listen lan`, `--listen tailscale`, or
89
+ `--listen <detected-IP>` chooses the network. Multiple LAN addresses require an
90
+ explicit IP. Without `--listen`, setup selects local-only access for this profile.
91
+ Choosing Local removes this profile from remote listener allowlists. LAN binds to the selected address,
92
+ not `0.0.0.0`. Discovery lists IPv4 interfaces; it does not configure port forwarding
93
+ or publish through external tunnel providers.
94
+
95
+ `setup` opens a live guide in the selected profile. On macOS, the Extensions tab
96
+ is immediately before the guide: copy the path, switch to the previous tab, choose
97
+ Load unpacked, paste the path, and return. Setup monitors installation and reads
98
+ the token automatically, then verifies the browser connection. The page updates
99
+ without a refresh and offers Retry connection if the handshake fails.
100
+
101
+ Manual token paste remains available in the guide and interactive terminal;
102
+ `--token` / `RECH_TOKEN` also work. Keep the setup process running while using the
103
+ guide. Installation/token detection and connection retries each time out after
104
+ 15 minutes. Non-interactive setup waits for browser installation rather than
105
+ prompting for stdin, then uses the default configuration save location.
70
106
 
71
107
  > **Managed QA profiles (experimental):** `rech provision-profile <name> --experimental` spins up a
72
108
  > fully isolated profile on **Chrome for Testing** (run `npx playwright install chromium` first) with
@@ -105,10 +141,24 @@ rech tab-list
105
141
  rech --help
106
142
  ```
107
143
 
108
- rechrome walks up from the current working directory to find `.env.local`, so each project can have its own connection URL, Chrome profile, and extension token.
144
+ rechrome walks up from the current working directory to find `.env.local`, so each project can have its own connection URL, Chrome profile, and extension token. Explicit environment variables take priority: `RECHROME_URL='http://KEY@host:13775' rech status` overrides the saved URL for that command.
109
145
 
110
146
  ## Configuration
111
147
 
148
+ Profiles are stored in `~/.rechrome/profiles.yaml`. Existing `profiles.json` registries are also supported and automatically migrated on first read; the original JSON is retained as a private backup. YAML takes precedence when both exist. Registry writes are atomic and use owner-only permissions because entries contain Playwright bridge tokens. Invalid YAML fails explicitly instead of falling back to potentially stale JSON credentials.
149
+
150
+ Connection parameters also accept URL fragments:
151
+
152
+ ```sh
153
+ RECHROME_URL='https://your-host.ts.net/rechrome/?profile=qa#key=DAEMON_KEY' rech status
154
+ ```
155
+
156
+ `rech setup` prints and saves this URI format. Retrieve it later with `rech profile qa --print-uri`, or omit `qa` to use the configured profile. `profiles` remains an alias. The command prints only the URI to stdout, using the configured `RECHROME_URL` endpoint; `--listener local` selects a local listener instead. For example: `rech profile qa --print-uri --listener local`. The output contains a secret daemon key.
157
+
158
+ Direct connections use the root path, such as `http://127.0.0.1:13775/?profile=qa#key=DAEMON_KEY`. A prefix is optional and only added when explicitly configured with `--prefix`, for example for a proxy mounted at `/rechrome/`. Tailscale can also serve at the root without a prefix.
159
+
160
+ Setup generates this format: the profile is in the query and the daemon listener's bearer `key` is in the fragment. The daemon looks up the registered profile's separate Playwright bridge token and browser paths locally. Advanced clients can still supply the bridge credential as `token` (for example `#key=DAEMON_KEY&token=BRIDGE_TOKEN`). Fragment parameters override matching query parameters; fragment `key` overrides legacy `KEY@host`. Both `#?key=…` and `#key=…` work. The CLI reads these locally and sends the daemon key as an Authorization header; fragments are omitted from HTTP request URLs. Opening the URL in a browser does not configure a client or display a dashboard. Fragments can still be stored in browser history and copied links, so treat the complete connection URL as a secret.
161
+
112
162
  Copy `.env.example` to `.env.local` and edit:
113
163
 
114
164
  ```bash
@@ -119,7 +169,7 @@ cp .env.example .env.local
119
169
  | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
120
170
  | `RECHROME_URL` | Connection URL (auto-generated by `rech serve`). Also accepts `?extension_id=`, `?token=`, `?profile=` query params | — |
121
171
  | `PLAYWRIGHT_CLI` | Override the playwright-cli command/path (defaults to the bundled `@playwright/cli`; set this only for a custom or forked CLI) | bundled `@playwright/cli` |
122
- | `RECH_HOST` | Server bind address | `127.0.0.1` |
172
+ | `RECH_HOST` | Legacy bind address, used only before listeners.json is configured | `127.0.0.1` |
123
173
  | `PLAYWRIGHT_MCP_EXTENSION_ID` | Chrome extension ID (client overrides server) | — |
124
174
  | `PLAYWRIGHT_MCP_EXTENSION_TOKEN` | Chrome extension token — profile-specific, get it from the extension's status page (client overrides server) | — |
125
175
  | `PLAYWRIGHT_MCP_USER_DATA_DIR` | Chrome user data directory — use to pin connections to a specific Chrome install (client overrides server) | — |
@@ -136,10 +186,59 @@ cp .env.example .env.local
136
186
 
137
187
  ### Remote access
138
188
 
139
- By default the server binds to `127.0.0.1` (localhost only). For remote access, either:
189
+ `rech setup --profile <profile> --listen tailscale` exposes that profile through a
190
+ dedicated authenticated listener while retaining local management. The daemon
191
+ stores listener addresses, keys, and profile allowlists in `~/.rechrome/listeners.json`
192
+ (mode 0600), reloads changes automatically, and rejects invalid changes while keeping
193
+ the last working configuration. Each listener has its own bearer key. Use `rech profile ls`
194
+ to see configured exposure, and `rech listener ls` to list bindings without credentials.
195
+
196
+ ```bash
197
+ rech listener add qa --listen tailscale --profile qamac-remote --port 13776
198
+ rech listener remove qa
199
+ ```
200
+
201
+ Repeat `--profile` to allow several registered profiles. A profile may appear on
202
+ multiple listeners; `setup --listen` selects its exposure afresh. Edit the protected
203
+ JSON file to update an existing listener's allowlist or key. Changes do not terminate
204
+ browser sessions; removed listeners reject further requests. An occupied or unavailable
205
+ address causes reload to retain the old configuration, so verify reachability after editing.
206
+
207
+ Remote listeners permit navigation, tabs, snapshots, screenshots, and basic page interactions.
208
+ They reject arbitrary `run-code`/`eval`, filesystem commands, global session commands, and
209
+ browser/config overrides. Those commands remain available on the unrestricted **loopback
210
+ management listener**. Scoped sessions and output directories are separate from management
211
+ sessions; a listener cannot download another profile's output. This is an application access
212
+ policy, not an OS sandbox for the browser. Never publish the management listener through a
213
+ proxy or tunnel to clients that should have restricted access.
214
+
215
+ LAN traffic is plain HTTP unless TLS is configured. Tailscale provides its private transport;
216
+ binding an address does not configure Tailscale ACLs, port forwarding, or public tunnels.
217
+
218
+ To mount rechrome under a Tailscale Serve path, use a separate scoped loopback listener:
219
+
220
+ ```bash
221
+ rech setup --profile taku3 --listen local --prefix=rechrome --port=13776
222
+ tailscale serve --bg --https=443 --set-path=/rechrome http://127.0.0.1:13776/rechrome
223
+ ```
140
224
 
141
- - Use an SSH tunnel: `ssh -L 13775:localhost:13775 server-host`
142
- - Or set `RECH_HOST=0.0.0.0` (⚠️ ensure network is trusted — traffic is plain HTTP)
225
+ `--prefix=rechrome` and `--prefix=/rechrome/` both normalize to `/rechrome/`.
226
+ Without `--port`, prefixed setup reuses a matching scoped listener or defaults to
227
+ the management port plus one. Existing profiles can also use
228
+ `rech listener add qa-proxy --listen local --profile <profile> --port 13776 --prefix=rechrome`.
229
+ Setup prints the matching Serve command but does not run it or change existing Serve routes.
230
+ Include the path in the proxy target: Serve strips the mount path and the target restores it.
231
+
232
+ For remote clients, change the saved connection URL's origin to
233
+ `https://<machine>.<tailnet>.ts.net`, retaining `/rechrome/`, the listener's bearer
234
+ userinfo, and the profile query. Health checks, commands, and file downloads honor
235
+ that base path. The setup guide remains a temporary local page owned by the setup
236
+ process; it is not published through Serve. Never proxy the unrestricted management listener.
237
+
238
+ Existing installations retain the legacy listener until `rech setup` initializes the new
239
+ configuration. The first migration restarts only the daemon to load the new source. Existing
240
+ remote access is retained for currently registered profiles with a **new scoped key**; remote
241
+ clients need the new listener credentials. Browser processes are left running.
143
242
 
144
243
  ## Session namespacing
145
244
 
@@ -0,0 +1,71 @@
1
+ // @bun
2
+ // daemon-manager.ts
3
+ var OXMGR_WINFIX_BASE = "0.4.0";
4
+ function compareVersion(a, b) {
5
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
6
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
7
+ for (let i = 0;i < 3; i++) {
8
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
9
+ if (d !== 0)
10
+ return d < 0 ? -1 : 1;
11
+ }
12
+ return 0;
13
+ }
14
+ function oxmgrHasWinfix(version) {
15
+ if (!version)
16
+ return false;
17
+ const plus = version.indexOf("+");
18
+ const base = plus === -1 ? version : version.slice(0, plus);
19
+ const build = plus === -1 ? "" : version.slice(plus + 1);
20
+ if (build.includes("winfix"))
21
+ return true;
22
+ return compareVersion(base.trim(), OXMGR_WINFIX_BASE) > 0;
23
+ }
24
+ function pickDaemonManager(opts) {
25
+ const override = opts.override?.toLowerCase();
26
+ if (override === "oxmgr" && !opts.oxmgrBin) {
27
+ throw new Error("RECH_DAEMON_MANAGER=oxmgr, but oxmgr is not on PATH. Install oxmgr and add it to PATH, or install pm2 with `bun add -g pm2` and set RECH_DAEMON_MANAGER=pm2.");
28
+ }
29
+ if (override === "pm2" && !opts.pm2Bin) {
30
+ throw new Error("RECH_DAEMON_MANAGER=pm2, but pm2 is not on PATH. Install it with `bun add -g pm2` and ensure the global bin directory is on PATH.");
31
+ }
32
+ if (!opts.oxmgrBin && !opts.pm2Bin) {
33
+ throw new Error("No daemon process manager found on PATH (oxmgr or pm2). Install pm2 with `bun add -g pm2`, ensure the global bin directory is on PATH, then rerun `bunx rechrome setup`.");
34
+ }
35
+ const oxmgr = { id: "oxmgr", bin: opts.oxmgrBin ?? "oxmgr" };
36
+ const pm2 = { id: "pm2", bin: opts.pm2Bin ?? "pm2" };
37
+ if (override === "pm2")
38
+ return pm2;
39
+ if (override === "oxmgr")
40
+ return oxmgr;
41
+ if (opts.isWindows) {
42
+ if (opts.oxmgrBin && oxmgrHasWinfix(opts.oxmgrVersion))
43
+ return oxmgr;
44
+ if (opts.pm2Bin)
45
+ return pm2;
46
+ return oxmgr;
47
+ }
48
+ if (opts.oxmgrBin)
49
+ return oxmgr;
50
+ if (opts.pm2Bin)
51
+ return pm2;
52
+ return oxmgr;
53
+ }
54
+ function oxmgrInstallCommand(env) {
55
+ const agent = env.npm_config_user_agent?.toLowerCase() ?? "";
56
+ if (agent.startsWith("npm/"))
57
+ return ["npm", "i", "-g", "oxmgr"];
58
+ if (agent.startsWith("bun/"))
59
+ return ["bun", "i", "-g", "oxmgr"];
60
+ const execPath = env.npm_execpath?.replace(/\\/g, "/") ?? "";
61
+ if (/(^|\/)(npm|npx)(-cli\.js|\.cmd|\.exe)?$/i.test(execPath))
62
+ return ["npm", "i", "-g", "oxmgr"];
63
+ return ["bun", "i", "-g", "oxmgr"];
64
+ }
65
+ export {
66
+ OXMGR_WINFIX_BASE,
67
+ compareVersion,
68
+ oxmgrHasWinfix,
69
+ oxmgrInstallCommand,
70
+ pickDaemonManager
71
+ };
@@ -0,0 +1,99 @@
1
+ // Process-manager selection policy for the `rech serve` daemon.
2
+ //
3
+ // oxmgr is the cross-platform default. On Windows, stock oxmgr 0.4.0 wedges after
4
+ // a daemon crash: it leaves the daemon's control socket bound to a dead PID, so
5
+ // every subsequent oxmgr command fails with "daemon did not become ready in
6
+ // time". The snomiao/OxMgr fork fixes it and stamps the build `0.4.0+winfix`.
7
+ // We therefore only PREFER oxmgr on Windows when the winfix build is present;
8
+ // stock 0.4.0 falls back to pm2's named-pipe daemon, which has no such failure
9
+ // mode. POSIX oxmgr is unaffected and stays the default there.
10
+ //
11
+ // This module is deliberately pure (no Bun/process/fs) so the selection policy is
12
+ // unit-testable; the impure probing (Bun.which, `oxmgr --version`) lives in
13
+ // rechrome.ts and feeds its results into pickDaemonManager.
14
+
15
+ export type DaemonManager = { id: "oxmgr" | "pm2"; bin: string };
16
+
17
+ // Base version that first shipped the winfix build tag. A strictly newer base
18
+ // version is assumed to carry the fix upstream even without the explicit
19
+ // `+winfix` tag, so a future release doesn't get pinned out.
20
+ export const OXMGR_WINFIX_BASE = "0.4.0";
21
+
22
+ // Compare dotted numeric versions (major.minor.patch); any build/prerelease
23
+ // suffix must be stripped by the caller. Returns <0, 0, or >0.
24
+ export function compareVersion(a: string, b: string): number {
25
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
26
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
27
+ for (let i = 0; i < 3; i++) {
28
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
29
+ if (d !== 0) return d < 0 ? -1 : 1;
30
+ }
31
+ return 0;
32
+ }
33
+
34
+ // True when an `oxmgr --version` string reports a build carrying the Windows
35
+ // daemon-wedge fix: the `+winfix` build tag, OR a base version newer than the
36
+ // 0.4.0 that first shipped it. Anything older/unknown/empty is treated as
37
+ // unfixed so a Windows install falls back to pm2 rather than the wedge.
38
+ export function oxmgrHasWinfix(version: string | null | undefined): boolean {
39
+ if (!version) return false;
40
+ const plus = version.indexOf("+");
41
+ const base = plus === -1 ? version : version.slice(0, plus);
42
+ const build = plus === -1 ? "" : version.slice(plus + 1);
43
+ if (build.includes("winfix")) return true;
44
+ return compareVersion(base.trim(), OXMGR_WINFIX_BASE) > 0;
45
+ }
46
+
47
+ // Decide which manager daemonizes `rech serve`, given what's discoverable at
48
+ // runtime. `override` is RECH_DAEMON_MANAGER (case-insensitive) and, when set to
49
+ // a known manager, wins outright — bypassing the winfix guard as an explicit
50
+ // opt-in. The `*Bin` inputs are resolved executable paths (null when not on
51
+ // PATH). Missing dependencies fail here, before callers mutate configuration.
52
+ export function pickDaemonManager(opts: {
53
+ oxmgrBin: string | null;
54
+ pm2Bin: string | null;
55
+ oxmgrVersion: string | null;
56
+ isWindows: boolean;
57
+ override?: string | null;
58
+ }): DaemonManager {
59
+ const override = opts.override?.toLowerCase();
60
+ if (override === "oxmgr" && !opts.oxmgrBin) {
61
+ throw new Error("RECH_DAEMON_MANAGER=oxmgr, but oxmgr is not on PATH. Install oxmgr and add it to PATH, or install pm2 with `bun add -g pm2` and set RECH_DAEMON_MANAGER=pm2.");
62
+ }
63
+ if (override === "pm2" && !opts.pm2Bin) {
64
+ throw new Error("RECH_DAEMON_MANAGER=pm2, but pm2 is not on PATH. Install it with `bun add -g pm2` and ensure the global bin directory is on PATH.");
65
+ }
66
+ if (!opts.oxmgrBin && !opts.pm2Bin) {
67
+ throw new Error("No daemon process manager found on PATH (oxmgr or pm2). Install pm2 with `bun add -g pm2`, ensure the global bin directory is on PATH, then rerun `bunx rechrome setup`.");
68
+ }
69
+ const oxmgr: DaemonManager = { id: "oxmgr", bin: opts.oxmgrBin ?? "oxmgr" };
70
+ const pm2: DaemonManager = { id: "pm2", bin: opts.pm2Bin ?? "pm2" };
71
+
72
+ if (override === "pm2") return pm2;
73
+ if (override === "oxmgr") return oxmgr;
74
+
75
+ if (opts.isWindows) {
76
+ // Prefer oxmgr only when it carries the winfix; otherwise pm2. If pm2 is
77
+ // missing, fall back to oxmgr regardless — a possibly-wedging manager still
78
+ // beats none, and the operator can install pm2 or the winfix build.
79
+ if (opts.oxmgrBin && oxmgrHasWinfix(opts.oxmgrVersion)) return oxmgr;
80
+ if (opts.pm2Bin) return pm2;
81
+ return oxmgr;
82
+ }
83
+
84
+ // POSIX: oxmgr preferred, pm2 fallback.
85
+ if (opts.oxmgrBin) return oxmgr;
86
+ if (opts.pm2Bin) return pm2;
87
+ return oxmgr;
88
+ }
89
+
90
+ // npm/npx sets npm_config_user_agent even when the CLI's shebang runs Bun.
91
+ // Prefer launcher metadata over the runtime, which is Bun for both launchers.
92
+ export function oxmgrInstallCommand(env: { npm_config_user_agent?: string; npm_execpath?: string; [name: string]: string | undefined }):["bun" | "npm", "i", "-g", "oxmgr"] {
93
+ const agent = env.npm_config_user_agent?.toLowerCase() ?? "";
94
+ if (agent.startsWith("npm/")) return ["npm", "i", "-g", "oxmgr"];
95
+ if (agent.startsWith("bun/")) return ["bun", "i", "-g", "oxmgr"];
96
+ const execPath = env.npm_execpath?.replace(/\\/g, "/") ?? "";
97
+ if (/(^|\/)(npm|npx)(-cli\.js|\.cmd|\.exe)?$/i.test(execPath)) return ["npm", "i", "-g", "oxmgr"];
98
+ return ["bun", "i", "-g", "oxmgr"];
99
+ }
@@ -0,0 +1,202 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Portions Copyright (c) Microsoft Corporation.
190
+ Portions Copyright 2017 Google Inc.
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -0,0 +1,19 @@
1
+ # Rechrome extension modifications
2
+
3
+ Derived from microsoft/playwright, Apache-2.0. Upstream extension baseline:
4
+ `bb6c00957` (extension 0.2.1); audited fork revision: `62472742d`.
5
+
6
+ Modified by rechrome contributors:
7
+
8
+ - `manifest.json`: version and tab-group permission for the fork.
9
+ - `src/background.ts`, `src/connectedTabGroup.ts`, `src/pendingConnection.ts`,
10
+ `src/relayConnection.ts`: concurrent clients, client tab groups, shorter titles,
11
+ and recovery from stalled relay connections.
12
+ - `src/ui/connect.tsx`: client name forwarding on token-bypass connections and
13
+ recovery from a stalled extension service worker.
14
+ - `vite.config.mts`, `vite.sw.config.mts`: ship licensing documents and retain
15
+ modification notices in generated bundles through `licensePlugin.ts`.
16
+
17
+ Generated JavaScript and the manifest in the shipped extension incorporate these
18
+ changes. Upstream copyright and license notices remain applicable. Rechrome's
19
+ root MIT license does not replace the Apache-2.0 terms for inherited code.
@@ -0,0 +1,10 @@
1
+ Playwright
2
+ Copyright (c) Microsoft Corporation
3
+
4
+ This software contains code derived from the Puppeteer project (https://github.com/puppeteer/puppeteer),
5
+ available under the Apache 2.0 license (https://github.com/puppeteer/puppeteer/blob/master/LICENSE).
6
+
7
+ Rechrome extension fork
8
+ Modifications Copyright (c) 2026 rechrome contributors.
9
+ This is an independently maintained fork, not an official Microsoft release.
10
+ See MODIFICATIONS.md for changes and THIRD_PARTY_NOTICES.txt for bundled dependencies.