@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.
- package/bundle/manifest.json +11 -11
- package/bundle/parall-browser-pod.js +293 -107
- package/bundle/parall-claude-agent.js +51 -21
- package/bundle/parall-codex-agent.js +51 -21
- package/bundle/parall-daemon.js +343 -187
- package/dist/browser-pod.d.ts.map +1 -1
- package/dist/browser-pod.js +9 -14
- package/dist/browser-profile-viewer-control.d.ts +15 -0
- package/dist/browser-profile-viewer-control.d.ts.map +1 -0
- package/dist/browser-profile-viewer-control.js +27 -0
- package/dist/clip-runtime/bb-viewer-command-client.d.ts +19 -0
- package/dist/clip-runtime/bb-viewer-command-client.d.ts.map +1 -0
- package/dist/clip-runtime/bb-viewer-command-client.js +89 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts +3 -1
- package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
- package/dist/clip-runtime/browser-profile-manager.js +2 -2
- package/dist/clip-runtime/browser-viewer-streamer.d.ts +7 -7
- package/dist/clip-runtime/browser-viewer-streamer.d.ts.map +1 -1
- package/dist/clip-runtime/browser-viewer-streamer.js +39 -47
- package/dist/clip-runtime/clip-provider.d.ts +3 -2
- package/dist/clip-runtime/clip-provider.d.ts.map +1 -1
- package/dist/clip-runtime/clip-provider.js +13 -6
- package/dist/clip-runtime/hosted-viewer-command-queue.d.ts +43 -0
- package/dist/clip-runtime/hosted-viewer-command-queue.d.ts.map +1 -0
- package/dist/clip-runtime/hosted-viewer-command-queue.js +72 -0
- package/dist/clip-runtime/index.d.ts +1 -0
- package/dist/clip-runtime/index.d.ts.map +1 -1
- package/dist/clip-runtime/index.js +1 -0
- package/dist/clip-runtime/viewer-command-deadline.d.ts +14 -0
- package/dist/clip-runtime/viewer-command-deadline.d.ts.map +1 -0
- package/dist/clip-runtime/viewer-command-deadline.js +29 -0
- package/dist/clip-runtime/viewer-command-error.d.ts +18 -0
- package/dist/clip-runtime/viewer-command-error.d.ts.map +1 -0
- package/dist/clip-runtime/viewer-command-error.js +23 -0
- package/dist/supervisor.d.ts +0 -8
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +3 -32
- package/package.json +6 -6
package/bundle/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
3
|
-
"built_at": "2026-07-
|
|
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": "
|
|
20
|
-
"size":
|
|
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": "
|
|
28
|
-
"size":
|
|
27
|
+
"sha256": "6149ebdea186cc5de0cf5ec409e961c91846bc81285afb35dad0196ed1283f8c",
|
|
28
|
+
"size": 2738978
|
|
29
29
|
},
|
|
30
30
|
"parall-codex-agent.js": {
|
|
31
|
-
"sha256": "
|
|
32
|
-
"size":
|
|
31
|
+
"sha256": "dca73a7a445dab6be819f700f36676a9724165858d619eca93774b463db06738",
|
|
32
|
+
"size": 2780718
|
|
33
33
|
},
|
|
34
34
|
"parall-daemon.js": {
|
|
35
|
-
"sha256": "
|
|
36
|
-
"size":
|
|
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": "
|
|
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.
|
|
64295
|
-
// member profile Activity
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
67065
|
-
|
|
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
|
|
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(
|
|
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,
|
|
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
|
|
69539
|
-
this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${message}`);
|
|
69540
|
-
await this.sendViewerReply(requestId, { error:
|
|
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
|
-
|
|
70603
|
-
|
|
70604
|
-
|
|
70605
|
-
|
|
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,
|