@parall/daemon 1.36.1 → 1.37.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.
Files changed (56) hide show
  1. package/bundle/manifest.json +11 -11
  2. package/bundle/parall-browser-pod.js +307 -27
  3. package/bundle/parall-claude-agent.js +363 -50
  4. package/bundle/parall-codex-agent.js +362 -48
  5. package/bundle/parall-daemon.js +33473 -31706
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +2 -2
  8. package/dist/clip-runtime/browser-daemon-env.d.ts +29 -0
  9. package/dist/clip-runtime/browser-daemon-env.d.ts.map +1 -0
  10. package/dist/clip-runtime/browser-daemon-env.js +70 -0
  11. package/dist/clip-runtime/browser-profile-manager.d.ts +18 -5
  12. package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
  13. package/dist/clip-runtime/browser-profile-manager.js +149 -24
  14. package/dist/clip-runtime/browser-profile-pool.d.ts +250 -0
  15. package/dist/clip-runtime/browser-profile-pool.d.ts.map +1 -0
  16. package/dist/clip-runtime/browser-profile-pool.js +581 -0
  17. package/dist/clip-runtime/index.d.ts +2 -2
  18. package/dist/clip-runtime/index.d.ts.map +1 -1
  19. package/dist/clip-runtime/index.js +2 -1
  20. package/dist/clip-runtime/process-manager.d.ts +5 -3
  21. package/dist/clip-runtime/process-manager.d.ts.map +1 -1
  22. package/dist/clip-runtime/subprocess.d.ts +14 -0
  23. package/dist/clip-runtime/subprocess.d.ts.map +1 -1
  24. package/dist/clip-runtime/subprocess.js +49 -0
  25. package/dist/config.d.ts +14 -15
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +8 -26
  28. package/dist/daemon-main.d.ts +8 -0
  29. package/dist/daemon-main.d.ts.map +1 -0
  30. package/dist/daemon-main.js +165 -0
  31. package/dist/daemon-paths.d.ts +14 -0
  32. package/dist/daemon-paths.d.ts.map +1 -0
  33. package/dist/daemon-paths.js +26 -0
  34. package/dist/daemon-update-mode.d.ts +8 -0
  35. package/dist/daemon-update-mode.d.ts.map +1 -0
  36. package/dist/daemon-update-mode.js +18 -0
  37. package/dist/index.js +41 -167
  38. package/dist/runtime-bin-resolver.d.ts +4 -0
  39. package/dist/runtime-bin-resolver.d.ts.map +1 -1
  40. package/dist/runtime-bin-resolver.js +40 -7
  41. package/dist/runtime-detector.d.ts +30 -0
  42. package/dist/runtime-detector.d.ts.map +1 -0
  43. package/dist/runtime-detector.js +100 -0
  44. package/dist/supervisor.d.ts +53 -2
  45. package/dist/supervisor.d.ts.map +1 -1
  46. package/dist/supervisor.js +604 -67
  47. package/dist/update-health-gate.d.ts +66 -0
  48. package/dist/update-health-gate.d.ts.map +1 -0
  49. package/dist/update-health-gate.js +93 -0
  50. package/dist/updater-manifest.d.ts +2 -1
  51. package/dist/updater-manifest.d.ts.map +1 -1
  52. package/dist/updater-manifest.js +38 -7
  53. package/dist/updater.d.ts +13 -2
  54. package/dist/updater.d.ts.map +1 -1
  55. package/dist/updater.js +126 -17
  56. package/package.json +6 -6
@@ -0,0 +1,581 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { BrowserProfileManager, } from './browser-profile-manager.js';
4
+ /**
5
+ * BrowserProfilePool — true per-profile isolation for the BYOC daemon supervisor.
6
+ *
7
+ * WHY THIS EXISTS. A single bb-browser/Chromium shared across profiles isolates
8
+ * only at the bb-browser "account" (CDP BrowserContext) level. That is real for
9
+ * cookies/storage, but bb-browser's COMMAND routing is account-blind: any command
10
+ * issued without an explicit tabId falls through to a single GLOBAL current tab
11
+ * (cdp.currentTargetId, else targets[0]) regardless of the `account` field
12
+ * (bb-browser-pro daemon.js ensurePageTarget). So when the global current tab
13
+ * belongs to profile A, a command meant for profile B executes in A's context —
14
+ * e.g. logging into a second account overwrites the first, and two profiles can
15
+ * never hold two different sessions at once (reproduced on a real Chrome).
16
+ *
17
+ * THE FIX. Give every profile its OWN bb-browser daemon + Chromium + on-disk home
18
+ * (`<baseHomeDir>/<profileId>`). Each Chromium then hosts exactly one profile, so
19
+ * there is no cross-profile global tab to leak into — isolation is enforced by the
20
+ * OS process + user-data-dir boundary, not by remembering to scope every command.
21
+ * The pool owns one {@link BrowserProfileManager} per profile and routes by
22
+ * profileId; an idle (stopped) profile holds no Chromium, so cost scales with the
23
+ * number of CONCURRENTLY-OPEN profiles, not the number that ever existed.
24
+ *
25
+ * NOT used by the hosted browser pod (`browser-pod.ts`): a pod serves exactly one
26
+ * profile, so it keeps using a single BrowserProfileManager directly (and its
27
+ * BrowserStateStore snapshots that manager's flat homeDir to S3 — the pool's
28
+ * per-profile nesting would break that). The pool is supervisor-only.
29
+ *
30
+ * OWNERSHIP. The pool is a per-profile runtime REGISTRY + the per-profile mutex
31
+ * (create / stop / list-active / invoke / viewer, serialized via `enqueue()`). The
32
+ * DESIRED-STATE coordinator is the supervisor: the server (clip-service) is the
33
+ * SSOT for which profiles should be live, and `DaemonSupervisor.reconcileBrowserProfiles`
34
+ * converges the local registry to it (open/ensure desired, stop the rest via
35
+ * `activeProfileIds()`). The pool keeps one bit of lifecycle policy — the `stopped`
36
+ * fence — because invoke (hub channel) and viewer funnel through it and must reject
37
+ * a stale command without a server round-trip.
38
+ */
39
+ export class BrowserProfilePool {
40
+ opts;
41
+ managers = new Map();
42
+ // Per-profile op queue. Hub browser invokes arrive through ClipProcessManager,
43
+ // NOT the supervisor's per-profile lifecycle queue, so without this an invoke
44
+ // could interleave with a stop/reset that is disposing the same profile's
45
+ // runtime — racing a reset's wipe. Every op for a profileId runs serially;
46
+ // different profiles stay parallel.
47
+ opChains = new Map();
48
+ // Desired-state fence: profiles explicitly stopped/reset (desired = down).
49
+ // Serialization alone orders ops but does not stop a LATE invoke (authorized
50
+ // before the stop, arriving after it on the independent hub channel) from
51
+ // re-spawning a Chromium for a stopped profile and flipping it back to
52
+ // running. An invoke OR viewer command for a fenced profile is rejected until
53
+ // an explicit open/ensureRuntime (lifecycle/reconcile) revives it — a stale
54
+ // viewer command must not resurrect a profile the server already stopped. The
55
+ // full reconcile also fences profiles the server no longer lists as live.
56
+ stopped = new Set();
57
+ // Monotonic per-profile counter bumped every time the profile is fenced (stop /
58
+ // reset / reconcile release / pre-fence). A revive (open / ensureRuntime)
59
+ // captures this at ENQUEUE time and, when it finally runs, refuses to clear the
60
+ // fence if a newer stop was observed since — so a stale open/ensure queued
61
+ // before a stop (e.g. a reconnect reconcile racing a stop) can't un-fence the
62
+ // profile and let a late hub invoke spawn a transient Chromium. Last-write-wins.
63
+ stopSeq = new Map();
64
+ // Last lifecycle_generation observed for a profile (from a lifecycle event / reconcile
65
+ // snapshot). A hub invoke CAPTURES this at the pool entry (enqueue) so its `running`
66
+ // report carries the generation it was issued under — a later stop bumps the server's
67
+ // generation, so that captured (older) generation is then rejected as stale (PR #1650).
68
+ generations = new Map();
69
+ // The generation of the op CURRENTLY running for a profile, set at op start (the value
70
+ // captured at enqueue) and read by the manager-report wrapper so manager-driven status
71
+ // reports carry it. Per-profile ops are serialized, so this is stable for the op's
72
+ // duration — it is NOT a live read of `generations` (which a concurrent stop would move).
73
+ opGeneration = new Map();
74
+ // Set by stop() and NEVER reset (stop() is terminal — the supervisor discards
75
+ // the pool afterward). manager() refuses to create/return a runtime while true,
76
+ // so no queued or late op can spawn a manager that escapes teardown (the maps
77
+ // are cleared mid-stop and a queued op's closure may run after stop() returns).
78
+ stopping = false;
79
+ constructor(opts) {
80
+ this.opts = opts;
81
+ mkdirSync(this.opts.baseHomeDir, { recursive: true });
82
+ }
83
+ // All per-profile entrypoints go through enqueue() — it both serializes against
84
+ // concurrent lifecycle ops and turns a synchronous reject from manager() (e.g.
85
+ // an unsafe profile id) into a rejected promise, honoring the BrowserInvoker
86
+ // `Promise<…>` contract.
87
+ /** Inbound hub browser invoke for a profile — routed to that profile's own daemon. */
88
+ invoke(request) {
89
+ // Capture the profile's generation at the POOL ENTRY (enqueue), not when the op
90
+ // runs: if a stop arrives while this invoke waits in the queue, its captured
91
+ // (older) generation makes the server reject the `running` report as stale.
92
+ const generation = this.generations.get(request.profileId);
93
+ return this.enqueue(request.profileId, () => this.runWithGeneration(request.profileId, generation, () => {
94
+ // Reject a late invoke for a profile that was explicitly stopped/reset — do
95
+ // not resurrect it (see the `stopped` fence). An explicit open / ensureRuntime
96
+ // / viewer takeover clears the fence first. NOTE: a hub invoke reaches the pool
97
+ // directly while a lifecycle open may still be in the supervisor queue, so an
98
+ // invoke racing an in-flight open can hit the fence and be rejected here even
99
+ // though that open will clear it on its next turn — the rejection is transient
100
+ // (the hub retries next cycle), not a lost command.
101
+ if (this.stopped.has(request.profileId)) {
102
+ throw new Error(`browser profile ${request.profileId} is stopped; open it before invoking`);
103
+ }
104
+ // Pin account === profileId at the pool boundary. Each profile has its OWN
105
+ // Chromium hosting exactly one bb-browser account (the profileId), so a
106
+ // mismatched `account` in the request must never reach the manager — it would
107
+ // address a non-existent account and bb-browser's account-blind routing would
108
+ // fall through to the global current tab (the very leak this pool fixes).
109
+ return this.manager(request.profileId).invoke({ ...request, account: request.profileId });
110
+ }));
111
+ }
112
+ // Set opGeneration for the duration of a serialized op so manager-driven status
113
+ // reports carry the captured generation, then clear it. `gen` is captured at enqueue
114
+ // (invoke) or passed from the lifecycle event / reconcile snapshot (open/ensure/stop/
115
+ // reset). Per-profile ops are serialized, so opGeneration is stable for the op.
116
+ async runWithGeneration(profileId, gen, fn) {
117
+ if (gen !== undefined)
118
+ this.opGeneration.set(profileId, gen);
119
+ try {
120
+ return await fn();
121
+ }
122
+ finally {
123
+ if (gen !== undefined)
124
+ this.opGeneration.delete(profileId);
125
+ }
126
+ }
127
+ /**
128
+ * Reconcile-time: ensure the profile's runtime (account + tab) is live. If
129
+ * `sinceSeq` (the fence generation observed when this op was enqueued) is given
130
+ * and a newer stop has been observed since, skip — a stale reconcile/open queued
131
+ * before a stop must not un-fence and revive a profile about to be torn down
132
+ * (the pending stop wins; last-write-wins).
133
+ */
134
+ ensureRuntime(profileId, sinceSeq, generation, opts = {}) {
135
+ return this.enqueue(profileId, () => this.runWithGeneration(profileId, generation, async () => {
136
+ if (this.isStaleRevive(profileId, sinceSeq))
137
+ return;
138
+ if (generation !== undefined)
139
+ this.generations.set(profileId, generation);
140
+ this.stopped.delete(profileId); // reconcile = server desires it running
141
+ // forceStatusReport: pending-row reconnect recovery must re-ACK `running` even if
142
+ // the dedup marker already says running (else the server stays stuck at pending).
143
+ await this.manager(profileId).ensureRuntime(profileId, opts);
144
+ }));
145
+ }
146
+ /** Lifecycle `open` — open (or reuse) the profile's start tab. `sinceSeq`: see ensureRuntime. */
147
+ openProfile(profileId, startUrl, sinceSeq, generation) {
148
+ return this.enqueue(profileId, () => this.runWithGeneration(profileId, generation, async () => {
149
+ if (this.isStaleRevive(profileId, sinceSeq))
150
+ return;
151
+ if (generation !== undefined)
152
+ this.generations.set(profileId, generation);
153
+ this.stopped.delete(profileId); // explicit open revives a stopped profile
154
+ await this.manager(profileId).openProfile(profileId, startUrl);
155
+ }));
156
+ }
157
+ /** True if a newer stop was observed after this revive captured `sinceSeq`. */
158
+ isStaleRevive(profileId, sinceSeq) {
159
+ if (sinceSeq === undefined || this.stopSeqOf(profileId) === sinceSeq)
160
+ return false;
161
+ this.opts.log.info(`browser profile ${profileId}: skipping stale revive (newer stop observed since enqueue)`);
162
+ return true;
163
+ }
164
+ /** Live-viewer control command for a profile — routed to its own daemon/streamer. */
165
+ handleViewerCommand(profileId, sessionId, command, input, turn) {
166
+ return this.enqueue(profileId, () => {
167
+ // Reject viewer commands on a fenced (stopped/reset) profile. A stale
168
+ // command — queued to the machine before the stop, delivered after — is
169
+ // indistinguishable from a fresh takeover here, and the server has already
170
+ // moved the profile to stopped; reviving it would resurrect a Chromium with
171
+ // the server still reading stopped. A real takeover goes through an explicit
172
+ // lifecycle reopen (open), which clears the fence and updates server status.
173
+ if (this.stopped.has(profileId)) {
174
+ throw new Error(`browser profile ${profileId} is stopped; reopen it before viewing`);
175
+ }
176
+ return this.manager(profileId).handleViewerCommand(profileId, sessionId, command, input, turn);
177
+ });
178
+ }
179
+ /**
180
+ * Lifecycle `stop` (explicit, owner-driven) — stop the profile's viewer + tear
181
+ * down its bb-browser daemon and Chromium to free the resources (an idle profile
182
+ * must not keep a browser running). Confirms `stopped` back to the server: the
183
+ * stop API already set the server row to `stopped`, and this report keeps the
184
+ * daemon authoritative over the actual runtime state (the no-runtime case still
185
+ * confirms). For reconcile-driven cleanup of profiles the server no longer owns
186
+ * here, use {@link releaseRuntime} — it must NOT report.
187
+ */
188
+ stopProfile(profileId, generation) {
189
+ return this.enqueue(profileId, () => this.runWithGeneration(profileId, generation, async () => {
190
+ // Already fenced by the WS pre-fence (fence() bumped the generation for this
191
+ // event); only ensure the fence here — do NOT bump again, or a newer open
192
+ // that captured its seq AFTER the pre-fence would look stale and be dropped.
193
+ this.stopped.add(profileId);
194
+ if (generation !== undefined)
195
+ this.generations.set(profileId, generation);
196
+ const manager = this.managers.get(profileId);
197
+ if (!manager) {
198
+ // Direct report (no manager wrapper to inject the generation) — pass it.
199
+ this.opts.reportStatus?.(profileId, 'stopped', undefined, generation);
200
+ return;
201
+ }
202
+ try {
203
+ await manager.stopProfile(profileId); // reports `stopped` (via wrapper), kills viewer
204
+ }
205
+ finally {
206
+ await this.dispose(profileId, manager); // release the bb-browser daemon + Chromium
207
+ }
208
+ }));
209
+ }
210
+ /**
211
+ * Reconcile teardown (negative convergence) — release a local runtime the server
212
+ * no longer lists as live on this machine (stopped / deleted / reassigned).
213
+ * Fences + disposes like a stop, but does NOT report status: here the server is
214
+ * the desired-state SSOT, so a daemon-side `stopped` push is semantically wrong
215
+ * (for a profile reassigned to another machine it isn't ours to report — the
216
+ * server's machine-binding check rejects it as 404 anyway) and only emits
217
+ * spurious calls. Local-only cleanup.
218
+ */
219
+ releaseRuntime(profileId) {
220
+ return this.enqueue(profileId, async () => {
221
+ // Pre-fenced by the reconcile at enqueue (fence() bumped the generation);
222
+ // only ensure the fence here — don't bump, or a reopen requested after the
223
+ // reconcile decision but before this runs would be dropped as stale.
224
+ this.stopped.add(profileId);
225
+ const manager = this.managers.get(profileId);
226
+ if (manager)
227
+ await this.dispose(profileId, manager);
228
+ });
229
+ }
230
+ /**
231
+ * Synchronously fence a profile (reject late invoke/viewer until an explicit
232
+ * reopen) WITHOUT tearing anything down. The supervisor calls this the moment a
233
+ * stop/reset lifecycle event is received, before the queued stop/reset op runs:
234
+ * a hub invoke — which bypasses the supervisor queue and hits {@link invoke}
235
+ * directly — authorized just before the server row flipped to `stopped` could
236
+ * otherwise arrive in that window and spawn a transient Chromium the stop then
237
+ * immediately tears down. Fencing now rejects it instead. An in-flight invoke
238
+ * already past the fence check is unaffected (the queued stop serializes behind
239
+ * it); open/ensure clears the fence. Idempotent; no-op once stopping.
240
+ */
241
+ fence(profileId) {
242
+ if (this.stopping)
243
+ return;
244
+ this.stopped.add(profileId);
245
+ // SINGLE generation-bump point. Every path that decides to stop/reset/release
246
+ // a profile calls fence() the moment it OBSERVES the intent (WS event receipt /
247
+ // reconcile enqueue) — synchronously, before queuing the teardown op — so a
248
+ // revive (open/ensureRuntime) that captures the generation afterward sees the
249
+ // new value and isn't dropped as stale. Queued teardown ops only ensure the
250
+ // fence (stopped.add); they never bump, or a reopen requested between observe
251
+ // and execute would be stranded at stopped.
252
+ this.stopSeq.set(profileId, (this.stopSeq.get(profileId) ?? 0) + 1);
253
+ }
254
+ /** The current fence generation for a profile — captured by a revive at enqueue. */
255
+ stopSeqOf(profileId) {
256
+ return this.stopSeq.get(profileId) ?? 0;
257
+ }
258
+ /**
259
+ * Lifecycle `reset` — wipe the profile's persisted state (cookies, localStorage,
260
+ * IndexedDB, cache) for a clean slate. Removes BOTH the per-profile home AND any
261
+ * unmigrated legacy flat cookie file — a reset done before the profile's first
262
+ * post-upgrade open would otherwise leave the legacy file in place, and the lazy
263
+ * migration would re-import the old login on the next open, silently undoing the
264
+ * reset. Done AFTER the daemon is stopped so Chromium has released its files.
265
+ * Isolated by construction — it can never touch another profile's state.
266
+ * Fail-closed: if the wipe fails (e.g. Chromium still holds a file, or a
267
+ * permission error), report `error` and throw rather than report `stopped` —
268
+ * a reset that left state on disk must not be marked clean, since the next open
269
+ * could restore the old login. Reports `stopped` only on a confirmed wipe.
270
+ */
271
+ resetProfile(profileId, generation, resetGeneration) {
272
+ return this.enqueue(profileId, () => this.runWithGeneration(profileId, generation, async () => {
273
+ if (generation !== undefined)
274
+ this.generations.set(profileId, generation);
275
+ try {
276
+ // Wipe + record the applied reset_generation (so a reconnect won't re-wipe).
277
+ await this.doResetWipe(profileId, resetGeneration);
278
+ }
279
+ catch (err) {
280
+ const message = err instanceof Error ? err.message : String(err);
281
+ this.opts.reportStatus?.(profileId, 'error', message, generation);
282
+ throw err;
283
+ }
284
+ this.opts.reportStatus?.(profileId, 'stopped', undefined, generation);
285
+ }));
286
+ }
287
+ /**
288
+ * Apply a reset the daemon hasn't yet (caught on reconnect when the server's
289
+ * reset_generation is newer than this daemon's recorded one). Same wipe as
290
+ * resetProfile but WITHOUT a status report — the caller drives desired state (an
291
+ * open/ensure reports running for a still-desired profile; a stopped profile is
292
+ * left stopped). Must run BEFORE any open/ensure for the profile so a reopen starts
293
+ * from clean state, never the old cookies (the supervisor enqueues it first).
294
+ */
295
+ applyResetWipe(profileId, resetGeneration) {
296
+ return this.enqueue(profileId, () => this.doResetWipe(profileId, resetGeneration));
297
+ }
298
+ // Core reset wipe (runs inside an enqueue closure): fence, dispose the runtime, wipe
299
+ // the home, THEN record the applied reset_generation — strictly in that order, so the
300
+ // sidecar is written only after a successful wipe (a crash in between just re-wipes,
301
+ // idempotent). removeProfileState throws on failure → reset_generation NOT recorded.
302
+ async doResetWipe(profileId, resetGeneration) {
303
+ this.stopped.add(profileId); // fence late invokes until an explicit reopen
304
+ const manager = this.managers.get(profileId);
305
+ if (manager)
306
+ await this.dispose(profileId, manager);
307
+ this.removeProfileState(profileId);
308
+ if (resetGeneration !== undefined)
309
+ this.writeResetGeneration(profileId, resetGeneration);
310
+ }
311
+ /**
312
+ * Reconnect cleanup for a profile the server no longer lists AT ALL (deleted or
313
+ * reassigned away): dispose any runtime and wipe the per-profile home — the old host
314
+ * must not keep its cookies/storage. NO status report (the server is the SSOT and the
315
+ * row is gone there); fences too. Idempotent (a wiped home simply isn't found again).
316
+ */
317
+ wipeAbsent(profileId) {
318
+ return this.enqueue(profileId, async () => {
319
+ this.stopped.add(profileId);
320
+ const manager = this.managers.get(profileId);
321
+ if (manager)
322
+ await this.dispose(profileId, manager);
323
+ this.removeProfileState(profileId);
324
+ });
325
+ }
326
+ /**
327
+ * Tear down every profile's daemon (supervisor shutdown). TERMINAL: the
328
+ * supervisor discards the pool after this, so `stopping` stays set forever —
329
+ * manager() refuses any further op. Drain outstanding per-profile chains BEFORE
330
+ * clearing the maps, so a queued op whose `prev` settles only after stop()
331
+ * returns can't slip through manager() and recreate a runtime (orphaning a
332
+ * Chromium past shutdown); `stopping` is the backstop if one still races in.
333
+ */
334
+ async stop() {
335
+ this.stopping = true;
336
+ await Promise.allSettled([...this.opChains.values()]);
337
+ const managers = [...this.managers.values()];
338
+ this.managers.clear();
339
+ this.opChains.clear();
340
+ this.stopped.clear();
341
+ // allSettled (not all): one manager's stop() throwing must not skip tearing
342
+ // down the rest — every Chromium has to be released. (Bounded: each stop()
343
+ // awaits waitForChildExit, which has a hard cap.)
344
+ await Promise.allSettled(managers.map((m) => m.stop()));
345
+ }
346
+ /**
347
+ * Registry view: profile ids that currently hold a live local runtime. The
348
+ * supervisor (the desired-state coordinator) diffs this against the server's
349
+ * desired-live set on full reconcile and stops the extras via stopProfile() —
350
+ * the convergence policy lives there, not here; the pool only owns runtime
351
+ * lifecycle mechanics.
352
+ */
353
+ activeProfileIds() {
354
+ return [...this.managers.keys()];
355
+ }
356
+ /**
357
+ * Run `op` after every prior operation for this profile settles — a per-profile
358
+ * serialization so invoke / open / stop / reset / viewer never interleave for
359
+ * one profile (different profiles still run in parallel). Bounded: bb-browser
360
+ * commands time out (~30s), so a stop never waits forever behind a hung invoke.
361
+ * Also normalizes a synchronous throw from `op` into a rejected promise.
362
+ *
363
+ * QUEUE CONTRACT. This is the FINAL per-profile mutex — EVERY pool entrypoint
364
+ * (invoke, open, ensure, stop, reset, viewer) goes through it, so it alone
365
+ * guarantees per-profile mutual exclusion. The supervisor's separate
366
+ * `enqueueBrowserProfileOp` is a higher-level ORDERING layer for the ops it
367
+ * originates (WS lifecycle/viewer events + reconcile), not a second mutex. Hub
368
+ * invokes arrive from the clip runtime and do NOT pass through the supervisor
369
+ * queue — only this one. So: do not assume "all profile ops share one queue";
370
+ * they share THIS one.
371
+ */
372
+ enqueue(profileId, op) {
373
+ // Reject new work once teardown has begun, so a stop-time invoke/open/viewer
374
+ // never queues or spawns a runtime (the manager() guard backstops any op
375
+ // already queued whose closure runs after the maps are cleared).
376
+ if (this.stopping) {
377
+ return Promise.reject(new Error('browser profile pool is stopping'));
378
+ }
379
+ const prev = this.opChains.get(profileId) ?? Promise.resolve();
380
+ const run = prev.then(op, op); // run op whether the prior op fulfilled or rejected
381
+ this.opChains.set(profileId, run);
382
+ void run
383
+ .catch(() => { })
384
+ .finally(() => {
385
+ if (this.opChains.get(profileId) === run)
386
+ this.opChains.delete(profileId);
387
+ });
388
+ return run;
389
+ }
390
+ /**
391
+ * Get (or lazily create) the dedicated manager for a profile. Synchronous so the
392
+ * get-or-create is atomic — the bb-browser daemon itself starts lazily on first
393
+ * command, not here.
394
+ */
395
+ manager(profileId) {
396
+ if (!profileId)
397
+ throw new Error('browser profile id is required');
398
+ // During teardown, refuse any op so it can't spawn/use a manager that would
399
+ // escape stop() (which clears the maps mid-flight). See `stopping`.
400
+ if (this.stopping)
401
+ throw new Error('browser profile pool is stopping');
402
+ let manager = this.managers.get(profileId);
403
+ if (manager)
404
+ return manager;
405
+ this.migrateFlatProfileState(profileId);
406
+ const create = this.opts.createManager ?? ((opts) => new BrowserProfileManager(opts));
407
+ manager = create({
408
+ homeDir: this.profileHome(profileId),
409
+ log: this.opts.log,
410
+ // The manager reports with 3 args; inject the current op's generation so the
411
+ // server can fence stale reports. opGeneration is set for the duration of each
412
+ // serialized op (see runWithGeneration), so this reads THIS op's captured
413
+ // generation, not a live value a concurrent stop could have moved.
414
+ reportStatus: (id, status, errorMsg) => this.opts.reportStatus?.(id, status, errorMsg, this.opGeneration.get(id)),
415
+ // Thread the per-profile outbound proxy resolver through to this profile's own
416
+ // manager, so each isolated Chromium binds its own egress proxy at
417
+ // account_create (the resolver is keyed by profileId === account).
418
+ resolveProxy: this.opts.resolveProxy,
419
+ });
420
+ this.managers.set(profileId, manager);
421
+ return manager;
422
+ }
423
+ /** Stop a manager and drop it from the map (identity-checked so a concurrent
424
+ * recreate during the async stop is never evicted). */
425
+ async dispose(profileId, manager) {
426
+ try {
427
+ await manager.stop();
428
+ }
429
+ finally {
430
+ if (this.managers.get(profileId) === manager)
431
+ this.managers.delete(profileId);
432
+ }
433
+ }
434
+ profileHome(profileId) {
435
+ return path.join(this.opts.baseHomeDir, sanitizeProfileId(profileId));
436
+ }
437
+ /** Legacy shared-layout cookie file for a profile (pre per-profile homes). */
438
+ legacyAccountFile(profileId) {
439
+ return path.join(this.opts.baseHomeDir, 'accounts', `${sanitizeProfileId(profileId)}.json`);
440
+ }
441
+ /**
442
+ * Remove every on-disk trace of a profile: its per-profile home AND any
443
+ * unmigrated legacy flat cookie file — otherwise a later open would re-import
444
+ * the legacy cookies (see resetProfile). Throws if any target could not be
445
+ * removed so the caller can fail the reset closed rather than report a clean
446
+ * slate while state survives on disk.
447
+ */
448
+ removeProfileState(profileId) {
449
+ const failures = [];
450
+ for (const target of [this.profileHome(profileId), this.legacyAccountFile(profileId)]) {
451
+ try {
452
+ rmSync(target, { recursive: true, force: true });
453
+ }
454
+ catch (err) {
455
+ failures.push(`${target}: ${String(err)}`);
456
+ }
457
+ }
458
+ if (failures.length > 0) {
459
+ throw new Error(`could not remove profile state for ${profileId} — ${failures.join('; ')}`);
460
+ }
461
+ }
462
+ // --- per-profile reset sidecar: the last reset_generation this daemon APPLIED ---
463
+ // A tiny JSON file inside the profile home, so removeProfileState() clears it with
464
+ // the rest of the state. On reconnect the reconcile compares the server's
465
+ // reset_generation to this value: a newer server value means a reset was missed
466
+ // while offline → wipe. Missing/corrupt reads as 0 (so any server reset wins).
467
+ metaFile(profileId) {
468
+ return path.join(this.profileHome(profileId), '.parall-profile-meta.json');
469
+ }
470
+ /** The reset_generation this daemon has applied for the profile (0 if unknown). */
471
+ appliedResetGeneration(profileId) {
472
+ try {
473
+ const parsed = JSON.parse(readFileSync(this.metaFile(profileId), 'utf8'));
474
+ const gen = parsed.reset_generation;
475
+ return typeof gen === 'number' && Number.isFinite(gen) ? gen : 0;
476
+ }
477
+ catch {
478
+ return 0; // missing or corrupt → treat as never-applied (re-wipe if server is newer)
479
+ }
480
+ }
481
+ // Record the applied reset_generation atomically (temp file + rename). Called ONLY
482
+ // after a successful wipe, so a crash before the rename just leaves the daemon to
483
+ // re-wipe on the next reconcile (idempotent) rather than skipping a real reset.
484
+ writeResetGeneration(profileId, generation) {
485
+ const home = this.profileHome(profileId);
486
+ mkdirSync(home, { recursive: true });
487
+ const tmp = path.join(home, `.parall-profile-meta.json.tmp-${process.pid}`);
488
+ writeFileSync(tmp, JSON.stringify({ reset_generation: generation }));
489
+ renameSync(tmp, this.metaFile(profileId));
490
+ }
491
+ /**
492
+ * Profile ids that have ANY local state on this host — the reconcile's inventory for
493
+ * finding deleted/reassigned profiles whose residue must be wiped. Union of the two
494
+ * storage layouts this pool owns:
495
+ * - per-profile home dirs `<baseHomeDir>/brp_*`
496
+ * - legacy flat account files `<baseHomeDir>/accounts/brp_*.json` (pre per-profile
497
+ * homes; a profile deleted before its first post-upgrade reopen has ONLY this file
498
+ * and no home dir, so home-only enumeration would miss its login residue)
499
+ * Only names passing the strict brp_ check are returned, and the two layouts are
500
+ * deduped, so reconnect cleanup never touches the `accounts/` dir itself, a hidden
501
+ * dir, `legacy.json`, or any non-profile entry. removeProfileState wipes both layouts.
502
+ */
503
+ localStateProfileIds() {
504
+ const ids = new Set();
505
+ try {
506
+ for (const e of readdirSync(this.opts.baseHomeDir, { withFileTypes: true })) {
507
+ if (e.isDirectory() && isSafeProfileId(e.name))
508
+ ids.add(e.name);
509
+ }
510
+ }
511
+ catch {
512
+ // baseHomeDir missing → no per-profile home state
513
+ }
514
+ try {
515
+ const accountsDir = path.join(this.opts.baseHomeDir, 'accounts');
516
+ for (const e of readdirSync(accountsDir, { withFileTypes: true })) {
517
+ if (!e.isFile() || !e.name.endsWith('.json'))
518
+ continue;
519
+ const id = e.name.slice(0, -'.json'.length);
520
+ if (isSafeProfileId(id))
521
+ ids.add(id);
522
+ }
523
+ }
524
+ catch {
525
+ // no legacy accounts/ dir → no flat account state
526
+ }
527
+ return [...ids];
528
+ }
529
+ /**
530
+ * One-time, lazy migration from the legacy shared layout. Before per-profile
531
+ * homes, all profiles shared one bb-browser home and bb-browser persisted each
532
+ * profile's cookies to `<baseHomeDir>/accounts/<profileId>.json` (account name =
533
+ * profileId). Move that file into the profile's own `accounts/` dir on first use
534
+ * so existing cookie logins survive the upgrade instead of forcing a re-login.
535
+ * Only cookies are migrated — the shared `chrome-data/` was a single comingled
536
+ * Chromium profile that cannot be split per profile, and bb-browser never
537
+ * persisted localStorage/IndexedDB across restarts anyway.
538
+ */
539
+ migrateFlatProfileState(profileId) {
540
+ const legacy = this.legacyAccountFile(profileId);
541
+ if (!existsSync(legacy))
542
+ return;
543
+ const destDir = path.join(this.profileHome(profileId), 'accounts');
544
+ const dest = path.join(destDir, path.basename(legacy));
545
+ if (existsSync(dest))
546
+ return; // already migrated (or a fresh per-profile file exists)
547
+ try {
548
+ mkdirSync(destDir, { recursive: true });
549
+ renameSync(legacy, dest);
550
+ this.opts.log.info(`[bb-browser] migrated cookie state for ${profileId} to per-profile home`);
551
+ }
552
+ catch (err) {
553
+ // error-level (not warn): a visible data-loss event — the profile's
554
+ // pre-upgrade login is lost and it starts fresh (re-login required). Stays
555
+ // fail-open (the profile still works); we do NOT reportStatus('error'), which
556
+ // has no "running-but-degraded" state and would wrongly block a working
557
+ // profile — the operator-visible signal is this log.
558
+ this.opts.log.error(`[bb-browser] cookie-state migration FAILED for ${profileId}; profile will start without its prior login (re-login required): ${String(err)}`);
559
+ }
560
+ }
561
+ }
562
+ /**
563
+ * Validate a profile id as the real browser-profile form (`brp_` + nanoid,
564
+ * alphabet `[A-Za-z0-9_-]`) and return it unchanged. Require the `brp_` prefix, not
565
+ * just a safe charset: a bare safe name like `accounts` would make
566
+ * `profileHome('accounts')` collide with the legacy flat `<baseHomeDir>/accounts`
567
+ * cookie dir, and folding (e.g. `a/b` → `a_b`) could alias two distinct ids onto
568
+ * one home while `managers` keys them separately — both silently collapse the very
569
+ * isolation this pool exists to enforce. Anything else is **rejected, not
570
+ * rewritten** (empty / `.` / `..` / path separators / reserved names all fail).
571
+ */
572
+ /** True for a server-generated browser profile id (`brp_` + nanoid alphabet). */
573
+ function isSafeProfileId(id) {
574
+ return /^brp_[A-Za-z0-9_-]+$/.test(id);
575
+ }
576
+ function sanitizeProfileId(profileId) {
577
+ if (!isSafeProfileId(profileId)) {
578
+ throw new Error(`unsafe browser profile id: ${JSON.stringify(profileId)}`);
579
+ }
580
+ return profileId;
581
+ }
@@ -4,7 +4,7 @@ export { type IpcMessage, type IpcManifest, type ListClipInfo, type ListCommandI
4
4
  export { type ClipConfig, type ManifestCache, type CommandDetail, type ClipJson, } from './manifest.js';
5
5
  export { ClipProvider, type ClipProviderOptions } from './clip-provider.js';
6
6
  export { HubClient, HubCommandError, type HubClientOptions, type ClipBinding, } from './hub-client.js';
7
- export { BrowserProfileManager } from './browser-profile-manager.js';
8
- export type { BrowserProxyConfig } from './browser-profile-manager.js';
7
+ export { BrowserProfileManager, type BrowserInvoker, type BrowserInvokeRequest, type BrowserProxyConfig, } from './browser-profile-manager.js';
8
+ export { BrowserProfilePool, type BrowserProfilePoolOptions } from './browser-profile-pool.js';
9
9
  export { installClip, removeClip, parseSource, type InstallOptions, type InstallResult, } from './clip-installer.js';
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,QAAQ,EACb,WAAW,EACX,YAAY,EACZ,YAAY,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,QAAQ,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACL,SAAS,EACT,eAAe,EACf,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,QAAQ,EACb,WAAW,EACX,YAAY,EACZ,YAAY,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,QAAQ,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACL,SAAS,EACT,eAAe,EACf,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAC/F,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
@@ -3,5 +3,6 @@ export { ClipProcess, ClipCommandError, } from './process.js';
3
3
  export { MessageType, NdjsonReader, NdjsonWriter, } from './ipc.js';
4
4
  export { ClipProvider } from './clip-provider.js';
5
5
  export { HubClient, HubCommandError, } from './hub-client.js';
6
- export { BrowserProfileManager } from './browser-profile-manager.js';
6
+ export { BrowserProfileManager, } from './browser-profile-manager.js';
7
+ export { BrowserProfilePool } from './browser-profile-pool.js';
7
8
  export { installClip, removeClip, parseSource, } from './clip-installer.js';
@@ -1,6 +1,6 @@
1
1
  import { type ClipInvokeContext, type ClipProcessStatus } from './process.js';
2
2
  import type { ClipConfig, ManifestCache } from './manifest.js';
3
- import type { BrowserProfileManager } from './browser-profile-manager.js';
3
+ import type { BrowserInvoker } from './browser-profile-manager.js';
4
4
  import { type HubClient } from './hub-client.js';
5
5
  export interface ClipProcessManagerOptions {
6
6
  bunPath?: string;
@@ -10,9 +10,11 @@ export interface ClipProcessManagerOptions {
10
10
  * Host-side browser runtime. Present only on daemons that can host a
11
11
  * BrowserProfile; drives inbound hub browser InvokeCommands. The execution
12
12
  * side never calls this directly — nested browser invokes go out through the
13
- * hub (see hubClient).
13
+ * hub (see hubClient). Typed as the narrow {@link BrowserInvoker} so either a
14
+ * single BrowserProfileManager (hosted browser pod) or a BrowserProfilePool
15
+ * (BYOC supervisor, one bb-browser per profile) can back it.
14
16
  */
15
- browserProfileManager?: BrowserProfileManager;
17
+ browserProfileManager?: BrowserInvoker;
16
18
  /**
17
19
  * Lazily-supplied hub client used by the execution side to resolve a
18
20
  * dependency binding (GetBindings) and forward the dependency invoke
@@ -1 +1 @@
1
- {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/process-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/process-manager.ts"],"names":[],"mappings":"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"}