@parall/daemon 1.49.0 → 1.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/manifest.json +11 -11
- package/bundle/parall-browser-pod.js +318 -104
- package/bundle/parall-claude-agent.js +124 -29
- package/bundle/parall-codex-agent.js +124 -29
- package/bundle/parall-daemon.js +368 -184
- 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/parall-daemon.js
CHANGED
|
@@ -123,10 +123,20 @@ var init_lane_key = __esm({
|
|
|
123
123
|
}
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
// ts/sdk/dist/
|
|
127
|
-
|
|
128
|
-
"
|
|
126
|
+
// ts/sdk/dist/browser-viewer.js
|
|
127
|
+
function browserViewerRequestOptions(command, opts) {
|
|
128
|
+
if (command !== "stream.answer" && command !== "stream.switch")
|
|
129
|
+
return opts;
|
|
130
|
+
return {
|
|
131
|
+
...opts,
|
|
132
|
+
timeoutMs: Math.max(opts?.timeoutMs ?? 0, BROWSER_VIEWER_READINESS_TIMEOUT_MS)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
var BROWSER_VIEWER_READINESS_TIMEOUT_MS;
|
|
136
|
+
var init_browser_viewer = __esm({
|
|
137
|
+
"ts/sdk/dist/browser-viewer.js"() {
|
|
129
138
|
"use strict";
|
|
139
|
+
BROWSER_VIEWER_READINESS_TIMEOUT_MS = 6e4;
|
|
130
140
|
}
|
|
131
141
|
});
|
|
132
142
|
|
|
@@ -168,6 +178,12 @@ var init_constants = __esm({
|
|
|
168
178
|
ORG_MEMBER: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
|
|
169
179
|
ORG_MEMBER_CHATS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/chats`,
|
|
170
180
|
ORG_MEMBER_TASKS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/tasks`,
|
|
181
|
+
// Org-scoped public profile (title / description). GET readable by every
|
|
182
|
+
// active member; PATCH is CAS-guarded (expected_version).
|
|
183
|
+
ORG_MEMBER_PROFILE: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}/profile`,
|
|
184
|
+
// Private agent Instructions — visibility gated server-side
|
|
185
|
+
// (agent self + Human org admin/owner). Never cached.
|
|
186
|
+
AGENT_INSTRUCTIONS: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/instructions`,
|
|
171
187
|
REF_SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/refs/search`,
|
|
172
188
|
// Direct messages (org-scoped, atomic find-or-create + send)
|
|
173
189
|
DM: (orgId) => `${API_BASE}/orgs/${orgId}/dm`,
|
|
@@ -451,6 +467,11 @@ var init_constants = __esm({
|
|
|
451
467
|
SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/search`,
|
|
452
468
|
// Feature flags (org-scoped, server-evaluated)
|
|
453
469
|
FEATURE_FLAGS: (orgId) => `${API_BASE}/orgs/${orgId}/feature-flags`,
|
|
470
|
+
// Team templates (org-scoped, owner/admin only)
|
|
471
|
+
TEMPLATES: (orgId) => `${API_BASE}/orgs/${orgId}/templates`,
|
|
472
|
+
TEMPLATE: (orgId, templateId) => `${API_BASE}/orgs/${orgId}/templates/${templateId}`,
|
|
473
|
+
TEMPLATE_DEPLOYMENTS: (orgId) => `${API_BASE}/orgs/${orgId}/template-deployments`,
|
|
474
|
+
TEMPLATE_DEPLOYMENT: (orgId, deploymentId) => `${API_BASE}/orgs/${orgId}/template-deployments/${deploymentId}`,
|
|
454
475
|
// Billing & Credits (org-scoped)
|
|
455
476
|
BILLING: (orgId) => `${API_BASE}/orgs/${orgId}/billing`,
|
|
456
477
|
BILLING_TRANSACTIONS: (orgId) => `${API_BASE}/orgs/${orgId}/billing/transactions`,
|
|
@@ -644,6 +665,7 @@ var ParallClient, ApiError;
|
|
|
644
665
|
var init_client = __esm({
|
|
645
666
|
"ts/sdk/dist/client.js"() {
|
|
646
667
|
"use strict";
|
|
668
|
+
init_browser_viewer();
|
|
647
669
|
init_constants();
|
|
648
670
|
ParallClient = class _ParallClient {
|
|
649
671
|
baseUrl;
|
|
@@ -1015,6 +1037,30 @@ var init_client = __esm({
|
|
|
1015
1037
|
async updateOrgMember(orgId, userId, role) {
|
|
1016
1038
|
return this.request("PATCH", ENDPOINTS.ORG_MEMBER(orgId, userId), { role });
|
|
1017
1039
|
}
|
|
1040
|
+
/** Org-scoped public profile (title / description) of any active member. */
|
|
1041
|
+
async getMemberProfile(orgId, userId) {
|
|
1042
|
+
return this.request("GET", ENDPOINTS.ORG_MEMBER_PROFILE(orgId, userId));
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* CAS full-replacement of a member's public profile. Humans edit their
|
|
1046
|
+
* own; Human org admins/owners edit agents'. A stale expected_version
|
|
1047
|
+
* gets 409 PROFILE_VERSION_CONFLICT with error.details.current_version.
|
|
1048
|
+
*/
|
|
1049
|
+
async updateMemberProfile(orgId, userId, req) {
|
|
1050
|
+
return this.request("PATCH", ENDPOINTS.ORG_MEMBER_PROFILE(orgId, userId), req);
|
|
1051
|
+
}
|
|
1052
|
+
/** Private agent Instructions — agent self or Human org admin/owner only. */
|
|
1053
|
+
async getAgentInstructions(orgId, agentId) {
|
|
1054
|
+
return this.request("GET", ENDPOINTS.AGENT_INSTRUCTIONS(orgId, agentId));
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* CAS update of the private Instructions (Human org admin/owner only).
|
|
1058
|
+
* A stale expected_version gets 409 INSTRUCTIONS_VERSION_CONFLICT with
|
|
1059
|
+
* error.details.current_version.
|
|
1060
|
+
*/
|
|
1061
|
+
async updateAgentInstructions(orgId, agentId, req) {
|
|
1062
|
+
return this.request("PATCH", ENDPOINTS.AGENT_INSTRUCTIONS(orgId, agentId), req);
|
|
1063
|
+
}
|
|
1018
1064
|
// Member activity surfaces — chats the member participates in within this org,
|
|
1019
1065
|
// ordered by most recent activity. Powers the member profile Activity tab.
|
|
1020
1066
|
async getMemberChats(orgId, memberId, params) {
|
|
@@ -1572,11 +1618,7 @@ var init_client = __esm({
|
|
|
1572
1618
|
async postBrowseResponse(requestId, response) {
|
|
1573
1619
|
return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSE_RESPONSE(requestId), response);
|
|
1574
1620
|
}
|
|
1575
|
-
/**
|
|
1576
|
-
* `POST /machines/me/browser-profiles/viewer-response/{requestId}` — daemon
|
|
1577
|
-
* reply to a `machine.browser_profile.viewer` control command. Wakes the
|
|
1578
|
-
* api-server request/reply bridge (mirrors {@link postBrowseResponse}).
|
|
1579
|
-
*/
|
|
1621
|
+
/** Reply to `machine.browser_profile.viewer` and wake its request/reply bridge. */
|
|
1580
1622
|
async postBrowserProfileViewerResponse(requestId, response) {
|
|
1581
1623
|
return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_VIEWER_RESPONSE(requestId), response);
|
|
1582
1624
|
}
|
|
@@ -1586,7 +1628,7 @@ var init_client = __esm({
|
|
|
1586
1628
|
* owner or org admin. api-server brokers the command to the host daemon.
|
|
1587
1629
|
*/
|
|
1588
1630
|
async browserViewerCommand(orgId, profileId, req, opts) {
|
|
1589
|
-
return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, opts);
|
|
1631
|
+
return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, browserViewerRequestOptions(req.command, opts));
|
|
1590
1632
|
}
|
|
1591
1633
|
/**
|
|
1592
1634
|
* Drive the Cloud Edge live viewer (V1b, design §6): WebRTC signaling + input +
|
|
@@ -2358,6 +2400,22 @@ var init_client = __esm({
|
|
|
2358
2400
|
async getFeatureFlags(orgId) {
|
|
2359
2401
|
return this.request("GET", ENDPOINTS.FEATURE_FLAGS(orgId));
|
|
2360
2402
|
}
|
|
2403
|
+
// ---- Official Template catalog (org-contextual owner/admin reads + Hire) ----
|
|
2404
|
+
async listOfficialTemplates(orgId) {
|
|
2405
|
+
const response = await this.request("GET", ENDPOINTS.TEMPLATES(orgId));
|
|
2406
|
+
return response.templates ?? [];
|
|
2407
|
+
}
|
|
2408
|
+
async getOfficialTemplate(orgId, templateId) {
|
|
2409
|
+
return this.request("GET", ENDPOINTS.TEMPLATE(orgId, templateId));
|
|
2410
|
+
}
|
|
2411
|
+
async deployTemplate(orgId, request3) {
|
|
2412
|
+
return this.request("POST", ENDPOINTS.TEMPLATE_DEPLOYMENTS(orgId), request3, void 0, false, {
|
|
2413
|
+
timeoutMs: 185e3
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
async getTemplateDeployment(orgId, deploymentId) {
|
|
2417
|
+
return this.request("GET", ENDPOINTS.TEMPLATE_DEPLOYMENT(orgId, deploymentId));
|
|
2418
|
+
}
|
|
2361
2419
|
// ---- Billing & Credits (org-scoped) ----
|
|
2362
2420
|
async getBilling(orgId) {
|
|
2363
2421
|
return this.request("GET", ENDPOINTS.BILLING(orgId));
|
|
@@ -2719,6 +2777,13 @@ var init_client = __esm({
|
|
|
2719
2777
|
}
|
|
2720
2778
|
});
|
|
2721
2779
|
|
|
2780
|
+
// ts/sdk/dist/types.js
|
|
2781
|
+
var init_types2 = __esm({
|
|
2782
|
+
"ts/sdk/dist/types.js"() {
|
|
2783
|
+
"use strict";
|
|
2784
|
+
}
|
|
2785
|
+
});
|
|
2786
|
+
|
|
2722
2787
|
// ts/sdk/dist/ws.js
|
|
2723
2788
|
function isRetryableNetworkError(err) {
|
|
2724
2789
|
return err instanceof Error && err.name === "ApiError" && "status" in err && err.status === 0;
|
|
@@ -3063,9 +3128,10 @@ var init_ws = __esm({
|
|
|
3063
3128
|
var init_dist = __esm({
|
|
3064
3129
|
"ts/sdk/dist/index.js"() {
|
|
3065
3130
|
"use strict";
|
|
3066
|
-
|
|
3067
|
-
init_constants();
|
|
3131
|
+
init_browser_viewer();
|
|
3068
3132
|
init_client();
|
|
3133
|
+
init_constants();
|
|
3134
|
+
init_types2();
|
|
3069
3135
|
init_ws();
|
|
3070
3136
|
}
|
|
3071
3137
|
});
|
|
@@ -56204,6 +56270,147 @@ var init_cli = __esm({
|
|
|
56204
56270
|
}
|
|
56205
56271
|
});
|
|
56206
56272
|
|
|
56273
|
+
// ts/daemon/dist/browser-profile-reconcile.js
|
|
56274
|
+
async function reconcileBrowserProfiles(ctx) {
|
|
56275
|
+
const pool = ctx.pool;
|
|
56276
|
+
const activeBeforeList = new Set(pool.activeProfileIds());
|
|
56277
|
+
const pendingReviveBeforeList = ctx.pendingReviveIds();
|
|
56278
|
+
const localStateBeforeList = new Set(pool.localStateProfileIds());
|
|
56279
|
+
let profiles;
|
|
56280
|
+
try {
|
|
56281
|
+
profiles = await ctx.client.listMachineBrowserProfiles();
|
|
56282
|
+
} catch (err) {
|
|
56283
|
+
ctx.log.warn(`browser profile reconcile: list failed: ${String(err)}`);
|
|
56284
|
+
return;
|
|
56285
|
+
}
|
|
56286
|
+
if (ctx.currentPool() !== pool)
|
|
56287
|
+
return;
|
|
56288
|
+
const desiredLive = /* @__PURE__ */ new Set();
|
|
56289
|
+
const serverIds = /* @__PURE__ */ new Set();
|
|
56290
|
+
const serverResetGen = /* @__PURE__ */ new Map();
|
|
56291
|
+
const ops = [];
|
|
56292
|
+
const missedResetGen = (profileId) => {
|
|
56293
|
+
const gen = serverResetGen.get(profileId) ?? 0;
|
|
56294
|
+
return gen > pool.appliedResetGeneration(profileId) ? gen : null;
|
|
56295
|
+
};
|
|
56296
|
+
const enqueueResetWipeIfMissed = (profileId) => {
|
|
56297
|
+
const gen = missedResetGen(profileId);
|
|
56298
|
+
if (gen === null)
|
|
56299
|
+
return false;
|
|
56300
|
+
pool.fence(profileId);
|
|
56301
|
+
ops.push(ctx.enqueueOp(profileId, () => pool.applyResetWipe(profileId, gen)));
|
|
56302
|
+
return true;
|
|
56303
|
+
};
|
|
56304
|
+
for (const profile of profiles) {
|
|
56305
|
+
if (!profile.machine_id)
|
|
56306
|
+
continue;
|
|
56307
|
+
serverIds.add(profile.id);
|
|
56308
|
+
serverResetGen.set(profile.id, profile.reset_generation ?? 0);
|
|
56309
|
+
if (profile.status !== "running" && profile.status !== "pending")
|
|
56310
|
+
continue;
|
|
56311
|
+
desiredLive.add(profile.id);
|
|
56312
|
+
const wipeGen = missedResetGen(profile.id);
|
|
56313
|
+
if (wipeGen !== null)
|
|
56314
|
+
pool.fence(profile.id);
|
|
56315
|
+
const sinceSeq = pool.stopSeqOf(profile.id);
|
|
56316
|
+
const generation = profile.lifecycle_generation;
|
|
56317
|
+
const forceStatusReport = profile.status === "pending";
|
|
56318
|
+
ops.push(ctx.enqueueRevive(profile.id, async () => {
|
|
56319
|
+
if (wipeGen !== null)
|
|
56320
|
+
await ctx.wipeBeforeRevive(pool, profile.id, wipeGen, generation);
|
|
56321
|
+
await pool.ensureRuntime(profile.id, sinceSeq, generation, { forceStatusReport });
|
|
56322
|
+
}));
|
|
56323
|
+
}
|
|
56324
|
+
const candidates = /* @__PURE__ */ new Set([
|
|
56325
|
+
...activeBeforeList,
|
|
56326
|
+
...pendingReviveBeforeList,
|
|
56327
|
+
...localStateBeforeList
|
|
56328
|
+
]);
|
|
56329
|
+
for (const profileId of candidates) {
|
|
56330
|
+
if (desiredLive.has(profileId))
|
|
56331
|
+
continue;
|
|
56332
|
+
const isActive = activeBeforeList.has(profileId);
|
|
56333
|
+
const hasLocalState = localStateBeforeList.has(profileId);
|
|
56334
|
+
if (!isActive && !hasLocalState) {
|
|
56335
|
+
pool.fence(profileId);
|
|
56336
|
+
let cleanup;
|
|
56337
|
+
if (!serverIds.has(profileId)) {
|
|
56338
|
+
cleanup = pool.wipeAbsent(profileId);
|
|
56339
|
+
} else {
|
|
56340
|
+
const wipeGen = missedResetGen(profileId);
|
|
56341
|
+
cleanup = wipeGen !== null ? pool.applyResetWipe(profileId, wipeGen) : pool.releaseRuntime(profileId);
|
|
56342
|
+
}
|
|
56343
|
+
void cleanup.catch((err) => ctx.log.warn(`browser profile pending-revive cleanup failed for ${profileId}: ${String(err)}`));
|
|
56344
|
+
continue;
|
|
56345
|
+
}
|
|
56346
|
+
if (!serverIds.has(profileId)) {
|
|
56347
|
+
pool.fence(profileId);
|
|
56348
|
+
ops.push(ctx.enqueueOp(profileId, () => pool.wipeAbsent(profileId)));
|
|
56349
|
+
} else if (enqueueResetWipeIfMissed(profileId)) {
|
|
56350
|
+
} else if (isActive) {
|
|
56351
|
+
pool.fence(profileId);
|
|
56352
|
+
ops.push(ctx.enqueueOp(profileId, () => pool.releaseRuntime(profileId)));
|
|
56353
|
+
}
|
|
56354
|
+
}
|
|
56355
|
+
for (const result of await Promise.allSettled(ops)) {
|
|
56356
|
+
if (result.status === "rejected") {
|
|
56357
|
+
ctx.log.warn(`browser profile reconcile op failed: ${String(result.reason)}`);
|
|
56358
|
+
}
|
|
56359
|
+
}
|
|
56360
|
+
}
|
|
56361
|
+
var init_browser_profile_reconcile = __esm({
|
|
56362
|
+
"ts/daemon/dist/browser-profile-reconcile.js"() {
|
|
56363
|
+
"use strict";
|
|
56364
|
+
}
|
|
56365
|
+
});
|
|
56366
|
+
|
|
56367
|
+
// ts/daemon/dist/clip-runtime/viewer-command-error.js
|
|
56368
|
+
function viewerCommandFailure(error) {
|
|
56369
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56370
|
+
if (error instanceof ViewerCommandError && error.code) {
|
|
56371
|
+
return { code: error.code, message };
|
|
56372
|
+
}
|
|
56373
|
+
return { message };
|
|
56374
|
+
}
|
|
56375
|
+
var ViewerCommandError;
|
|
56376
|
+
var init_viewer_command_error = __esm({
|
|
56377
|
+
"ts/daemon/dist/clip-runtime/viewer-command-error.js"() {
|
|
56378
|
+
"use strict";
|
|
56379
|
+
ViewerCommandError = class extends Error {
|
|
56380
|
+
code;
|
|
56381
|
+
constructor(message, code) {
|
|
56382
|
+
super(message);
|
|
56383
|
+
this.code = code;
|
|
56384
|
+
this.name = "ViewerCommandError";
|
|
56385
|
+
}
|
|
56386
|
+
};
|
|
56387
|
+
}
|
|
56388
|
+
});
|
|
56389
|
+
|
|
56390
|
+
// ts/daemon/dist/browser-profile-viewer-control.js
|
|
56391
|
+
async function handleBrowserProfileViewerCommand(deps, data) {
|
|
56392
|
+
if (!deps.pool) {
|
|
56393
|
+
await deps.client.postBrowserProfileViewerResponse(data.request_id, {
|
|
56394
|
+
error: { message: "Browser profile runtime is disabled on this daemon" }
|
|
56395
|
+
}).catch((err) => deps.log.warn(`browser profile viewer negative ACK failed: ${String(err)}`));
|
|
56396
|
+
return;
|
|
56397
|
+
}
|
|
56398
|
+
try {
|
|
56399
|
+
const result = await deps.pool.handleViewerCommand(data.profile_id, data.session_id, data.command, data.input, data.turn);
|
|
56400
|
+
await deps.client.postBrowserProfileViewerResponse(data.request_id, { result });
|
|
56401
|
+
} catch (err) {
|
|
56402
|
+
const failure = viewerCommandFailure(err);
|
|
56403
|
+
deps.log.warn(`browser profile viewer command failed (profile=${data.profile_id}, command=${data.command}): ${failure.message}`);
|
|
56404
|
+
await deps.client.postBrowserProfileViewerResponse(data.request_id, { error: failure }).catch((replyErr) => deps.log.warn(`browser profile viewer error ACK failed: ${String(replyErr)}`));
|
|
56405
|
+
}
|
|
56406
|
+
}
|
|
56407
|
+
var init_browser_profile_viewer_control = __esm({
|
|
56408
|
+
"ts/daemon/dist/browser-profile-viewer-control.js"() {
|
|
56409
|
+
"use strict";
|
|
56410
|
+
init_viewer_command_error();
|
|
56411
|
+
}
|
|
56412
|
+
});
|
|
56413
|
+
|
|
56207
56414
|
// ts/node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/constants.js
|
|
56208
56415
|
var require_constants9 = __commonJS({
|
|
56209
56416
|
"ts/node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/constants.js"(exports2, module2) {
|
|
@@ -62394,6 +62601,31 @@ var init_process_manager = __esm({
|
|
|
62394
62601
|
}
|
|
62395
62602
|
});
|
|
62396
62603
|
|
|
62604
|
+
// ts/daemon/dist/clip-runtime/viewer-command-deadline.js
|
|
62605
|
+
function isViewerReadinessCommand(command) {
|
|
62606
|
+
return command === "stream.answer" || command === "stream.switch";
|
|
62607
|
+
}
|
|
62608
|
+
function parseViewerDispatchDeadlineUnixMs(value) {
|
|
62609
|
+
let parsed;
|
|
62610
|
+
if (typeof value === "number") {
|
|
62611
|
+
parsed = value;
|
|
62612
|
+
} else if (typeof value === "string" && /^[1-9]\d*$/.test(value)) {
|
|
62613
|
+
parsed = Number(value);
|
|
62614
|
+
} else {
|
|
62615
|
+
return void 0;
|
|
62616
|
+
}
|
|
62617
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
62618
|
+
}
|
|
62619
|
+
function viewerReadinessDeadlineError(phase) {
|
|
62620
|
+
return new ViewerCommandError(`viewer readiness deadline elapsed ${phase}`, "VIEWER_STREAM_CANCELLED");
|
|
62621
|
+
}
|
|
62622
|
+
var init_viewer_command_deadline = __esm({
|
|
62623
|
+
"ts/daemon/dist/clip-runtime/viewer-command-deadline.js"() {
|
|
62624
|
+
"use strict";
|
|
62625
|
+
init_viewer_command_error();
|
|
62626
|
+
}
|
|
62627
|
+
});
|
|
62628
|
+
|
|
62397
62629
|
// ts/daemon/dist/clip-runtime/clip-provider.js
|
|
62398
62630
|
import * as http23 from "node:http2";
|
|
62399
62631
|
function encodeEnvelope2(msg) {
|
|
@@ -62411,8 +62643,10 @@ var ENVELOPE_FLAG_DATA2, ENVELOPE_FLAG_TRAILER2, EnvelopeDecoder2, ClipProvider;
|
|
|
62411
62643
|
var init_clip_provider = __esm({
|
|
62412
62644
|
"ts/daemon/dist/clip-runtime/clip-provider.js"() {
|
|
62413
62645
|
"use strict";
|
|
62414
|
-
init_process();
|
|
62415
62646
|
init_browser_dependency();
|
|
62647
|
+
init_process();
|
|
62648
|
+
init_viewer_command_deadline();
|
|
62649
|
+
init_viewer_command_error();
|
|
62416
62650
|
ENVELOPE_FLAG_DATA2 = 0;
|
|
62417
62651
|
ENVELOPE_FLAG_TRAILER2 = 2;
|
|
62418
62652
|
EnvelopeDecoder2 = class _EnvelopeDecoder {
|
|
@@ -62768,8 +63002,8 @@ var init_clip_provider = __esm({
|
|
|
62768
63002
|
/**
|
|
62769
63003
|
* Handle a hosted-browser live-viewer command relayed by clip-service over the
|
|
62770
63004
|
* stream and reply on the same stream (design §3.4). Always answers the
|
|
62771
|
-
* request/reply bridge exactly once — `{result}` on success,
|
|
62772
|
-
* on failure — and never throws into the message loop, mirroring the BYOC
|
|
63005
|
+
* request/reply bridge exactly once — `{result}` on success,
|
|
63006
|
+
* `{error:{code?,message}}` on failure — and never throws into the message loop, mirroring the BYOC
|
|
62773
63007
|
* daemon's handleBrowserProfileViewer (which replies over HTTP instead).
|
|
62774
63008
|
*/
|
|
62775
63009
|
async handleViewerCommand(cmd) {
|
|
@@ -62782,6 +63016,10 @@ var init_clip_provider = __esm({
|
|
|
62782
63016
|
return;
|
|
62783
63017
|
}
|
|
62784
63018
|
try {
|
|
63019
|
+
const dispatchDeadlineUnixMs = parseViewerDispatchDeadlineUnixMs(cmd.dispatchDeadlineUnixMs);
|
|
63020
|
+
if (isViewerReadinessCommand(cmd.command) && dispatchDeadlineUnixMs === void 0) {
|
|
63021
|
+
throw viewerReadinessDeadlineError("because the ProviderStream command has no authenticated deadline");
|
|
63022
|
+
}
|
|
62785
63023
|
let input;
|
|
62786
63024
|
if (cmd.input) {
|
|
62787
63025
|
const decoded = Buffer.from(cmd.input, "base64").toString("utf-8");
|
|
@@ -62796,13 +63034,14 @@ var init_clip_provider = __esm({
|
|
|
62796
63034
|
sessionId: cmd.sessionId,
|
|
62797
63035
|
command: cmd.command,
|
|
62798
63036
|
input,
|
|
62799
|
-
turn: cmd.turn
|
|
63037
|
+
turn: cmd.turn,
|
|
63038
|
+
dispatchDeadlineUnixMs
|
|
62800
63039
|
});
|
|
62801
63040
|
await this.sendViewerReply(requestId, { result });
|
|
62802
63041
|
} catch (err) {
|
|
62803
|
-
const
|
|
62804
|
-
this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${message}`);
|
|
62805
|
-
await this.sendViewerReply(requestId, { error:
|
|
63042
|
+
const failure = viewerCommandFailure(err);
|
|
63043
|
+
this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${failure.message}`);
|
|
63044
|
+
await this.sendViewerReply(requestId, { error: failure }).catch(() => {
|
|
62806
63045
|
});
|
|
62807
63046
|
}
|
|
62808
63047
|
}
|
|
@@ -63183,6 +63422,80 @@ var init_browser_target_registry = __esm({
|
|
|
63183
63422
|
}
|
|
63184
63423
|
});
|
|
63185
63424
|
|
|
63425
|
+
// ts/daemon/dist/clip-runtime/bb-viewer-command-client.js
|
|
63426
|
+
function bbViewerCommandTimeoutMs(method) {
|
|
63427
|
+
return method === "answer" || method === "switch" ? BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS : BB_VIEWER_COMMAND_TIMEOUT_MS;
|
|
63428
|
+
}
|
|
63429
|
+
function bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs, now = Date.now()) {
|
|
63430
|
+
const configured = bbViewerCommandTimeoutMs(method);
|
|
63431
|
+
if (deadlineUnixMs === void 0)
|
|
63432
|
+
return configured;
|
|
63433
|
+
const remaining = deadlineUnixMs - now;
|
|
63434
|
+
if (remaining <= 0) {
|
|
63435
|
+
throw viewerReadinessDeadlineError(`before bb-viewer ${method}`);
|
|
63436
|
+
}
|
|
63437
|
+
return Math.max(1, Math.min(configured, remaining));
|
|
63438
|
+
}
|
|
63439
|
+
async function sendBBViewerCommand(port, method, params, deadlineUnixMs) {
|
|
63440
|
+
const controller = new AbortController();
|
|
63441
|
+
const timeoutMs = bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs);
|
|
63442
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
63443
|
+
try {
|
|
63444
|
+
const resp = await fetch(`http://127.0.0.1:${port}/command`, {
|
|
63445
|
+
method: "POST",
|
|
63446
|
+
headers: { "Content-Type": "application/json" },
|
|
63447
|
+
body: JSON.stringify({ method, params }),
|
|
63448
|
+
signal: controller.signal
|
|
63449
|
+
});
|
|
63450
|
+
const text = await resp.text();
|
|
63451
|
+
return parseBBViewerCommandResponse(resp.status, text, method);
|
|
63452
|
+
} catch (err) {
|
|
63453
|
+
if (controller.signal.aborted && (method === "answer" || method === "switch")) {
|
|
63454
|
+
throw viewerReadinessDeadlineError(`while waiting for bb-viewer ${method}`);
|
|
63455
|
+
}
|
|
63456
|
+
throw err;
|
|
63457
|
+
} finally {
|
|
63458
|
+
clearTimeout(timer);
|
|
63459
|
+
}
|
|
63460
|
+
}
|
|
63461
|
+
function parseBBViewerCommandResponse(status, text, method) {
|
|
63462
|
+
let parsed;
|
|
63463
|
+
try {
|
|
63464
|
+
parsed = text ? JSON.parse(text) : {};
|
|
63465
|
+
} catch {
|
|
63466
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned invalid JSON`);
|
|
63467
|
+
}
|
|
63468
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
63469
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned an invalid response`);
|
|
63470
|
+
}
|
|
63471
|
+
const envelope = parsed;
|
|
63472
|
+
if (envelope.error) {
|
|
63473
|
+
const code = typeof envelope.error.code === "string" && envelope.error.code.trim() ? envelope.error.code.trim() : void 0;
|
|
63474
|
+
const message = typeof envelope.error.message === "string" && envelope.error.message.trim() ? envelope.error.message : `bb-viewer ${method} failed`;
|
|
63475
|
+
throw new ViewerCommandError(message, code);
|
|
63476
|
+
}
|
|
63477
|
+
if (status < 200 || status >= 300) {
|
|
63478
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned ${status}`);
|
|
63479
|
+
}
|
|
63480
|
+
if (envelope.result === void 0 || envelope.result === null) {
|
|
63481
|
+
return {};
|
|
63482
|
+
}
|
|
63483
|
+
if (typeof envelope.result !== "object" || Array.isArray(envelope.result)) {
|
|
63484
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned an invalid result`);
|
|
63485
|
+
}
|
|
63486
|
+
return envelope.result;
|
|
63487
|
+
}
|
|
63488
|
+
var BB_VIEWER_COMMAND_TIMEOUT_MS, BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS;
|
|
63489
|
+
var init_bb_viewer_command_client = __esm({
|
|
63490
|
+
"ts/daemon/dist/clip-runtime/bb-viewer-command-client.js"() {
|
|
63491
|
+
"use strict";
|
|
63492
|
+
init_viewer_command_deadline();
|
|
63493
|
+
init_viewer_command_error();
|
|
63494
|
+
BB_VIEWER_COMMAND_TIMEOUT_MS = 15e3;
|
|
63495
|
+
BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS = 3e4;
|
|
63496
|
+
}
|
|
63497
|
+
});
|
|
63498
|
+
|
|
63186
63499
|
// ts/daemon/dist/clip-runtime/browser-viewer-streamer.js
|
|
63187
63500
|
import { spawn as spawn3 } from "node:child_process";
|
|
63188
63501
|
function isLoopbackBindHost(host) {
|
|
@@ -63226,13 +63539,15 @@ function filterTabListToAccount(result, account) {
|
|
|
63226
63539
|
const owned = tabs.filter((t) => t?.account === account);
|
|
63227
63540
|
return { ...result, tabs: owned, activeIndex: owned.findIndex((t) => t?.active === true) };
|
|
63228
63541
|
}
|
|
63229
|
-
var BB_VIEWER_HEALTH_TIMEOUT_MS,
|
|
63542
|
+
var BB_VIEWER_HEALTH_TIMEOUT_MS, BB_VIEWER_UNANSWERED_REAP_MS, BB_VIEWER_TERM_GRACE_MS, BrowserViewerStreamer, REFLESS_PIN_NAV, VIEWER_NAVIGABLE_PROTOCOLS;
|
|
63230
63543
|
var init_browser_viewer_streamer = __esm({
|
|
63231
63544
|
"ts/daemon/dist/clip-runtime/browser-viewer-streamer.js"() {
|
|
63232
63545
|
"use strict";
|
|
63546
|
+
init_bb_viewer_command_client();
|
|
63233
63547
|
init_subprocess();
|
|
63548
|
+
init_viewer_command_deadline();
|
|
63549
|
+
init_bb_viewer_command_client();
|
|
63234
63550
|
BB_VIEWER_HEALTH_TIMEOUT_MS = 1e4;
|
|
63235
|
-
BB_VIEWER_COMMAND_TIMEOUT_MS = 15e3;
|
|
63236
63551
|
BB_VIEWER_UNANSWERED_REAP_MS = 9e4;
|
|
63237
63552
|
BB_VIEWER_TERM_GRACE_MS = 5e3;
|
|
63238
63553
|
BrowserViewerStreamer = class {
|
|
@@ -63261,7 +63576,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63261
63576
|
* partial result. `data.input` / `data.turn` / `data.session_id` come from
|
|
63262
63577
|
* MachineBrowserProfileViewerData.
|
|
63263
63578
|
*/
|
|
63264
|
-
async handleViewerCommand(profileId, sessionId, command, input, turn) {
|
|
63579
|
+
async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
|
|
63265
63580
|
if (!profileId)
|
|
63266
63581
|
throw new Error("browser profile id is required");
|
|
63267
63582
|
if (command !== "stream.start" && sessionId && this.kickedSessions.get(profileId) === sessionId) {
|
|
@@ -63271,11 +63586,11 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63271
63586
|
case "stream.start":
|
|
63272
63587
|
return this.viewerStreamStart(profileId, sessionId, turn);
|
|
63273
63588
|
case "stream.answer":
|
|
63274
|
-
return this.viewerStreamAnswer(profileId, sessionId, input);
|
|
63589
|
+
return this.viewerStreamAnswer(profileId, sessionId, input, execution?.deadlineUnixMs);
|
|
63275
63590
|
case "stream.close":
|
|
63276
63591
|
return this.viewerStreamClose(profileId, sessionId, input);
|
|
63277
63592
|
case "stream.switch":
|
|
63278
|
-
return this.viewerStreamSwitch(profileId, sessionId, input);
|
|
63593
|
+
return this.viewerStreamSwitch(profileId, sessionId, input, execution?.deadlineUnixMs);
|
|
63279
63594
|
case "kick":
|
|
63280
63595
|
return this.viewerKick(profileId, sessionId, input);
|
|
63281
63596
|
case "close":
|
|
@@ -63327,7 +63642,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63327
63642
|
await this.bringTargetToFront(cdpUrl);
|
|
63328
63643
|
const streamer = await this.spawnStreamer(profileId, sessionId, turn);
|
|
63329
63644
|
try {
|
|
63330
|
-
const result = await
|
|
63645
|
+
const result = await sendBBViewerCommand(streamer.port, "connect", { cdpUrl });
|
|
63331
63646
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
63332
63647
|
const reapTimer = setTimeout(() => {
|
|
63333
63648
|
const current = this.streamers.get(profileId);
|
|
@@ -63348,14 +63663,17 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63348
63663
|
* Rejects if the streamer's sessionId no longer matches (a stale viewer
|
|
63349
63664
|
* session from a superseded stream.start).
|
|
63350
63665
|
*/
|
|
63351
|
-
async viewerStreamAnswer(profileId, sessionId, input) {
|
|
63666
|
+
async viewerStreamAnswer(profileId, sessionId, input, deadlineUnixMs) {
|
|
63352
63667
|
const streamer = this.streamers.get(profileId);
|
|
63353
63668
|
if (!streamer)
|
|
63354
63669
|
throw new Error("no active viewer streamer for profile");
|
|
63355
63670
|
if (streamer.sessionId !== sessionId)
|
|
63356
63671
|
throw new Error("stale viewer session");
|
|
63672
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "before bb-viewer answer");
|
|
63357
63673
|
streamer.answered = true;
|
|
63358
|
-
|
|
63674
|
+
const result = await sendBBViewerCommand(streamer.port, "answer", input ?? {}, deadlineUnixMs);
|
|
63675
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "after bb-viewer answer");
|
|
63676
|
+
return result;
|
|
63359
63677
|
}
|
|
63360
63678
|
/**
|
|
63361
63679
|
* stream.close — best-effort stop the streamer, then kill + remove it. A
|
|
@@ -63376,7 +63694,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63376
63694
|
return { ok: true, stale: true };
|
|
63377
63695
|
}
|
|
63378
63696
|
try {
|
|
63379
|
-
await
|
|
63697
|
+
await sendBBViewerCommand(streamer.port, "stop", {});
|
|
63380
63698
|
} catch (err) {
|
|
63381
63699
|
this.host.log.warn(`[bb-viewer] stop command failed for ${profileId} (killing anyway): ${formatErrorForLog(err)}`);
|
|
63382
63700
|
}
|
|
@@ -63393,7 +63711,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63393
63711
|
* the stream.start response), so a session-less switch is by definition not
|
|
63394
63712
|
* the current viewer.
|
|
63395
63713
|
*/
|
|
63396
|
-
async viewerStreamSwitch(profileId, sessionId, input) {
|
|
63714
|
+
async viewerStreamSwitch(profileId, sessionId, input, deadlineUnixMs) {
|
|
63397
63715
|
const streamer = this.streamers.get(profileId);
|
|
63398
63716
|
if (!streamer)
|
|
63399
63717
|
throw new Error("no active viewer streamer for profile");
|
|
@@ -63403,9 +63721,13 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63403
63721
|
if (tab === void 0 || tab === null || tab === "") {
|
|
63404
63722
|
throw new Error("stream.switch requires input.tab");
|
|
63405
63723
|
}
|
|
63724
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "before resolving the switch target");
|
|
63406
63725
|
const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId, tab), `viewer stream.switch ${profileId}`);
|
|
63407
|
-
|
|
63408
|
-
|
|
63726
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "after resolving the switch target");
|
|
63727
|
+
await this.bringTargetToFront(cdpUrl, deadlineUnixMs);
|
|
63728
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "before bb-viewer switch");
|
|
63729
|
+
const result = await sendBBViewerCommand(streamer.port, "switch", { cdpUrl }, deadlineUnixMs);
|
|
63730
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "after bb-viewer switch");
|
|
63409
63731
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
63410
63732
|
return { ...result, streamed_tab_id: streamer.streamedTabId };
|
|
63411
63733
|
}
|
|
@@ -63429,7 +63751,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63429
63751
|
}
|
|
63430
63752
|
const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId), `viewer close re-switch ${profileId}`);
|
|
63431
63753
|
await this.bringTargetToFront(cdpUrl);
|
|
63432
|
-
const sw = await
|
|
63754
|
+
const sw = await sendBBViewerCommand(streamer.port, "switch", { cdpUrl });
|
|
63433
63755
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
63434
63756
|
return { ...result, ...sw, streamed_tab_id: streamer.streamedTabId };
|
|
63435
63757
|
}
|
|
@@ -63441,15 +63763,17 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63441
63763
|
* must not block the stream. host:port + targetId are parsed from the cdpUrl
|
|
63442
63764
|
* (`ws://host:port/devtools/page/<targetId>`) to avoid an extra /status call.
|
|
63443
63765
|
*/
|
|
63444
|
-
async bringTargetToFront(cdpUrl) {
|
|
63766
|
+
async bringTargetToFront(cdpUrl, deadlineUnixMs) {
|
|
63445
63767
|
const m = /^ws:\/\/([^/]+)\/devtools\/page\/(.+)$/.exec(cdpUrl);
|
|
63446
63768
|
if (!m)
|
|
63447
63769
|
return;
|
|
63448
63770
|
const [, hostPort, targetId] = m;
|
|
63771
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "before foregrounding the switch target");
|
|
63772
|
+
const timeoutMs = deadlineUnixMs === void 0 ? 5e3 : Math.max(1, Math.min(5e3, deadlineUnixMs - Date.now()));
|
|
63449
63773
|
try {
|
|
63450
63774
|
const resp = await fetch(`http://${hostPort}/json/activate/${targetId}`, {
|
|
63451
63775
|
method: "GET",
|
|
63452
|
-
signal: AbortSignal.timeout(
|
|
63776
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
63453
63777
|
});
|
|
63454
63778
|
if (!resp.ok) {
|
|
63455
63779
|
this.host.log.warn(`[bb-viewer] activate target ${targetId} returned ${resp.status} (continuing)`);
|
|
@@ -63458,6 +63782,11 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63458
63782
|
this.host.log.warn(`[bb-viewer] activate target ${targetId} failed (continuing): ${formatErrorForLog(err)}`);
|
|
63459
63783
|
}
|
|
63460
63784
|
}
|
|
63785
|
+
assertViewerExecutionDeadline(deadlineUnixMs, phase) {
|
|
63786
|
+
if (deadlineUnixMs !== void 0 && Date.now() >= deadlineUnixMs) {
|
|
63787
|
+
throw viewerReadinessDeadlineError(phase);
|
|
63788
|
+
}
|
|
63789
|
+
}
|
|
63461
63790
|
/**
|
|
63462
63791
|
* Forward a navigation command (tab_list/tab_new/open/reload/back/forward/
|
|
63463
63792
|
* close) to bb-browser scoped to the profile's account. This lets the human
|
|
@@ -63658,34 +63987,6 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63658
63987
|
killTimer.unref?.();
|
|
63659
63988
|
child.once("exit", () => clearTimeout(killTimer));
|
|
63660
63989
|
}
|
|
63661
|
-
/**
|
|
63662
|
-
* POST a command to the profile's bb-viewer `/command` endpoint. Parses the
|
|
63663
|
-
* `{result}` / `{error:{message}}` envelope (bb-viewer api.go) and returns
|
|
63664
|
-
* the inner result, throwing the error message on failure.
|
|
63665
|
-
*/
|
|
63666
|
-
async streamerCommand(streamer, method, params) {
|
|
63667
|
-
const controller = new AbortController();
|
|
63668
|
-
const timer = setTimeout(() => controller.abort(), BB_VIEWER_COMMAND_TIMEOUT_MS);
|
|
63669
|
-
try {
|
|
63670
|
-
const resp = await fetch(`http://127.0.0.1:${streamer.port}/command`, {
|
|
63671
|
-
method: "POST",
|
|
63672
|
-
headers: { "Content-Type": "application/json" },
|
|
63673
|
-
body: JSON.stringify({ method, params }),
|
|
63674
|
-
signal: controller.signal
|
|
63675
|
-
});
|
|
63676
|
-
const text = await resp.text();
|
|
63677
|
-
const parsed = text ? JSON.parse(text) : {};
|
|
63678
|
-
if (parsed.error) {
|
|
63679
|
-
throw new Error(parsed.error.message || `bb-viewer ${method} failed`);
|
|
63680
|
-
}
|
|
63681
|
-
if (!resp.ok) {
|
|
63682
|
-
throw new Error(`bb-viewer ${method} returned ${resp.status}: ${text}`);
|
|
63683
|
-
}
|
|
63684
|
-
return parsed.result ?? {};
|
|
63685
|
-
} finally {
|
|
63686
|
-
clearTimeout(timer);
|
|
63687
|
-
}
|
|
63688
|
-
}
|
|
63689
63990
|
/**
|
|
63690
63991
|
* Kill + remove the profile's bb-viewer streamer, if any. Idempotent. Also
|
|
63691
63992
|
* clears any kick marker for the profile: a (re)kill establishes a clean
|
|
@@ -63981,8 +64282,8 @@ var init_browser_profile_manager = __esm({
|
|
|
63981
64282
|
* (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
|
|
63982
64283
|
* subprocesses and viewer tab navigation.
|
|
63983
64284
|
*/
|
|
63984
|
-
async handleViewerCommand(profileId, sessionId, command, input, turn) {
|
|
63985
|
-
return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn);
|
|
64285
|
+
async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
|
|
64286
|
+
return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn, execution);
|
|
63986
64287
|
}
|
|
63987
64288
|
/** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
|
|
63988
64289
|
async cdpEndpoint() {
|
|
@@ -64940,104 +65241,11 @@ var init_clip_runtime = __esm({
|
|
|
64940
65241
|
init_hub_client();
|
|
64941
65242
|
init_browser_profile_manager();
|
|
64942
65243
|
init_browser_profile_pool();
|
|
65244
|
+
init_viewer_command_error();
|
|
64943
65245
|
init_clip_installer();
|
|
64944
65246
|
}
|
|
64945
65247
|
});
|
|
64946
65248
|
|
|
64947
|
-
// ts/daemon/dist/browser-profile-reconcile.js
|
|
64948
|
-
async function reconcileBrowserProfiles(ctx) {
|
|
64949
|
-
const pool = ctx.pool;
|
|
64950
|
-
const activeBeforeList = new Set(pool.activeProfileIds());
|
|
64951
|
-
const pendingReviveBeforeList = ctx.pendingReviveIds();
|
|
64952
|
-
const localStateBeforeList = new Set(pool.localStateProfileIds());
|
|
64953
|
-
let profiles;
|
|
64954
|
-
try {
|
|
64955
|
-
profiles = await ctx.client.listMachineBrowserProfiles();
|
|
64956
|
-
} catch (err) {
|
|
64957
|
-
ctx.log.warn(`browser profile reconcile: list failed: ${String(err)}`);
|
|
64958
|
-
return;
|
|
64959
|
-
}
|
|
64960
|
-
if (ctx.currentPool() !== pool)
|
|
64961
|
-
return;
|
|
64962
|
-
const desiredLive = /* @__PURE__ */ new Set();
|
|
64963
|
-
const serverIds = /* @__PURE__ */ new Set();
|
|
64964
|
-
const serverResetGen = /* @__PURE__ */ new Map();
|
|
64965
|
-
const ops = [];
|
|
64966
|
-
const missedResetGen = (profileId) => {
|
|
64967
|
-
const gen = serverResetGen.get(profileId) ?? 0;
|
|
64968
|
-
return gen > pool.appliedResetGeneration(profileId) ? gen : null;
|
|
64969
|
-
};
|
|
64970
|
-
const enqueueResetWipeIfMissed = (profileId) => {
|
|
64971
|
-
const gen = missedResetGen(profileId);
|
|
64972
|
-
if (gen === null)
|
|
64973
|
-
return false;
|
|
64974
|
-
pool.fence(profileId);
|
|
64975
|
-
ops.push(ctx.enqueueOp(profileId, () => pool.applyResetWipe(profileId, gen)));
|
|
64976
|
-
return true;
|
|
64977
|
-
};
|
|
64978
|
-
for (const profile of profiles) {
|
|
64979
|
-
if (!profile.machine_id)
|
|
64980
|
-
continue;
|
|
64981
|
-
serverIds.add(profile.id);
|
|
64982
|
-
serverResetGen.set(profile.id, profile.reset_generation ?? 0);
|
|
64983
|
-
if (profile.status !== "running" && profile.status !== "pending")
|
|
64984
|
-
continue;
|
|
64985
|
-
desiredLive.add(profile.id);
|
|
64986
|
-
const wipeGen = missedResetGen(profile.id);
|
|
64987
|
-
if (wipeGen !== null)
|
|
64988
|
-
pool.fence(profile.id);
|
|
64989
|
-
const sinceSeq = pool.stopSeqOf(profile.id);
|
|
64990
|
-
const generation = profile.lifecycle_generation;
|
|
64991
|
-
const forceStatusReport = profile.status === "pending";
|
|
64992
|
-
ops.push(ctx.enqueueRevive(profile.id, async () => {
|
|
64993
|
-
if (wipeGen !== null)
|
|
64994
|
-
await ctx.wipeBeforeRevive(pool, profile.id, wipeGen, generation);
|
|
64995
|
-
await pool.ensureRuntime(profile.id, sinceSeq, generation, { forceStatusReport });
|
|
64996
|
-
}));
|
|
64997
|
-
}
|
|
64998
|
-
const candidates = /* @__PURE__ */ new Set([
|
|
64999
|
-
...activeBeforeList,
|
|
65000
|
-
...pendingReviveBeforeList,
|
|
65001
|
-
...localStateBeforeList
|
|
65002
|
-
]);
|
|
65003
|
-
for (const profileId of candidates) {
|
|
65004
|
-
if (desiredLive.has(profileId))
|
|
65005
|
-
continue;
|
|
65006
|
-
const isActive = activeBeforeList.has(profileId);
|
|
65007
|
-
const hasLocalState = localStateBeforeList.has(profileId);
|
|
65008
|
-
if (!isActive && !hasLocalState) {
|
|
65009
|
-
pool.fence(profileId);
|
|
65010
|
-
let cleanup;
|
|
65011
|
-
if (!serverIds.has(profileId)) {
|
|
65012
|
-
cleanup = pool.wipeAbsent(profileId);
|
|
65013
|
-
} else {
|
|
65014
|
-
const wipeGen = missedResetGen(profileId);
|
|
65015
|
-
cleanup = wipeGen !== null ? pool.applyResetWipe(profileId, wipeGen) : pool.releaseRuntime(profileId);
|
|
65016
|
-
}
|
|
65017
|
-
void cleanup.catch((err) => ctx.log.warn(`browser profile pending-revive cleanup failed for ${profileId}: ${String(err)}`));
|
|
65018
|
-
continue;
|
|
65019
|
-
}
|
|
65020
|
-
if (!serverIds.has(profileId)) {
|
|
65021
|
-
pool.fence(profileId);
|
|
65022
|
-
ops.push(ctx.enqueueOp(profileId, () => pool.wipeAbsent(profileId)));
|
|
65023
|
-
} else if (enqueueResetWipeIfMissed(profileId)) {
|
|
65024
|
-
} else if (isActive) {
|
|
65025
|
-
pool.fence(profileId);
|
|
65026
|
-
ops.push(ctx.enqueueOp(profileId, () => pool.releaseRuntime(profileId)));
|
|
65027
|
-
}
|
|
65028
|
-
}
|
|
65029
|
-
for (const result of await Promise.allSettled(ops)) {
|
|
65030
|
-
if (result.status === "rejected") {
|
|
65031
|
-
ctx.log.warn(`browser profile reconcile op failed: ${String(result.reason)}`);
|
|
65032
|
-
}
|
|
65033
|
-
}
|
|
65034
|
-
}
|
|
65035
|
-
var init_browser_profile_reconcile = __esm({
|
|
65036
|
-
"ts/daemon/dist/browser-profile-reconcile.js"() {
|
|
65037
|
-
"use strict";
|
|
65038
|
-
}
|
|
65039
|
-
});
|
|
65040
|
-
|
|
65041
65249
|
// ts/daemon/dist/filesystem.js
|
|
65042
65250
|
import * as fs9 from "fs";
|
|
65043
65251
|
import * as path16 from "path";
|
|
@@ -66393,12 +66601,13 @@ var init_supervisor = __esm({
|
|
|
66393
66601
|
"use strict";
|
|
66394
66602
|
init_dist2();
|
|
66395
66603
|
init_dist();
|
|
66604
|
+
init_browser_profile_reconcile();
|
|
66605
|
+
init_browser_profile_viewer_control();
|
|
66396
66606
|
init_browser_readiness();
|
|
66397
66607
|
init_clip_installer();
|
|
66398
66608
|
init_manifest();
|
|
66399
66609
|
init_clip_runtime();
|
|
66400
66610
|
init_config();
|
|
66401
|
-
init_browser_profile_reconcile();
|
|
66402
66611
|
init_filesystem();
|
|
66403
66612
|
init_local_profile_control();
|
|
66404
66613
|
init_home_isolation();
|
|
@@ -66642,7 +66851,7 @@ var init_supervisor = __esm({
|
|
|
66642
66851
|
});
|
|
66643
66852
|
this.ws.on("machine.browser_profile.viewer", (data) => {
|
|
66644
66853
|
this.log.info(`WS: browser profile viewer command \u2014 profile=${data.profile_id} command=${data.command}`);
|
|
66645
|
-
this.
|
|
66854
|
+
void this.enqueueBrowserProfileOp(data.profile_id, () => handleBrowserProfileViewerCommand({ pool: this.browserProfilePool, client: this.client, log: this.log }, data));
|
|
66646
66855
|
});
|
|
66647
66856
|
this.ws.onStateChange((state) => {
|
|
66648
66857
|
if (state === "disconnected" || state === "reconnecting") {
|
|
@@ -67105,31 +67314,6 @@ var init_supervisor = __esm({
|
|
|
67105
67314
|
this.browserProfilePendingRevives.set(profileId, remaining);
|
|
67106
67315
|
});
|
|
67107
67316
|
}
|
|
67108
|
-
/**
|
|
67109
|
-
* Handle one viewer control command and reply via REST. Always answers the
|
|
67110
|
-
* request/reply bridge exactly once ({result} on success, {error:{message}}
|
|
67111
|
-
* on failure) and never throws into the WS event loop — mirrors
|
|
67112
|
-
* handleFilesystemBrowse. Serialized per profile by enqueueBrowserProfileViewer.
|
|
67113
|
-
*/
|
|
67114
|
-
async handleBrowserProfileViewer(data) {
|
|
67115
|
-
if (!this.browserProfilePool) {
|
|
67116
|
-
await this.client.postBrowserProfileViewerResponse(data.request_id, {
|
|
67117
|
-
error: { message: "Browser profile runtime is disabled on this daemon" }
|
|
67118
|
-
}).catch((err) => this.log.warn(`browser profile viewer negative ACK failed: ${String(err)}`));
|
|
67119
|
-
return;
|
|
67120
|
-
}
|
|
67121
|
-
try {
|
|
67122
|
-
const result = await this.browserProfilePool.handleViewerCommand(data.profile_id, data.session_id, data.command, data.input, data.turn);
|
|
67123
|
-
await this.client.postBrowserProfileViewerResponse(data.request_id, { result });
|
|
67124
|
-
} catch (err) {
|
|
67125
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
67126
|
-
this.log.warn(`browser profile viewer command failed (profile=${data.profile_id}, command=${data.command}): ${message}`);
|
|
67127
|
-
await this.client.postBrowserProfileViewerResponse(data.request_id, { error: { message } }).catch((replyErr) => this.log.warn(`browser profile viewer error ACK failed: ${String(replyErr)}`));
|
|
67128
|
-
}
|
|
67129
|
-
}
|
|
67130
|
-
enqueueBrowserProfileViewer(data) {
|
|
67131
|
-
void this.enqueueBrowserProfileOp(data.profile_id, () => this.handleBrowserProfileViewer(data));
|
|
67132
|
-
}
|
|
67133
67317
|
async handleClipSync(_data) {
|
|
67134
67318
|
await this.reconcileMachineClips();
|
|
67135
67319
|
}
|