@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/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
|
|
|
@@ -462,6 +472,9 @@ var init_constants = __esm({
|
|
|
462
472
|
TEMPLATE: (orgId, templateId) => `${API_BASE}/orgs/${orgId}/templates/${templateId}`,
|
|
463
473
|
TEMPLATE_DEPLOYMENTS: (orgId) => `${API_BASE}/orgs/${orgId}/template-deployments`,
|
|
464
474
|
TEMPLATE_DEPLOYMENT: (orgId, deploymentId) => `${API_BASE}/orgs/${orgId}/template-deployments/${deploymentId}`,
|
|
475
|
+
CLIP_DEPENDENCIES_CHECK: (orgId) => `${API_BASE}/orgs/${orgId}/clip-dependencies/check`,
|
|
476
|
+
// Onboarding wizard progress (org-scoped, self)
|
|
477
|
+
ONBOARDING_PROGRESS: (orgId) => `${API_BASE}/orgs/${orgId}/onboarding`,
|
|
465
478
|
// Billing & Credits (org-scoped)
|
|
466
479
|
BILLING: (orgId) => `${API_BASE}/orgs/${orgId}/billing`,
|
|
467
480
|
BILLING_TRANSACTIONS: (orgId) => `${API_BASE}/orgs/${orgId}/billing/transactions`,
|
|
@@ -655,6 +668,7 @@ var ParallClient, ApiError;
|
|
|
655
668
|
var init_client = __esm({
|
|
656
669
|
"ts/sdk/dist/client.js"() {
|
|
657
670
|
"use strict";
|
|
671
|
+
init_browser_viewer();
|
|
658
672
|
init_constants();
|
|
659
673
|
ParallClient = class _ParallClient {
|
|
660
674
|
baseUrl;
|
|
@@ -1060,8 +1074,10 @@ var init_client = __esm({
|
|
|
1060
1074
|
q.limit = String(params.limit);
|
|
1061
1075
|
return this.request("GET", ENDPOINTS.ORG_MEMBER_CHATS(orgId, memberId), void 0, q);
|
|
1062
1076
|
}
|
|
1063
|
-
// Pending tasks (todo + in_progress) assigned to a member.
|
|
1064
|
-
// member profile Activity
|
|
1077
|
+
// Pending tasks (todo + in_progress) assigned to a member. On-demand listing
|
|
1078
|
+
// (member profile Activity; agents via `parall tasks`) — startup catch-up
|
|
1079
|
+
// runs on dispatch redrive, not this endpoint. Self-assigned tasks appear
|
|
1080
|
+
// here without any dispatch WorkItem (self-assign is deliberately silent).
|
|
1065
1081
|
async getMemberTasks(orgId, memberId, params) {
|
|
1066
1082
|
const q = {};
|
|
1067
1083
|
if (params?.cursor)
|
|
@@ -1403,7 +1419,12 @@ var init_client = __esm({
|
|
|
1403
1419
|
async getAgentRuntimeRelease(orgId, agentId, tag) {
|
|
1404
1420
|
return this.request("GET", ENDPOINTS.AGENT_RUNTIME_RELEASE(orgId, agentId, tag));
|
|
1405
1421
|
}
|
|
1406
|
-
/**
|
|
1422
|
+
/**
|
|
1423
|
+
* Fetch all pending tasks (todo/in_progress) assigned to an agent. Pages
|
|
1424
|
+
* automatically. On-demand listing only — not a dispatch source: startup
|
|
1425
|
+
* catch-up runs on dispatch redrive, and self-assigned tasks listed here
|
|
1426
|
+
* deliberately have no WorkItem behind them.
|
|
1427
|
+
*/
|
|
1407
1428
|
async getAgentTasks(orgId, agentId) {
|
|
1408
1429
|
const all = [];
|
|
1409
1430
|
let cursor;
|
|
@@ -1607,11 +1628,7 @@ var init_client = __esm({
|
|
|
1607
1628
|
async postBrowseResponse(requestId, response) {
|
|
1608
1629
|
return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSE_RESPONSE(requestId), response);
|
|
1609
1630
|
}
|
|
1610
|
-
/**
|
|
1611
|
-
* `POST /machines/me/browser-profiles/viewer-response/{requestId}` — daemon
|
|
1612
|
-
* reply to a `machine.browser_profile.viewer` control command. Wakes the
|
|
1613
|
-
* api-server request/reply bridge (mirrors {@link postBrowseResponse}).
|
|
1614
|
-
*/
|
|
1631
|
+
/** Reply to `machine.browser_profile.viewer` and wake its request/reply bridge. */
|
|
1615
1632
|
async postBrowserProfileViewerResponse(requestId, response) {
|
|
1616
1633
|
return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_VIEWER_RESPONSE(requestId), response);
|
|
1617
1634
|
}
|
|
@@ -1621,7 +1638,7 @@ var init_client = __esm({
|
|
|
1621
1638
|
* owner or org admin. api-server brokers the command to the host daemon.
|
|
1622
1639
|
*/
|
|
1623
1640
|
async browserViewerCommand(orgId, profileId, req, opts) {
|
|
1624
|
-
return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, opts);
|
|
1641
|
+
return this.request("POST", ENDPOINTS.BROWSER_PROFILE_VIEWER_COMMAND(orgId, profileId), req, void 0, false, browserViewerRequestOptions(req.command, opts));
|
|
1625
1642
|
}
|
|
1626
1643
|
/**
|
|
1627
1644
|
* Drive the Cloud Edge live viewer (V1b, design §6): WebRTC signaling + input +
|
|
@@ -2409,6 +2426,19 @@ var init_client = __esm({
|
|
|
2409
2426
|
async getTemplateDeployment(orgId, deploymentId) {
|
|
2410
2427
|
return this.request("GET", ENDPOINTS.TEMPLATE_DEPLOYMENT(orgId, deploymentId));
|
|
2411
2428
|
}
|
|
2429
|
+
/** Pre-deploy dependency connection state (wizard S3). Live answer, no
|
|
2430
|
+
* cache — poll it the way getTemplateDeployment is polled. */
|
|
2431
|
+
async checkClipDependencies(orgId, refs) {
|
|
2432
|
+
const response = await this.request("POST", ENDPOINTS.CLIP_DEPENDENCIES_CHECK(orgId), { refs });
|
|
2433
|
+
return response.results ?? [];
|
|
2434
|
+
}
|
|
2435
|
+
// ---- Onboarding wizard progress (org-scoped, self) ----
|
|
2436
|
+
async getOnboardingProgress(orgId) {
|
|
2437
|
+
return this.request("GET", ENDPOINTS.ONBOARDING_PROGRESS(orgId));
|
|
2438
|
+
}
|
|
2439
|
+
async updateOnboardingProgress(orgId, request3) {
|
|
2440
|
+
return this.request("PATCH", ENDPOINTS.ONBOARDING_PROGRESS(orgId), request3);
|
|
2441
|
+
}
|
|
2412
2442
|
// ---- Billing & Credits (org-scoped) ----
|
|
2413
2443
|
async getBilling(orgId) {
|
|
2414
2444
|
return this.request("GET", ENDPOINTS.BILLING(orgId));
|
|
@@ -2770,6 +2800,13 @@ var init_client = __esm({
|
|
|
2770
2800
|
}
|
|
2771
2801
|
});
|
|
2772
2802
|
|
|
2803
|
+
// ts/sdk/dist/types.js
|
|
2804
|
+
var init_types2 = __esm({
|
|
2805
|
+
"ts/sdk/dist/types.js"() {
|
|
2806
|
+
"use strict";
|
|
2807
|
+
}
|
|
2808
|
+
});
|
|
2809
|
+
|
|
2773
2810
|
// ts/sdk/dist/ws.js
|
|
2774
2811
|
function isRetryableNetworkError(err) {
|
|
2775
2812
|
return err instanceof Error && err.name === "ApiError" && "status" in err && err.status === 0;
|
|
@@ -3114,9 +3151,10 @@ var init_ws = __esm({
|
|
|
3114
3151
|
var init_dist = __esm({
|
|
3115
3152
|
"ts/sdk/dist/index.js"() {
|
|
3116
3153
|
"use strict";
|
|
3117
|
-
|
|
3118
|
-
init_constants();
|
|
3154
|
+
init_browser_viewer();
|
|
3119
3155
|
init_client();
|
|
3156
|
+
init_constants();
|
|
3157
|
+
init_types2();
|
|
3120
3158
|
init_ws();
|
|
3121
3159
|
}
|
|
3122
3160
|
});
|
|
@@ -56255,6 +56293,147 @@ var init_cli = __esm({
|
|
|
56255
56293
|
}
|
|
56256
56294
|
});
|
|
56257
56295
|
|
|
56296
|
+
// ts/daemon/dist/browser-profile-reconcile.js
|
|
56297
|
+
async function reconcileBrowserProfiles(ctx) {
|
|
56298
|
+
const pool = ctx.pool;
|
|
56299
|
+
const activeBeforeList = new Set(pool.activeProfileIds());
|
|
56300
|
+
const pendingReviveBeforeList = ctx.pendingReviveIds();
|
|
56301
|
+
const localStateBeforeList = new Set(pool.localStateProfileIds());
|
|
56302
|
+
let profiles;
|
|
56303
|
+
try {
|
|
56304
|
+
profiles = await ctx.client.listMachineBrowserProfiles();
|
|
56305
|
+
} catch (err) {
|
|
56306
|
+
ctx.log.warn(`browser profile reconcile: list failed: ${String(err)}`);
|
|
56307
|
+
return;
|
|
56308
|
+
}
|
|
56309
|
+
if (ctx.currentPool() !== pool)
|
|
56310
|
+
return;
|
|
56311
|
+
const desiredLive = /* @__PURE__ */ new Set();
|
|
56312
|
+
const serverIds = /* @__PURE__ */ new Set();
|
|
56313
|
+
const serverResetGen = /* @__PURE__ */ new Map();
|
|
56314
|
+
const ops = [];
|
|
56315
|
+
const missedResetGen = (profileId) => {
|
|
56316
|
+
const gen = serverResetGen.get(profileId) ?? 0;
|
|
56317
|
+
return gen > pool.appliedResetGeneration(profileId) ? gen : null;
|
|
56318
|
+
};
|
|
56319
|
+
const enqueueResetWipeIfMissed = (profileId) => {
|
|
56320
|
+
const gen = missedResetGen(profileId);
|
|
56321
|
+
if (gen === null)
|
|
56322
|
+
return false;
|
|
56323
|
+
pool.fence(profileId);
|
|
56324
|
+
ops.push(ctx.enqueueOp(profileId, () => pool.applyResetWipe(profileId, gen)));
|
|
56325
|
+
return true;
|
|
56326
|
+
};
|
|
56327
|
+
for (const profile of profiles) {
|
|
56328
|
+
if (!profile.machine_id)
|
|
56329
|
+
continue;
|
|
56330
|
+
serverIds.add(profile.id);
|
|
56331
|
+
serverResetGen.set(profile.id, profile.reset_generation ?? 0);
|
|
56332
|
+
if (profile.status !== "running" && profile.status !== "pending")
|
|
56333
|
+
continue;
|
|
56334
|
+
desiredLive.add(profile.id);
|
|
56335
|
+
const wipeGen = missedResetGen(profile.id);
|
|
56336
|
+
if (wipeGen !== null)
|
|
56337
|
+
pool.fence(profile.id);
|
|
56338
|
+
const sinceSeq = pool.stopSeqOf(profile.id);
|
|
56339
|
+
const generation = profile.lifecycle_generation;
|
|
56340
|
+
const forceStatusReport = profile.status === "pending";
|
|
56341
|
+
ops.push(ctx.enqueueRevive(profile.id, async () => {
|
|
56342
|
+
if (wipeGen !== null)
|
|
56343
|
+
await ctx.wipeBeforeRevive(pool, profile.id, wipeGen, generation);
|
|
56344
|
+
await pool.ensureRuntime(profile.id, sinceSeq, generation, { forceStatusReport });
|
|
56345
|
+
}));
|
|
56346
|
+
}
|
|
56347
|
+
const candidates = /* @__PURE__ */ new Set([
|
|
56348
|
+
...activeBeforeList,
|
|
56349
|
+
...pendingReviveBeforeList,
|
|
56350
|
+
...localStateBeforeList
|
|
56351
|
+
]);
|
|
56352
|
+
for (const profileId of candidates) {
|
|
56353
|
+
if (desiredLive.has(profileId))
|
|
56354
|
+
continue;
|
|
56355
|
+
const isActive = activeBeforeList.has(profileId);
|
|
56356
|
+
const hasLocalState = localStateBeforeList.has(profileId);
|
|
56357
|
+
if (!isActive && !hasLocalState) {
|
|
56358
|
+
pool.fence(profileId);
|
|
56359
|
+
let cleanup;
|
|
56360
|
+
if (!serverIds.has(profileId)) {
|
|
56361
|
+
cleanup = pool.wipeAbsent(profileId);
|
|
56362
|
+
} else {
|
|
56363
|
+
const wipeGen = missedResetGen(profileId);
|
|
56364
|
+
cleanup = wipeGen !== null ? pool.applyResetWipe(profileId, wipeGen) : pool.releaseRuntime(profileId);
|
|
56365
|
+
}
|
|
56366
|
+
void cleanup.catch((err) => ctx.log.warn(`browser profile pending-revive cleanup failed for ${profileId}: ${String(err)}`));
|
|
56367
|
+
continue;
|
|
56368
|
+
}
|
|
56369
|
+
if (!serverIds.has(profileId)) {
|
|
56370
|
+
pool.fence(profileId);
|
|
56371
|
+
ops.push(ctx.enqueueOp(profileId, () => pool.wipeAbsent(profileId)));
|
|
56372
|
+
} else if (enqueueResetWipeIfMissed(profileId)) {
|
|
56373
|
+
} else if (isActive) {
|
|
56374
|
+
pool.fence(profileId);
|
|
56375
|
+
ops.push(ctx.enqueueOp(profileId, () => pool.releaseRuntime(profileId)));
|
|
56376
|
+
}
|
|
56377
|
+
}
|
|
56378
|
+
for (const result of await Promise.allSettled(ops)) {
|
|
56379
|
+
if (result.status === "rejected") {
|
|
56380
|
+
ctx.log.warn(`browser profile reconcile op failed: ${String(result.reason)}`);
|
|
56381
|
+
}
|
|
56382
|
+
}
|
|
56383
|
+
}
|
|
56384
|
+
var init_browser_profile_reconcile = __esm({
|
|
56385
|
+
"ts/daemon/dist/browser-profile-reconcile.js"() {
|
|
56386
|
+
"use strict";
|
|
56387
|
+
}
|
|
56388
|
+
});
|
|
56389
|
+
|
|
56390
|
+
// ts/daemon/dist/clip-runtime/viewer-command-error.js
|
|
56391
|
+
function viewerCommandFailure(error) {
|
|
56392
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56393
|
+
if (error instanceof ViewerCommandError && error.code) {
|
|
56394
|
+
return { code: error.code, message };
|
|
56395
|
+
}
|
|
56396
|
+
return { message };
|
|
56397
|
+
}
|
|
56398
|
+
var ViewerCommandError;
|
|
56399
|
+
var init_viewer_command_error = __esm({
|
|
56400
|
+
"ts/daemon/dist/clip-runtime/viewer-command-error.js"() {
|
|
56401
|
+
"use strict";
|
|
56402
|
+
ViewerCommandError = class extends Error {
|
|
56403
|
+
code;
|
|
56404
|
+
constructor(message, code) {
|
|
56405
|
+
super(message);
|
|
56406
|
+
this.code = code;
|
|
56407
|
+
this.name = "ViewerCommandError";
|
|
56408
|
+
}
|
|
56409
|
+
};
|
|
56410
|
+
}
|
|
56411
|
+
});
|
|
56412
|
+
|
|
56413
|
+
// ts/daemon/dist/browser-profile-viewer-control.js
|
|
56414
|
+
async function handleBrowserProfileViewerCommand(deps, data) {
|
|
56415
|
+
if (!deps.pool) {
|
|
56416
|
+
await deps.client.postBrowserProfileViewerResponse(data.request_id, {
|
|
56417
|
+
error: { message: "Browser profile runtime is disabled on this daemon" }
|
|
56418
|
+
}).catch((err) => deps.log.warn(`browser profile viewer negative ACK failed: ${String(err)}`));
|
|
56419
|
+
return;
|
|
56420
|
+
}
|
|
56421
|
+
try {
|
|
56422
|
+
const result = await deps.pool.handleViewerCommand(data.profile_id, data.session_id, data.command, data.input, data.turn);
|
|
56423
|
+
await deps.client.postBrowserProfileViewerResponse(data.request_id, { result });
|
|
56424
|
+
} catch (err) {
|
|
56425
|
+
const failure = viewerCommandFailure(err);
|
|
56426
|
+
deps.log.warn(`browser profile viewer command failed (profile=${data.profile_id}, command=${data.command}): ${failure.message}`);
|
|
56427
|
+
await deps.client.postBrowserProfileViewerResponse(data.request_id, { error: failure }).catch((replyErr) => deps.log.warn(`browser profile viewer error ACK failed: ${String(replyErr)}`));
|
|
56428
|
+
}
|
|
56429
|
+
}
|
|
56430
|
+
var init_browser_profile_viewer_control = __esm({
|
|
56431
|
+
"ts/daemon/dist/browser-profile-viewer-control.js"() {
|
|
56432
|
+
"use strict";
|
|
56433
|
+
init_viewer_command_error();
|
|
56434
|
+
}
|
|
56435
|
+
});
|
|
56436
|
+
|
|
56258
56437
|
// ts/node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/constants.js
|
|
56259
56438
|
var require_constants9 = __commonJS({
|
|
56260
56439
|
"ts/node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/constants.js"(exports2, module2) {
|
|
@@ -62445,6 +62624,31 @@ var init_process_manager = __esm({
|
|
|
62445
62624
|
}
|
|
62446
62625
|
});
|
|
62447
62626
|
|
|
62627
|
+
// ts/daemon/dist/clip-runtime/viewer-command-deadline.js
|
|
62628
|
+
function isViewerReadinessCommand(command) {
|
|
62629
|
+
return command === "stream.answer" || command === "stream.switch";
|
|
62630
|
+
}
|
|
62631
|
+
function parseViewerDispatchDeadlineUnixMs(value) {
|
|
62632
|
+
let parsed;
|
|
62633
|
+
if (typeof value === "number") {
|
|
62634
|
+
parsed = value;
|
|
62635
|
+
} else if (typeof value === "string" && /^[1-9]\d*$/.test(value)) {
|
|
62636
|
+
parsed = Number(value);
|
|
62637
|
+
} else {
|
|
62638
|
+
return void 0;
|
|
62639
|
+
}
|
|
62640
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
62641
|
+
}
|
|
62642
|
+
function viewerReadinessDeadlineError(phase) {
|
|
62643
|
+
return new ViewerCommandError(`viewer readiness deadline elapsed ${phase}`, "VIEWER_STREAM_CANCELLED");
|
|
62644
|
+
}
|
|
62645
|
+
var init_viewer_command_deadline = __esm({
|
|
62646
|
+
"ts/daemon/dist/clip-runtime/viewer-command-deadline.js"() {
|
|
62647
|
+
"use strict";
|
|
62648
|
+
init_viewer_command_error();
|
|
62649
|
+
}
|
|
62650
|
+
});
|
|
62651
|
+
|
|
62448
62652
|
// ts/daemon/dist/clip-runtime/clip-provider.js
|
|
62449
62653
|
import * as http23 from "node:http2";
|
|
62450
62654
|
function encodeEnvelope2(msg) {
|
|
@@ -62462,8 +62666,10 @@ var ENVELOPE_FLAG_DATA2, ENVELOPE_FLAG_TRAILER2, EnvelopeDecoder2, ClipProvider;
|
|
|
62462
62666
|
var init_clip_provider = __esm({
|
|
62463
62667
|
"ts/daemon/dist/clip-runtime/clip-provider.js"() {
|
|
62464
62668
|
"use strict";
|
|
62465
|
-
init_process();
|
|
62466
62669
|
init_browser_dependency();
|
|
62670
|
+
init_process();
|
|
62671
|
+
init_viewer_command_deadline();
|
|
62672
|
+
init_viewer_command_error();
|
|
62467
62673
|
ENVELOPE_FLAG_DATA2 = 0;
|
|
62468
62674
|
ENVELOPE_FLAG_TRAILER2 = 2;
|
|
62469
62675
|
EnvelopeDecoder2 = class _EnvelopeDecoder {
|
|
@@ -62819,8 +63025,8 @@ var init_clip_provider = __esm({
|
|
|
62819
63025
|
/**
|
|
62820
63026
|
* Handle a hosted-browser live-viewer command relayed by clip-service over the
|
|
62821
63027
|
* stream and reply on the same stream (design §3.4). Always answers the
|
|
62822
|
-
* request/reply bridge exactly once — `{result}` on success,
|
|
62823
|
-
* on failure — and never throws into the message loop, mirroring the BYOC
|
|
63028
|
+
* request/reply bridge exactly once — `{result}` on success,
|
|
63029
|
+
* `{error:{code?,message}}` on failure — and never throws into the message loop, mirroring the BYOC
|
|
62824
63030
|
* daemon's handleBrowserProfileViewer (which replies over HTTP instead).
|
|
62825
63031
|
*/
|
|
62826
63032
|
async handleViewerCommand(cmd) {
|
|
@@ -62833,6 +63039,10 @@ var init_clip_provider = __esm({
|
|
|
62833
63039
|
return;
|
|
62834
63040
|
}
|
|
62835
63041
|
try {
|
|
63042
|
+
const dispatchDeadlineUnixMs = parseViewerDispatchDeadlineUnixMs(cmd.dispatchDeadlineUnixMs);
|
|
63043
|
+
if (isViewerReadinessCommand(cmd.command) && dispatchDeadlineUnixMs === void 0) {
|
|
63044
|
+
throw viewerReadinessDeadlineError("because the ProviderStream command has no authenticated deadline");
|
|
63045
|
+
}
|
|
62836
63046
|
let input;
|
|
62837
63047
|
if (cmd.input) {
|
|
62838
63048
|
const decoded = Buffer.from(cmd.input, "base64").toString("utf-8");
|
|
@@ -62847,13 +63057,14 @@ var init_clip_provider = __esm({
|
|
|
62847
63057
|
sessionId: cmd.sessionId,
|
|
62848
63058
|
command: cmd.command,
|
|
62849
63059
|
input,
|
|
62850
|
-
turn: cmd.turn
|
|
63060
|
+
turn: cmd.turn,
|
|
63061
|
+
dispatchDeadlineUnixMs
|
|
62851
63062
|
});
|
|
62852
63063
|
await this.sendViewerReply(requestId, { result });
|
|
62853
63064
|
} catch (err) {
|
|
62854
|
-
const
|
|
62855
|
-
this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${message}`);
|
|
62856
|
-
await this.sendViewerReply(requestId, { error:
|
|
63065
|
+
const failure = viewerCommandFailure(err);
|
|
63066
|
+
this.opts.log.warn(`[clip-provider] viewer command failed (profile=${cmd.profileId}, command=${cmd.command}): ${failure.message}`);
|
|
63067
|
+
await this.sendViewerReply(requestId, { error: failure }).catch(() => {
|
|
62857
63068
|
});
|
|
62858
63069
|
}
|
|
62859
63070
|
}
|
|
@@ -63234,6 +63445,80 @@ var init_browser_target_registry = __esm({
|
|
|
63234
63445
|
}
|
|
63235
63446
|
});
|
|
63236
63447
|
|
|
63448
|
+
// ts/daemon/dist/clip-runtime/bb-viewer-command-client.js
|
|
63449
|
+
function bbViewerCommandTimeoutMs(method) {
|
|
63450
|
+
return method === "answer" || method === "switch" ? BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS : BB_VIEWER_COMMAND_TIMEOUT_MS;
|
|
63451
|
+
}
|
|
63452
|
+
function bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs, now = Date.now()) {
|
|
63453
|
+
const configured = bbViewerCommandTimeoutMs(method);
|
|
63454
|
+
if (deadlineUnixMs === void 0)
|
|
63455
|
+
return configured;
|
|
63456
|
+
const remaining = deadlineUnixMs - now;
|
|
63457
|
+
if (remaining <= 0) {
|
|
63458
|
+
throw viewerReadinessDeadlineError(`before bb-viewer ${method}`);
|
|
63459
|
+
}
|
|
63460
|
+
return Math.max(1, Math.min(configured, remaining));
|
|
63461
|
+
}
|
|
63462
|
+
async function sendBBViewerCommand(port, method, params, deadlineUnixMs) {
|
|
63463
|
+
const controller = new AbortController();
|
|
63464
|
+
const timeoutMs = bbViewerCommandEffectiveTimeoutMs(method, deadlineUnixMs);
|
|
63465
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
63466
|
+
try {
|
|
63467
|
+
const resp = await fetch(`http://127.0.0.1:${port}/command`, {
|
|
63468
|
+
method: "POST",
|
|
63469
|
+
headers: { "Content-Type": "application/json" },
|
|
63470
|
+
body: JSON.stringify({ method, params }),
|
|
63471
|
+
signal: controller.signal
|
|
63472
|
+
});
|
|
63473
|
+
const text = await resp.text();
|
|
63474
|
+
return parseBBViewerCommandResponse(resp.status, text, method);
|
|
63475
|
+
} catch (err) {
|
|
63476
|
+
if (controller.signal.aborted && (method === "answer" || method === "switch")) {
|
|
63477
|
+
throw viewerReadinessDeadlineError(`while waiting for bb-viewer ${method}`);
|
|
63478
|
+
}
|
|
63479
|
+
throw err;
|
|
63480
|
+
} finally {
|
|
63481
|
+
clearTimeout(timer);
|
|
63482
|
+
}
|
|
63483
|
+
}
|
|
63484
|
+
function parseBBViewerCommandResponse(status, text, method) {
|
|
63485
|
+
let parsed;
|
|
63486
|
+
try {
|
|
63487
|
+
parsed = text ? JSON.parse(text) : {};
|
|
63488
|
+
} catch {
|
|
63489
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned invalid JSON`);
|
|
63490
|
+
}
|
|
63491
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
63492
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned an invalid response`);
|
|
63493
|
+
}
|
|
63494
|
+
const envelope = parsed;
|
|
63495
|
+
if (envelope.error) {
|
|
63496
|
+
const code = typeof envelope.error.code === "string" && envelope.error.code.trim() ? envelope.error.code.trim() : void 0;
|
|
63497
|
+
const message = typeof envelope.error.message === "string" && envelope.error.message.trim() ? envelope.error.message : `bb-viewer ${method} failed`;
|
|
63498
|
+
throw new ViewerCommandError(message, code);
|
|
63499
|
+
}
|
|
63500
|
+
if (status < 200 || status >= 300) {
|
|
63501
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned ${status}`);
|
|
63502
|
+
}
|
|
63503
|
+
if (envelope.result === void 0 || envelope.result === null) {
|
|
63504
|
+
return {};
|
|
63505
|
+
}
|
|
63506
|
+
if (typeof envelope.result !== "object" || Array.isArray(envelope.result)) {
|
|
63507
|
+
throw new ViewerCommandError(`bb-viewer ${method} returned an invalid result`);
|
|
63508
|
+
}
|
|
63509
|
+
return envelope.result;
|
|
63510
|
+
}
|
|
63511
|
+
var BB_VIEWER_COMMAND_TIMEOUT_MS, BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS;
|
|
63512
|
+
var init_bb_viewer_command_client = __esm({
|
|
63513
|
+
"ts/daemon/dist/clip-runtime/bb-viewer-command-client.js"() {
|
|
63514
|
+
"use strict";
|
|
63515
|
+
init_viewer_command_deadline();
|
|
63516
|
+
init_viewer_command_error();
|
|
63517
|
+
BB_VIEWER_COMMAND_TIMEOUT_MS = 15e3;
|
|
63518
|
+
BB_VIEWER_READINESS_COMMAND_TIMEOUT_MS = 3e4;
|
|
63519
|
+
}
|
|
63520
|
+
});
|
|
63521
|
+
|
|
63237
63522
|
// ts/daemon/dist/clip-runtime/browser-viewer-streamer.js
|
|
63238
63523
|
import { spawn as spawn3 } from "node:child_process";
|
|
63239
63524
|
function isLoopbackBindHost(host) {
|
|
@@ -63277,13 +63562,15 @@ function filterTabListToAccount(result, account) {
|
|
|
63277
63562
|
const owned = tabs.filter((t) => t?.account === account);
|
|
63278
63563
|
return { ...result, tabs: owned, activeIndex: owned.findIndex((t) => t?.active === true) };
|
|
63279
63564
|
}
|
|
63280
|
-
var BB_VIEWER_HEALTH_TIMEOUT_MS,
|
|
63565
|
+
var BB_VIEWER_HEALTH_TIMEOUT_MS, BB_VIEWER_UNANSWERED_REAP_MS, BB_VIEWER_TERM_GRACE_MS, BrowserViewerStreamer, REFLESS_PIN_NAV, VIEWER_NAVIGABLE_PROTOCOLS;
|
|
63281
63566
|
var init_browser_viewer_streamer = __esm({
|
|
63282
63567
|
"ts/daemon/dist/clip-runtime/browser-viewer-streamer.js"() {
|
|
63283
63568
|
"use strict";
|
|
63569
|
+
init_bb_viewer_command_client();
|
|
63284
63570
|
init_subprocess();
|
|
63571
|
+
init_viewer_command_deadline();
|
|
63572
|
+
init_bb_viewer_command_client();
|
|
63285
63573
|
BB_VIEWER_HEALTH_TIMEOUT_MS = 1e4;
|
|
63286
|
-
BB_VIEWER_COMMAND_TIMEOUT_MS = 15e3;
|
|
63287
63574
|
BB_VIEWER_UNANSWERED_REAP_MS = 9e4;
|
|
63288
63575
|
BB_VIEWER_TERM_GRACE_MS = 5e3;
|
|
63289
63576
|
BrowserViewerStreamer = class {
|
|
@@ -63312,7 +63599,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63312
63599
|
* partial result. `data.input` / `data.turn` / `data.session_id` come from
|
|
63313
63600
|
* MachineBrowserProfileViewerData.
|
|
63314
63601
|
*/
|
|
63315
|
-
async handleViewerCommand(profileId, sessionId, command, input, turn) {
|
|
63602
|
+
async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
|
|
63316
63603
|
if (!profileId)
|
|
63317
63604
|
throw new Error("browser profile id is required");
|
|
63318
63605
|
if (command !== "stream.start" && sessionId && this.kickedSessions.get(profileId) === sessionId) {
|
|
@@ -63322,11 +63609,11 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63322
63609
|
case "stream.start":
|
|
63323
63610
|
return this.viewerStreamStart(profileId, sessionId, turn);
|
|
63324
63611
|
case "stream.answer":
|
|
63325
|
-
return this.viewerStreamAnswer(profileId, sessionId, input);
|
|
63612
|
+
return this.viewerStreamAnswer(profileId, sessionId, input, execution?.deadlineUnixMs);
|
|
63326
63613
|
case "stream.close":
|
|
63327
63614
|
return this.viewerStreamClose(profileId, sessionId, input);
|
|
63328
63615
|
case "stream.switch":
|
|
63329
|
-
return this.viewerStreamSwitch(profileId, sessionId, input);
|
|
63616
|
+
return this.viewerStreamSwitch(profileId, sessionId, input, execution?.deadlineUnixMs);
|
|
63330
63617
|
case "kick":
|
|
63331
63618
|
return this.viewerKick(profileId, sessionId, input);
|
|
63332
63619
|
case "close":
|
|
@@ -63378,7 +63665,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63378
63665
|
await this.bringTargetToFront(cdpUrl);
|
|
63379
63666
|
const streamer = await this.spawnStreamer(profileId, sessionId, turn);
|
|
63380
63667
|
try {
|
|
63381
|
-
const result = await
|
|
63668
|
+
const result = await sendBBViewerCommand(streamer.port, "connect", { cdpUrl });
|
|
63382
63669
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
63383
63670
|
const reapTimer = setTimeout(() => {
|
|
63384
63671
|
const current = this.streamers.get(profileId);
|
|
@@ -63399,14 +63686,17 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63399
63686
|
* Rejects if the streamer's sessionId no longer matches (a stale viewer
|
|
63400
63687
|
* session from a superseded stream.start).
|
|
63401
63688
|
*/
|
|
63402
|
-
async viewerStreamAnswer(profileId, sessionId, input) {
|
|
63689
|
+
async viewerStreamAnswer(profileId, sessionId, input, deadlineUnixMs) {
|
|
63403
63690
|
const streamer = this.streamers.get(profileId);
|
|
63404
63691
|
if (!streamer)
|
|
63405
63692
|
throw new Error("no active viewer streamer for profile");
|
|
63406
63693
|
if (streamer.sessionId !== sessionId)
|
|
63407
63694
|
throw new Error("stale viewer session");
|
|
63695
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "before bb-viewer answer");
|
|
63408
63696
|
streamer.answered = true;
|
|
63409
|
-
|
|
63697
|
+
const result = await sendBBViewerCommand(streamer.port, "answer", input ?? {}, deadlineUnixMs);
|
|
63698
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "after bb-viewer answer");
|
|
63699
|
+
return result;
|
|
63410
63700
|
}
|
|
63411
63701
|
/**
|
|
63412
63702
|
* stream.close — best-effort stop the streamer, then kill + remove it. A
|
|
@@ -63427,7 +63717,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63427
63717
|
return { ok: true, stale: true };
|
|
63428
63718
|
}
|
|
63429
63719
|
try {
|
|
63430
|
-
await
|
|
63720
|
+
await sendBBViewerCommand(streamer.port, "stop", {});
|
|
63431
63721
|
} catch (err) {
|
|
63432
63722
|
this.host.log.warn(`[bb-viewer] stop command failed for ${profileId} (killing anyway): ${formatErrorForLog(err)}`);
|
|
63433
63723
|
}
|
|
@@ -63444,7 +63734,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63444
63734
|
* the stream.start response), so a session-less switch is by definition not
|
|
63445
63735
|
* the current viewer.
|
|
63446
63736
|
*/
|
|
63447
|
-
async viewerStreamSwitch(profileId, sessionId, input) {
|
|
63737
|
+
async viewerStreamSwitch(profileId, sessionId, input, deadlineUnixMs) {
|
|
63448
63738
|
const streamer = this.streamers.get(profileId);
|
|
63449
63739
|
if (!streamer)
|
|
63450
63740
|
throw new Error("no active viewer streamer for profile");
|
|
@@ -63454,9 +63744,13 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63454
63744
|
if (tab === void 0 || tab === null || tab === "") {
|
|
63455
63745
|
throw new Error("stream.switch requires input.tab");
|
|
63456
63746
|
}
|
|
63747
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "before resolving the switch target");
|
|
63457
63748
|
const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId, tab), `viewer stream.switch ${profileId}`);
|
|
63458
|
-
|
|
63459
|
-
|
|
63749
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "after resolving the switch target");
|
|
63750
|
+
await this.bringTargetToFront(cdpUrl, deadlineUnixMs);
|
|
63751
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "before bb-viewer switch");
|
|
63752
|
+
const result = await sendBBViewerCommand(streamer.port, "switch", { cdpUrl }, deadlineUnixMs);
|
|
63753
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "after bb-viewer switch");
|
|
63460
63754
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
63461
63755
|
return { ...result, streamed_tab_id: streamer.streamedTabId };
|
|
63462
63756
|
}
|
|
@@ -63480,7 +63774,7 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63480
63774
|
}
|
|
63481
63775
|
const cdpUrl = await this.host.withDaemonRecovery(() => this.resolveAccountPageCdpUrl(profileId), `viewer close re-switch ${profileId}`);
|
|
63482
63776
|
await this.bringTargetToFront(cdpUrl);
|
|
63483
|
-
const sw = await
|
|
63777
|
+
const sw = await sendBBViewerCommand(streamer.port, "switch", { cdpUrl });
|
|
63484
63778
|
streamer.streamedTabId = targetIdFromCdpUrl(cdpUrl);
|
|
63485
63779
|
return { ...result, ...sw, streamed_tab_id: streamer.streamedTabId };
|
|
63486
63780
|
}
|
|
@@ -63492,15 +63786,17 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63492
63786
|
* must not block the stream. host:port + targetId are parsed from the cdpUrl
|
|
63493
63787
|
* (`ws://host:port/devtools/page/<targetId>`) to avoid an extra /status call.
|
|
63494
63788
|
*/
|
|
63495
|
-
async bringTargetToFront(cdpUrl) {
|
|
63789
|
+
async bringTargetToFront(cdpUrl, deadlineUnixMs) {
|
|
63496
63790
|
const m = /^ws:\/\/([^/]+)\/devtools\/page\/(.+)$/.exec(cdpUrl);
|
|
63497
63791
|
if (!m)
|
|
63498
63792
|
return;
|
|
63499
63793
|
const [, hostPort, targetId] = m;
|
|
63794
|
+
this.assertViewerExecutionDeadline(deadlineUnixMs, "before foregrounding the switch target");
|
|
63795
|
+
const timeoutMs = deadlineUnixMs === void 0 ? 5e3 : Math.max(1, Math.min(5e3, deadlineUnixMs - Date.now()));
|
|
63500
63796
|
try {
|
|
63501
63797
|
const resp = await fetch(`http://${hostPort}/json/activate/${targetId}`, {
|
|
63502
63798
|
method: "GET",
|
|
63503
|
-
signal: AbortSignal.timeout(
|
|
63799
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
63504
63800
|
});
|
|
63505
63801
|
if (!resp.ok) {
|
|
63506
63802
|
this.host.log.warn(`[bb-viewer] activate target ${targetId} returned ${resp.status} (continuing)`);
|
|
@@ -63509,6 +63805,11 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63509
63805
|
this.host.log.warn(`[bb-viewer] activate target ${targetId} failed (continuing): ${formatErrorForLog(err)}`);
|
|
63510
63806
|
}
|
|
63511
63807
|
}
|
|
63808
|
+
assertViewerExecutionDeadline(deadlineUnixMs, phase) {
|
|
63809
|
+
if (deadlineUnixMs !== void 0 && Date.now() >= deadlineUnixMs) {
|
|
63810
|
+
throw viewerReadinessDeadlineError(phase);
|
|
63811
|
+
}
|
|
63812
|
+
}
|
|
63512
63813
|
/**
|
|
63513
63814
|
* Forward a navigation command (tab_list/tab_new/open/reload/back/forward/
|
|
63514
63815
|
* close) to bb-browser scoped to the profile's account. This lets the human
|
|
@@ -63709,34 +64010,6 @@ var init_browser_viewer_streamer = __esm({
|
|
|
63709
64010
|
killTimer.unref?.();
|
|
63710
64011
|
child.once("exit", () => clearTimeout(killTimer));
|
|
63711
64012
|
}
|
|
63712
|
-
/**
|
|
63713
|
-
* POST a command to the profile's bb-viewer `/command` endpoint. Parses the
|
|
63714
|
-
* `{result}` / `{error:{message}}` envelope (bb-viewer api.go) and returns
|
|
63715
|
-
* the inner result, throwing the error message on failure.
|
|
63716
|
-
*/
|
|
63717
|
-
async streamerCommand(streamer, method, params) {
|
|
63718
|
-
const controller = new AbortController();
|
|
63719
|
-
const timer = setTimeout(() => controller.abort(), BB_VIEWER_COMMAND_TIMEOUT_MS);
|
|
63720
|
-
try {
|
|
63721
|
-
const resp = await fetch(`http://127.0.0.1:${streamer.port}/command`, {
|
|
63722
|
-
method: "POST",
|
|
63723
|
-
headers: { "Content-Type": "application/json" },
|
|
63724
|
-
body: JSON.stringify({ method, params }),
|
|
63725
|
-
signal: controller.signal
|
|
63726
|
-
});
|
|
63727
|
-
const text = await resp.text();
|
|
63728
|
-
const parsed = text ? JSON.parse(text) : {};
|
|
63729
|
-
if (parsed.error) {
|
|
63730
|
-
throw new Error(parsed.error.message || `bb-viewer ${method} failed`);
|
|
63731
|
-
}
|
|
63732
|
-
if (!resp.ok) {
|
|
63733
|
-
throw new Error(`bb-viewer ${method} returned ${resp.status}: ${text}`);
|
|
63734
|
-
}
|
|
63735
|
-
return parsed.result ?? {};
|
|
63736
|
-
} finally {
|
|
63737
|
-
clearTimeout(timer);
|
|
63738
|
-
}
|
|
63739
|
-
}
|
|
63740
64013
|
/**
|
|
63741
64014
|
* Kill + remove the profile's bb-viewer streamer, if any. Idempotent. Also
|
|
63742
64015
|
* clears any kick marker for the profile: a (re)kill establishes a clean
|
|
@@ -64032,8 +64305,8 @@ var init_browser_profile_manager = __esm({
|
|
|
64032
64305
|
* (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
|
|
64033
64306
|
* subprocesses and viewer tab navigation.
|
|
64034
64307
|
*/
|
|
64035
|
-
async handleViewerCommand(profileId, sessionId, command, input, turn) {
|
|
64036
|
-
return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn);
|
|
64308
|
+
async handleViewerCommand(profileId, sessionId, command, input, turn, execution) {
|
|
64309
|
+
return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn, execution);
|
|
64037
64310
|
}
|
|
64038
64311
|
/** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
|
|
64039
64312
|
async cdpEndpoint() {
|
|
@@ -64991,104 +65264,11 @@ var init_clip_runtime = __esm({
|
|
|
64991
65264
|
init_hub_client();
|
|
64992
65265
|
init_browser_profile_manager();
|
|
64993
65266
|
init_browser_profile_pool();
|
|
65267
|
+
init_viewer_command_error();
|
|
64994
65268
|
init_clip_installer();
|
|
64995
65269
|
}
|
|
64996
65270
|
});
|
|
64997
65271
|
|
|
64998
|
-
// ts/daemon/dist/browser-profile-reconcile.js
|
|
64999
|
-
async function reconcileBrowserProfiles(ctx) {
|
|
65000
|
-
const pool = ctx.pool;
|
|
65001
|
-
const activeBeforeList = new Set(pool.activeProfileIds());
|
|
65002
|
-
const pendingReviveBeforeList = ctx.pendingReviveIds();
|
|
65003
|
-
const localStateBeforeList = new Set(pool.localStateProfileIds());
|
|
65004
|
-
let profiles;
|
|
65005
|
-
try {
|
|
65006
|
-
profiles = await ctx.client.listMachineBrowserProfiles();
|
|
65007
|
-
} catch (err) {
|
|
65008
|
-
ctx.log.warn(`browser profile reconcile: list failed: ${String(err)}`);
|
|
65009
|
-
return;
|
|
65010
|
-
}
|
|
65011
|
-
if (ctx.currentPool() !== pool)
|
|
65012
|
-
return;
|
|
65013
|
-
const desiredLive = /* @__PURE__ */ new Set();
|
|
65014
|
-
const serverIds = /* @__PURE__ */ new Set();
|
|
65015
|
-
const serverResetGen = /* @__PURE__ */ new Map();
|
|
65016
|
-
const ops = [];
|
|
65017
|
-
const missedResetGen = (profileId) => {
|
|
65018
|
-
const gen = serverResetGen.get(profileId) ?? 0;
|
|
65019
|
-
return gen > pool.appliedResetGeneration(profileId) ? gen : null;
|
|
65020
|
-
};
|
|
65021
|
-
const enqueueResetWipeIfMissed = (profileId) => {
|
|
65022
|
-
const gen = missedResetGen(profileId);
|
|
65023
|
-
if (gen === null)
|
|
65024
|
-
return false;
|
|
65025
|
-
pool.fence(profileId);
|
|
65026
|
-
ops.push(ctx.enqueueOp(profileId, () => pool.applyResetWipe(profileId, gen)));
|
|
65027
|
-
return true;
|
|
65028
|
-
};
|
|
65029
|
-
for (const profile of profiles) {
|
|
65030
|
-
if (!profile.machine_id)
|
|
65031
|
-
continue;
|
|
65032
|
-
serverIds.add(profile.id);
|
|
65033
|
-
serverResetGen.set(profile.id, profile.reset_generation ?? 0);
|
|
65034
|
-
if (profile.status !== "running" && profile.status !== "pending")
|
|
65035
|
-
continue;
|
|
65036
|
-
desiredLive.add(profile.id);
|
|
65037
|
-
const wipeGen = missedResetGen(profile.id);
|
|
65038
|
-
if (wipeGen !== null)
|
|
65039
|
-
pool.fence(profile.id);
|
|
65040
|
-
const sinceSeq = pool.stopSeqOf(profile.id);
|
|
65041
|
-
const generation = profile.lifecycle_generation;
|
|
65042
|
-
const forceStatusReport = profile.status === "pending";
|
|
65043
|
-
ops.push(ctx.enqueueRevive(profile.id, async () => {
|
|
65044
|
-
if (wipeGen !== null)
|
|
65045
|
-
await ctx.wipeBeforeRevive(pool, profile.id, wipeGen, generation);
|
|
65046
|
-
await pool.ensureRuntime(profile.id, sinceSeq, generation, { forceStatusReport });
|
|
65047
|
-
}));
|
|
65048
|
-
}
|
|
65049
|
-
const candidates = /* @__PURE__ */ new Set([
|
|
65050
|
-
...activeBeforeList,
|
|
65051
|
-
...pendingReviveBeforeList,
|
|
65052
|
-
...localStateBeforeList
|
|
65053
|
-
]);
|
|
65054
|
-
for (const profileId of candidates) {
|
|
65055
|
-
if (desiredLive.has(profileId))
|
|
65056
|
-
continue;
|
|
65057
|
-
const isActive = activeBeforeList.has(profileId);
|
|
65058
|
-
const hasLocalState = localStateBeforeList.has(profileId);
|
|
65059
|
-
if (!isActive && !hasLocalState) {
|
|
65060
|
-
pool.fence(profileId);
|
|
65061
|
-
let cleanup;
|
|
65062
|
-
if (!serverIds.has(profileId)) {
|
|
65063
|
-
cleanup = pool.wipeAbsent(profileId);
|
|
65064
|
-
} else {
|
|
65065
|
-
const wipeGen = missedResetGen(profileId);
|
|
65066
|
-
cleanup = wipeGen !== null ? pool.applyResetWipe(profileId, wipeGen) : pool.releaseRuntime(profileId);
|
|
65067
|
-
}
|
|
65068
|
-
void cleanup.catch((err) => ctx.log.warn(`browser profile pending-revive cleanup failed for ${profileId}: ${String(err)}`));
|
|
65069
|
-
continue;
|
|
65070
|
-
}
|
|
65071
|
-
if (!serverIds.has(profileId)) {
|
|
65072
|
-
pool.fence(profileId);
|
|
65073
|
-
ops.push(ctx.enqueueOp(profileId, () => pool.wipeAbsent(profileId)));
|
|
65074
|
-
} else if (enqueueResetWipeIfMissed(profileId)) {
|
|
65075
|
-
} else if (isActive) {
|
|
65076
|
-
pool.fence(profileId);
|
|
65077
|
-
ops.push(ctx.enqueueOp(profileId, () => pool.releaseRuntime(profileId)));
|
|
65078
|
-
}
|
|
65079
|
-
}
|
|
65080
|
-
for (const result of await Promise.allSettled(ops)) {
|
|
65081
|
-
if (result.status === "rejected") {
|
|
65082
|
-
ctx.log.warn(`browser profile reconcile op failed: ${String(result.reason)}`);
|
|
65083
|
-
}
|
|
65084
|
-
}
|
|
65085
|
-
}
|
|
65086
|
-
var init_browser_profile_reconcile = __esm({
|
|
65087
|
-
"ts/daemon/dist/browser-profile-reconcile.js"() {
|
|
65088
|
-
"use strict";
|
|
65089
|
-
}
|
|
65090
|
-
});
|
|
65091
|
-
|
|
65092
65272
|
// ts/daemon/dist/filesystem.js
|
|
65093
65273
|
import * as fs9 from "fs";
|
|
65094
65274
|
import * as path16 from "path";
|
|
@@ -66444,12 +66624,13 @@ var init_supervisor = __esm({
|
|
|
66444
66624
|
"use strict";
|
|
66445
66625
|
init_dist2();
|
|
66446
66626
|
init_dist();
|
|
66627
|
+
init_browser_profile_reconcile();
|
|
66628
|
+
init_browser_profile_viewer_control();
|
|
66447
66629
|
init_browser_readiness();
|
|
66448
66630
|
init_clip_installer();
|
|
66449
66631
|
init_manifest();
|
|
66450
66632
|
init_clip_runtime();
|
|
66451
66633
|
init_config();
|
|
66452
|
-
init_browser_profile_reconcile();
|
|
66453
66634
|
init_filesystem();
|
|
66454
66635
|
init_local_profile_control();
|
|
66455
66636
|
init_home_isolation();
|
|
@@ -66693,7 +66874,7 @@ var init_supervisor = __esm({
|
|
|
66693
66874
|
});
|
|
66694
66875
|
this.ws.on("machine.browser_profile.viewer", (data) => {
|
|
66695
66876
|
this.log.info(`WS: browser profile viewer command \u2014 profile=${data.profile_id} command=${data.command}`);
|
|
66696
|
-
this.
|
|
66877
|
+
void this.enqueueBrowserProfileOp(data.profile_id, () => handleBrowserProfileViewerCommand({ pool: this.browserProfilePool, client: this.client, log: this.log }, data));
|
|
66697
66878
|
});
|
|
66698
66879
|
this.ws.onStateChange((state) => {
|
|
66699
66880
|
if (state === "disconnected" || state === "reconnecting") {
|
|
@@ -67156,31 +67337,6 @@ var init_supervisor = __esm({
|
|
|
67156
67337
|
this.browserProfilePendingRevives.set(profileId, remaining);
|
|
67157
67338
|
});
|
|
67158
67339
|
}
|
|
67159
|
-
/**
|
|
67160
|
-
* Handle one viewer control command and reply via REST. Always answers the
|
|
67161
|
-
* request/reply bridge exactly once ({result} on success, {error:{message}}
|
|
67162
|
-
* on failure) and never throws into the WS event loop — mirrors
|
|
67163
|
-
* handleFilesystemBrowse. Serialized per profile by enqueueBrowserProfileViewer.
|
|
67164
|
-
*/
|
|
67165
|
-
async handleBrowserProfileViewer(data) {
|
|
67166
|
-
if (!this.browserProfilePool) {
|
|
67167
|
-
await this.client.postBrowserProfileViewerResponse(data.request_id, {
|
|
67168
|
-
error: { message: "Browser profile runtime is disabled on this daemon" }
|
|
67169
|
-
}).catch((err) => this.log.warn(`browser profile viewer negative ACK failed: ${String(err)}`));
|
|
67170
|
-
return;
|
|
67171
|
-
}
|
|
67172
|
-
try {
|
|
67173
|
-
const result = await this.browserProfilePool.handleViewerCommand(data.profile_id, data.session_id, data.command, data.input, data.turn);
|
|
67174
|
-
await this.client.postBrowserProfileViewerResponse(data.request_id, { result });
|
|
67175
|
-
} catch (err) {
|
|
67176
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
67177
|
-
this.log.warn(`browser profile viewer command failed (profile=${data.profile_id}, command=${data.command}): ${message}`);
|
|
67178
|
-
await this.client.postBrowserProfileViewerResponse(data.request_id, { error: { message } }).catch((replyErr) => this.log.warn(`browser profile viewer error ACK failed: ${String(replyErr)}`));
|
|
67179
|
-
}
|
|
67180
|
-
}
|
|
67181
|
-
enqueueBrowserProfileViewer(data) {
|
|
67182
|
-
void this.enqueueBrowserProfileOp(data.profile_id, () => this.handleBrowserProfileViewer(data));
|
|
67183
|
-
}
|
|
67184
67340
|
async handleClipSync(_data) {
|
|
67185
67341
|
await this.reconcileMachineClips();
|
|
67186
67342
|
}
|