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 +111 -12
- package/daemon-manager.js +71 -0
- package/daemon-manager.ts +99 -0
- package/extension/LICENSE +202 -0
- package/extension/MODIFICATIONS.md +19 -0
- package/extension/NOTICE +10 -0
- package/extension/THIRD_PARTY_NOTICES.txt +81 -0
- package/extension/lib/background.mjs +3 -0
- package/extension/lib/ui/authToken.js +3 -0
- package/extension/lib/ui/connect.js +3 -0
- package/extension/lib/ui/status.js +3 -0
- package/extension/manifest.json +1 -1
- package/extension-token.js +177 -0
- package/extension-token.ts +133 -0
- package/listeners.js +104 -0
- package/listeners.ts +84 -0
- package/package.json +19 -8
- package/{rech.js → rechrome.js} +1010 -425
- package/{rech.ts → rechrome.ts} +1010 -425
- package/serve.js +101 -88
- package/serve.ts +101 -88
- package/skills/rechrome-setup/SKILL.md +53 -0
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 #
|
|
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. **
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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` |
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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.
|
package/extension/NOTICE
ADDED
|
@@ -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.
|