@zixt/host 0.0.156 → 0.0.158
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/dist/index.js +427 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ import { homedir as homedir6 } from "node:os";
|
|
|
28
28
|
// package.json
|
|
29
29
|
var package_default = {
|
|
30
30
|
name: "@zixt/host",
|
|
31
|
-
version: "0.0.
|
|
31
|
+
version: "0.0.158",
|
|
32
32
|
type: "module",
|
|
33
33
|
exports: {
|
|
34
34
|
".": "./src/client.ts",
|
|
@@ -14881,6 +14881,23 @@ var BrowserFramePayload = external_exports.object({
|
|
|
14881
14881
|
width: external_exports.number().int().min(1).max(3840),
|
|
14882
14882
|
height: external_exports.number().int().min(1).max(2160)
|
|
14883
14883
|
}).strict();
|
|
14884
|
+
var BrowserCookie = external_exports.object({
|
|
14885
|
+
name: external_exports.string().min(1).max(256),
|
|
14886
|
+
value: external_exports.string().max(8192),
|
|
14887
|
+
domain: external_exports.string().min(1).max(253),
|
|
14888
|
+
path: external_exports.string().min(1).max(2048),
|
|
14889
|
+
expires: external_exports.number().finite().min(-1).max(253402300799),
|
|
14890
|
+
httpOnly: external_exports.boolean(),
|
|
14891
|
+
secure: external_exports.boolean(),
|
|
14892
|
+
sameSite: external_exports.enum(["Strict", "Lax", "None"]),
|
|
14893
|
+
/** Chromium partition key for CHIPS cookies, when present. */
|
|
14894
|
+
partitionKey: external_exports.string().min(1).max(2048).optional()
|
|
14895
|
+
}).strict();
|
|
14896
|
+
var BROWSER_MAX_SYNCED_COOKIES = 512;
|
|
14897
|
+
var BrowserCookieState = external_exports.object({
|
|
14898
|
+
revision: external_exports.number().int().min(0),
|
|
14899
|
+
cookies: external_exports.array(BrowserCookie).max(BROWSER_MAX_SYNCED_COOKIES)
|
|
14900
|
+
}).strict();
|
|
14884
14901
|
var inputCoordinate = external_exports.number().min(0).max(16384);
|
|
14885
14902
|
var BrowserMouseButton = external_exports.enum(["left", "middle", "right"]);
|
|
14886
14903
|
var BrowserInputEvent = external_exports.discriminatedUnion("kind", [
|
|
@@ -14940,7 +14957,7 @@ var BrowserSessionProjection = external_exports.object({
|
|
|
14940
14957
|
canStart: external_exports.boolean(),
|
|
14941
14958
|
/** Member-safe reason when canStart is false. */
|
|
14942
14959
|
reason: external_exports.string().max(300).optional(),
|
|
14943
|
-
/** Present only when active work
|
|
14960
|
+
/** Present only when active work uses a different local profile Machine. */
|
|
14944
14961
|
profileContext: BrowserProfileContext.optional()
|
|
14945
14962
|
}).strict();
|
|
14946
14963
|
var StartBrowserSessionRequest = external_exports.object({ url: external_exports.string().min(1).max(2e3).optional() }).strict();
|
|
@@ -18338,7 +18355,7 @@ var ListTasksResponse = external_exports.object({
|
|
|
18338
18355
|
}).strict();
|
|
18339
18356
|
|
|
18340
18357
|
// ../../packages/contracts/src/protocol.ts
|
|
18341
|
-
var PROTOCOL_VERSION =
|
|
18358
|
+
var PROTOCOL_VERSION = 14;
|
|
18342
18359
|
var BROWSER_PROFILE_INVENTORY_PAGE_SIZE = 200;
|
|
18343
18360
|
var HELLO_UNWOUND_ASSIGNMENT_LIMIT = 1e3;
|
|
18344
18361
|
var TASK_CANCEL_ACK_EVENT = "zixt.task.cancel.acknowledged";
|
|
@@ -19512,6 +19529,44 @@ var BrowserSessionEndedFrame = external_exports.object({
|
|
|
19512
19529
|
agentId: AgentId,
|
|
19513
19530
|
reason: BrowserSessionEndReason
|
|
19514
19531
|
});
|
|
19532
|
+
var BrowserCookieSyncFrame = external_exports.object({
|
|
19533
|
+
type: external_exports.literal("browser.cookies.sync"),
|
|
19534
|
+
requestId: external_exports.string().min(1).max(200),
|
|
19535
|
+
projectId: ProjectId,
|
|
19536
|
+
taskId: TaskId,
|
|
19537
|
+
agentId: AgentId,
|
|
19538
|
+
browserSessionId: external_exports.string().min(1).max(200),
|
|
19539
|
+
/** Omitted only by a Machine that has not received the canonical jar yet. */
|
|
19540
|
+
baseRevision: external_exports.number().int().min(0).optional(),
|
|
19541
|
+
cookies: external_exports.array(BrowserCookie).max(BROWSER_MAX_SYNCED_COOKIES)
|
|
19542
|
+
}).strict();
|
|
19543
|
+
var BrowserCookieSyncResultFrame = external_exports.discriminatedUnion("ok", [
|
|
19544
|
+
external_exports.object({
|
|
19545
|
+
type: external_exports.literal("browser.cookies.result"),
|
|
19546
|
+
requestId: external_exports.string().min(1).max(200),
|
|
19547
|
+
projectId: ProjectId,
|
|
19548
|
+
taskId: TaskId,
|
|
19549
|
+
agentId: AgentId,
|
|
19550
|
+
browserSessionId: external_exports.string().min(1).max(200),
|
|
19551
|
+
ok: external_exports.literal(true),
|
|
19552
|
+
/** False asks the Host to reconcile its local changes over this newer jar. */
|
|
19553
|
+
accepted: external_exports.boolean(),
|
|
19554
|
+
state: BrowserCookieState,
|
|
19555
|
+
error: external_exports.never().optional()
|
|
19556
|
+
}).strict(),
|
|
19557
|
+
external_exports.object({
|
|
19558
|
+
type: external_exports.literal("browser.cookies.result"),
|
|
19559
|
+
requestId: external_exports.string().min(1).max(200),
|
|
19560
|
+
projectId: ProjectId,
|
|
19561
|
+
taskId: TaskId,
|
|
19562
|
+
agentId: AgentId,
|
|
19563
|
+
browserSessionId: external_exports.string().min(1).max(200),
|
|
19564
|
+
ok: external_exports.literal(false),
|
|
19565
|
+
accepted: external_exports.never().optional(),
|
|
19566
|
+
state: external_exports.never().optional(),
|
|
19567
|
+
error: external_exports.string().min(1).max(300)
|
|
19568
|
+
}).strict()
|
|
19569
|
+
]);
|
|
19515
19570
|
var BrowserCredentialRequestFrame = external_exports.object({
|
|
19516
19571
|
type: external_exports.literal("browser.credential.request"),
|
|
19517
19572
|
projectId: ProjectId,
|
|
@@ -19939,6 +19994,7 @@ var HostToCloudFrame = external_exports.discriminatedUnion("type", [
|
|
|
19939
19994
|
BrowserStateFrame,
|
|
19940
19995
|
BrowserScreencastFrame,
|
|
19941
19996
|
BrowserSessionEndedFrame,
|
|
19997
|
+
BrowserCookieSyncFrame,
|
|
19942
19998
|
BrowserCredentialRequestFrame,
|
|
19943
19999
|
BrowserProfilePurged,
|
|
19944
20000
|
PingFrame,
|
|
@@ -19946,6 +20002,7 @@ var HostToCloudFrame = external_exports.discriminatedUnion("type", [
|
|
|
19946
20002
|
]);
|
|
19947
20003
|
var CloudToHostFrame = external_exports.discriminatedUnion("type", [
|
|
19948
20004
|
HelloAckFrame,
|
|
20005
|
+
BrowserCookieSyncResultFrame,
|
|
19949
20006
|
DeliverFrame,
|
|
19950
20007
|
ConnectionProbeFrame,
|
|
19951
20008
|
ApiOperationCallFrame,
|
|
@@ -19970,6 +20027,11 @@ var CLOSE_CODES = {
|
|
|
19970
20027
|
revoked: 4004
|
|
19971
20028
|
};
|
|
19972
20029
|
var UNSUPPORTED_PROTOCOL_CLOSE_REASON = "unsupported protocol version";
|
|
20030
|
+
function parseUnsupportedProtocolCloseReason(detail) {
|
|
20031
|
+
if (detail === UNSUPPORTED_PROTOCOL_CLOSE_REASON) return { cloudProtocolVersion: null };
|
|
20032
|
+
const match = /^unsupported protocol version; cloud protocol (\d{1,6})$/.exec(detail);
|
|
20033
|
+
return match ? { cloudProtocolVersion: Number(match[1]) } : null;
|
|
20034
|
+
}
|
|
19973
20035
|
var CLOSE_REASON_MAX_BYTES = 123;
|
|
19974
20036
|
|
|
19975
20037
|
// ../../packages/contracts/src/webhook-automations.ts
|
|
@@ -25813,6 +25875,8 @@ var HostClient = class _HostClient {
|
|
|
25813
25875
|
operationGrantWaiters = /* @__PURE__ */ new Map();
|
|
25814
25876
|
/** Project/Task-qualified, socket-generation-bound web_login exchanges. */
|
|
25815
25877
|
browserCredentialWaiters = /* @__PURE__ */ new Map();
|
|
25878
|
+
/** Project-scoped, socket-generation-bound Browser cookie exchanges. */
|
|
25879
|
+
browserCookieSyncWaiters = /* @__PURE__ */ new Map();
|
|
25816
25880
|
/** True only after the connected cloud's helloAck advertised the feature. */
|
|
25817
25881
|
browserFeatureActive = false;
|
|
25818
25882
|
workingContextFeatureActive = false;
|
|
@@ -25978,6 +26042,13 @@ var HostClient = class _HostClient {
|
|
|
25978
26042
|
waiter.deny("No sign-in was attempted: this machine lost its connection to Zixt.");
|
|
25979
26043
|
}
|
|
25980
26044
|
}
|
|
26045
|
+
rejectBrowserCookieSyncWaiters() {
|
|
26046
|
+
for (const [requestId, waiter] of this.browserCookieSyncWaiters) {
|
|
26047
|
+
clearTimeout(waiter.timer);
|
|
26048
|
+
this.browserCookieSyncWaiters.delete(requestId);
|
|
26049
|
+
waiter.deny("Browser cookie synchronization lost its Zixt connection.");
|
|
26050
|
+
}
|
|
26051
|
+
}
|
|
25981
26052
|
async unwindAllRuns(reason, stopReason) {
|
|
25982
26053
|
const runs = [...this.activeRuns.values()];
|
|
25983
26054
|
const unwindingAssignments = [...this.activeAssignments.values()];
|
|
@@ -26017,6 +26088,7 @@ var HostClient = class _HostClient {
|
|
|
26017
26088
|
}
|
|
26018
26089
|
this.rejectOperationGrantWaiters();
|
|
26019
26090
|
this.rejectBrowserCredentialWaiters();
|
|
26091
|
+
this.rejectBrowserCookieSyncWaiters();
|
|
26020
26092
|
for (const deadline of this.authorityExpiryTimers.values()) clearTimeout(deadline.timer);
|
|
26021
26093
|
this.cancels.clear();
|
|
26022
26094
|
this.secretGrants.clear();
|
|
@@ -26315,7 +26387,7 @@ var HostClient = class _HostClient {
|
|
|
26315
26387
|
await this.unwindAllRuns("host connection was replaced by a newer client", "client_shutdown");
|
|
26316
26388
|
return;
|
|
26317
26389
|
}
|
|
26318
|
-
const versionMismatch = code === CLOSE_CODES.protocolError && detail
|
|
26390
|
+
const versionMismatch = code === CLOSE_CODES.protocolError && detail !== void 0 && parseUnsupportedProtocolCloseReason(detail) !== null;
|
|
26319
26391
|
if (versionMismatch) {
|
|
26320
26392
|
this.opts.onStatus?.("incompatible", detail);
|
|
26321
26393
|
this.stopped = true;
|
|
@@ -26369,10 +26441,46 @@ var HostClient = class _HostClient {
|
|
|
26369
26441
|
* frame at a non-advertising cloud would each close the socket outright.
|
|
26370
26442
|
*/
|
|
26371
26443
|
sendBrowserFrame(frame) {
|
|
26372
|
-
if (!this.browserFeatureActive) return;
|
|
26444
|
+
if (!this.browserFeatureActive) return false;
|
|
26373
26445
|
const projectId = frame.type === "browser.state" ? frame.state.projectId : frame.type === "browser.frame" ? frame.frame.projectId : frame.projectId;
|
|
26374
|
-
if (this.purgingProjects.has(projectId)) return;
|
|
26375
|
-
this.send(frame);
|
|
26446
|
+
if (this.purgingProjects.has(projectId)) return false;
|
|
26447
|
+
return this.send(frame);
|
|
26448
|
+
}
|
|
26449
|
+
requestBrowserCookieSync(input) {
|
|
26450
|
+
const socket = this.ws;
|
|
26451
|
+
if (!socket || !this.browserFeatureActive || !this.protocolReady) {
|
|
26452
|
+
return Promise.reject(
|
|
26453
|
+
new Error("Browser cookie synchronization has no live Zixt connection.")
|
|
26454
|
+
);
|
|
26455
|
+
}
|
|
26456
|
+
const requestId = crypto.randomUUID();
|
|
26457
|
+
return new Promise((resolve24, reject3) => {
|
|
26458
|
+
const timer = setTimeout(() => {
|
|
26459
|
+
if (this.browserCookieSyncWaiters.delete(requestId)) {
|
|
26460
|
+
reject3(new Error("Zixt did not answer Browser cookie synchronization in time."));
|
|
26461
|
+
}
|
|
26462
|
+
}, 3e4);
|
|
26463
|
+
timer.unref?.();
|
|
26464
|
+
this.browserCookieSyncWaiters.set(requestId, {
|
|
26465
|
+
projectId: input.projectId,
|
|
26466
|
+
taskId: input.taskId,
|
|
26467
|
+
agentId: input.agentId,
|
|
26468
|
+
browserSessionId: input.browserSessionId,
|
|
26469
|
+
socket,
|
|
26470
|
+
timer,
|
|
26471
|
+
accept: resolve24,
|
|
26472
|
+
deny: (reason) => reject3(new Error(reason))
|
|
26473
|
+
});
|
|
26474
|
+
if (!this.sendBrowserFrame({
|
|
26475
|
+
type: "browser.cookies.sync",
|
|
26476
|
+
requestId,
|
|
26477
|
+
...input
|
|
26478
|
+
})) {
|
|
26479
|
+
clearTimeout(timer);
|
|
26480
|
+
this.browserCookieSyncWaiters.delete(requestId);
|
|
26481
|
+
reject3(new Error("Browser cookie synchronization has no live Zixt connection."));
|
|
26482
|
+
}
|
|
26483
|
+
});
|
|
26376
26484
|
}
|
|
26377
26485
|
replaceablePendingUp(message) {
|
|
26378
26486
|
return message.type === "host.report" || message.type === "task.working_context" || message.type === "task.runner_runtime" || message.type === "task.title" || message.type === "task.event" && message.event.ephemeral === true;
|
|
@@ -26594,7 +26702,8 @@ var HostClient = class _HostClient {
|
|
|
26594
26702
|
taskId,
|
|
26595
26703
|
agentId,
|
|
26596
26704
|
reason
|
|
26597
|
-
})
|
|
26705
|
+
}),
|
|
26706
|
+
syncCookies: (input) => this.requestBrowserCookieSync(input)
|
|
26598
26707
|
});
|
|
26599
26708
|
} else {
|
|
26600
26709
|
this.opts.browser?.attach(null);
|
|
@@ -26708,6 +26817,17 @@ var HostClient = class _HostClient {
|
|
|
26708
26817
|
waiter.accept(grant);
|
|
26709
26818
|
return;
|
|
26710
26819
|
}
|
|
26820
|
+
case "browser.cookies.result": {
|
|
26821
|
+
const waiter = this.browserCookieSyncWaiters.get(frame.requestId);
|
|
26822
|
+
if (!waiter || waiter.socket !== sourceSocket || waiter.projectId !== frame.projectId || waiter.taskId !== frame.taskId || waiter.agentId !== frame.agentId || waiter.browserSessionId !== frame.browserSessionId) {
|
|
26823
|
+
return;
|
|
26824
|
+
}
|
|
26825
|
+
clearTimeout(waiter.timer);
|
|
26826
|
+
this.browserCookieSyncWaiters.delete(frame.requestId);
|
|
26827
|
+
if (frame.ok) waiter.accept({ accepted: frame.accepted, state: frame.state });
|
|
26828
|
+
else waiter.deny(frame.error);
|
|
26829
|
+
return;
|
|
26830
|
+
}
|
|
26711
26831
|
case "browser.open": {
|
|
26712
26832
|
if (!this.acceptsVolatileProjectWork(frame.projectId, frame.projectWorkFenceRevision)) {
|
|
26713
26833
|
return;
|
|
@@ -30384,6 +30504,29 @@ function installedReleaseVersion(entry, root = versionsRoot()) {
|
|
|
30384
30504
|
if (!version2 || !VERSION_DIR.test(version2)) return null;
|
|
30385
30505
|
return resolve8(entry) === resolve8(installedReleaseEntry(version2, root)) ? version2 : null;
|
|
30386
30506
|
}
|
|
30507
|
+
function compareReleaseVersions(left, right) {
|
|
30508
|
+
const parse4 = (version2) => version2.split(/[-+]/, 1)[0].split(".").map((part) => Number(part));
|
|
30509
|
+
const leftParts = parse4(left);
|
|
30510
|
+
const rightParts = parse4(right);
|
|
30511
|
+
for (let index = 0; index < 3; index += 1) {
|
|
30512
|
+
const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
|
|
30513
|
+
if (difference !== 0) return difference;
|
|
30514
|
+
}
|
|
30515
|
+
return 0;
|
|
30516
|
+
}
|
|
30517
|
+
async function newestInstalledReleaseBelow(version2, excluded, root = versionsRoot()) {
|
|
30518
|
+
let names;
|
|
30519
|
+
try {
|
|
30520
|
+
names = await readdir6(root);
|
|
30521
|
+
} catch {
|
|
30522
|
+
return null;
|
|
30523
|
+
}
|
|
30524
|
+
const candidates = names.filter((name) => VERSION_DIR.test(name)).filter((name) => !excluded.has(name) && compareReleaseVersions(name, version2) < 0).sort(compareReleaseVersions).reverse();
|
|
30525
|
+
for (const candidate of candidates) {
|
|
30526
|
+
if (await validInstalledRelease(candidate, root)) return candidate;
|
|
30527
|
+
}
|
|
30528
|
+
return null;
|
|
30529
|
+
}
|
|
30387
30530
|
function currentReleaseEntry(root = versionsRoot(), platform = process.platform) {
|
|
30388
30531
|
return join12(root, platform === "win32" ? "current-launcher.cjs" : CURRENT_RELEASE_ENTRY);
|
|
30389
30532
|
}
|
|
@@ -31488,6 +31631,7 @@ async function superviseHost(options = {}) {
|
|
|
31488
31631
|
});
|
|
31489
31632
|
const attempted = /* @__PURE__ */ new Set();
|
|
31490
31633
|
let unsatisfiableUpdates = 0;
|
|
31634
|
+
const cloudRefusedVersions = /* @__PURE__ */ new Set();
|
|
31491
31635
|
const waitOrShutdown = async (ms) => {
|
|
31492
31636
|
if (shuttingDown2) return false;
|
|
31493
31637
|
if (!customDelay) {
|
|
@@ -31568,6 +31712,14 @@ async function superviseHost(options = {}) {
|
|
|
31568
31712
|
rollbackCandidate = null;
|
|
31569
31713
|
}
|
|
31570
31714
|
};
|
|
31715
|
+
const parkRefusedCandidate = async (version2) => {
|
|
31716
|
+
if (!releaseStore || releaseState?.candidateVersion !== version2) return;
|
|
31717
|
+
await releaseStore.clear();
|
|
31718
|
+
if (releaseState?.candidateVersion === version2) {
|
|
31719
|
+
releaseState = null;
|
|
31720
|
+
rollbackCandidate = null;
|
|
31721
|
+
}
|
|
31722
|
+
};
|
|
31571
31723
|
const markReleaseValidated = async (version2) => {
|
|
31572
31724
|
if (releaseState?.phase !== "probation" || releaseState.candidateVersion !== version2 || !releaseStore) {
|
|
31573
31725
|
return;
|
|
@@ -31876,21 +32028,66 @@ async function superviseHost(options = {}) {
|
|
|
31876
32028
|
const target = await published();
|
|
31877
32029
|
let updateLanded = false;
|
|
31878
32030
|
let rejectedUpdate = null;
|
|
32031
|
+
if (runtimeMs >= FAST_UPDATE_EXIT_MS) cloudRefusedVersions.clear();
|
|
31879
32032
|
if (target === null) {
|
|
31880
32033
|
log2(
|
|
31881
32034
|
"Zixt Host: a newer release was reported but the registry is unreachable; staying on the current version"
|
|
31882
32035
|
);
|
|
31883
32036
|
} else if (target === command.version) {
|
|
32037
|
+
cloudRefusedVersions.add(target);
|
|
32038
|
+
await parkRefusedCandidate(target);
|
|
31884
32039
|
if (unsatisfiableUpdates === 0) {
|
|
31885
32040
|
log2(
|
|
31886
32041
|
`Zixt Host: ${target} is the newest published Zixt Host, so no update can satisfy this cloud; the cloud this Machine is paired to speaks a newer Host protocol than any published release`
|
|
31887
32042
|
);
|
|
32043
|
+
}
|
|
32044
|
+
const compatible = await newestInstalledReleaseBelow(target, cloudRefusedVersions);
|
|
32045
|
+
if (compatible) {
|
|
32046
|
+
log2(
|
|
32047
|
+
`Zixt Host: running installed ${compatible} while the cloud catches up with ${target}; this Machine returns to ${target} by itself once the cloud speaks its protocol`
|
|
32048
|
+
);
|
|
32049
|
+
command = {
|
|
32050
|
+
entry: installedReleaseEntry(compatible),
|
|
32051
|
+
version: compatible,
|
|
32052
|
+
// The parked newest release is not for this worker to re-request:
|
|
32053
|
+
// its own update checks skip it while the cloud stays behind.
|
|
32054
|
+
rejectedVersion: target
|
|
32055
|
+
};
|
|
32056
|
+
updateLanded = true;
|
|
32057
|
+
} else if (unsatisfiableUpdates === 0) {
|
|
31888
32058
|
log2(
|
|
31889
|
-
"Zixt Host: pair this Machine with a cloud running a published release, or start the Host from the same source checkout that cloud runs; this Machine connects by itself once a matching release is published"
|
|
32059
|
+
"Zixt Host: no older installed release remains to serve this cloud; pair this Machine with a cloud running a published release, or start the Host from the same source checkout that cloud runs; this Machine connects by itself once a matching release is published"
|
|
32060
|
+
);
|
|
32061
|
+
}
|
|
32062
|
+
} else if (cloudRefusedVersions.has(target)) {
|
|
32063
|
+
if (command.version && runtimeMs < FAST_UPDATE_EXIT_MS) {
|
|
32064
|
+
cloudRefusedVersions.add(command.version);
|
|
32065
|
+
await parkRefusedCandidate(command.version);
|
|
32066
|
+
}
|
|
32067
|
+
const compatible = command.version ? await newestInstalledReleaseBelow(command.version, cloudRefusedVersions) : null;
|
|
32068
|
+
if (compatible) {
|
|
32069
|
+
log2(
|
|
32070
|
+
`Zixt Host: ${target} is still refused by the paired cloud; running installed ${compatible} until the cloud updates`
|
|
32071
|
+
);
|
|
32072
|
+
command = {
|
|
32073
|
+
entry: installedReleaseEntry(compatible),
|
|
32074
|
+
version: compatible,
|
|
32075
|
+
rejectedVersion: target
|
|
32076
|
+
};
|
|
32077
|
+
updateLanded = true;
|
|
32078
|
+
} else {
|
|
32079
|
+
log2(
|
|
32080
|
+
`Zixt Host: the paired cloud refuses every installed release; waiting for the cloud or a newer published release`
|
|
31890
32081
|
);
|
|
31891
32082
|
}
|
|
31892
32083
|
} else if (attempted.has(target)) {
|
|
31893
|
-
|
|
32084
|
+
if (command.version !== target && await validInstalledRelease(target)) {
|
|
32085
|
+
log2(`Zixt Host: returning to installed ${target} now that the cloud accepts it`);
|
|
32086
|
+
command = { entry: installedReleaseEntry(target), version: target };
|
|
32087
|
+
updateLanded = true;
|
|
32088
|
+
} else {
|
|
32089
|
+
log2(`Zixt Host: already running ${target}; ignoring a repeated update request`);
|
|
32090
|
+
}
|
|
31894
32091
|
} else {
|
|
31895
32092
|
let entry;
|
|
31896
32093
|
const attempt = { failure: null };
|
|
@@ -32486,6 +32683,7 @@ function createDemoBrowserAdapterFactory() {
|
|
|
32486
32683
|
const tabs = [newDemoTab()];
|
|
32487
32684
|
let activeIndex = 0;
|
|
32488
32685
|
let sink = null;
|
|
32686
|
+
let cookies = [];
|
|
32489
32687
|
const listeners = [];
|
|
32490
32688
|
const frame = () => ({
|
|
32491
32689
|
jpegBase64: DEMO_FRAME_JPEG_BASE64,
|
|
@@ -32629,6 +32827,12 @@ function createDemoBrowserAdapterFactory() {
|
|
|
32629
32827
|
}
|
|
32630
32828
|
emit();
|
|
32631
32829
|
},
|
|
32830
|
+
async cookies() {
|
|
32831
|
+
return cookies.map((cookie) => ({ ...cookie }));
|
|
32832
|
+
},
|
|
32833
|
+
async replaceCookies(next) {
|
|
32834
|
+
cookies = next.map((cookie) => ({ ...cookie }));
|
|
32835
|
+
},
|
|
32632
32836
|
async close() {
|
|
32633
32837
|
sink = null;
|
|
32634
32838
|
}
|
|
@@ -44384,6 +44588,10 @@ var BrowserManager = class {
|
|
|
44384
44588
|
deniedAuthorizations = /* @__PURE__ */ new Map();
|
|
44385
44589
|
/** Serializes profile lifecycle and session open/close per teammate. */
|
|
44386
44590
|
locks = /* @__PURE__ */ new Map();
|
|
44591
|
+
/** Last cloud-canonical jar observed for each exact Project teammate. */
|
|
44592
|
+
cookieStates = /* @__PURE__ */ new Map();
|
|
44593
|
+
cookieSyncTails = /* @__PURE__ */ new Map();
|
|
44594
|
+
cookieSyncTimers = /* @__PURE__ */ new Map();
|
|
44387
44595
|
events = null;
|
|
44388
44596
|
profileRoot;
|
|
44389
44597
|
profileStateRoot;
|
|
@@ -44400,8 +44608,15 @@ var BrowserManager = class {
|
|
|
44400
44608
|
attach(events) {
|
|
44401
44609
|
this.events = events;
|
|
44402
44610
|
if (!events) return;
|
|
44611
|
+
const scheduledProfiles = /* @__PURE__ */ new Set();
|
|
44403
44612
|
for (const session of this.sessions.values()) {
|
|
44404
|
-
if (
|
|
44613
|
+
if (session.closed) continue;
|
|
44614
|
+
events.state(this.stateOf(session));
|
|
44615
|
+
const profileKey = this.profileKey(session.projectId, session.agentId);
|
|
44616
|
+
if (!scheduledProfiles.has(profileKey)) {
|
|
44617
|
+
scheduledProfiles.add(profileKey);
|
|
44618
|
+
this.scheduleCookieSync(session, 0);
|
|
44619
|
+
}
|
|
44405
44620
|
}
|
|
44406
44621
|
}
|
|
44407
44622
|
capability() {
|
|
@@ -44486,6 +44701,39 @@ var BrowserManager = class {
|
|
|
44486
44701
|
profileKey(projectId, agentId) {
|
|
44487
44702
|
return `${projectId}:${agentId}`;
|
|
44488
44703
|
}
|
|
44704
|
+
cookieKey(cookie) {
|
|
44705
|
+
return `${cookie.name}\0${cookie.domain}\0${cookie.path}\0${cookie.partitionKey ?? ""}`;
|
|
44706
|
+
}
|
|
44707
|
+
normalizedCookies(cookies) {
|
|
44708
|
+
return [
|
|
44709
|
+
...new Map(cookies.map((cookie) => [this.cookieKey(cookie), { ...cookie }])).values()
|
|
44710
|
+
].sort((left, right) => this.cookieKey(left).localeCompare(this.cookieKey(right)));
|
|
44711
|
+
}
|
|
44712
|
+
cookiesEqual(left, right) {
|
|
44713
|
+
return JSON.stringify(this.normalizedCookies(left)) === JSON.stringify(this.normalizedCookies(right));
|
|
44714
|
+
}
|
|
44715
|
+
/**
|
|
44716
|
+
* Rebase only the changes this Machine made since its last canonical jar
|
|
44717
|
+
* over the newer jar returned by another Machine. Unchanged cookies retain
|
|
44718
|
+
* the remote value; local additions, changes, and deletions win per cookie.
|
|
44719
|
+
*/
|
|
44720
|
+
mergeCookieChanges(base, local, remote) {
|
|
44721
|
+
const baseByKey = new Map(base.map((cookie) => [this.cookieKey(cookie), cookie]));
|
|
44722
|
+
const localByKey = new Map(local.map((cookie) => [this.cookieKey(cookie), cookie]));
|
|
44723
|
+
const merged = new Map(remote.map((cookie) => [this.cookieKey(cookie), { ...cookie }]));
|
|
44724
|
+
for (const [key, baseCookie] of baseByKey) {
|
|
44725
|
+
const localCookie = localByKey.get(key);
|
|
44726
|
+
if (!localCookie) {
|
|
44727
|
+
merged.delete(key);
|
|
44728
|
+
} else if (!this.cookiesEqual([baseCookie], [localCookie])) {
|
|
44729
|
+
merged.set(key, { ...localCookie });
|
|
44730
|
+
}
|
|
44731
|
+
}
|
|
44732
|
+
for (const [key, localCookie] of localByKey) {
|
|
44733
|
+
if (!baseByKey.has(key)) merged.set(key, { ...localCookie });
|
|
44734
|
+
}
|
|
44735
|
+
return this.normalizedCookies([...merged.values()]);
|
|
44736
|
+
}
|
|
44489
44737
|
exactChild(root, child) {
|
|
44490
44738
|
const canonicalRoot = resolve16(root);
|
|
44491
44739
|
const target = resolve16(canonicalRoot, child);
|
|
@@ -44606,6 +44854,87 @@ var BrowserManager = class {
|
|
|
44606
44854
|
throw error52;
|
|
44607
44855
|
}
|
|
44608
44856
|
}
|
|
44857
|
+
synchronizeCookies(session) {
|
|
44858
|
+
const profileKey = this.profileKey(session.projectId, session.agentId);
|
|
44859
|
+
const tail = this.cookieSyncTails.get(profileKey) ?? Promise.resolve();
|
|
44860
|
+
const next = tail.then(async () => {
|
|
44861
|
+
if (session.closed) return;
|
|
44862
|
+
const synchronize = this.events?.syncCookies;
|
|
44863
|
+
if (!synchronize) return;
|
|
44864
|
+
let baseline = this.cookieStates.get(profileKey);
|
|
44865
|
+
let observedLocal = this.normalizedCookies(await session.adapter.cookies());
|
|
44866
|
+
let desired = observedLocal;
|
|
44867
|
+
for (let attempt = 0; attempt < 8; attempt++) {
|
|
44868
|
+
if (desired.length > BROWSER_MAX_SYNCED_COOKIES) {
|
|
44869
|
+
throw new Error(
|
|
44870
|
+
`the browser profile has more than ${BROWSER_MAX_SYNCED_COOKIES} cookies and cannot be synchronized safely`
|
|
44871
|
+
);
|
|
44872
|
+
}
|
|
44873
|
+
const result = await synchronize({
|
|
44874
|
+
projectId: session.projectId,
|
|
44875
|
+
taskId: session.taskId,
|
|
44876
|
+
agentId: session.agentId,
|
|
44877
|
+
browserSessionId: session.browserSessionId,
|
|
44878
|
+
...baseline ? { baseRevision: baseline.revision } : {},
|
|
44879
|
+
cookies: desired
|
|
44880
|
+
});
|
|
44881
|
+
if (result.accepted) {
|
|
44882
|
+
const canonical = {
|
|
44883
|
+
revision: result.state.revision,
|
|
44884
|
+
cookies: this.normalizedCookies(result.state.cookies)
|
|
44885
|
+
};
|
|
44886
|
+
this.cookieStates.set(profileKey, canonical);
|
|
44887
|
+
const current2 = this.normalizedCookies(await session.adapter.cookies());
|
|
44888
|
+
if (!this.cookiesEqual(current2, observedLocal)) {
|
|
44889
|
+
baseline = canonical;
|
|
44890
|
+
desired = this.mergeCookieChanges(observedLocal, current2, canonical.cookies);
|
|
44891
|
+
observedLocal = current2;
|
|
44892
|
+
continue;
|
|
44893
|
+
}
|
|
44894
|
+
if (!this.cookiesEqual(current2, canonical.cookies)) {
|
|
44895
|
+
await session.adapter.replaceCookies(canonical.cookies);
|
|
44896
|
+
}
|
|
44897
|
+
return;
|
|
44898
|
+
}
|
|
44899
|
+
const remote = {
|
|
44900
|
+
revision: result.state.revision,
|
|
44901
|
+
cookies: this.normalizedCookies(result.state.cookies)
|
|
44902
|
+
};
|
|
44903
|
+
if (!baseline) {
|
|
44904
|
+
this.cookieStates.set(profileKey, remote);
|
|
44905
|
+
await session.adapter.replaceCookies(remote.cookies);
|
|
44906
|
+
return;
|
|
44907
|
+
}
|
|
44908
|
+
const current = this.normalizedCookies(await session.adapter.cookies());
|
|
44909
|
+
const currentDesired = this.cookiesEqual(current, observedLocal) ? desired : this.mergeCookieChanges(observedLocal, current, desired);
|
|
44910
|
+
desired = this.mergeCookieChanges(baseline.cookies, currentDesired, remote.cookies);
|
|
44911
|
+
baseline = remote;
|
|
44912
|
+
observedLocal = current;
|
|
44913
|
+
}
|
|
44914
|
+
throw new Error("browser cookies changed too many times while synchronizing");
|
|
44915
|
+
});
|
|
44916
|
+
const tracked = next.catch(() => {
|
|
44917
|
+
}).finally(() => {
|
|
44918
|
+
if (this.cookieSyncTails.get(profileKey) === tracked)
|
|
44919
|
+
this.cookieSyncTails.delete(profileKey);
|
|
44920
|
+
});
|
|
44921
|
+
this.cookieSyncTails.set(profileKey, tracked);
|
|
44922
|
+
return next;
|
|
44923
|
+
}
|
|
44924
|
+
scheduleCookieSync(session, delayMs = 250) {
|
|
44925
|
+
if (session.closed || !this.events?.syncCookies) return;
|
|
44926
|
+
const profileKey = this.profileKey(session.projectId, session.agentId);
|
|
44927
|
+
const existing = this.cookieSyncTimers.get(profileKey);
|
|
44928
|
+
if (existing) clearTimeout(existing);
|
|
44929
|
+
const timer = setTimeout(() => {
|
|
44930
|
+
if (this.cookieSyncTimers.get(profileKey) !== timer) return;
|
|
44931
|
+
this.cookieSyncTimers.delete(profileKey);
|
|
44932
|
+
void this.synchronizeCookies(session).catch(() => {
|
|
44933
|
+
});
|
|
44934
|
+
}, delayMs);
|
|
44935
|
+
timer.unref?.();
|
|
44936
|
+
this.cookieSyncTimers.set(profileKey, timer);
|
|
44937
|
+
}
|
|
44609
44938
|
/**
|
|
44610
44939
|
* Ensure a live session for this Task. An existing session is adopted
|
|
44611
44940
|
* as-is (the cloud-minted id loses to the live one — the cloud reconciles
|
|
@@ -44683,8 +45012,20 @@ var BrowserManager = class {
|
|
|
44683
45012
|
closeFailed: false
|
|
44684
45013
|
};
|
|
44685
45014
|
this.sessions.set(taskKey, session);
|
|
45015
|
+
try {
|
|
45016
|
+
await this.synchronizeCookies(session);
|
|
45017
|
+
} catch (error52) {
|
|
45018
|
+
session.closed = true;
|
|
45019
|
+
this.sessions.delete(taskKey);
|
|
45020
|
+
await adapter.close().catch(() => {
|
|
45021
|
+
});
|
|
45022
|
+
throw error52;
|
|
45023
|
+
}
|
|
44686
45024
|
adapter.onStateChanged(() => {
|
|
44687
|
-
if (!session.closed)
|
|
45025
|
+
if (!session.closed) {
|
|
45026
|
+
this.events?.state(this.stateOf(session));
|
|
45027
|
+
this.scheduleCookieSync(session);
|
|
45028
|
+
}
|
|
44688
45029
|
});
|
|
44689
45030
|
this.armIdleTimer(session);
|
|
44690
45031
|
this.events?.state(this.stateOf(session));
|
|
@@ -44714,6 +45055,14 @@ var BrowserManager = class {
|
|
|
44714
45055
|
return;
|
|
44715
45056
|
}
|
|
44716
45057
|
if (!session || session.closed && !session.closeFailed) return;
|
|
45058
|
+
const profileKey = this.profileKey(session.projectId, session.agentId);
|
|
45059
|
+
const pendingCookieTimer = this.cookieSyncTimers.get(profileKey);
|
|
45060
|
+
if (pendingCookieTimer) {
|
|
45061
|
+
clearTimeout(pendingCookieTimer);
|
|
45062
|
+
this.cookieSyncTimers.delete(profileKey);
|
|
45063
|
+
}
|
|
45064
|
+
await this.synchronizeCookies(session).catch(() => {
|
|
45065
|
+
});
|
|
44717
45066
|
session.closed = true;
|
|
44718
45067
|
session.closeFailed = false;
|
|
44719
45068
|
if (session.frameTimer) clearTimeout(session.frameTimer);
|
|
@@ -44763,6 +45112,12 @@ var BrowserManager = class {
|
|
|
44763
45112
|
force: true,
|
|
44764
45113
|
maxRetries: 3
|
|
44765
45114
|
});
|
|
45115
|
+
const profileKey = this.profileKey(projectId, agentId);
|
|
45116
|
+
const timer = this.cookieSyncTimers.get(profileKey);
|
|
45117
|
+
if (timer) clearTimeout(timer);
|
|
45118
|
+
this.cookieSyncTimers.delete(profileKey);
|
|
45119
|
+
this.cookieSyncTails.delete(profileKey);
|
|
45120
|
+
this.cookieStates.delete(profileKey);
|
|
44766
45121
|
await this.syncDirectory(this.projectProfileRoot(projectId));
|
|
44767
45122
|
});
|
|
44768
45123
|
}
|
|
@@ -44797,6 +45152,17 @@ var BrowserManager = class {
|
|
|
44797
45152
|
this.deniedAuthorizations.forEach((_revision, key) => {
|
|
44798
45153
|
if (key.startsWith(`${projectId}:`)) this.deniedAuthorizations.delete(key);
|
|
44799
45154
|
});
|
|
45155
|
+
this.cookieSyncTimers.forEach((timer, key) => {
|
|
45156
|
+
if (!key.startsWith(`${projectId}:`)) return;
|
|
45157
|
+
clearTimeout(timer);
|
|
45158
|
+
this.cookieSyncTimers.delete(key);
|
|
45159
|
+
});
|
|
45160
|
+
this.cookieSyncTails.forEach((_tail, key) => {
|
|
45161
|
+
if (key.startsWith(`${projectId}:`)) this.cookieSyncTails.delete(key);
|
|
45162
|
+
});
|
|
45163
|
+
this.cookieStates.forEach((_state, key) => {
|
|
45164
|
+
if (key.startsWith(`${projectId}:`)) this.cookieStates.delete(key);
|
|
45165
|
+
});
|
|
44800
45166
|
await this.syncDirectory(this.profileRoot);
|
|
44801
45167
|
await this.syncDirectory(this.profileStateRoot);
|
|
44802
45168
|
}
|
|
@@ -44865,6 +45231,11 @@ var BrowserManager = class {
|
|
|
44865
45231
|
const next = session.inputQueue.then(work, work);
|
|
44866
45232
|
session.inputQueue = next.catch(() => {
|
|
44867
45233
|
});
|
|
45234
|
+
void next.then(
|
|
45235
|
+
() => this.scheduleCookieSync(session),
|
|
45236
|
+
() => {
|
|
45237
|
+
}
|
|
45238
|
+
);
|
|
44868
45239
|
return next;
|
|
44869
45240
|
}
|
|
44870
45241
|
/** Tool surface: every call ensures the session and counts as activity. */
|
|
@@ -45666,6 +46037,37 @@ function createPlaywrightBrowserAdapterFactory(dependencies = {}) {
|
|
|
45666
46037
|
}
|
|
45667
46038
|
}
|
|
45668
46039
|
},
|
|
46040
|
+
async cookies() {
|
|
46041
|
+
return (await context.cookies()).map((cookie) => ({
|
|
46042
|
+
name: cookie.name,
|
|
46043
|
+
value: cookie.value,
|
|
46044
|
+
domain: cookie.domain,
|
|
46045
|
+
path: cookie.path,
|
|
46046
|
+
expires: cookie.expires,
|
|
46047
|
+
httpOnly: cookie.httpOnly,
|
|
46048
|
+
secure: cookie.secure,
|
|
46049
|
+
sameSite: cookie.sameSite,
|
|
46050
|
+
...cookie.partitionKey ? { partitionKey: cookie.partitionKey } : {}
|
|
46051
|
+
}));
|
|
46052
|
+
},
|
|
46053
|
+
async replaceCookies(cookies) {
|
|
46054
|
+
await context.clearCookies();
|
|
46055
|
+
if (cookies.length > 0) {
|
|
46056
|
+
await context.addCookies(
|
|
46057
|
+
cookies.map((cookie) => ({
|
|
46058
|
+
name: cookie.name,
|
|
46059
|
+
value: cookie.value,
|
|
46060
|
+
domain: cookie.domain,
|
|
46061
|
+
path: cookie.path,
|
|
46062
|
+
expires: cookie.expires,
|
|
46063
|
+
httpOnly: cookie.httpOnly,
|
|
46064
|
+
secure: cookie.secure,
|
|
46065
|
+
sameSite: cookie.sameSite,
|
|
46066
|
+
...cookie.partitionKey !== void 0 ? { partitionKey: cookie.partitionKey } : {}
|
|
46067
|
+
}))
|
|
46068
|
+
);
|
|
46069
|
+
}
|
|
46070
|
+
},
|
|
45669
46071
|
async close() {
|
|
45670
46072
|
closed = true;
|
|
45671
46073
|
for (const tab of [...tabs]) {
|
|
@@ -48384,14 +48786,21 @@ var client = new HostClient({
|
|
|
48384
48786
|
});
|
|
48385
48787
|
queueMicrotask(() => shutdown(DO_NOT_RESTART_EXIT_CODE));
|
|
48386
48788
|
break;
|
|
48387
|
-
case "incompatible":
|
|
48388
|
-
|
|
48389
|
-
|
|
48390
|
-
|
|
48391
|
-
|
|
48392
|
-
|
|
48789
|
+
case "incompatible": {
|
|
48790
|
+
const refusal = detail ? parseUnsupportedProtocolCloseReason(detail) : null;
|
|
48791
|
+
const cloudBehind = refusal?.cloudProtocolVersion != null && refusal.cloudProtocolVersion < PROTOCOL_VERSION;
|
|
48792
|
+
log.error(
|
|
48793
|
+
cloudBehind ? "Zixt Cloud is behind this Host and refused the connection" : "Zixt Cloud refused this Host as too old to connect",
|
|
48794
|
+
{
|
|
48795
|
+
...connectionContext,
|
|
48796
|
+
protocol: PROTOCOL_VERSION,
|
|
48797
|
+
...refusal?.cloudProtocolVersion != null ? { cloudProtocol: refusal.cloudProtocolVersion } : {},
|
|
48798
|
+
next: packagedBuild ? cloudBehind ? "Zixt runs the newest installed release this cloud accepts until the cloud updates" : "Zixt checks the published release now; nothing to do on this Machine" : cloudBehind ? "Deploy the paired cloud, or run this checkout at the revision that cloud runs" : "Update this checkout through Git and restart the Host"
|
|
48799
|
+
}
|
|
48800
|
+
);
|
|
48393
48801
|
queueMicrotask(() => shutdown(packagedBuild ? UPDATE_EXIT_CODE : DO_NOT_RESTART_EXIT_CODE));
|
|
48394
48802
|
break;
|
|
48803
|
+
}
|
|
48395
48804
|
case "rejected":
|
|
48396
48805
|
log.error("Zixt Cloud refused this Machine", {
|
|
48397
48806
|
...connectionContext,
|