@parall/daemon 1.50.0 → 1.51.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 (38) hide show
  1. package/bundle/manifest.json +11 -11
  2. package/bundle/parall-browser-pod.js +293 -107
  3. package/bundle/parall-claude-agent.js +51 -21
  4. package/bundle/parall-codex-agent.js +51 -21
  5. package/bundle/parall-daemon.js +343 -187
  6. package/dist/browser-pod.d.ts.map +1 -1
  7. package/dist/browser-pod.js +9 -14
  8. package/dist/browser-profile-viewer-control.d.ts +15 -0
  9. package/dist/browser-profile-viewer-control.d.ts.map +1 -0
  10. package/dist/browser-profile-viewer-control.js +27 -0
  11. package/dist/clip-runtime/bb-viewer-command-client.d.ts +19 -0
  12. package/dist/clip-runtime/bb-viewer-command-client.d.ts.map +1 -0
  13. package/dist/clip-runtime/bb-viewer-command-client.js +89 -0
  14. package/dist/clip-runtime/browser-profile-manager.d.ts +3 -1
  15. package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
  16. package/dist/clip-runtime/browser-profile-manager.js +2 -2
  17. package/dist/clip-runtime/browser-viewer-streamer.d.ts +7 -7
  18. package/dist/clip-runtime/browser-viewer-streamer.d.ts.map +1 -1
  19. package/dist/clip-runtime/browser-viewer-streamer.js +39 -47
  20. package/dist/clip-runtime/clip-provider.d.ts +3 -2
  21. package/dist/clip-runtime/clip-provider.d.ts.map +1 -1
  22. package/dist/clip-runtime/clip-provider.js +13 -6
  23. package/dist/clip-runtime/hosted-viewer-command-queue.d.ts +43 -0
  24. package/dist/clip-runtime/hosted-viewer-command-queue.d.ts.map +1 -0
  25. package/dist/clip-runtime/hosted-viewer-command-queue.js +72 -0
  26. package/dist/clip-runtime/index.d.ts +1 -0
  27. package/dist/clip-runtime/index.d.ts.map +1 -1
  28. package/dist/clip-runtime/index.js +1 -0
  29. package/dist/clip-runtime/viewer-command-deadline.d.ts +14 -0
  30. package/dist/clip-runtime/viewer-command-deadline.d.ts.map +1 -0
  31. package/dist/clip-runtime/viewer-command-deadline.js +29 -0
  32. package/dist/clip-runtime/viewer-command-error.d.ts +18 -0
  33. package/dist/clip-runtime/viewer-command-error.d.ts.map +1 -0
  34. package/dist/clip-runtime/viewer-command-error.js +23 -0
  35. package/dist/supervisor.d.ts +0 -8
  36. package/dist/supervisor.d.ts.map +1 -1
  37. package/dist/supervisor.js +3 -32
  38. package/package.json +6 -6
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.50.0",
3
- "built_at": "2026-07-27T10:07:52.208Z",
2
+ "version": "1.51.0",
3
+ "built_at": "2026-07-31T08:44:12.249Z",
4
4
  "min_node_version": "20.0.0",
5
5
  "files": {
6
6
  "bb-browser-daemon.js": {
@@ -16,29 +16,29 @@
16
16
  "size": 18
17
17
  },
18
18
  "parall-browser-pod.js": {
19
- "sha256": "0025fc6f85af4dcfe22a6d532c322bd2bf064f83378d88c77cbcca00a469ed82",
20
- "size": 2901740
19
+ "sha256": "3477bc46dd9589ae9d825561476d66682868e72f28742410772e5d3c338ec444",
20
+ "size": 2909774
21
21
  },
22
22
  "parall-channel-exec.js": {
23
23
  "sha256": "7c2a6e12483a45e9eb4a42cd7d4cdd3c4fa21dd09ac1347d9a3e177b4ddee930",
24
24
  "size": 7220
25
25
  },
26
26
  "parall-claude-agent.js": {
27
- "sha256": "3786edb7e4d2dc22a95f2789416ee1fa1bf191e166d443c868cd8772a6033278",
28
- "size": 2737538
27
+ "sha256": "6149ebdea186cc5de0cf5ec409e961c91846bc81285afb35dad0196ed1283f8c",
28
+ "size": 2738978
29
29
  },
30
30
  "parall-codex-agent.js": {
31
- "sha256": "ba13d01de7a89860d75bbc1abd5ea5c9c605cc9104161a8d7960c38f84f7870c",
32
- "size": 2779278
31
+ "sha256": "dca73a7a445dab6be819f700f36676a9724165858d619eca93774b463db06738",
32
+ "size": 2780718
33
33
  },
34
34
  "parall-daemon.js": {
35
- "sha256": "a135d16586ea01a0ed3aa811dac509827efc76d2eb14de6430e1421590afc70e",
36
- "size": 2963977
35
+ "sha256": "519f67858bfb9ce09f0d8c76d3e7c0e5e7082f528f9a3a361a14581bb689e904",
36
+ "size": 2970452
37
37
  },
38
38
  "parall-openclaw-agent.js": {
39
39
  "sha256": "856a1c3a9ae0ffef8efe2481fa1171dbeb1d67cccb88616aa9bf16da96a69355",
40
40
  "size": 9923
41
41
  }
42
42
  },
43
- "signature": "KflWD7Xe1CWN7YZuBW3BTXirw4TfZITSwiKrRO+SU/OXNfXf3IC0nm4OWsR5zHb9J+y8cmqzpK2Zz8vXXSgwDw=="
43
+ "signature": "dHFmMEI+U+2KrQRTHUsEjaN6eQoqTZ9R5ObammDEo8/UXAdfq9jKx9QpoMmYpme62i8wDHgy5XIeV2qiT+V0Dg=="
44
44
  }
@@ -63495,6 +63495,17 @@ function clearAllProviderCreds(env) {
63495
63495
  delete env.PRLL_CLAUDE_ALLOW_API_KEY;
63496
63496
  }
63497
63497
 
63498
+ // ts/sdk/dist/browser-viewer.js
63499
+ var BROWSER_VIEWER_READINESS_TIMEOUT_MS = 6e4;
63500
+ function browserViewerRequestOptions(command, opts) {
63501
+ if (command !== "stream.answer" && command !== "stream.switch")
63502
+ return opts;
63503
+ return {
63504
+ ...opts,
63505
+ timeoutMs: Math.max(opts?.timeoutMs ?? 0, BROWSER_VIEWER_READINESS_TIMEOUT_MS)
63506
+ };
63507
+ }
63508
+
63498
63509
  // ts/sdk/dist/constants.js
63499
63510
  var API_BASE = "/api/v1";
63500
63511
  var WIKI_BASE = "/wiki/v1";
@@ -63823,6 +63834,9 @@ var ENDPOINTS = {
63823
63834
  TEMPLATE: (orgId, templateId) => `${API_BASE}/orgs/${orgId}/templates/${templateId}`,
63824
63835
  TEMPLATE_DEPLOYMENTS: (orgId) => `${API_BASE}/orgs/${orgId}/template-deployments`,
63825
63836
  TEMPLATE_DEPLOYMENT: (orgId, deploymentId) => `${API_BASE}/orgs/${orgId}/template-deployments/${deploymentId}`,
63837
+ CLIP_DEPENDENCIES_CHECK: (orgId) => `${API_BASE}/orgs/${orgId}/clip-dependencies/check`,
63838
+ // Onboarding wizard progress (org-scoped, self)
63839
+ ONBOARDING_PROGRESS: (orgId) => `${API_BASE}/orgs/${orgId}/onboarding`,
63826
63840
  // Billing & Credits (org-scoped)
63827
63841
  BILLING: (orgId) => `${API_BASE}/orgs/${orgId}/billing`,
63828
63842
  BILLING_TRANSACTIONS: (orgId) => `${API_BASE}/orgs/${orgId}/billing/transactions`,
@@ -64291,8 +64305,10 @@ var ParallClient = class _ParallClient {
64291
64305
  q2.limit = String(params.limit);
64292
64306
  return this.request("GET", ENDPOINTS.ORG_MEMBER_CHATS(orgId, memberId), void 0, q2);
64293
64307
  }
64294
- // Pending tasks (todo + in_progress) assigned to a member. Powers both
64295
- // member profile Activity and the agent startup catch-up flow.
64308
+ // Pending tasks (todo + in_progress) assigned to a member. On-demand listing
64309
+ // (member profile Activity; agents via `parall tasks`) — startup catch-up
64310
+ // runs on dispatch redrive, not this endpoint. Self-assigned tasks appear
64311
+ // here without any dispatch WorkItem (self-assign is deliberately silent).
64296
64312
  async getMemberTasks(orgId, memberId, params) {
64297
64313
  const q2 = {};
64298
64314
  if (params?.cursor)
@@ -64634,7 +64650,12 @@ var ParallClient = class _ParallClient {
64634
64650
  async getAgentRuntimeRelease(orgId, agentId, tag2) {
64635
64651
  return this.request("GET", ENDPOINTS.AGENT_RUNTIME_RELEASE(orgId, agentId, tag2));
64636
64652
  }
64637
- /** Fetch all pending tasks (todo/in_progress) assigned to an agent. Pages automatically. */
64653
+ /**
64654
+ * Fetch all pending tasks (todo/in_progress) assigned to an agent. Pages
64655
+ * automatically. On-demand listing only — not a dispatch source: startup
64656
+ * catch-up runs on dispatch redrive, and self-assigned tasks listed here
64657
+ * deliberately have no WorkItem behind them.
64658
+ */
64638
64659
  async getAgentTasks(orgId, agentId) {
64639
64660
  const all = [];
64640
64661
  let cursor2;
@@ -64838,11 +64859,7 @@ var ParallClient = class _ParallClient {
64838
64859
  async postBrowseResponse(requestId, response) {
64839
64860
  return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSE_RESPONSE(requestId), response);
64840
64861
  }
64841
- /**
64842
- * `POST /machines/me/browser-profiles/viewer-response/{requestId}` — daemon
64843
- * reply to a `machine.browser_profile.viewer` control command. Wakes the
64844
- * api-server request/reply bridge (mirrors {@link postBrowseResponse}).
64845
- */
64862
+ /** Reply to `machine.browser_profile.viewer` and wake its request/reply bridge. */
64846
64863
  async postBrowserProfileViewerResponse(requestId, response) {
64847
64864
  return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_VIEWER_RESPONSE(requestId), response);
64848
64865
  }
@@ -64852,7 +64869,7 @@ var ParallClient = class _ParallClient {
64852
64869
  * owner or org admin. api-server brokers the command to the host daemon.
64853
64870
  */
64854
64871
  async browserViewerCommand(orgId, profileId, req, opts) {
64855
- return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, opts);
64872
+ return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, browserViewerRequestOptions(req.command, opts));
64856
64873
  }
64857
64874
  /**
64858
64875
  * Drive the Cloud Edge live viewer (V1b, design §6): WebRTC signaling + input +
@@ -65640,6 +65657,19 @@ var ParallClient = class _ParallClient {
65640
65657
  async getTemplateDeployment(orgId, deploymentId) {
65641
65658
  return this.request("GET", ENDPOINTS.TEMPLATE_DEPLOYMENT(orgId, deploymentId));
65642
65659
  }
65660
+ /** Pre-deploy dependency connection state (wizard S3). Live answer, no
65661
+ * cache — poll it the way getTemplateDeployment is polled. */
65662
+ async checkClipDependencies(orgId, refs) {
65663
+ const response = await this.request("POST", ENDPOINTS.CLIP_DEPENDENCIES_CHECK(orgId), { refs });
65664
+ return response.results ?? [];
65665
+ }
65666
+ // ---- Onboarding wizard progress (org-scoped, self) ----
65667
+ async getOnboardingProgress(orgId) {
65668
+ return this.request("GET", ENDPOINTS.ONBOARDING_PROGRESS(orgId));
65669
+ }
65670
+ async updateOnboardingProgress(orgId, request) {
65671
+ return this.request("PATCH", ENDPOINTS.ONBOARDING_PROGRESS(orgId), request);
65672
+ }
65643
65673
  // ---- Billing & Credits (org-scoped) ----
65644
65674
  async getBilling(orgId) {
65645
65675
  return this.request("GET", ENDPOINTS.BILLING(orgId));
@@ -66880,8 +66910,113 @@ function comparableUrl(url) {
66880
66910
 
66881
66911
  // ts/daemon/dist/clip-runtime/browser-viewer-streamer.js
66882
66912
  import { spawn } from "node:child_process";
66883
- var BB_VIEWER_HEALTH_TIMEOUT_MS = 1e4;
66913
+
66914
+ // ts/daemon/dist/clip-runtime/viewer-command-error.js
66915
+ var ViewerCommandError = class extends Error {
66916
+ code;
66917
+ constructor(message, code) {
66918
+ super(message);
66919
+ this.code = code;
66920
+ this.name = "ViewerCommandError";
66921
+ }
66922
+ };
66923
+ function viewerCommandFailure(error2) {
66924
+ const message = error2 instanceof Error ? error2.message : String(error2);
66925
+ if (error2 instanceof ViewerCommandError && error2.code) {
66926
+ return { code: error2.code, message };
66927
+ }
66928
+ return { message };
66929
+ }
66930
+
66931
+ // ts/daemon/dist/clip-runtime/viewer-command-deadline.js
66932
+ var HOSTED_VIEWER_READINESS_POD_BUDGET_MS = 3e4;
66933
+ var HOSTED_VIEWER_REPLY_RESERVE_MS = 5e3;
66934
+ function isViewerReadinessCommand(command) {
66935
+ return command === "stream.answer" || command === "stream.switch";
66936
+ }
66937
+ function parseViewerDispatchDeadlineUnixMs(value) {
66938
+ let parsed;
66939
+ if (typeof value === "number") {
66940
+ parsed = value;
66941
+ } else if (typeof value === "string" && /^[1-9]\d*$/.test(value)) {
66942
+ parsed = Number(value);
66943
+ } else {
66944
+ return void 0;
66945
+ }
66946
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
66947
+ }
66948
+ function viewerReadinessDeadlineError(phase) {
66949
+ return new ViewerCommandError(`viewer readiness deadline elapsed ${phase}`, "VIEWER_STREAM_CANCELLED");
66950
+ }
66951
+
66952
+ // ts/daemon/dist/clip-runtime/bb-viewer-command-client.js
66884
66953
  var BB_VIEWER_COMMAND_TIMEOUT_MS = 15e3;
66954
+ var BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS = 3e4;
66955
+ function bbViewerCommandTimeoutMs(method) {
66956
+ return method === "answer" || method === "switch" ? BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS : BB_VIEWER_COMMAND_TIMEOUT_MS;
66957
+ }
66958
+ function bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs, now = Date.now()) {
66959
+ const configured = bbViewerCommandTimeoutMs(method);
66960
+ if (deadlineUnixMs === void 0)
66961
+ return configured;
66962
+ const remaining = deadlineUnixMs - now;
66963
+ if (remaining <= 0) {
66964
+ throw viewerReadinessDeadlineError(`before bb-viewer ${method}`);
66965
+ }
66966
+ return Math.max(1, Math.min(configured, remaining));
66967
+ }
66968
+ async function sendBBViewerCommand(port, method, params, deadlineUnixMs) {
66969
+ const controller = new AbortController();
66970
+ const timeoutMs = bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs);
66971
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
66972
+ try {
66973
+ const resp = await fetch(`http://127.0.0.1:${port}/command`, {
66974
+ method: "POST",
66975
+ headers: { "Content-Type": "application/json" },
66976
+ body: JSON.stringify({ method, params }),
66977
+ signal: controller.signal
66978
+ });
66979
+ const text = await resp.text();
66980
+ return parseBBViewerCommandResponse(resp.status, text, method);
66981
+ } catch (err) {
66982
+ if (controller.signal.aborted && (method === "answer" || method === "switch")) {
66983
+ throw viewerReadinessDeadlineError(`while waiting for bb-viewer ${method}`);
66984
+ }
66985
+ throw err;
66986
+ } finally {
66987
+ clearTimeout(timer);
66988
+ }
66989
+ }
66990
+ function parseBBViewerCommandResponse(status, text, method) {
66991
+ let parsed;
66992
+ try {
66993
+ parsed = text ? JSON.parse(text) : {};
66994
+ } catch {
66995
+ throw new ViewerCommandError(`bb-viewer ${method} returned invalid JSON`);
66996
+ }
66997
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
66998
+ throw new ViewerCommandError(`bb-viewer ${method} returned an invalid response`);
66999
+ }
67000
+ const envelope = parsed;
67001
+ if (envelope.error) {
67002
+ const code = typeof envelope.error.code === "string" && envelope.error.code.trim() ? envelope.error.code.trim() : void 0;
67003
+ const message = typeof envelope.error.message === "string" && envelope.error.message.trim() ? envelope.error.message : `bb-viewer ${method} failed`;
67004
+ throw new ViewerCommandError(message, code);
67005
+ }
67006
+ if (status < 200 || status >= 300) {
67007
+ throw new ViewerCommandError(`bb-viewer ${method} returned ${status}`);
67008
+ }
67009
+ if (envelope.result === void 0 || envelope.result === null) {
67010
+ return {};
67011
+ }
67012
+ if (typeof envelope.result !== "object" || Array.isArray(envelope.result)) {
67013
+ throw new ViewerCommandError(`bb-viewer ${method} returned an invalid result`);
67014
+ }
67015
+ return envelope.result;
67016
+ }
67017
+
67018
+ // ts/daemon/dist/clip-runtime/browser-viewer-streamer.js
67019
+ var BB_VIEWER_HEALTH_TIMEOUT_MS = 1e4;
66885
67020
  var BB_VIEWER_UNANSWERED_REAP_MS = 9e4;
66886
67021
  var BB_VIEWER_TERM_GRACE_MS = 5e3;
66887
67022
  function isLoopbackBindHost(host) {
@@ -66918,7 +67053,7 @@ var BrowserViewerStreamer = class {
66918
67053
  * partial result. `data.input` / `data.turn` / `data.session_id` come from
66919
67054
  * MachineBrowserProfileViewerData.
66920
67055
  */
66921
- async handleViewerCommand(profileId, sessionId, command, input, turn) {
67056
+ async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
66922
67057
  if (!profileId)
66923
67058
  throw new Error("browser profile id is required");
66924
67059
  if (command !== "stream.start" && sessionId && this.kickedSessions.get(profileId) === sessionId) {
@@ -66928,11 +67063,11 @@ var BrowserViewerStreamer = class {
66928
67063
  case "stream.start":
66929
67064
  return this.viewerStreamStart(profileId, sessionId, turn);
66930
67065
  case "stream.answer":
66931
- return this.viewerStreamAnswer(profileId, sessionId, input);
67066
+ return this.viewerStreamAnswer(profileId, sessionId, input, execution?.deadlineUnixMs);
66932
67067
  case "stream.close":
66933
67068
  return this.viewerStreamClose(profileId, sessionId, input);
66934
67069
  case "stream.switch":
66935
- return this.viewerStreamSwitch(profileId, sessionId, input);
67070
+ return this.viewerStreamSwitch(profileId, sessionId, input, execution?.deadlineUnixMs);
66936
67071
  case "kick":
66937
67072
  return this.viewerKick(profileId, sessionId, input);
66938
67073
  case "close":
@@ -66984,7 +67119,7 @@ var BrowserViewerStreamer = class {
66984
67119
  await this.bringTargetToFront(cdpUrl);
66985
67120
  const streamer = await this.spawnStreamer(profileId, sessionId, turn);
66986
67121
  try {
66987
- const result = await this.streamerCommand(streamer, "connect", { cdpUrl });
67122
+ const result = await sendBBViewerCommand(streamer.port, "connect", { cdpUrl });
66988
67123
  streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
66989
67124
  const reapTimer = setTimeout(() => {
66990
67125
  const current = this.streamers.get(profileId);
@@ -67005,14 +67140,17 @@ var BrowserViewerStreamer = class {
67005
67140
  * Rejects if the streamer's sessionId no longer matches (a stale viewer
67006
67141
  * session from a superseded stream.start).
67007
67142
  */
67008
- async viewerStreamAnswer(profileId, sessionId, input) {
67143
+ async viewerStreamAnswer(profileId, sessionId, input, deadlineUnixMs) {
67009
67144
  const streamer = this.streamers.get(profileId);
67010
67145
  if (!streamer)
67011
67146
  throw new Error("no active viewer streamer for profile");
67012
67147
  if (streamer.sessionId !== sessionId)
67013
67148
  throw new Error("stale viewer session");
67149
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "before bb-viewer answer");
67014
67150
  streamer.answered = true;
67015
- return this.streamerCommand(streamer, "answer", input ?? {});
67151
+ const result = await sendBBViewerCommand(streamer.port, "answer", input ?? {}, deadlineUnixMs);
67152
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "after bb-viewer answer");
67153
+ return result;
67016
67154
  }
67017
67155
  /**
67018
67156
  * stream.close — best-effort stop the streamer, then kill + remove it. A
@@ -67033,7 +67171,7 @@ var BrowserViewerStreamer = class {
67033
67171
  return { ok: true, stale: true };
67034
67172
  }
67035
67173
  try {
67036
- await this.streamerCommand(streamer, "stop", {});
67174
+ await sendBBViewerCommand(streamer.port, "stop", {});
67037
67175
  } catch (err) {
67038
67176
  this.host.log.warn(`[bb-viewer] stop command failed for ${profileId} (killing anyway): ${formatErrorForLog(err)}`);
67039
67177
  }
@@ -67050,7 +67188,7 @@ var BrowserViewerStreamer = class {
67050
67188
  * the stream.start response), so a session-less switch is by definition not
67051
67189
  * the current viewer.
67052
67190
  */
67053
- async viewerStreamSwitch(profileId, sessionId, input) {
67191
+ async viewerStreamSwitch(profileId, sessionId, input, deadlineUnixMs) {
67054
67192
  const streamer = this.streamers.get(profileId);
67055
67193
  if (!streamer)
67056
67194
  throw new Error("no active viewer streamer for profile");
@@ -67060,9 +67198,13 @@ var BrowserViewerStreamer = class {
67060
67198
  if (tab === void 0 || tab === null || tab === "") {
67061
67199
  throw new Error("stream.switch requires input.tab");
67062
67200
  }
67201
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "before resolving the switch target");
67063
67202
  const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId, tab), `viewer stream.switch ${profileId}`);
67064
- await this.bringTargetToFront(cdpUrl);
67065
- const result = await this.streamerCommand(streamer, "switch", { cdpUrl });
67203
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "after resolving the switch target");
67204
+ await this.bringTargetToFront(cdpUrl, deadlineUnixMs);
67205
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "before bb-viewer switch");
67206
+ const result = await sendBBViewerCommand(streamer.port, "switch", { cdpUrl }, deadlineUnixMs);
67207
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "after bb-viewer switch");
67066
67208
  streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
67067
67209
  return { ...result, streamed_tab_id: streamer.streamedTabId };
67068
67210
  }
@@ -67086,7 +67228,7 @@ var BrowserViewerStreamer = class {
67086
67228
  }
67087
67229
  const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId), `viewer close re-switch ${profileId}`);
67088
67230
  await this.bringTargetToFront(cdpUrl);
67089
- const sw = await this.streamerCommand(streamer, "switch", { cdpUrl });
67231
+ const sw = await sendBBViewerCommand(streamer.port, "switch", { cdpUrl });
67090
67232
  streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
67091
67233
  return { ...result, ...sw, streamed_tab_id: streamer.streamedTabId };
67092
67234
  }
@@ -67098,15 +67240,17 @@ var BrowserViewerStreamer = class {
67098
67240
  * must not block the stream. host:port + targetId are parsed from the cdpUrl
67099
67241
  * (`ws://host:port/devtools/page/<targetId>`) to avoid an extra /status call.
67100
67242
  */
67101
- async bringTargetToFront(cdpUrl) {
67243
+ async bringTargetToFront(cdpUrl, deadlineUnixMs) {
67102
67244
  const m3 = /^ws:\/\/([^/]+)\/devtools\/page\/(.+)$/.exec(cdpUrl);
67103
67245
  if (!m3)
67104
67246
  return;
67105
67247
  const [, hostPort, targetId] = m3;
67248
+ this.assertViewerExecutionDeadline(deadlineUnixMs, "before foregrounding the switch target");
67249
+ const timeoutMs = deadlineUnixMs === void 0 ? 5e3 : Math.max(1, Math.min(5e3, deadlineUnixMs - Date.now()));
67106
67250
  try {
67107
67251
  const resp = await fetch(`http://${hostPort}/json/activate/${targetId}`, {
67108
67252
  method: "GET",
67109
- signal: AbortSignal.timeout(5e3)
67253
+ signal: AbortSignal.timeout(timeoutMs)
67110
67254
  });
67111
67255
  if (!resp.ok) {
67112
67256
  this.host.log.warn(`[bb-viewer] activate target ${targetId} returned ${resp.status} (continuing)`);
@@ -67115,6 +67259,11 @@ var BrowserViewerStreamer = class {
67115
67259
  this.host.log.warn(`[bb-viewer] activate target ${targetId} failed (continuing): ${formatErrorForLog(err)}`);
67116
67260
  }
67117
67261
  }
67262
+ assertViewerExecutionDeadline(deadlineUnixMs, phase) {
67263
+ if (deadlineUnixMs !== void 0 && Date.now() >= deadlineUnixMs) {
67264
+ throw viewerReadinessDeadlineError(phase);
67265
+ }
67266
+ }
67118
67267
  /**
67119
67268
  * Forward a navigation command (tab_list/tab_new/open/reload/back/forward/
67120
67269
  * close) to bb-browser scoped to the profile's account. This lets the human
@@ -67315,34 +67464,6 @@ var BrowserViewerStreamer = class {
67315
67464
  killTimer.unref?.();
67316
67465
  child.once("exit", () => clearTimeout(killTimer));
67317
67466
  }
67318
- /**
67319
- * POST a command to the profile's bb-viewer `/command` endpoint. Parses the
67320
- * `{result}` / `{error:{message}}` envelope (bb-viewer api.go) and returns
67321
- * the inner result, throwing the error message on failure.
67322
- */
67323
- async streamerCommand(streamer, method, params) {
67324
- const controller = new AbortController();
67325
- const timer = setTimeout(() => controller.abort(), BB_VIEWER_COMMAND_TIMEOUT_MS);
67326
- try {
67327
- const resp = await fetch(`http://127.0.0.1:${streamer.port}/command`, {
67328
- method: "POST",
67329
- headers: { "Content-Type": "application/json" },
67330
- body: JSON.stringify({ method, params }),
67331
- signal: controller.signal
67332
- });
67333
- const text = await resp.text();
67334
- const parsed = text ? JSON.parse(text) : {};
67335
- if (parsed.error) {
67336
- throw new Error(parsed.error.message || `bb-viewer ${method} failed`);
67337
- }
67338
- if (!resp.ok) {
67339
- throw new Error(`bb-viewer ${method} returned ${resp.status}: ${text}`);
67340
- }
67341
- return parsed.result ?? {};
67342
- } finally {
67343
- clearTimeout(timer);
67344
- }
67345
- }
67346
67467
  /**
67347
67468
  * Kill + remove the profile's bb-viewer streamer, if any. Idempotent. Also
67348
67469
  * clears any kick marker for the profile: a (re)kill establishes a clean
@@ -67602,8 +67723,8 @@ var BrowserProfileManager = class {
67602
67723
  * (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
67603
67724
  * subprocesses and viewer tab navigation.
67604
67725
  */
67605
- async handleViewerCommand(profileId, sessionId, command, input, turn) {
67606
- return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn);
67726
+ async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
67727
+ return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn, execution);
67607
67728
  }
67608
67729
  /** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
67609
67730
  async cdpEndpoint() {
@@ -68453,6 +68574,46 @@ function errMsg(err) {
68453
68574
  // ts/daemon/dist/clip-runtime/clip-provider.js
68454
68575
  import * as http2 from "node:http2";
68455
68576
 
68577
+ // ts/daemon/dist/clip-runtime/browser-dependency.js
68578
+ var BROWSER_CAPABILITY_NAME = "browser";
68579
+ var BROWSER_CAPABILITY_PACKAGE = "@pinixai/bb-browser-pro";
68580
+ var BROWSER_DEPENDENCY_NAMES = /* @__PURE__ */ new Set([
68581
+ BROWSER_CAPABILITY_NAME,
68582
+ "@pinix/browser",
68583
+ "@pinixai/bb-browser-pro",
68584
+ "bb-browser"
68585
+ ]);
68586
+ function isBrowserDependencyName(name) {
68587
+ return !!name && BROWSER_DEPENDENCY_NAMES.has(name);
68588
+ }
68589
+ function browserCapabilityCommands() {
68590
+ return [
68591
+ { name: "eval", description: "Run JavaScript in a page context" },
68592
+ { name: "open", description: "Open a URL in a new tab" },
68593
+ { name: "snapshot", description: "Capture the accessibility/DOM snapshot of a page" },
68594
+ { name: "click", description: "Click an element" },
68595
+ { name: "type", description: "Type text into an element" }
68596
+ ];
68597
+ }
68598
+ function manifestDeclaresBrowserDependency(manifest) {
68599
+ return dependencyMaps(manifest).some((deps) => Object.entries(deps).some(([name, dep]) => isBrowserDependencyName(name) || isBrowserDependencyName(dep?.package)));
68600
+ }
68601
+ function manifestMapsDependencyToBrowser(manifest, dependencyName) {
68602
+ const maps = dependencyMaps(manifest);
68603
+ if (maps.length === 0)
68604
+ return false;
68605
+ for (const deps of maps) {
68606
+ const declared = deps[dependencyName];
68607
+ if (declared) {
68608
+ return isBrowserDependencyName(dependencyName) || isBrowserDependencyName(declared.package);
68609
+ }
68610
+ }
68611
+ return isBrowserDependencyName(dependencyName) && manifestDeclaresBrowserDependency(manifest);
68612
+ }
68613
+ function dependencyMaps(manifest) {
68614
+ return [manifest?.dependencies, manifest?.dependencySlots].filter((deps) => !!deps);
68615
+ }
68616
+
68456
68617
  // ts/daemon/dist/clip-runtime/process.js
68457
68618
  import { spawn as spawn4 } from "node:child_process";
68458
68619
  import { createInterface as createInterface2 } from "node:readline";
@@ -68709,46 +68870,6 @@ function finalizeManifest(manifest) {
68709
68870
  return m3;
68710
68871
  }
68711
68872
 
68712
- // ts/daemon/dist/clip-runtime/browser-dependency.js
68713
- var BROWSER_CAPABILITY_NAME = "browser";
68714
- var BROWSER_CAPABILITY_PACKAGE = "@pinixai/bb-browser-pro";
68715
- var BROWSER_DEPENDENCY_NAMES = /* @__PURE__ */ new Set([
68716
- BROWSER_CAPABILITY_NAME,
68717
- "@pinix/browser",
68718
- "@pinixai/bb-browser-pro",
68719
- "bb-browser"
68720
- ]);
68721
- function isBrowserDependencyName(name) {
68722
- return !!name && BROWSER_DEPENDENCY_NAMES.has(name);
68723
- }
68724
- function browserCapabilityCommands() {
68725
- return [
68726
- { name: "eval", description: "Run JavaScript in a page context" },
68727
- { name: "open", description: "Open a URL in a new tab" },
68728
- { name: "snapshot", description: "Capture the accessibility/DOM snapshot of a page" },
68729
- { name: "click", description: "Click an element" },
68730
- { name: "type", description: "Type text into an element" }
68731
- ];
68732
- }
68733
- function manifestDeclaresBrowserDependency(manifest) {
68734
- return dependencyMaps(manifest).some((deps) => Object.entries(deps).some(([name, dep]) => isBrowserDependencyName(name) || isBrowserDependencyName(dep?.package)));
68735
- }
68736
- function manifestMapsDependencyToBrowser(manifest, dependencyName) {
68737
- const maps = dependencyMaps(manifest);
68738
- if (maps.length === 0)
68739
- return false;
68740
- for (const deps of maps) {
68741
- const declared = deps[dependencyName];
68742
- if (declared) {
68743
- return isBrowserDependencyName(dependencyName) || isBrowserDependencyName(declared.package);
68744
- }
68745
- }
68746
- return isBrowserDependencyName(dependencyName) && manifestDeclaresBrowserDependency(manifest);
68747
- }
68748
- function dependencyMaps(manifest) {
68749
- return [manifest?.dependencies, manifest?.dependencySlots].filter((deps) => !!deps);
68750
- }
68751
-
68752
68873
  // ts/daemon/dist/clip-runtime/process.js
68753
68874
  var CLIP_REGISTER_TIMEOUT_MS = 1e4;
68754
68875
  var CLIP_STOP_TIMEOUT_MS = 5e3;
@@ -69503,8 +69624,8 @@ var ClipProvider = class _ClipProvider {
69503
69624
  /**
69504
69625
  * Handle a hosted-browser live-viewer command relayed by clip-service over the
69505
69626
  * stream and reply on the same stream (design §3.4). Always answers the
69506
- * request/reply bridge exactly once — `{result}` on success, `{error:{message}}`
69507
- * on failure — and never throws into the message loop, mirroring the BYOC
69627
+ * request/reply bridge exactly once — `{result}` on success,
69628
+ * `{error:{code?,message}}` on failure — and never throws into the message loop, mirroring the BYOC
69508
69629
  * daemon's handleBrowserProfileViewer (which replies over HTTP instead).
69509
69630
  */
69510
69631
  async handleViewerCommand(cmd) {
@@ -69517,6 +69638,10 @@ var ClipProvider = class _ClipProvider {
69517
69638
  return;
69518
69639
  }
69519
69640
  try {
69641
+ const dispatchDeadlineUnixMs = parseViewerDispatchDeadlineUnixMs(cmd.dispatchDeadlineUnixMs);
69642
+ if (isViewerReadinessCommand(cmd.command) && dispatchDeadlineUnixMs === void 0) {
69643
+ throw viewerReadinessDeadlineError("because the ProviderStream command has no authenticated deadline");
69644
+ }
69520
69645
  let input;
69521
69646
  if (cmd.input) {
69522
69647
  const decoded = Buffer.from(cmd.input, "base64").toString("utf-8");
@@ -69531,13 +69656,14 @@ var ClipProvider = class _ClipProvider {
69531
69656
  sessionId: cmd.sessionId,
69532
69657
  command: cmd.command,
69533
69658
  input,
69534
- turn: cmd.turn
69659
+ turn: cmd.turn,
69660
+ dispatchDeadlineUnixMs
69535
69661
  });
69536
69662
  await this.sendViewerReply(requestId, { result });
69537
69663
  } catch (err) {
69538
- const message = err instanceof Error ? err.message : String(err);
69539
- this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${message}`);
69540
- await this.sendViewerReply(requestId, { error: { message } }).catch(() => {
69664
+ const failure = viewerCommandFailure(err);
69665
+ this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${failure.message}`);
69666
+ await this.sendViewerReply(requestId, { error: failure }).catch(() => {
69541
69667
  });
69542
69668
  }
69543
69669
  }
@@ -69623,6 +69749,70 @@ var ClipProvider = class _ClipProvider {
69623
69749
  }
69624
69750
  };
69625
69751
 
69752
+ // ts/daemon/dist/clip-runtime/hosted-viewer-command-queue.js
69753
+ var HostedViewerCommandQueue = class {
69754
+ run;
69755
+ tail = Promise.resolve();
69756
+ now;
69757
+ readinessBudgetMs;
69758
+ replyReserveMs;
69759
+ schedule;
69760
+ constructor(run, opts = {}) {
69761
+ this.run = run;
69762
+ this.now = opts.now ?? Date.now;
69763
+ this.readinessBudgetMs = opts.readinessBudgetMs ?? HOSTED_VIEWER_READINESS_POD_BUDGET_MS;
69764
+ this.replyReserveMs = opts.replyReserveMs ?? HOSTED_VIEWER_REPLY_RESERVE_MS;
69765
+ this.schedule = opts.schedule ?? ((callback, delayMs) => {
69766
+ const handle = setTimeout(callback, delayMs);
69767
+ return {
69768
+ cancel: () => clearTimeout(handle),
69769
+ unref: () => handle.unref?.()
69770
+ };
69771
+ });
69772
+ }
69773
+ enqueue(command) {
69774
+ const receivedAt = this.now();
69775
+ const executionDeadline = this.executionDeadline(command, receivedAt);
69776
+ const run = this.tail.catch(() => {
69777
+ }).then(() => {
69778
+ if (executionDeadline !== void 0 && this.now() >= executionDeadline) {
69779
+ throw viewerReadinessDeadlineError("before hosted pod execution");
69780
+ }
69781
+ return this.run(command, executionDeadline);
69782
+ });
69783
+ this.tail = run.catch(() => {
69784
+ });
69785
+ if (executionDeadline === void 0) {
69786
+ return run;
69787
+ }
69788
+ let timer;
69789
+ const timeout = new Promise((_, reject) => {
69790
+ const delay = Math.max(0, executionDeadline - this.now());
69791
+ timer = this.schedule(() => {
69792
+ reject(viewerReadinessDeadlineError("inside the hosted pod queue"));
69793
+ }, delay);
69794
+ timer.unref?.();
69795
+ });
69796
+ return Promise.race([run, timeout]).finally(() => {
69797
+ timer?.cancel();
69798
+ });
69799
+ }
69800
+ executionDeadline(command, receivedAt) {
69801
+ if (!isViewerReadinessCommand(command.command)) {
69802
+ return void 0;
69803
+ }
69804
+ const dispatchDeadline = command.dispatchDeadlineUnixMs;
69805
+ if (!Number.isSafeInteger(dispatchDeadline) || dispatchDeadline === void 0) {
69806
+ throw viewerReadinessDeadlineError("because the authenticated deadline is missing");
69807
+ }
69808
+ const deadline = Math.min(receivedAt + this.readinessBudgetMs, dispatchDeadline - this.replyReserveMs);
69809
+ if (deadline <= receivedAt) {
69810
+ throw viewerReadinessDeadlineError("before hosted pod receipt");
69811
+ }
69812
+ return deadline;
69813
+ }
69814
+ };
69815
+
69626
69816
  // ts/daemon/dist/clip-runtime/process-manager.js
69627
69817
  import * as fs3 from "node:fs";
69628
69818
  import * as path5 from "node:path";
@@ -70599,14 +70789,10 @@ async function preparePodRuntime(config, env, log) {
70599
70789
  dataDir: path6.join(config.stateDir, "clip-data"),
70600
70790
  browserProfileManager: browser
70601
70791
  });
70602
- let viewerChain = Promise.resolve();
70603
- const onViewerCommand = (cmd) => {
70604
- const run = viewerChain.catch(() => {
70605
- }).then(() => browser.handleViewerCommand(cmd.profileId, cmd.sessionId, cmd.command, cmd.input, cmd.turn));
70606
- viewerChain = run.catch(() => {
70607
- });
70608
- return run;
70609
- };
70792
+ const viewerCommands = new HostedViewerCommandQueue((cmd, executionDeadlineUnixMs) => browser.handleViewerCommand(cmd.profileId, cmd.sessionId, cmd.command, cmd.input, cmd.turn, {
70793
+ deadlineUnixMs: executionDeadlineUnixMs
70794
+ }));
70795
+ const onViewerCommand = viewerCommands.enqueue.bind(viewerCommands);
70610
70796
  const provider = new ClipProvider({
70611
70797
  serviceUrl: config.clipRpcUrl,
70612
70798
  authKey: config.assignmentToken,