@omercnet/paseo-shared-browser 0.4.0 → 0.4.1

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
@@ -12,13 +12,17 @@ input.
12
12
 
13
13
  ## Screenshots
14
14
 
15
+ Both PNGs show the same isolated Chromium session from a dedicated Paseo test daemon. They were
16
+ captured at 2× pixel density after browser DevTools verified the rendered page contained no private
17
+ organization names.
18
+
15
19
  ### Wide desktop
16
20
 
17
- ![Shared Browser showing the same live Paseo page to two viewers on desktop](docs/images/shared-browser-wide.png)
21
+ ![Shared Browser showing the same live Paseo page to two viewers on desktop](https://raw.githubusercontent.com/omercnet/paseo-plugins/main/paseo-shared-browser/docs/images/shared-browser-wide.png)
18
22
 
19
23
  ### Compact client
20
24
 
21
- ![Shared Browser controls and the same canonical browser viewport on a compact client](docs/images/shared-browser-compact.png)
25
+ ![Shared Browser controls and the same canonical browser viewport on a compact client](https://raw.githubusercontent.com/omercnet/paseo-plugins/main/paseo-shared-browser/docs/images/shared-browser-compact.png)
22
26
 
23
27
  ## Runtime model
24
28
 
@@ -87,12 +91,16 @@ npm run prepare:runtime
87
91
  paseo plugin install "$PWD"
88
92
  ```
89
93
 
90
- The build requires Node.js 24 or newer and npm on the daemon host. The runtime also needs a
91
- Chromium-compatible executable. On Linux ARM64, where the bundled download is unavailable, the
92
- installer automatically uses native Chromium from `/usr/bin/chromium`. Install a non-Snap Chromium
93
- build with the system package manager before adding the plugin. Set
94
- `PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE` to an absolute path when Chromium is installed elsewhere.
95
- The plugin does not emulate x64 Chromium.
94
+ The build requires Node.js 24 or newer and npm on the daemon host. On Linux x64, macOS, and
95
+ Windows, runtime preparation installs and stages the platform's Chrome for Testing distribution. On
96
+ Linux ARM64, where that download is unavailable, the installer automatically uses native Chromium
97
+ from `/usr/bin/chromium`. Install a non-Snap Chromium build with the system package manager before
98
+ adding the plugin. Set `PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE` to an absolute path when Chromium
99
+ is installed elsewhere. The plugin does not emulate x64 Chromium on Linux.
100
+
101
+ On Windows, the upstream installer retains its Chrome download cache under the OS user profile.
102
+ Before upgrading the plugin on Windows, close active Shared Browser sessions and stop the Paseo
103
+ daemon; Windows does not allow the installer to replace runtime executables that are still running.
96
104
 
97
105
  Open a workspace, search the Command Center for **Open Shared Browser**, or tap the **Shared
98
106
  Browser** composer pill while a workspace session is open.
@@ -124,11 +132,12 @@ disconnect does not close or reset either process.
124
132
 
125
133
  The plugin recognizes only these deployment overrides:
126
134
 
127
- | Variable | Meaning |
128
- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
129
- | `PASEO_HOME` | Paseo data root. Defaults to `~/.paseo`; browser runtime, supervisor IPC, and profiles live below `plugin-data/shared-browser`. |
130
- | `PASEO_SHARED_BROWSER_AGENT_BROWSER_BINARY` | Absolute path to the pinned `agent-browser` executable. Defaults to `$PASEO_HOME/plugin-data/shared-browser/runtime/node_modules/.bin/agent-browser`. |
131
- | `PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE` | Absolute path to Chromium. Defaults to `$PASEO_HOME/plugin-data/shared-browser/runtime/chromium/chrome`. On Linux ARM64, installation automatically links `/usr/bin/chromium`; use this override for another compatible, non-Snap location. |
135
+ | Variable | Meaning |
136
+ | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
137
+ | `PASEO_HOME` | Paseo data root. Defaults to `~/.paseo`; browser runtime, supervisor IPC, and profiles live below `plugin-data/shared-browser`. |
138
+ | `PASEO_SHARED_BROWSER_AGENT_BROWSER_BINARY` | Absolute path to the pinned `agent-browser` executable. Defaults to `$PASEO_HOME/plugin-data/shared-browser/runtime/node_modules/.bin/agent-browser` (`agent-browser.exe` on Windows). |
139
+ | `PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE` | Absolute path to Chromium. Defaults to `$PASEO_HOME/plugin-data/shared-browser/runtime/chromium/chrome` (`chrome.exe` on Windows). On Linux ARM64, installation automatically links `/usr/bin/chromium`; use this override for another compatible, non-Snap location. |
140
+ | `PASEO_SHARED_BROWSER_CHROMIUM_ARGS` | Optional Chromium arguments passed through the managed runtime. Intended for host requirements such as `--no-sandbox` in an already-isolated CI runner; do not disable the browser sandbox on a general-purpose host. |
132
141
 
133
142
  User-supplied `AGENT_BROWSER_*` variables are deliberately ignored.
134
143
 
@@ -143,11 +152,16 @@ User-supplied `AGENT_BROWSER_*` variables are deliberately ignored.
143
152
  ## Security boundary
144
153
 
145
154
  - Paseo plugins are trusted, unsandboxed code. The plugin server, detached supervisor,
146
- `agent-browser`, and Chromium execute as the daemon OS user and can reach that user's files,
147
- processes, credentials, and network.
148
- - Supervisor IPC uses a user-private Unix socket and token files with owner-only permissions.
149
- `agent-browser` IPC metadata and workspace profile directories are also owner-only. The plugin
150
- rejects a non-loopback CDP endpoint and disables the `agent-browser` stream port.
155
+ `agent-browser`, Chromium, and other processes running as the daemon OS user share one trust
156
+ boundary and can reach that user's files, processes, credentials, and network.
157
+ - Supervisor IPC uses a user-private Unix socket on Linux and macOS or an installation-specific
158
+ named pipe on Windows, plus a token file under Paseo's user data root for every connection. On
159
+ Windows, file privacy relies on the ACL inherited from `PASEO_HOME`; custom locations must remain
160
+ private to the daemon user. `agent-browser` uses loopback TCP for its command and stream services
161
+ on Windows, so Windows support assumes a trusted single-user host; these services are not an
162
+ isolation boundary between local OS users.
163
+ - `agent-browser` IPC metadata and workspace profile directories are owner-only on POSIX systems.
164
+ The plugin rejects a non-loopback CDP endpoint.
151
165
  - Viewer and control tokens coordinate clients already paired to the same Paseo daemon. Paseo v0.8
152
166
  plugin RPC callbacks expose no authenticated caller identity, so these human-viewer tokens are a
153
167
  workflow safeguard, not an authorization boundary. The stdio MCP adapter separately uses an
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-shared-browser",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "One real Chromium session per Paseo workspace, shared live across every connected client.",
5
5
  "keywords": [
6
6
  "browser",
@@ -22,19 +22,16 @@
22
22
  "directory": "paseo-shared-browser"
23
23
  },
24
24
  "files": [
25
- "CHANGELOG.md",
26
25
  "LICENSE",
27
26
  "README.md",
28
27
  "index.client.tsx",
29
28
  "index.server.ts",
30
29
  "client",
31
- "docs",
32
30
  "scripts/prepare-dependencies.mjs",
33
31
  "scripts/prepare-runtime.mjs",
34
32
  "server",
35
33
  "shared",
36
- "paseo-plugin.json",
37
- "tsconfig.json"
34
+ "paseo-plugin.json"
38
35
  ],
39
36
  "type": "module",
40
37
  "publishConfig": {
@@ -43,8 +40,8 @@
43
40
  "scripts": {
44
41
  "typecheck": "tsc --noEmit",
45
42
  "lint": "oxlint --deny-warnings .",
46
- "format": "oxfmt . '!CHANGELOG.md'",
47
- "format:check": "oxfmt --check . '!CHANGELOG.md'",
43
+ "format": "oxfmt --ignore-path .formatignore .",
44
+ "format:check": "oxfmt --check --ignore-path .formatignore .",
48
45
  "prepare:runtime": "node scripts/prepare-runtime.mjs",
49
46
  "test:unit": "node --test scripts/prepare-dependencies.test.mjs && vitest run tests/contracts.test.ts tests/lease.test.ts tests/runtime-lifecycle.test.ts tests/agent-access.test.ts",
50
47
  "test:smoke": "vitest run tests/browser.smoke.test.ts --testTimeout=30000"
@@ -14,13 +14,15 @@ import {
14
14
  writeFile,
15
15
  } from "node:fs/promises";
16
16
  import { constants } from "node:fs";
17
- import { arch, homedir, platform } from "node:os";
17
+ import { arch, homedir, platform, userInfo } from "node:os";
18
18
  import { dirname, join, resolve } from "node:path";
19
19
  import { promisify } from "node:util";
20
20
  import { fileURLToPath } from "node:url";
21
21
 
22
22
  const execFileAsync = promisify(execFile);
23
23
  const expectedVersion = "0.37.1";
24
+ const hostPlatform = platform();
25
+ const hostArch = arch();
24
26
  const projectRoot = dirname(dirname(fileURLToPath(import.meta.url)));
25
27
  const paseoHome = process.env.PASEO_HOME || join(homedir(), ".paseo");
26
28
  const pluginDataRoot = join(paseoHome, "plugin-data", "shared-browser");
@@ -28,7 +30,16 @@ const runtimeRoot = join(pluginDataRoot, "runtime");
28
30
  const stagingRoot = join(pluginDataRoot, `.runtime-${process.pid}`);
29
31
  const runtimeModules = join(stagingRoot, "node_modules");
30
32
  const packagedAgentBrowser = resolveDependencyRoot("agent-browser");
31
- const runtimeEntry = join(runtimeModules, ".bin", "agent-browser");
33
+ const runtimeEntry = join(
34
+ runtimeModules,
35
+ ".bin",
36
+ hostPlatform === "win32" ? "agent-browser.exe" : "agent-browser",
37
+ );
38
+ const runtimeChromiumEntry = join(
39
+ stagingRoot,
40
+ "chromium",
41
+ hostPlatform === "win32" ? "chrome.exe" : "chrome",
42
+ );
32
43
  const supervisorEntry = join(projectRoot, "server", "supervisor-entry.ts");
33
44
  const mcpEntry = join(projectRoot, "server", "mcp-entry.ts");
34
45
 
@@ -49,6 +60,15 @@ async function validateAgentBrowser(path) {
49
60
  throw new Error(`Expected agent-browser ${expectedVersion}, received ${version ?? "unknown"}`);
50
61
  }
51
62
  }
63
+
64
+ async function validateChromium(path) {
65
+ await execFileAsync(path, ["--version"], {
66
+ encoding: "utf8",
67
+ timeout: 15_000,
68
+ windowsHide: true,
69
+ });
70
+ }
71
+
52
72
  async function bundleRuntimeEntries() {
53
73
  await build({
54
74
  entryPoints: {
@@ -81,11 +101,18 @@ async function applyPrivatePermissions(path) {
81
101
  async function stageChromiumExecutable(executable) {
82
102
  const chromiumRoot = join(stagingRoot, "chromium");
83
103
  await mkdir(chromiumRoot, { recursive: true, mode: 0o700 });
84
- await symlink(executable, join(chromiumRoot, "chrome"));
104
+ if (hostPlatform === "darwin") {
105
+ const quotedExecutable = `'${executable.replaceAll("'", `'"'"'`)}'`;
106
+ await writeFile(runtimeChromiumEntry, `#!/bin/sh\nexec ${quotedExecutable} "$@"\n`);
107
+ await chmod(runtimeChromiumEntry, 0o700);
108
+ } else {
109
+ await symlink(executable, runtimeChromiumEntry);
110
+ }
111
+ return runtimeChromiumEntry;
85
112
  }
86
113
 
87
114
  async function findLinuxArm64Chromium() {
88
- if (platform() !== "linux" || arch() !== "arm64") return null;
115
+ if (hostPlatform !== "linux" || hostArch !== "arm64") return null;
89
116
 
90
117
  const candidate = "/usr/bin/chromium";
91
118
  try {
@@ -99,6 +126,25 @@ async function findLinuxArm64Chromium() {
99
126
  }
100
127
  }
101
128
 
129
+ function installedChromiumExecutable(releaseDir) {
130
+ switch (hostPlatform) {
131
+ case "darwin":
132
+ return join(
133
+ releaseDir,
134
+ "Google Chrome for Testing.app",
135
+ "Contents",
136
+ "MacOS",
137
+ "Google Chrome for Testing",
138
+ );
139
+ case "linux":
140
+ return join(releaseDir, "chrome");
141
+ case "win32":
142
+ return join(releaseDir, "chrome.exe");
143
+ default:
144
+ throw new Error(`Unsupported Chromium platform: ${hostPlatform}`);
145
+ }
146
+ }
147
+
102
148
  async function installChromium() {
103
149
  const override = process.env.PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE;
104
150
  if (override) {
@@ -106,35 +152,70 @@ async function installChromium() {
106
152
  override,
107
153
  "PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE",
108
154
  );
109
- await stageChromiumExecutable(executable);
110
- return;
155
+ if (hostPlatform === "win32") return executable;
156
+ return stageChromiumExecutable(executable);
111
157
  }
112
158
 
113
159
  const systemChromium = await findLinuxArm64Chromium();
114
160
  if (systemChromium) {
115
- await stageChromiumExecutable(systemChromium);
161
+ const executable = await stageChromiumExecutable(systemChromium);
116
162
  console.log(`Using system Chromium at ${systemChromium}`);
117
- return;
163
+ return executable;
118
164
  }
119
165
 
120
- const installHome = join(stagingRoot, "install-home");
121
- await mkdir(installHome, { recursive: true, mode: 0o700 });
166
+ // Rust's dirs::home_dir() ignores HOME on Windows and resolves the profile
167
+ // through SHGetKnownFolderPath. Use that cache there; Unix stays isolated.
168
+ const installHome =
169
+ hostPlatform === "win32" ? userInfo().homedir : join(stagingRoot, "install-home");
170
+ if (hostPlatform !== "win32") {
171
+ await mkdir(installHome, { recursive: true, mode: 0o700 });
172
+ }
122
173
  await execFileAsync(runtimeEntry, ["install"], {
123
- env: { ...process.env, HOME: installHome },
174
+ env: hostPlatform === "win32" ? process.env : { ...process.env, HOME: installHome },
124
175
  timeout: 180_000,
176
+ windowsHide: true,
125
177
  });
126
178
 
127
179
  const browsersRoot = join(installHome, ".agent-browser", "browsers");
128
180
  const releases = (await readdir(browsersRoot, { withFileTypes: true }))
129
181
  .filter((entry) => entry.isDirectory() && entry.name.startsWith("chrome-"))
130
182
  .map((entry) => entry.name)
131
- .sort()
132
- .reverse();
133
- const chrome = releases[0] && join(browsersRoot, releases[0], "chrome");
134
- if (!chrome) throw new Error("agent-browser install did not produce a Chromium release");
183
+ .sort((left, right) => right.localeCompare(left, undefined, { numeric: true }));
184
+ const releaseDir = releases[0] && join(browsersRoot, releases[0]);
185
+ if (!releaseDir) throw new Error("agent-browser install did not produce a Chromium release");
186
+ const chrome = installedChromiumExecutable(releaseDir);
135
187
  await requireExecutable(chrome, "Installed Chromium executable");
136
- await cp(dirname(chrome), join(stagingRoot, "chromium"), { recursive: true, force: true });
137
- await rm(installHome, { recursive: true, force: true });
188
+ await cp(releaseDir, join(stagingRoot, "chromium"), { recursive: true, force: true });
189
+ if (hostPlatform === "darwin") {
190
+ // chrome can't be a symlink to the .app's real binary here: macOS dyld
191
+ // resolves @executable_path from the invoked path's own directory, so a
192
+ // symlink at chromium/chrome would break the bundle's relative Frameworks
193
+ // lookup. Exec the real binary by its actual path instead.
194
+ await writeFile(
195
+ runtimeChromiumEntry,
196
+ `#!/bin/sh\nexec "$(cd "$(dirname "$0")" && pwd)/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing" "$@"\n`,
197
+ );
198
+ await chmod(runtimeChromiumEntry, 0o700);
199
+ }
200
+ if (hostPlatform !== "win32") await rm(installHome, { recursive: true, force: true });
201
+ return runtimeChromiumEntry;
202
+ }
203
+
204
+ async function stageAgentBrowserEntry() {
205
+ if (hostPlatform === "win32") {
206
+ const executableArch = hostArch === "arm64" ? "x64" : hostArch;
207
+ if (executableArch !== "x64") {
208
+ throw new Error(`Unsupported agent-browser Windows architecture: ${hostArch}`);
209
+ }
210
+ await cp(
211
+ join(packagedAgentBrowser, "bin", `agent-browser-win32-${executableArch}.exe`),
212
+ runtimeEntry,
213
+ { force: true },
214
+ );
215
+ } else {
216
+ await symlink("../agent-browser/bin/agent-browser.js", runtimeEntry);
217
+ }
218
+ await chmod(runtimeEntry, 0o700);
138
219
  }
139
220
 
140
221
  await rm(stagingRoot, { recursive: true, force: true });
@@ -144,8 +225,7 @@ await cp(packagedAgentBrowser, join(runtimeModules, "agent-browser"), {
144
225
  recursive: true,
145
226
  force: true,
146
227
  });
147
- await symlink("../agent-browser/bin/agent-browser.js", runtimeEntry);
148
- await chmod(runtimeEntry, 0o700);
228
+ await stageAgentBrowserEntry();
149
229
 
150
230
  const binaryOverride = process.env.PASEO_SHARED_BROWSER_AGENT_BROWSER_BINARY;
151
231
  if (binaryOverride)
@@ -153,7 +233,7 @@ if (binaryOverride)
153
233
  await requireExecutable(binaryOverride, "PASEO_SHARED_BROWSER_AGENT_BROWSER_BINARY"),
154
234
  );
155
235
  await validateAgentBrowser(await requireExecutable(runtimeEntry, "Packaged agent-browser entry"));
156
- await installChromium();
236
+ const chromiumEntry = await installChromium();
157
237
  await bundleRuntimeEntries();
158
238
 
159
239
  await writeFile(
@@ -162,7 +242,9 @@ await writeFile(
162
242
  { mode: 0o600 },
163
243
  );
164
244
  await applyPrivatePermissions(stagingRoot);
165
- await rm(runtimeRoot, { recursive: true, force: true });
245
+ if (hostPlatform !== "win32") {
246
+ await validateChromium(await requireExecutable(chromiumEntry, "Staged Chromium executable"));
247
+ }
166
248
  await rename(stagingRoot, runtimeRoot);
167
249
 
168
250
  console.log(`Prepared immutable Shared Browser runtime assets in ${runtimeRoot}`);
@@ -139,6 +139,15 @@ function runtimeEnvironment(ipcDirectory: string): NodeJS.ProcessEnv {
139
139
  "DISPLAY",
140
140
  "WAYLAND_DISPLAY",
141
141
  "XAUTHORITY",
142
+ "APPDATA",
143
+ "LOCALAPPDATA",
144
+ "USERPROFILE",
145
+ "HOMEDRIVE",
146
+ "HOMEPATH",
147
+ "SYSTEMROOT",
148
+ "WINDIR",
149
+ "COMSPEC",
150
+ "PATHEXT",
142
151
  ]) {
143
152
  const value = process.env[key];
144
153
  if (value !== undefined) environment[key] = value;
@@ -221,6 +230,7 @@ export class AgentBrowserRuntime {
221
230
  chmod(this.ipcDirectory, PRIVATE_DIRECTORY_MODE),
222
231
  ]);
223
232
  await this.assertVersion();
233
+ const chromiumArguments = process.env.PASEO_SHARED_BROWSER_CHROMIUM_ARGS;
224
234
  const opened = await this.invoke([
225
235
  "--session",
226
236
  this.session,
@@ -228,6 +238,7 @@ export class AgentBrowserRuntime {
228
238
  this.profilePath,
229
239
  "--executable-path",
230
240
  this.executablePath,
241
+ ...(chromiumArguments ? ["--args", chromiumArguments] : []),
231
242
  "--json",
232
243
  "open",
233
244
  this.initialUrl,
@@ -18,12 +18,25 @@ function paseoHome(): string {
18
18
 
19
19
  export async function createRuntimeOwner(): Promise<RuntimeOwner<OwnedRuntime>> {
20
20
  const root = join(paseoHome(), "plugin-data", "shared-browser");
21
+ const ipcDirectory =
22
+ process.platform === "win32"
23
+ ? join(root, "ipc")
24
+ : join(
25
+ "/tmp",
26
+ `paseo-shared-browser-${createHash("sha256").update(root).digest("hex").slice(0, 16)}`,
27
+ );
21
28
  const binaryPath =
22
29
  process.env.PASEO_SHARED_BROWSER_AGENT_BROWSER_BINARY ??
23
- join(root, "runtime", "node_modules", ".bin", "agent-browser");
30
+ join(
31
+ root,
32
+ "runtime",
33
+ "node_modules",
34
+ ".bin",
35
+ process.platform === "win32" ? "agent-browser.exe" : "agent-browser",
36
+ );
24
37
  const executablePath =
25
38
  process.env.PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE ??
26
- join(root, "runtime", "chromium", "chrome");
39
+ join(root, "runtime", "chromium", process.platform === "win32" ? "chrome.exe" : "chrome");
27
40
  return {
28
41
  async create(workspaceId) {
29
42
  const hash = createHash("sha256").update(workspaceId).digest("hex");
@@ -31,7 +44,7 @@ export async function createRuntimeOwner(): Promise<RuntimeOwner<OwnedRuntime>>
31
44
  binaryPath,
32
45
  executablePath,
33
46
  profilePath: join(root, "profiles", hash),
34
- ipcDirectory: join(root, "ipc"),
47
+ ipcDirectory,
35
48
  session: `ws-${hash.slice(0, 16)}`,
36
49
  initialUrl: DEFAULT_BROWSER_URL,
37
50
  });
@@ -393,10 +393,12 @@ export class AgentSupervisorClient {
393
393
  }
394
394
 
395
395
  async function readPrivateFile(path: string): Promise<string> {
396
- const metadata = await stat(path);
397
- if ((metadata.mode & 0o077) !== 0) throw new Error(`Supervisor file is not private: ${path}`);
398
- if (typeof process.getuid === "function" && metadata.uid !== process.getuid()) {
399
- throw new Error(`Supervisor file is owned by another user: ${path}`);
396
+ if (process.platform !== "win32") {
397
+ const metadata = await stat(path);
398
+ if ((metadata.mode & 0o077) !== 0) throw new Error(`Supervisor file is not private: ${path}`);
399
+ if (typeof process.getuid === "function" && metadata.uid !== process.getuid()) {
400
+ throw new Error(`Supervisor file is owned by another user: ${path}`);
401
+ }
400
402
  }
401
403
  return await readFile(path, "utf8");
402
404
  }
@@ -1,7 +1,7 @@
1
- import { randomBytes, timingSafeEqual } from "node:crypto";
1
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
2
2
  import { chmod, mkdir, open, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { createServer, type Socket } from "node:net";
4
- import { homedir } from "node:os";
4
+ import { homedir, tmpdir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
  import type { BrowserFrame, BrowserInputEvent, BrowserState, Viewport } from "../shared/browser";
7
7
  import { SessionManager } from "./browser-policy";
@@ -826,15 +826,24 @@ export function resolveSupervisorPaths(
826
826
  "shared-browser",
827
827
  `supervisor-v${RUNTIME_PROTOCOL_VERSION}`,
828
828
  );
829
+ const socketId = createHash("sha256").update(root).digest("hex");
830
+ const socket =
831
+ process.platform === "win32"
832
+ ? String.raw`\\.\pipe\paseo-shared-browser-${socketId}`
833
+ : join(tmpdir(), `psb-${socketId.slice(0, 16)}.sock`);
829
834
  return {
830
835
  root,
831
- socket: join(root, "runtime.sock"),
836
+ socket,
832
837
  token: join(root, "runtime.token"),
833
838
  endpoint: join(root, "runtime.json"),
834
839
  lock: join(root, "startup.lock"),
835
840
  };
836
841
  }
837
842
 
843
+ async function removeSupervisorSocket(path: string): Promise<void> {
844
+ if (process.platform !== "win32") await rm(path, { force: true });
845
+ }
846
+
838
847
  export async function acquireStartupLock(path: string): Promise<() => Promise<void>> {
839
848
  await mkdir(dirname(path), { recursive: true, mode: DIRECTORY_MODE });
840
849
  await chmod(dirname(path), DIRECTORY_MODE);
@@ -956,7 +965,7 @@ export async function startSupervisorServer<Runtime extends RuntimeInstance>(
956
965
  await tokenHandle.close();
957
966
  }
958
967
  await chmod(paths.token, FILE_MODE);
959
- await rm(paths.socket, { force: true });
968
+ await removeSupervisorSocket(paths.socket);
960
969
  await new Promise<void>((resolve, reject) => {
961
970
  server.once("error", reject);
962
971
  server.listen(paths.socket, () => {
@@ -964,7 +973,7 @@ export async function startSupervisorServer<Runtime extends RuntimeInstance>(
964
973
  resolve();
965
974
  });
966
975
  });
967
- await chmod(paths.socket, SOCKET_MODE);
976
+ if (process.platform !== "win32") await chmod(paths.socket, SOCKET_MODE);
968
977
  await writeFile(
969
978
  paths.endpoint,
970
979
  JSON.stringify({ version: RUNTIME_PROTOCOL_VERSION, socket: paths.socket }),
@@ -974,7 +983,7 @@ export async function startSupervisorServer<Runtime extends RuntimeInstance>(
974
983
  } catch (error) {
975
984
  server.close();
976
985
  await Promise.allSettled([
977
- rm(paths.socket, { force: true }),
986
+ removeSupervisorSocket(paths.socket),
978
987
  rm(paths.endpoint, { force: true }),
979
988
  rm(paths.token, { force: true }),
980
989
  releaseLock(),
@@ -1006,7 +1015,7 @@ export async function startSupervisorServer<Runtime extends RuntimeInstance>(
1006
1015
  );
1007
1016
  } finally {
1008
1017
  await Promise.allSettled([
1009
- rm(paths.socket, { force: true }),
1018
+ removeSupervisorSocket(paths.socket),
1010
1019
  rm(paths.endpoint, { force: true }),
1011
1020
  rm(paths.token, { force: true }),
1012
1021
  releaseLock(),
package/CHANGELOG.md DELETED
@@ -1,83 +0,0 @@
1
- # Changelog
2
-
3
- ## [0.4.0](https://github.com/omercnet/paseo-plugins/compare/shared-browser-v0.3.1...shared-browser-v0.4.0) (2026-09-18)
4
-
5
-
6
- ### Features
7
-
8
- * **release:** publish plugins to npm ([#80](https://github.com/omercnet/paseo-plugins/issues/80)) ([3c93048](https://github.com/omercnet/paseo-plugins/commit/3c93048cfefda97d8c2bc1631e3428fb64bdad09))
9
-
10
-
11
- ### Bug Fixes
12
-
13
- * **shared-browser:** support Paseo 0.9 beta ([#86](https://github.com/omercnet/paseo-plugins/issues/86)) ([3af209f](https://github.com/omercnet/paseo-plugins/commit/3af209f3a8fa347ba7dc05c300061a682d809f88))
14
-
15
- ## [0.3.1](https://github.com/omercnet/paseo-plugins/compare/shared-browser-v0.3.0...shared-browser-v0.3.1) (2026-09-15)
16
-
17
-
18
- ### Bug Fixes
19
-
20
- * **shared-browser:** support system Chromium on Linux ARM64 ([#58](https://github.com/omercnet/paseo-plugins/issues/58)) ([f9c3dcb](https://github.com/omercnet/paseo-plugins/commit/f9c3dcb1031840c7cd0aed4a70f984d9d9bd24c1))
21
-
22
- ## [0.3.0](https://github.com/omercnet/paseo-plugins/compare/shared-browser-v0.2.2...shared-browser-v0.3.0) (2026-09-12)
23
-
24
-
25
- ### Features
26
-
27
- * **shared-browser:** adopt agent-browser runtime ([#18](https://github.com/omercnet/paseo-plugins/issues/18)) ([09623f3](https://github.com/omercnet/paseo-plugins/commit/09623f34f2b93943c19f809a865813fa37f467bd))
28
-
29
-
30
- ### Bug Fixes
31
-
32
- * **shared-browser:** skip MCP injection for OMP ([#21](https://github.com/omercnet/paseo-plugins/issues/21)) ([ee2db08](https://github.com/omercnet/paseo-plugins/commit/ee2db08243640600c5592debe72f680446fa26d0))
33
-
34
- ## [1.0.0](https://github.com/omercnet/paseo-plugins/compare/shared-browser-v0.2.2...shared-browser-v1.0.0) (2026-09-12)
35
-
36
- ### ⚠ BREAKING CHANGES
37
-
38
- * replace the browser runtime in place with plugin-owned `agent-browser` 0.37.1 and a detached daemon-host supervisor; deployments must provide the packaged runtime and a compatible Chromium executable, with Linux ARM64 deployments supplying native Chromium through `PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE`
39
-
40
- ### Bug Fixes
41
-
42
- * leave OMP agent creation unchanged because the provider rejects external MCP servers
43
-
44
- ### Features
45
-
46
- * preserve workspace browser processes across client disconnects and plugin reloads during a 120-second orphan grace, while retaining per-workspace profiles and tearing down active runtimes when workspace archive events are received
47
- * add daemon-local runtime overrides through `PASEO_SHARED_BROWSER_AGENT_BROWSER_BINARY` and `PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE`
48
-
49
- ## [0.2.2](https://github.com/omercnet/paseo-plugins/compare/shared-browser-v0.2.1...shared-browser-v0.2.2) (2026-09-10)
50
-
51
-
52
- ### Bug Fixes
53
-
54
- * upgrade plugins to Paseo 0.8.0 ([c6b4e40](https://github.com/omercnet/paseo-plugins/commit/c6b4e4081fc9527de0ca6183a3903ff5ec4f93c4))
55
- * upgrade plugins to Paseo 0.8.0 ([d7ad454](https://github.com/omercnet/paseo-plugins/commit/d7ad4540d2e1875bdf4a4d21f76ded25cac2f3dc))
56
-
57
- ## [0.2.1](https://github.com/omercnet/paseo-shared-browser/compare/v0.2.0...v0.2.1) (2026-09-08)
58
-
59
-
60
- ### Bug Fixes
61
-
62
- * address Paseo 0.8 migration review feedback ([#10](https://github.com/omercnet/paseo-shared-browser/issues/10)) ([15d2515](https://github.com/omercnet/paseo-shared-browser/commit/15d25157503b7fb70d02c5e420522764250cab62))
63
-
64
- ## [0.2.0](https://github.com/omercnet/paseo-shared-browser/compare/v0.1.1...v0.2.0) (2026-09-08)
65
-
66
- ### Features
67
-
68
- * migrate shared browser to Paseo 0.8 ([#8](https://github.com/omercnet/paseo-shared-browser/issues/8)) ([36f27ab](https://github.com/omercnet/paseo-shared-browser/commit/36f27abc5dab4a16c9770d6296916631965af333))
69
- ## [0.1.1](https://github.com/omercnet/paseo-shared-browser/compare/v0.1.0...v0.1.1) (2026-09-07)
70
-
71
- ### Bug Fixes
72
-
73
- * include README screenshots in release archives ([#6](https://github.com/omercnet/paseo-shared-browser/issues/6)) ([a231db4](https://github.com/omercnet/paseo-shared-browser/commit/a231db4b20e9c52ebe8624ad69e34e907037ac0a))
74
-
75
- ## 0.1.0 (2026-09-07)
76
-
77
- ### Features
78
-
79
- * share one live Chromium session per Paseo workspace ([b1e9fac](https://github.com/omercnet/paseo-shared-browser/commit/b1e9fac5021746cf2dc09f7bfb9ccf0cb3b82e5d))
80
-
81
- ### Bug Fixes
82
-
83
- * keep format check off the generated changelog ([#4](https://github.com/omercnet/paseo-shared-browser/issues/4)) ([2219adf](https://github.com/omercnet/paseo-shared-browser/commit/2219adf3c11591a88dc54b899dece76003f313c4))
Binary file
package/tsconfig.json DELETED
@@ -1,29 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "paths": {
7
- "electron": ["./server/electron.d.ts"]
8
- },
9
- "lib": ["ES2024"],
10
- "types": ["node", "react"],
11
- "jsx": "react-jsx",
12
- "strict": true,
13
- "noUncheckedIndexedAccess": true,
14
- "exactOptionalPropertyTypes": true,
15
- "skipLibCheck": true,
16
- "noEmit": true,
17
- "esModuleInterop": true,
18
- "allowSyntheticDefaultImports": true
19
- },
20
- "include": [
21
- "index.client.tsx",
22
- "index.server.ts",
23
- "client/**/*.ts",
24
- "client/**/*.tsx",
25
- "server/**/*.ts",
26
- "shared/**/*.ts",
27
- "tests/**/*.ts"
28
- ]
29
- }