@codexhost/cli-linux-x64 0.2.2

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.
@@ -0,0 +1,1519 @@
1
+ // packages/desktop-control/src/production-controller.ts
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ // packages/desktop-control/src/controller-attachment-server.ts
6
+ import { createServer } from "node:net";
7
+ var MAX_REQUEST_BYTES = 96;
8
+ function validPort(value) {
9
+ return Number.isInteger(value) && value >= 1 && value <= 65535;
10
+ }
11
+ function validNonce(value) {
12
+ return /^[0-9a-f]{32}$/.test(value);
13
+ }
14
+ function closeServer(server) {
15
+ return new Promise((resolve, reject) => {
16
+ server.close((error) => {
17
+ if (error) reject(error);
18
+ else resolve();
19
+ });
20
+ });
21
+ }
22
+ function respond(socket, value) {
23
+ socket.end(`${value}
24
+ `);
25
+ }
26
+ async function startControllerAttachmentServer(options) {
27
+ if (!validPort(options.port)) throw new Error("attachment port must be a valid TCP port");
28
+ if (!validNonce(options.nonce)) {
29
+ throw new Error("attachment nonce must be 32 lowercase hexadecimal characters");
30
+ }
31
+ const sockets = /* @__PURE__ */ new Set();
32
+ const server = createServer((socket) => {
33
+ sockets.add(socket);
34
+ socket.once("close", () => sockets.delete(socket));
35
+ socket.setEncoding("utf8");
36
+ socket.setTimeout(5e3, () => socket.destroy());
37
+ let request = "";
38
+ let handled = false;
39
+ socket.on("data", (chunk) => {
40
+ if (handled) return;
41
+ request += chunk;
42
+ if (request.length > MAX_REQUEST_BYTES) {
43
+ handled = true;
44
+ respond(socket, "rejected");
45
+ return;
46
+ }
47
+ const newline = request.indexOf("\n");
48
+ if (newline < 0) return;
49
+ handled = true;
50
+ const line = request.slice(0, newline).replace(/\r$/, "");
51
+ if (line === `ATTACH ${options.nonce}`) {
52
+ void options.attach().then(
53
+ () => respond(socket, "ready"),
54
+ () => respond(socket, "failed")
55
+ );
56
+ return;
57
+ }
58
+ if (line === `COMPATIBILITY_UPDATE ${options.nonce}`) {
59
+ socket.setTimeout(2e4);
60
+ void options.compatibilityUpdate().then(
61
+ (outcome) => respond(socket, outcome),
62
+ () => respond(socket, "failed")
63
+ );
64
+ return;
65
+ }
66
+ respond(socket, "rejected");
67
+ });
68
+ });
69
+ await new Promise((resolve, reject) => {
70
+ const onError = (error) => reject(error);
71
+ server.once("error", onError);
72
+ server.listen(options.port, "127.0.0.1", () => {
73
+ server.off("error", onError);
74
+ resolve();
75
+ });
76
+ });
77
+ let closed = false;
78
+ return {
79
+ async close() {
80
+ if (closed) return;
81
+ closed = true;
82
+ for (const socket of sockets) socket.destroy();
83
+ await closeServer(server);
84
+ }
85
+ };
86
+ }
87
+
88
+ // packages/desktop-control/src/cdp-client.ts
89
+ function isRecord(value) {
90
+ return typeof value === "object" && value !== null && !Array.isArray(value);
91
+ }
92
+ function nonEmptyString(value, field) {
93
+ if (typeof value !== "string" || value.length === 0) {
94
+ throw new Error(`CDP target '${field}' must be non-empty text`);
95
+ }
96
+ return value;
97
+ }
98
+ function loopbackUrl(value, protocols) {
99
+ const url = new URL(value);
100
+ if (!protocols.includes(url.protocol)) {
101
+ throw new Error(`CDP endpoint must use ${protocols.join(" or ")}`);
102
+ }
103
+ if (!["127.0.0.1", "localhost", "[::1]"].includes(url.hostname)) {
104
+ throw new Error("CDP endpoint must use a loopback host");
105
+ }
106
+ return url;
107
+ }
108
+ function parseTarget(value) {
109
+ if (!isRecord(value)) throw new Error("CDP target must be an object");
110
+ const target = {
111
+ id: nonEmptyString(value.id, "id"),
112
+ type: nonEmptyString(value.type, "type"),
113
+ title: typeof value.title === "string" ? value.title : "",
114
+ url: nonEmptyString(value.url, "url"),
115
+ webSocketDebuggerUrl: nonEmptyString(value.webSocketDebuggerUrl, "webSocketDebuggerUrl")
116
+ };
117
+ loopbackUrl(target.webSocketDebuggerUrl, ["ws:", "wss:"]);
118
+ return target;
119
+ }
120
+ function defaultFetch(url) {
121
+ return fetch(url);
122
+ }
123
+ function defaultSocketFactory(url) {
124
+ return new WebSocket(url);
125
+ }
126
+ function messageText(value) {
127
+ if (typeof value === "string") return value;
128
+ if (value instanceof ArrayBuffer) return new TextDecoder().decode(value);
129
+ if (ArrayBuffer.isView(value)) {
130
+ return new TextDecoder().decode(
131
+ new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
132
+ );
133
+ }
134
+ throw new Error("CDP WebSocket returned a non-text message");
135
+ }
136
+ function parseResponse(value) {
137
+ if (!isRecord(value) || typeof value.id !== "number") return null;
138
+ const response = { id: value.id };
139
+ if ("result" in value) response.result = value.result;
140
+ if (isRecord(value.error)) {
141
+ response.error = {
142
+ ...typeof value.error.code === "number" ? { code: value.error.code } : {},
143
+ ...typeof value.error.message === "string" ? { message: value.error.message } : {}
144
+ };
145
+ }
146
+ return response;
147
+ }
148
+ async function listCdpTargets(endpoint, fetchImpl = defaultFetch) {
149
+ const baseUrl = loopbackUrl(endpoint, ["http:", "https:"]);
150
+ const targetsUrl = new URL("/json/list", baseUrl).toString();
151
+ const response = await fetchImpl(targetsUrl);
152
+ if (!response.ok) throw new Error(`CDP target discovery failed with HTTP ${response.status}`);
153
+ const value = await response.json();
154
+ if (!Array.isArray(value)) throw new Error("CDP target discovery did not return an array");
155
+ return value.map(parseTarget);
156
+ }
157
+ var CdpClient = class _CdpClient {
158
+ #commandTimeoutMs;
159
+ #socket;
160
+ #closed = false;
161
+ #nextId = 1;
162
+ #pending = /* @__PURE__ */ new Map();
163
+ #listeners = /* @__PURE__ */ new Map();
164
+ constructor(socket, commandTimeoutMs) {
165
+ this.#socket = socket;
166
+ this.#commandTimeoutMs = commandTimeoutMs;
167
+ socket.addEventListener("message", (event) => this.#onMessage(event));
168
+ socket.addEventListener("error", () => this.#fail(new Error("CDP WebSocket failed")));
169
+ socket.addEventListener("close", () => this.#fail(new Error("CDP WebSocket closed")));
170
+ }
171
+ static async connect(url, options = {}) {
172
+ loopbackUrl(url, ["ws:", "wss:"]);
173
+ const socketFactory = options.socketFactory ?? defaultSocketFactory;
174
+ const socket = socketFactory(url);
175
+ const connectTimeoutMs = options.connectTimeoutMs ?? 1e4;
176
+ await new Promise((resolve, reject) => {
177
+ const timeout = setTimeout(() => {
178
+ cleanup();
179
+ socket.close();
180
+ reject(new Error("CDP WebSocket connection timed out"));
181
+ }, connectTimeoutMs);
182
+ const onOpen = () => {
183
+ cleanup();
184
+ resolve();
185
+ };
186
+ const onError = () => {
187
+ cleanup();
188
+ reject(new Error("CDP WebSocket connection failed"));
189
+ };
190
+ const cleanup = () => {
191
+ clearTimeout(timeout);
192
+ socket.removeEventListener("open", onOpen);
193
+ socket.removeEventListener("error", onError);
194
+ };
195
+ socket.addEventListener("open", onOpen);
196
+ socket.addEventListener("error", onError);
197
+ });
198
+ return new _CdpClient(socket, options.commandTimeoutMs ?? 1e4);
199
+ }
200
+ command(method, params = {}) {
201
+ return this.#send(method, params);
202
+ }
203
+ sessionCommand(sessionId, method, params = {}) {
204
+ if (sessionId.length === 0) return Promise.reject(new Error("CDP session ID is required"));
205
+ return this.#send(method, params, sessionId);
206
+ }
207
+ on(method, listener) {
208
+ const listeners = this.#listeners.get(method) ?? /* @__PURE__ */ new Set();
209
+ listeners.add(listener);
210
+ this.#listeners.set(method, listeners);
211
+ return () => {
212
+ listeners.delete(listener);
213
+ if (listeners.size === 0) this.#listeners.delete(method);
214
+ };
215
+ }
216
+ #send(method, params, sessionId) {
217
+ if (this.#closed) return Promise.reject(new Error("CDP client is closed"));
218
+ const id = this.#nextId++;
219
+ return new Promise((resolve, reject) => {
220
+ const timeout = setTimeout(() => {
221
+ this.#pending.delete(id);
222
+ reject(new Error(`CDP command '${method}' timed out`));
223
+ }, this.#commandTimeoutMs);
224
+ this.#pending.set(id, { resolve, reject, timeout });
225
+ try {
226
+ this.#socket.send(
227
+ JSON.stringify({ id, method, params, ...sessionId ? { sessionId } : {} })
228
+ );
229
+ } catch (error) {
230
+ clearTimeout(timeout);
231
+ this.#pending.delete(id);
232
+ reject(error instanceof Error ? error : new Error(String(error)));
233
+ }
234
+ });
235
+ }
236
+ async evaluate(expression) {
237
+ const response = await this.command("Runtime.evaluate", {
238
+ expression,
239
+ awaitPromise: true,
240
+ returnByValue: true
241
+ });
242
+ if (!isRecord(response)) throw new Error("Runtime.evaluate returned an invalid result");
243
+ if (isRecord(response.exceptionDetails)) {
244
+ const text = typeof response.exceptionDetails.text === "string" ? response.exceptionDetails.text : "Renderer evaluation failed";
245
+ throw new Error(text);
246
+ }
247
+ if (!isRecord(response.result) || !("value" in response.result)) {
248
+ throw new Error("Runtime.evaluate did not return a value");
249
+ }
250
+ return response.result.value;
251
+ }
252
+ close() {
253
+ if (this.#closed) return;
254
+ this.#closed = true;
255
+ this.#fail(new Error("CDP client closed"));
256
+ this.#listeners.clear();
257
+ this.#socket.close();
258
+ }
259
+ #onMessage(event) {
260
+ try {
261
+ const value = JSON.parse(messageText(event.data));
262
+ const response = parseResponse(value);
263
+ if (!response) {
264
+ if (!isRecord(value) || typeof value.method !== "string") return;
265
+ const sessionId = typeof value.sessionId === "string" ? value.sessionId : void 0;
266
+ for (const listener of this.#listeners.get(value.method) ?? []) {
267
+ listener(value.params, sessionId);
268
+ }
269
+ return;
270
+ }
271
+ const pending = this.#pending.get(response.id);
272
+ if (!pending) return;
273
+ clearTimeout(pending.timeout);
274
+ this.#pending.delete(response.id);
275
+ if (response.error) {
276
+ const code = response.error.code === void 0 ? "unknown" : String(response.error.code);
277
+ pending.reject(
278
+ new Error(response.error.message ?? `CDP command failed with error ${code}`)
279
+ );
280
+ } else {
281
+ pending.resolve(response.result);
282
+ }
283
+ } catch (error) {
284
+ this.#fail(error instanceof Error ? error : new Error(String(error)));
285
+ }
286
+ }
287
+ #fail(error) {
288
+ for (const pending of this.#pending.values()) {
289
+ clearTimeout(pending.timeout);
290
+ pending.reject(error);
291
+ }
292
+ this.#pending.clear();
293
+ }
294
+ };
295
+
296
+ // packages/desktop-control/src/main-process-title-policy.ts
297
+ function isRecord2(value) {
298
+ return typeof value === "object" && value !== null && !Array.isArray(value);
299
+ }
300
+ function resultRecord(value, command) {
301
+ if (!isRecord2(value)) throw new Error(`${command} returned an invalid result`);
302
+ if (isRecord2(value.exceptionDetails)) {
303
+ const exception = value.exceptionDetails.exception;
304
+ const description = isRecord2(exception) ? exception.description : null;
305
+ const text = value.exceptionDetails.text;
306
+ throw new Error(
307
+ typeof description === "string" ? description : typeof text === "string" ? text : `${command} failed`
308
+ );
309
+ }
310
+ return value;
311
+ }
312
+ function remoteObjectId(value, label) {
313
+ if (!isRecord2(value) || typeof value.objectId !== "string") {
314
+ throw new Error(`${label} is unavailable`);
315
+ }
316
+ return value.objectId;
317
+ }
318
+ function properties(value, command) {
319
+ const result = resultRecord(value, command).result;
320
+ if (!Array.isArray(result)) throw new Error(`${command} returned invalid properties`);
321
+ return result.filter(isRecord2);
322
+ }
323
+ var ELECTRON_MODULE_EXPRESSION = `(() => {
324
+ const mainModule = process.mainModule;
325
+ if (mainModule != null && typeof mainModule.require === 'function') {
326
+ return mainModule.require('electron');
327
+ }
328
+ const { createRequire } = process.getBuiltinModule('module');
329
+ return createRequire(process.execPath)('electron');
330
+ })()`;
331
+ var CONNECT_APP_HOST_CHANNEL = "codex_desktop:connect-app-host";
332
+ var REVIEWED_TITLE_SERVICE_IDENTITIES = ["Dhe", "Nye", "wbe", "nxe", "tTe"];
333
+ var POLICY_STATE_SYMBOL = "codexhost.main-process-title-policy.v1";
334
+ var SERVICE_OWNER_SYMBOL = "codexhost.main-process-title-policy.owner.v1";
335
+ var RENDERER_READY_EXPRESSION = "(() => { Object.defineProperty(window, '__codexhostMainProcessTitlePolicyV1', { configurable: true, value: { state: 'ready' } }); return 'ready'; })()";
336
+ var INSTALL_POLICY_FUNCTION = `async function (rendererWebContentsId) {
337
+ const mainModule = process.mainModule;
338
+ const electron = mainModule != null && typeof mainModule.require === 'function'
339
+ ? mainModule.require('electron')
340
+ : process.getBuiltinModule('module').createRequire(process.execPath)('electron');
341
+ const selected = electron.webContents.fromId(rendererWebContentsId);
342
+ if (selected == null || selected.isDestroyed() || selected.getType() !== 'window') {
343
+ throw new Error('Owned Renderer unavailable for title policy');
344
+ }
345
+
346
+ const context = this(selected);
347
+ if (context == null || typeof context.createAppHost !== 'function') {
348
+ throw new Error('WindowContext unavailable for title policy');
349
+ }
350
+ const stateSymbol = Symbol.for(${JSON.stringify(POLICY_STATE_SYMBOL)});
351
+ const ownerSymbol = Symbol.for(${JSON.stringify(SERVICE_OWNER_SYMBOL)});
352
+ globalThis[stateSymbol]?.dispose?.();
353
+
354
+ const originalCreateAppHost = context.createAppHost;
355
+ const sampleHost = originalCreateAppHost.call(context, selected);
356
+ const sampleService = sampleHost?.services?.threadMetadataGeneration;
357
+ const servicePrototype = sampleService == null ? null : Object.getPrototypeOf(sampleService);
358
+ const originalGenerateTitle = servicePrototype?.generateTitle;
359
+ if (
360
+ servicePrototype == null ||
361
+ typeof originalGenerateTitle !== 'function' ||
362
+ !Function.prototype.toString.call(originalGenerateTitle).includes('Failed to generate thread title')
363
+ ) {
364
+ throw new Error('ThreadMetadataGenerationService signature mismatch');
365
+ }
366
+ const rawServiceClass = sampleService?.constructor?.name;
367
+ const serviceClass = typeof rawServiceClass === 'string' && /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.test(rawServiceClass)
368
+ ? rawServiceClass
369
+ : 'unknown';
370
+ const warnings = ${JSON.stringify(REVIEWED_TITLE_SERVICE_IDENTITIES)}.includes(serviceClass)
371
+ ? []
372
+ : [{
373
+ capability: 'title-isolation',
374
+ reason: 'unreviewed-title-service-identity',
375
+ observedIdentity: serviceClass,
376
+ }];
377
+
378
+ const counters = {
379
+ codexTitleCalls: 0,
380
+ piTitleSkips: 0,
381
+ externalTitleSkips: 0,
382
+ ambiguousTitleSkips: 0,
383
+ };
384
+ const ownedWebContentsIds = new Set();
385
+ const ownService = (service, contents) => {
386
+ const existingOwner = service[ownerSymbol];
387
+ if (existingOwner != null && existingOwner !== contents) {
388
+ throw new Error('Thread metadata service ownership mismatch');
389
+ }
390
+ if (existingOwner == null) {
391
+ Object.defineProperty(service, ownerSymbol, { value: contents });
392
+ }
393
+ ownedWebContentsIds.add(contents.id);
394
+ };
395
+ ownService(sampleService, selected);
396
+ const wrappedCreateAppHost = function (contents) {
397
+ const host = originalCreateAppHost.call(this, contents);
398
+ const service = host?.services?.threadMetadataGeneration;
399
+ if (service != null) ownService(service, contents);
400
+ return host;
401
+ };
402
+ const wrappedGenerateTitle = async function (params) {
403
+ const owner = this[ownerSymbol];
404
+ if (owner == null || owner.isDestroyed()) {
405
+ counters.ambiguousTitleSkips += 1;
406
+ return null;
407
+ }
408
+ let selection = null;
409
+ try {
410
+ selection = await owner.executeJavaScript(
411
+ "window.__codexhostRendererBindingProbeV1?.lockedSelection() ?? null",
412
+ true,
413
+ );
414
+ } catch {}
415
+ if (selection?.phase !== 'locked') {
416
+ counters.ambiguousTitleSkips += 1;
417
+ return null;
418
+ }
419
+ if (selection.agent === 'pi') {
420
+ counters.piTitleSkips += 1;
421
+ return null;
422
+ }
423
+ if (selection.agent !== 'codex') {
424
+ counters.externalTitleSkips += 1;
425
+ return null;
426
+ }
427
+ counters.codexTitleCalls += 1;
428
+ return originalGenerateTitle.call(this, params);
429
+ };
430
+
431
+ context.createAppHost = wrappedCreateAppHost;
432
+ servicePrototype.generateTitle = wrappedGenerateTitle;
433
+ const state = {
434
+ counters,
435
+ ownedWebContentsIds,
436
+ dispose() {
437
+ if (context.createAppHost === wrappedCreateAppHost) {
438
+ context.createAppHost = originalCreateAppHost;
439
+ }
440
+ if (servicePrototype.generateTitle === wrappedGenerateTitle) {
441
+ servicePrototype.generateTitle = originalGenerateTitle;
442
+ }
443
+ if (globalThis[stateSymbol] === state) delete globalThis[stateSymbol];
444
+ },
445
+ };
446
+ globalThis[stateSymbol] = state;
447
+ return {
448
+ state: 'ready',
449
+ reason: 'ready',
450
+ requiresRendererReload: true,
451
+ warnings,
452
+ };
453
+ }`;
454
+ async function installMainProcessTitlePolicy(inspector, rendererWebContentsId) {
455
+ if (!Number.isInteger(rendererWebContentsId) || rendererWebContentsId <= 0) {
456
+ throw new Error("Renderer webContents ID must be a positive integer");
457
+ }
458
+ const listenerResponse = resultRecord(
459
+ await inspector.command("Runtime.evaluate", {
460
+ expression: `(${ELECTRON_MODULE_EXPRESSION}).ipcMain.listeners(${JSON.stringify(
461
+ CONNECT_APP_HOST_CHANNEL
462
+ )})[0]`
463
+ }),
464
+ "Runtime.evaluate"
465
+ );
466
+ const listener = listenerResponse.result;
467
+ const listenerId = remoteObjectId(listener, "connect-app-host listener");
468
+ const listenerProperties = resultRecord(
469
+ await inspector.command("Runtime.getProperties", { objectId: listenerId }),
470
+ "Runtime.getProperties"
471
+ );
472
+ const internalProperties = listenerProperties.internalProperties;
473
+ if (!Array.isArray(internalProperties)) {
474
+ throw new Error("connect-app-host listener scopes are unavailable");
475
+ }
476
+ const scopes = internalProperties.find(
477
+ (property) => isRecord2(property) && property.name === "[[Scopes]]"
478
+ );
479
+ const scopesId = remoteObjectId(
480
+ isRecord2(scopes) ? scopes.value : null,
481
+ "connect-app-host listener scopes"
482
+ );
483
+ const scopeProperties = properties(
484
+ await inspector.command("Runtime.getProperties", {
485
+ objectId: scopesId,
486
+ ownProperties: true
487
+ }),
488
+ "Runtime.getProperties"
489
+ );
490
+ const localScope = scopeProperties.find((property) => property.name === "0");
491
+ const localScopeId = remoteObjectId(localScope?.value, "connect-app-host local scope");
492
+ const localProperties = properties(
493
+ await inspector.command("Runtime.getProperties", {
494
+ objectId: localScopeId,
495
+ ownProperties: true
496
+ }),
497
+ "Runtime.getProperties"
498
+ );
499
+ const getContext = localProperties.find(
500
+ (property) => property.name === "f" && typeof property.value?.objectId === "string"
501
+ );
502
+ const getContextId = remoteObjectId(getContext?.value, "getContextForWebContents");
503
+ const installPromise = resultRecord(
504
+ await inspector.command("Runtime.callFunctionOn", {
505
+ objectId: getContextId,
506
+ functionDeclaration: INSTALL_POLICY_FUNCTION,
507
+ arguments: [{ value: rendererWebContentsId }]
508
+ }),
509
+ "Runtime.callFunctionOn"
510
+ );
511
+ const promiseObjectId = remoteObjectId(
512
+ installPromise.result,
513
+ "Main-process title policy installation promise"
514
+ );
515
+ const installResponse = resultRecord(
516
+ await inspector.command("Runtime.awaitPromise", {
517
+ promiseObjectId,
518
+ returnByValue: true
519
+ }),
520
+ "Runtime.awaitPromise"
521
+ );
522
+ const remoteResult = installResponse.result;
523
+ const value = isRecord2(remoteResult) ? remoteResult.value : null;
524
+ if (!isRecord2(value) || value.state !== "ready" || value.reason !== "ready" || value.requiresRendererReload !== true || !Array.isArray(value.warnings) || value.warnings.length > 1 || value.warnings.some(
525
+ (warning) => !isRecord2(warning) || warning.capability !== "title-isolation" || warning.reason !== "unreviewed-title-service-identity" || typeof warning.observedIdentity !== "string" || !/^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.test(warning.observedIdentity) || Object.keys(warning).length !== 3
526
+ )) {
527
+ throw new Error("Main-process title policy returned an invalid status");
528
+ }
529
+ return value;
530
+ }
531
+ async function markRendererTitlePolicyReady(inspector, rendererWebContentsId) {
532
+ if (!Number.isInteger(rendererWebContentsId) || rendererWebContentsId <= 0) {
533
+ throw new Error("Renderer webContents ID must be a positive integer");
534
+ }
535
+ const value = await inspector.evaluate(`(async () => {
536
+ const state = globalThis[Symbol.for(${JSON.stringify(POLICY_STATE_SYMBOL)})];
537
+ if (state == null) throw new Error('Main-process title policy is unavailable');
538
+ const mainModule = process.mainModule;
539
+ const electron = mainModule != null && typeof mainModule.require === 'function'
540
+ ? mainModule.require('electron')
541
+ : process.getBuiltinModule('module').createRequire(process.execPath)('electron');
542
+ const selected = electron.webContents.fromId(${rendererWebContentsId});
543
+ if (selected == null || selected.isDestroyed() || selected.getType() !== 'window') {
544
+ throw new Error('Owned Renderer unavailable for title policy readiness');
545
+ }
546
+ if (!state.ownedWebContentsIds.has(selected.id)) {
547
+ throw new Error('Renderer metadata service ownership is unavailable');
548
+ }
549
+ const marker = selected.executeJavaScript(
550
+ ${JSON.stringify(RENDERER_READY_EXPRESSION)},
551
+ true,
552
+ );
553
+ const markerTimeout = new Promise((_, reject) => {
554
+ setTimeout(() => reject(new Error('Renderer readiness marker timed out')), 2_000);
555
+ });
556
+ await Promise.race([marker, markerTimeout]);
557
+ return { state: 'ready', reason: 'owned-metadata-service' };
558
+ })()`);
559
+ if (!isRecord2(value) || value.state !== "ready" || value.reason !== "owned-metadata-service") {
560
+ throw new Error("Renderer title policy returned an invalid readiness status");
561
+ }
562
+ return value;
563
+ }
564
+ async function readMainProcessTitlePolicyCounters(inspector) {
565
+ const value = await inspector.evaluate(`(() => {
566
+ const state = globalThis[Symbol.for(${JSON.stringify(POLICY_STATE_SYMBOL)})];
567
+ return state == null ? null : { ...state.counters };
568
+ })()`);
569
+ if (value === null) return null;
570
+ if (!isRecord2(value) || !Number.isInteger(value.codexTitleCalls) || !Number.isInteger(value.piTitleSkips) || !Number.isInteger(value.externalTitleSkips) || !Number.isInteger(value.ambiguousTitleSkips)) {
571
+ throw new Error("Main-process title policy returned invalid counters");
572
+ }
573
+ return value;
574
+ }
575
+
576
+ // packages/desktop-control/src/renderer-draft-prewarm-runtime.ts
577
+ function installDraftPrewarmPolicyBridge(bridge, hostId, target) {
578
+ const existing = target.__codexhostDraftPrewarmPolicyV1;
579
+ existing?.dispose?.();
580
+ const originalSend = bridge.sendRequest;
581
+ let selectedModel = null;
582
+ let clearInFlight = null;
583
+ const isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
584
+ const routeCollaborationMode = (method, parameters) => {
585
+ if (selectedModel === null || !isRecord5(parameters)) return parameters;
586
+ const collaborationMode = parameters.collaborationMode;
587
+ const settings = isRecord5(collaborationMode) ? collaborationMode.settings : null;
588
+ if (!isRecord5(collaborationMode) || !isRecord5(settings)) {
589
+ throw new Error(`Codex ${method} omitted collaborationMode.settings`);
590
+ }
591
+ return {
592
+ ...parameters,
593
+ collaborationMode: {
594
+ ...collaborationMode,
595
+ settings: { ...settings, model: selectedModel }
596
+ }
597
+ };
598
+ };
599
+ const routeThreadStart = (method, parameters) => {
600
+ if (selectedModel === null || !isRecord5(parameters)) return parameters;
601
+ const threadParams = method === "prewarm-thread-start-for-host" ? parameters.params : parameters.method === "thread/start" ? parameters.params : null;
602
+ if (!isRecord5(threadParams) || threadParams.ephemeral === true) return parameters;
603
+ return { ...parameters, params: { ...threadParams, model: selectedModel } };
604
+ };
605
+ const routedSend = (method, parameters, options) => {
606
+ const routedParameters = method === "start-conversation" || method === "prewarm-conversation-for-host" ? routeCollaborationMode(method, parameters) : method === "prewarm-thread-start-for-host" || method === "send-cli-request-for-host" ? routeThreadStart(method, parameters) : parameters;
607
+ return options === void 0 ? originalSend.call(bridge, method, routedParameters) : originalSend.call(bridge, method, routedParameters, options);
608
+ };
609
+ bridge.sendRequest = routedSend;
610
+ const policy = Object.freeze({
611
+ state: "ready",
612
+ select(model) {
613
+ if (model !== null && (typeof model !== "string" || !model.startsWith("codexhost/"))) {
614
+ throw new Error("Draft route Model must be a codexhost transport carrier");
615
+ }
616
+ if (selectedModel === model) return false;
617
+ selectedModel = model;
618
+ return true;
619
+ },
620
+ clear() {
621
+ if (clearInFlight === null) {
622
+ clearInFlight = Promise.resolve(
623
+ originalSend.call(bridge, "clear-prewarmed-threads-for-host", { hostId })
624
+ ).then(() => void 0).finally(() => {
625
+ clearInFlight = null;
626
+ });
627
+ }
628
+ return clearInFlight;
629
+ },
630
+ dispose() {
631
+ if (bridge.sendRequest === routedSend) bridge.sendRequest = originalSend;
632
+ selectedModel = null;
633
+ }
634
+ });
635
+ Object.defineProperty(target, "__codexhostDraftPrewarmPolicyV1", {
636
+ configurable: true,
637
+ value: policy
638
+ });
639
+ return { state: "ready", reason: "owned-request-bridge" };
640
+ }
641
+ async function installDraftPrewarmPolicyInRenderer(contents, findRequestManagerExpression, installRendererPolicyFunction) {
642
+ if (contents === null || contents.isDestroyed() || contents.getType() !== "window") {
643
+ throw new Error("Owned Renderer is unavailable for draft prewarm policy");
644
+ }
645
+ let attachedHere = false;
646
+ try {
647
+ if (!contents.debugger.isAttached()) {
648
+ contents.debugger.attach("1.3");
649
+ attachedHere = true;
650
+ }
651
+ await contents.debugger.sendCommand("Runtime.enable");
652
+ const managerResult = await contents.debugger.sendCommand("Runtime.evaluate", {
653
+ expression: findRequestManagerExpression
654
+ });
655
+ const managerResultId = managerResult.result?.objectId;
656
+ if (typeof managerResultId !== "string") {
657
+ throw new Error("Renderer request manager inspection failed");
658
+ }
659
+ const managerProperties = await contents.debugger.sendCommand("Runtime.getProperties", {
660
+ objectId: managerResultId,
661
+ ownProperties: true
662
+ });
663
+ const candidateCount = managerProperties.result?.find(
664
+ (property) => property.name === "candidateCount"
665
+ )?.value?.value;
666
+ const hostId = managerProperties.result?.find((property) => property.name === "hostId")?.value?.value;
667
+ const manager = managerProperties.result?.find(
668
+ (property) => property.name === "manager"
669
+ )?.value;
670
+ if (candidateCount !== 1 || hostId !== "local" || typeof manager?.objectId !== "string") {
671
+ throw new Error("Renderer request manager is ambiguous");
672
+ }
673
+ const managerFunctions = await contents.debugger.sendCommand("Runtime.getProperties", {
674
+ objectId: manager.objectId
675
+ });
676
+ let managerSendRequestId = managerFunctions.result?.find(
677
+ (property) => property.name === "sendRequest"
678
+ )?.value?.objectId;
679
+ const managerPrototypeId = managerFunctions.internalProperties?.find(
680
+ (property) => property.name === "[[Prototype]]"
681
+ )?.value?.objectId;
682
+ if (typeof managerSendRequestId !== "string" && typeof managerPrototypeId === "string") {
683
+ const prototypeFunctions = await contents.debugger.sendCommand("Runtime.getProperties", {
684
+ objectId: managerPrototypeId,
685
+ ownProperties: true
686
+ });
687
+ managerSendRequestId = prototypeFunctions.result?.find(
688
+ (property) => property.name === "sendRequest"
689
+ )?.value?.objectId;
690
+ }
691
+ if (typeof managerSendRequestId !== "string") {
692
+ throw new Error("Renderer request manager sendRequest is unavailable");
693
+ }
694
+ const functionProperties = await contents.debugger.sendCommand("Runtime.getProperties", {
695
+ objectId: managerSendRequestId
696
+ });
697
+ const scopesId = functionProperties.internalProperties?.find(
698
+ (property) => property.name === "[[Scopes]]"
699
+ )?.value?.objectId;
700
+ const hostBridgeCandidates = [];
701
+ if (typeof scopesId === "string") {
702
+ const scopes = await contents.debugger.sendCommand("Runtime.getProperties", {
703
+ objectId: scopesId,
704
+ ownProperties: true
705
+ });
706
+ for (const scope of scopes.result ?? []) {
707
+ if (typeof scope.value?.objectId !== "string") continue;
708
+ const scopeProperties = await contents.debugger.sendCommand("Runtime.getProperties", {
709
+ objectId: scope.value.objectId,
710
+ ownProperties: true
711
+ });
712
+ for (const property of scopeProperties.result ?? []) {
713
+ if (property.value?.type !== "object" || typeof property.value.objectId !== "string") {
714
+ continue;
715
+ }
716
+ const candidateSignature = await contents.debugger.sendCommand(
717
+ "Runtime.callFunctionOn",
718
+ {
719
+ objectId: property.value.objectId,
720
+ functionDeclaration: "function(){const send=this.sendRequest;return typeof send==='function'&&Function.prototype.toString.call(send).includes('messageHandler')}",
721
+ returnByValue: true
722
+ }
723
+ );
724
+ if (candidateSignature.result?.value === true) {
725
+ hostBridgeCandidates.push({
726
+ name: String(property.name),
727
+ objectId: property.value.objectId
728
+ });
729
+ }
730
+ }
731
+ }
732
+ }
733
+ const hostBridge = hostBridgeCandidates[0];
734
+ if (hostBridgeCandidates.length !== 1 || !hostBridge) {
735
+ throw new Error("Renderer Host request bridge is ambiguous");
736
+ }
737
+ const installed = await contents.debugger.sendCommand("Runtime.callFunctionOn", {
738
+ objectId: hostBridge.objectId,
739
+ functionDeclaration: installRendererPolicyFunction,
740
+ arguments: [{ value: hostId }],
741
+ awaitPromise: true,
742
+ returnByValue: true
743
+ });
744
+ return installed.result?.value;
745
+ } finally {
746
+ if (attachedHere && contents.debugger.isAttached()) contents.debugger.detach();
747
+ }
748
+ }
749
+
750
+ // packages/desktop-control/src/renderer-draft-prewarm-policy.ts
751
+ function isRecord3(value) {
752
+ return typeof value === "object" && value !== null && !Array.isArray(value);
753
+ }
754
+ var FIND_REQUEST_MANAGER_EXPRESSION = `(() => {
755
+ const editors = [...document.querySelectorAll(
756
+ '[data-codex-composer], [contenteditable="true"][role="textbox"]',
757
+ )];
758
+ if (editors.length !== 1) {
759
+ return { candidateCount: 0, hostId: null, sendRequest: null };
760
+ }
761
+ let element = editors[0];
762
+ let fiber = null;
763
+ while (element != null && fiber == null) {
764
+ const key = Object.getOwnPropertyNames(element).find((name) =>
765
+ name.startsWith('__reactFiber$'),
766
+ );
767
+ if (key != null) fiber = element[key];
768
+ element = element.parentElement;
769
+ }
770
+ const managers = new Set();
771
+ for (let depth = 0; fiber != null && depth < 200; depth += 1, fiber = fiber.return) {
772
+ let hook = fiber.memoizedState;
773
+ for (let index = 0; hook != null && index < 120; index += 1, hook = hook.next) {
774
+ const value = hook.memoizedState;
775
+ if (
776
+ value != null &&
777
+ typeof value === 'object' &&
778
+ value.requestClient != null &&
779
+ typeof value.requestClient.prewarmThreadStart === 'function' &&
780
+ typeof value.sendRequest === 'function' &&
781
+ Function.prototype.toString.call(value.sendRequest).includes(
782
+ 'send-cli-request-for-host',
783
+ )
784
+ ) {
785
+ managers.add(value);
786
+ }
787
+ }
788
+ }
789
+ const manager = managers.size === 1 ? managers.values().next().value : null;
790
+ return {
791
+ candidateCount: managers.size,
792
+ hostId: manager?.getHostId?.() ?? null,
793
+ manager,
794
+ };
795
+ })()`;
796
+ var INSTALL_RENDERER_POLICY_FUNCTION = `function(hostId) {
797
+ return (${installDraftPrewarmPolicyBridge.toString()})(this, hostId, window);
798
+ }`;
799
+ var REQUEST_MANAGER_WAIT_TIMEOUT_MS = 6e4;
800
+ var REQUEST_MANAGER_POLL_INTERVAL_MS = 25;
801
+ function mainProcessInstaller(rendererWebContentsId) {
802
+ return `async function () {
803
+ const mainModule = process.mainModule;
804
+ const electron = mainModule != null && typeof mainModule.require === 'function'
805
+ ? mainModule.require('electron')
806
+ : process.getBuiltinModule('module').createRequire(process.execPath)('electron');
807
+ const contents = electron.webContents.fromId(${rendererWebContentsId});
808
+ return (${installDraftPrewarmPolicyInRenderer.toString()})(
809
+ contents,
810
+ ${JSON.stringify(FIND_REQUEST_MANAGER_EXPRESSION)},
811
+ ${JSON.stringify(INSTALL_RENDERER_POLICY_FUNCTION)}
812
+ );
813
+ }`;
814
+ }
815
+ async function installRendererDraftPrewarmPolicy(inspector, rendererWebContentsId) {
816
+ if (!Number.isInteger(rendererWebContentsId) || rendererWebContentsId <= 0) {
817
+ throw new Error("Renderer webContents ID must be a positive integer");
818
+ }
819
+ const installer = mainProcessInstaller(rendererWebContentsId);
820
+ const deadline = Date.now() + REQUEST_MANAGER_WAIT_TIMEOUT_MS;
821
+ while (true) {
822
+ try {
823
+ const value = await inspector.evaluate(`(${installer})()`);
824
+ if (!isRecord3(value) || value.state !== "ready" || value.reason !== "owned-request-bridge") {
825
+ throw new Error("Renderer draft prewarm policy returned an invalid status");
826
+ }
827
+ return value;
828
+ } catch (error) {
829
+ const message = error instanceof Error ? error.message : String(error);
830
+ const remaining = deadline - Date.now();
831
+ if (!message.includes("Renderer request manager is ambiguous") || remaining <= 0) throw error;
832
+ await new Promise((resolve) => {
833
+ setTimeout(resolve, Math.min(REQUEST_MANAGER_POLL_INTERVAL_MS, remaining));
834
+ });
835
+ }
836
+ }
837
+ }
838
+
839
+ // packages/desktop-control/src/renderer-control-session.ts
840
+ function isRecord4(value) {
841
+ return typeof value === "object" && value !== null && !Array.isArray(value);
842
+ }
843
+ function sleep(milliseconds) {
844
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
845
+ }
846
+ function sameAgents(actual, expected) {
847
+ return actual.length === expected.length && actual.every((agent, index) => agent === expected[index]);
848
+ }
849
+ var RendererAdapterReadinessError = class extends Error {
850
+ constructor(state, reason) {
851
+ super(`Production Renderer Adapter is ${state}: ${reason}`);
852
+ this.state = state;
853
+ this.reason = reason;
854
+ this.name = "RendererAdapterReadinessError";
855
+ }
856
+ state;
857
+ reason;
858
+ };
859
+ function validateBindingStatus(value, expectedAgents) {
860
+ if (!isRecord4(value) || value.version !== 2 || !Array.isArray(value.enabledAgents) || value.enabledAgents.some((agent) => typeof agent !== "string") || !sameAgents(value.enabledAgents, expectedAgents) || !isRecord4(value.adapter)) {
861
+ throw new Error("Production Renderer binding returned an invalid status");
862
+ }
863
+ if (value.adapter.state !== "ready" || typeof value.adapter.reason !== "string") {
864
+ const state = typeof value.adapter.state === "string" ? value.adapter.state : "invalid";
865
+ const reason = typeof value.adapter.reason === "string" ? value.adapter.reason : "unknown";
866
+ throw new RendererAdapterReadinessError(state, reason);
867
+ }
868
+ return value;
869
+ }
870
+ function selectRendererWebContents(contents) {
871
+ const candidates = contents.filter(
872
+ (item) => item.type === "window" && item.surface === "primary" && item.runtime.available && item.runtime.elementCount !== null
873
+ ).toSorted(
874
+ (left, right) => (right.runtime.elementCount ?? 0) - (left.runtime.elementCount ?? 0)
875
+ );
876
+ return candidates.find((candidate) => (candidate.runtime.elementCount ?? 0) > 0) ?? null;
877
+ }
878
+ async function waitForRendererTitlePolicyReady(markReadiness, options = {}) {
879
+ const timeoutMs = options.timeoutMs ?? 3e4;
880
+ const pollIntervalMs = options.pollIntervalMs ?? 250;
881
+ const now = options.now ?? Date.now;
882
+ const wait = options.sleep ?? sleep;
883
+ const deadline = now() + timeoutMs;
884
+ let lastError;
885
+ while (now() < deadline) {
886
+ try {
887
+ return await markReadiness();
888
+ } catch (error) {
889
+ lastError = error;
890
+ }
891
+ await wait(pollIntervalMs);
892
+ }
893
+ const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
894
+ throw new Error(`Renderer title policy ownership did not become ready${detail}`);
895
+ }
896
+ async function waitForInspectorTarget(endpoint, options = {}) {
897
+ const timeoutMs = options.timeoutMs ?? 3e4;
898
+ const pollIntervalMs = options.pollIntervalMs ?? 250;
899
+ const deadline = Date.now() + timeoutMs;
900
+ let lastError;
901
+ while (Date.now() < deadline) {
902
+ try {
903
+ const targets = await listCdpTargets(endpoint, options.fetchImpl);
904
+ const inspector = targets.find((target) => target.type === "node");
905
+ if (inspector) return inspector;
906
+ lastError = new Error("Inspector has no Node target");
907
+ } catch (error) {
908
+ lastError = error;
909
+ }
910
+ await sleep(pollIntervalMs);
911
+ }
912
+ const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
913
+ throw new Error(`Electron main-process Inspector did not become ready${detail}`);
914
+ }
915
+ var electronModuleExpression = `(() => {
916
+ const mainModule = process.mainModule;
917
+ if (mainModule != null && typeof mainModule.require === 'function') {
918
+ return mainModule.require('electron');
919
+ }
920
+ const { createRequire } = process.getBuiltinModule('module');
921
+ return createRequire(process.execPath)('electron');
922
+ })()`;
923
+ var webContentsRuntimeExpression = "(() => ({ elementCount: document.querySelectorAll('*').length }))()";
924
+ async function inspectElectronWebContents(inspector) {
925
+ const value = await inspector.evaluate(`(async () => {
926
+ const { webContents } = ${electronModuleExpression};
927
+ const result = [];
928
+ for (const contents of webContents.getAllWebContents()) {
929
+ let runtime = { available: false, elementCount: null };
930
+ try {
931
+ const evaluation = contents.executeJavaScript(${JSON.stringify(webContentsRuntimeExpression)}, true);
932
+ const timeout = new Promise((_, reject) => {
933
+ setTimeout(() => reject(new Error('Renderer inspection timed out')), 2_000);
934
+ });
935
+ runtime = { available: true, ...(await Promise.race([evaluation, timeout])) };
936
+ } catch {}
937
+ result.push({
938
+ id: contents.id,
939
+ type: contents.getType(),
940
+ surface: contents.getURL().includes('avatar-overlay') ? 'overlay' : 'primary',
941
+ runtime,
942
+ });
943
+ }
944
+ return result;
945
+ })()`);
946
+ if (!Array.isArray(value)) throw new Error("Electron webContents inspection returned an array");
947
+ return value.map((item) => {
948
+ if (!isRecord4(item) || !Number.isInteger(item.id) || typeof item.type !== "string" || !["primary", "overlay"].includes(String(item.surface)) || !isRecord4(item.runtime)) {
949
+ throw new Error("Electron webContents inspection returned an invalid item");
950
+ }
951
+ return {
952
+ id: item.id,
953
+ type: item.type,
954
+ surface: item.surface,
955
+ runtime: {
956
+ available: item.runtime.available === true,
957
+ elementCount: Number.isInteger(item.runtime.elementCount) ? item.runtime.elementCount : null
958
+ }
959
+ };
960
+ });
961
+ }
962
+ async function activateElectronDesktop(inspector) {
963
+ const value = await inspector.evaluate(`(() => {
964
+ const { BrowserWindow } = ${electronModuleExpression};
965
+ const windows = BrowserWindow.getAllWindows().filter((window) => !window.isDestroyed());
966
+ for (const window of windows) {
967
+ if (window.isMinimized()) window.restore();
968
+ window.show();
969
+ window.focus();
970
+ }
971
+ return windows.length;
972
+ })()`);
973
+ if (!Number.isInteger(value) || value < 1) {
974
+ throw new Error("Electron Desktop activation found no live window");
975
+ }
976
+ return value;
977
+ }
978
+ async function executeInWebContents(inspector, rendererWebContentsId, source) {
979
+ return inspector.evaluate(`(async () => {
980
+ const { webContents } = ${electronModuleExpression};
981
+ const contents = webContents.fromId(${rendererWebContentsId});
982
+ if (contents == null || contents.isDestroyed()) throw new Error('Renderer webContents is unavailable');
983
+ const result = await contents.executeJavaScript(${JSON.stringify(source)}, true);
984
+ return result === undefined ? null : result;
985
+ })()`);
986
+ }
987
+ async function reloadRenderer(inspector, rendererWebContentsId) {
988
+ await executeInWebContents(inspector, rendererWebContentsId, "location.reload(); null");
989
+ }
990
+ var defaultOperations = {
991
+ inspect: inspectElectronWebContents,
992
+ installTitlePolicy: installMainProcessTitlePolicy,
993
+ markTitlePolicyReady: markRendererTitlePolicyReady,
994
+ installDraftPrewarmPolicy: installRendererDraftPrewarmPolicy,
995
+ readDraftPrewarmPolicy: (inspector, rendererWebContentsId) => executeInWebContents(
996
+ inspector,
997
+ rendererWebContentsId,
998
+ "window.__codexhostDraftPrewarmPolicyV1?.state ?? null"
999
+ ),
1000
+ reload: reloadRenderer,
1001
+ execute: executeInWebContents,
1002
+ readBinding: (inspector, rendererWebContentsId) => executeInWebContents(
1003
+ inspector,
1004
+ rendererWebContentsId,
1005
+ "window.__codexhostRendererBindingProbeV1?.status() ?? null"
1006
+ ),
1007
+ readTitlePolicyCounters: readMainProcessTitlePolicyCounters
1008
+ };
1009
+ async function waitForRenderer(inspector, operations, timeoutMs, pollIntervalMs) {
1010
+ const deadline = Date.now() + timeoutMs;
1011
+ let candidateCount = 0;
1012
+ while (Date.now() < deadline) {
1013
+ const inventory = await operations.inspect(inspector);
1014
+ candidateCount = inventory.length;
1015
+ const renderer = selectRendererWebContents(inventory);
1016
+ if (renderer) return renderer;
1017
+ await sleep(pollIntervalMs);
1018
+ }
1019
+ throw new Error(`Inspector did not find a live Electron Renderer (${candidateCount} seen)`);
1020
+ }
1021
+ async function waitForBinding(inspector, operations, rendererWebContentsId, enabledAgents, timeoutMs, pollIntervalMs) {
1022
+ const deadline = Date.now() + timeoutMs;
1023
+ let lastError;
1024
+ while (Date.now() < deadline) {
1025
+ try {
1026
+ const value = await operations.readBinding(inspector, rendererWebContentsId);
1027
+ if (value !== null) return validateBindingStatus(value, enabledAgents);
1028
+ } catch (error) {
1029
+ lastError = error;
1030
+ if (error instanceof RendererAdapterReadinessError && error.state !== "installing") {
1031
+ throw error;
1032
+ }
1033
+ }
1034
+ await sleep(pollIntervalMs);
1035
+ }
1036
+ const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
1037
+ throw new Error(`Production Renderer binding did not become ready${detail}`);
1038
+ }
1039
+ var InstalledRendererControlSession = class {
1040
+ constructor(inspector, rendererSource, enabledAgents, timeoutMs, pollIntervalMs, operations, snapshot) {
1041
+ this.inspector = inspector;
1042
+ this.rendererSource = rendererSource;
1043
+ this.enabledAgents = enabledAgents;
1044
+ this.timeoutMs = timeoutMs;
1045
+ this.pollIntervalMs = pollIntervalMs;
1046
+ this.operations = operations;
1047
+ this.#snapshot = snapshot;
1048
+ }
1049
+ inspector;
1050
+ rendererSource;
1051
+ enabledAgents;
1052
+ timeoutMs;
1053
+ pollIntervalMs;
1054
+ operations;
1055
+ #closed = false;
1056
+ #snapshot;
1057
+ get snapshot() {
1058
+ return this.#snapshot;
1059
+ }
1060
+ async ensureInstalled() {
1061
+ if (this.#closed) throw new Error("Renderer Control Session is closed");
1062
+ const selected = await waitForRenderer(
1063
+ this.inspector,
1064
+ this.operations,
1065
+ this.timeoutMs,
1066
+ this.pollIntervalMs
1067
+ );
1068
+ const existing = await this.operations.readBinding(this.inspector, selected.id).catch(() => null);
1069
+ if (existing !== null) {
1070
+ const policyState = await this.operations.readDraftPrewarmPolicy?.(this.inspector, selected.id).catch(() => null);
1071
+ const draftPrewarmPolicy2 = policyState === "ready" ? this.#snapshot.draftPrewarmPolicy : await this.operations.installDraftPrewarmPolicy(this.inspector, selected.id);
1072
+ let binding2;
1073
+ try {
1074
+ binding2 = validateBindingStatus(existing, this.enabledAgents);
1075
+ } catch (error) {
1076
+ if (!(error instanceof RendererAdapterReadinessError)) throw error;
1077
+ await this.operations.execute(this.inspector, selected.id, this.rendererSource);
1078
+ binding2 = await waitForBinding(
1079
+ this.inspector,
1080
+ this.operations,
1081
+ selected.id,
1082
+ this.enabledAgents,
1083
+ this.timeoutMs,
1084
+ this.pollIntervalMs
1085
+ );
1086
+ }
1087
+ this.#snapshot = {
1088
+ ...this.#snapshot,
1089
+ renderer: selected,
1090
+ draftPrewarmPolicy: draftPrewarmPolicy2,
1091
+ binding: binding2
1092
+ };
1093
+ return this.#snapshot;
1094
+ }
1095
+ const titlePolicyReadiness = await waitForRendererTitlePolicyReady(
1096
+ () => this.operations.markTitlePolicyReady(this.inspector, selected.id),
1097
+ { timeoutMs: this.timeoutMs, pollIntervalMs: this.pollIntervalMs }
1098
+ );
1099
+ await activateElectronDesktop(this.inspector);
1100
+ await this.operations.execute(this.inspector, selected.id, this.rendererSource);
1101
+ const draftPrewarmPolicy = await this.operations.installDraftPrewarmPolicy(
1102
+ this.inspector,
1103
+ selected.id
1104
+ );
1105
+ const binding = await waitForBinding(
1106
+ this.inspector,
1107
+ this.operations,
1108
+ selected.id,
1109
+ this.enabledAgents,
1110
+ this.timeoutMs,
1111
+ this.pollIntervalMs
1112
+ );
1113
+ this.#snapshot = {
1114
+ ...this.#snapshot,
1115
+ renderer: selected,
1116
+ titlePolicyReadiness,
1117
+ draftPrewarmPolicy,
1118
+ binding
1119
+ };
1120
+ return this.#snapshot;
1121
+ }
1122
+ activateDesktop() {
1123
+ if (this.#closed) return Promise.reject(new Error("Renderer Control Session is closed"));
1124
+ return activateElectronDesktop(this.inspector);
1125
+ }
1126
+ async requestCompatibilityUpdate() {
1127
+ const value = await this.executeRenderer(
1128
+ "window.__codexhostRendererBindingProbeV1?.requestCompatibilityUpdate?.() ?? 'unavailable'"
1129
+ );
1130
+ if (value !== "update-started" && value !== "current" && value !== "unavailable") {
1131
+ throw new Error("Renderer returned an invalid compatibility update outcome");
1132
+ }
1133
+ return value;
1134
+ }
1135
+ executeRenderer(expression) {
1136
+ if (this.#closed) return Promise.reject(new Error("Renderer Control Session is closed"));
1137
+ return this.operations.execute(
1138
+ this.inspector,
1139
+ this.#snapshot.renderer.id,
1140
+ expression
1141
+ );
1142
+ }
1143
+ readTitlePolicyCounters() {
1144
+ if (this.#closed) return Promise.reject(new Error("Renderer Control Session is closed"));
1145
+ return this.operations.readTitlePolicyCounters(this.inspector);
1146
+ }
1147
+ close() {
1148
+ if (this.#closed) return;
1149
+ this.#closed = true;
1150
+ this.inspector.close();
1151
+ }
1152
+ };
1153
+ var startupTraceStartedAt = Date.now();
1154
+ function startupTrace(stage) {
1155
+ if (process.env.CODEXHOST_STARTUP_TRACE !== "1") return;
1156
+ console.error(
1157
+ `[codexhost startup +${Date.now() - startupTraceStartedAt}ms] renderer-session: ${stage}`
1158
+ );
1159
+ }
1160
+ async function createRendererControlSession(options) {
1161
+ const enabledAgents = options.enabledAgents ?? ["codex", "pi"];
1162
+ const timeoutMs = options.timeoutMs ?? 3e4;
1163
+ const pollIntervalMs = options.pollIntervalMs ?? 250;
1164
+ const operations = options.operations ?? defaultOperations;
1165
+ startupTrace("waiting for initial Renderer");
1166
+ const initial = await waitForRenderer(options.inspector, operations, timeoutMs, pollIntervalMs);
1167
+ startupTrace("installing title policy");
1168
+ const titlePolicy = await operations.installTitlePolicy(options.inspector, initial.id);
1169
+ startupTrace("reloading Renderer");
1170
+ await operations.reload(options.inspector, initial.id);
1171
+ startupTrace("waiting for reloaded Renderer");
1172
+ const selected = await waitForRenderer(options.inspector, operations, timeoutMs, pollIntervalMs);
1173
+ startupTrace("waiting for title policy readiness");
1174
+ const titlePolicyReadiness = await waitForRendererTitlePolicyReady(
1175
+ () => operations.markTitlePolicyReady(options.inspector, selected.id),
1176
+ {
1177
+ timeoutMs,
1178
+ pollIntervalMs
1179
+ }
1180
+ );
1181
+ startupTrace("injecting Renderer bundle");
1182
+ await operations.execute(options.inspector, selected.id, options.rendererSource);
1183
+ startupTrace("installing draft routing policy");
1184
+ const draftPrewarmPolicy = await operations.installDraftPrewarmPolicy(
1185
+ options.inspector,
1186
+ selected.id
1187
+ );
1188
+ startupTrace("waiting for Renderer binding");
1189
+ const binding = await waitForBinding(
1190
+ options.inspector,
1191
+ operations,
1192
+ selected.id,
1193
+ enabledAgents,
1194
+ timeoutMs,
1195
+ pollIntervalMs
1196
+ );
1197
+ return new InstalledRendererControlSession(
1198
+ options.inspector,
1199
+ options.rendererSource,
1200
+ enabledAgents,
1201
+ timeoutMs,
1202
+ pollIntervalMs,
1203
+ operations,
1204
+ {
1205
+ renderer: selected,
1206
+ titlePolicy,
1207
+ titlePolicyReadiness,
1208
+ draftPrewarmPolicy,
1209
+ binding
1210
+ }
1211
+ );
1212
+ }
1213
+ async function installRendererControlSession(options) {
1214
+ const timeoutMs = options.timeoutMs ?? 3e4;
1215
+ const pollIntervalMs = options.pollIntervalMs ?? 250;
1216
+ startupTrace("waiting for Electron Inspector target");
1217
+ const target = await waitForInspectorTarget(options.inspectorEndpoint, {
1218
+ timeoutMs,
1219
+ pollIntervalMs
1220
+ });
1221
+ startupTrace("connecting to Electron Inspector");
1222
+ const inspector = await CdpClient.connect(target.webSocketDebuggerUrl);
1223
+ try {
1224
+ startupTrace("enabling Inspector Runtime domain");
1225
+ await inspector.command("Runtime.enable");
1226
+ return await createRendererControlSession({
1227
+ ...options,
1228
+ inspector,
1229
+ timeoutMs,
1230
+ pollIntervalMs
1231
+ });
1232
+ } catch (error) {
1233
+ inspector.close();
1234
+ throw error;
1235
+ }
1236
+ }
1237
+
1238
+ // packages/desktop-control/src/production-controller.ts
1239
+ var PRODUCTION_INSTALL_TIMEOUT_MS = 9e4;
1240
+ var DESKTOP_CONTROLLER_READINESS_MAX_BYTES = 512;
1241
+ var TRANSIENT_INSTALL_ATTEMPTS = 3;
1242
+ var TRANSIENT_INSTALL_RETRY_MS = 250;
1243
+ var RECOVERY_RETRY_INITIAL_MS = 3e4;
1244
+ var RECOVERY_RETRY_MAX_MS = 3e5;
1245
+ var startupTraceStartedAt2 = Date.now();
1246
+ function startupTrace2(stage, detail) {
1247
+ if (process.env.CODEXHOST_STARTUP_TRACE !== "1") return;
1248
+ const suffix = detail === void 0 ? "" : `: ${detail instanceof Error ? detail.message : String(detail)}`;
1249
+ console.error(
1250
+ `[codexhost startup +${Date.now() - startupTraceStartedAt2}ms] controller: ${stage}${suffix}`
1251
+ );
1252
+ }
1253
+ function validCompatibilityIssue(state, issue) {
1254
+ const keys = Object.keys(issue);
1255
+ if (state === "compatible-with-warning") {
1256
+ return issue.capability === "title-isolation" && issue.reason === "unreviewed-title-service-identity" && typeof issue.observedIdentity === "string" && /^[A-Za-z_$][A-Za-z0-9_$]{0,63}$/.test(issue.observedIdentity) && keys.length === 3;
1257
+ }
1258
+ if (state === "degraded") {
1259
+ return [
1260
+ "permission-control",
1261
+ "sidebar-decoration",
1262
+ "fork-control",
1263
+ "usage-surface",
1264
+ "settings-surface"
1265
+ ].includes(issue.capability) && issue.reason === "capability-unavailable" && issue.observedIdentity === void 0 && keys.length === 2;
1266
+ }
1267
+ return false;
1268
+ }
1269
+ function serializeDesktopControllerReadiness(readiness) {
1270
+ if (readiness.schemaVersion !== 2 || !["compatible", "compatible-with-warning", "degraded"].includes(readiness.state) || !Array.isArray(readiness.issues) || readiness.issues.length > 1 || readiness.state === "compatible" && readiness.issues.length !== 0 || readiness.state !== "compatible" && (readiness.issues.length !== 1 || !readiness.issues[0] || !validCompatibilityIssue(readiness.state, readiness.issues[0])) || Object.keys(readiness).length !== 3) {
1271
+ throw new Error("Desktop Controller readiness is invalid");
1272
+ }
1273
+ const line = JSON.stringify(readiness);
1274
+ if (Buffer.byteLength(line, "utf8") > DESKTOP_CONTROLLER_READINESS_MAX_BYTES) {
1275
+ throw new Error("Desktop Controller readiness exceeds its size limit");
1276
+ }
1277
+ return line;
1278
+ }
1279
+ var defaultDependencies = {
1280
+ readRenderer: (filePath) => readFile(filePath, "utf8"),
1281
+ install: installRendererControlSession,
1282
+ startAttachmentServer: startControllerAttachmentServer,
1283
+ ready: (readiness) => {
1284
+ process.stdout.write(`${serializeDesktopControllerReadiness(readiness)}
1285
+ `);
1286
+ },
1287
+ sleep: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
1288
+ monitorIntervalMs: 500
1289
+ };
1290
+ function inspectorEndpoint(value) {
1291
+ const url = new URL(value);
1292
+ if (url.protocol !== "http:" || !["127.0.0.1", "localhost", "[::1]"].includes(url.hostname) || !url.port || url.username || url.password || url.pathname !== "/" && url.pathname !== "" || url.search || url.hash) {
1293
+ throw new Error("--inspector-endpoint must be a loopback HTTP origin with an explicit port");
1294
+ }
1295
+ return url.origin;
1296
+ }
1297
+ function parseDesktopControllerArguments(arguments_) {
1298
+ let endpoint;
1299
+ let rendererPath;
1300
+ let defaultAgent;
1301
+ let attachmentPort;
1302
+ let attachmentNonce;
1303
+ for (let index = 0; index < arguments_.length; index += 1) {
1304
+ const argument = arguments_[index];
1305
+ const value = arguments_[index + 1];
1306
+ if (argument === "--inspector-endpoint") {
1307
+ if (endpoint !== void 0) throw new Error("--inspector-endpoint may only be provided once");
1308
+ if (!value) throw new Error("--inspector-endpoint requires a value");
1309
+ endpoint = inspectorEndpoint(value);
1310
+ index += 1;
1311
+ continue;
1312
+ }
1313
+ if (argument === "--renderer") {
1314
+ if (rendererPath !== void 0) throw new Error("--renderer may only be provided once");
1315
+ if (!value) throw new Error("--renderer requires a value");
1316
+ if (!path.isAbsolute(value)) throw new Error("--renderer must be an absolute path");
1317
+ rendererPath = path.normalize(value);
1318
+ index += 1;
1319
+ continue;
1320
+ }
1321
+ if (argument === "--default-agent") {
1322
+ if (defaultAgent !== void 0) throw new Error("--default-agent may only be provided once");
1323
+ if (value !== "codex" && value !== "pi") {
1324
+ throw new Error("--default-agent must be 'codex' or 'pi'");
1325
+ }
1326
+ defaultAgent = value;
1327
+ index += 1;
1328
+ continue;
1329
+ }
1330
+ if (argument === "--attachment-port") {
1331
+ if (attachmentPort !== void 0) {
1332
+ throw new Error("--attachment-port may only be provided once");
1333
+ }
1334
+ const port = Number(value);
1335
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
1336
+ throw new Error("--attachment-port must be a valid TCP port");
1337
+ }
1338
+ attachmentPort = port;
1339
+ index += 1;
1340
+ continue;
1341
+ }
1342
+ if (argument === "--attachment-nonce") {
1343
+ if (attachmentNonce !== void 0) {
1344
+ throw new Error("--attachment-nonce may only be provided once");
1345
+ }
1346
+ if (value === void 0 || !/^[0-9a-f]{32}$/.test(value)) {
1347
+ throw new Error("--attachment-nonce must be 32 lowercase hexadecimal characters");
1348
+ }
1349
+ attachmentNonce = value;
1350
+ index += 1;
1351
+ continue;
1352
+ }
1353
+ throw new Error(`unknown Desktop Controller option: ${argument}`);
1354
+ }
1355
+ if (endpoint === void 0) throw new Error("--inspector-endpoint is required");
1356
+ if (rendererPath === void 0) throw new Error("--renderer is required");
1357
+ if (defaultAgent === void 0) throw new Error("--default-agent is required");
1358
+ if (attachmentPort === void 0) throw new Error("--attachment-port is required");
1359
+ if (attachmentNonce === void 0) throw new Error("--attachment-nonce is required");
1360
+ return {
1361
+ inspectorEndpoint: endpoint,
1362
+ rendererPath,
1363
+ defaultAgent,
1364
+ attachmentPort,
1365
+ attachmentNonce
1366
+ };
1367
+ }
1368
+ function isTransientElectronInstallError(error) {
1369
+ let current = error;
1370
+ for (let depth = 0; depth < 4; depth += 1) {
1371
+ const message = current instanceof Error ? current.message : String(current);
1372
+ if (message.includes("Execution context was destroyed") || message.includes("Promise was collected")) {
1373
+ return true;
1374
+ }
1375
+ current = current instanceof Error ? current.cause : void 0;
1376
+ if (current === void 0) break;
1377
+ }
1378
+ return false;
1379
+ }
1380
+ async function installProductionSession(options, dependencies) {
1381
+ for (let attempt = 1; attempt <= TRANSIENT_INSTALL_ATTEMPTS; attempt += 1) {
1382
+ try {
1383
+ return await dependencies.install(options);
1384
+ } catch (error) {
1385
+ if (attempt === TRANSIENT_INSTALL_ATTEMPTS || !isTransientElectronInstallError(error)) {
1386
+ throw error;
1387
+ }
1388
+ await dependencies.sleep(TRANSIENT_INSTALL_RETRY_MS);
1389
+ }
1390
+ }
1391
+ throw new Error("Desktop Controller exhausted Renderer installation attempts");
1392
+ }
1393
+ async function runDesktopController(options, signal, dependencies = defaultDependencies) {
1394
+ const configuration = `Object.defineProperty(window, "__codexhostProductionConfigV1", { configurable: true, value: { defaultAgent: ${JSON.stringify(options.defaultAgent)} } });`;
1395
+ const now = dependencies.now ?? Date.now;
1396
+ let session;
1397
+ let nextRecoveryAt = 0;
1398
+ let recoveryDelayMs = RECOVERY_RETRY_INITIAL_MS;
1399
+ const recordRecoveryFailure = () => {
1400
+ nextRecoveryAt = now() + recoveryDelayMs;
1401
+ recoveryDelayMs = Math.min(recoveryDelayMs * 2, RECOVERY_RETRY_MAX_MS);
1402
+ };
1403
+ const recordRecoverySuccess = () => {
1404
+ nextRecoveryAt = 0;
1405
+ recoveryDelayMs = RECOVERY_RETRY_INITIAL_MS;
1406
+ };
1407
+ const createSession = async () => {
1408
+ startupTrace2("reading Renderer bundle");
1409
+ const rendererSource = await dependencies.readRenderer(options.rendererPath);
1410
+ if (rendererSource.trim().length === 0) throw new Error("production Renderer Bundle is empty");
1411
+ startupTrace2("installing Renderer Session");
1412
+ const installed = await installProductionSession(
1413
+ {
1414
+ inspectorEndpoint: options.inspectorEndpoint,
1415
+ rendererSource: `${configuration}
1416
+ ${rendererSource}`,
1417
+ enabledAgents: ["codex", "pi", "claude-code", "deepseek-harness", "grok"],
1418
+ timeoutMs: PRODUCTION_INSTALL_TIMEOUT_MS
1419
+ },
1420
+ dependencies
1421
+ );
1422
+ startupTrace2("Renderer Session installed");
1423
+ return installed;
1424
+ };
1425
+ startupTrace2("initialization started");
1426
+ try {
1427
+ session = await createSession();
1428
+ recordRecoverySuccess();
1429
+ } catch (error) {
1430
+ startupTrace2("initial Renderer Session unavailable", error);
1431
+ session = void 0;
1432
+ recordRecoveryFailure();
1433
+ }
1434
+ let operation = Promise.resolve(void 0);
1435
+ const useSession = (callback) => {
1436
+ const next = operation.then(callback, callback);
1437
+ operation = next.then(
1438
+ () => void 0,
1439
+ () => void 0
1440
+ );
1441
+ return next;
1442
+ };
1443
+ const resetSession = () => {
1444
+ session?.close();
1445
+ session = void 0;
1446
+ };
1447
+ const ensureSession = async () => {
1448
+ if (!session) session = await createSession();
1449
+ else await session.ensureInstalled();
1450
+ return session;
1451
+ };
1452
+ const recoverSession = async () => {
1453
+ try {
1454
+ const current = await ensureSession();
1455
+ recordRecoverySuccess();
1456
+ return current;
1457
+ } catch (error) {
1458
+ resetSession();
1459
+ recordRecoveryFailure();
1460
+ throw error;
1461
+ }
1462
+ };
1463
+ let attachmentServer;
1464
+ try {
1465
+ startupTrace2("starting attachment server");
1466
+ attachmentServer = await dependencies.startAttachmentServer({
1467
+ port: options.attachmentPort,
1468
+ nonce: options.attachmentNonce,
1469
+ attach: () => useSession(async () => {
1470
+ const current = await recoverSession();
1471
+ await current.activateDesktop();
1472
+ }),
1473
+ compatibilityUpdate: () => useSession(async () => {
1474
+ const current = await recoverSession();
1475
+ return current.requestCompatibilityUpdate();
1476
+ })
1477
+ });
1478
+ startupTrace2("attachment server ready");
1479
+ const issues = session?.snapshot.titlePolicy.warnings ?? [];
1480
+ startupTrace2("publishing readiness");
1481
+ dependencies.ready({
1482
+ schemaVersion: 2,
1483
+ state: issues.length === 0 ? "compatible" : "compatible-with-warning",
1484
+ issues
1485
+ });
1486
+ while (!signal.aborted) {
1487
+ await dependencies.sleep(dependencies.monitorIntervalMs);
1488
+ if (signal.aborted) continue;
1489
+ await useSession(async () => {
1490
+ if (!session && now() < nextRecoveryAt) return;
1491
+ try {
1492
+ await recoverSession();
1493
+ } catch {
1494
+ }
1495
+ });
1496
+ }
1497
+ } finally {
1498
+ await attachmentServer?.close();
1499
+ await operation;
1500
+ resetSession();
1501
+ }
1502
+ }
1503
+
1504
+ // packages/desktop-control/src/release-main.ts
1505
+ var abort = new AbortController();
1506
+ var stop = () => abort.abort();
1507
+ process.once("SIGINT", stop);
1508
+ process.once("SIGTERM", stop);
1509
+ try {
1510
+ await runDesktopController(parseDesktopControllerArguments(process.argv.slice(2)), abort.signal);
1511
+ } catch (error) {
1512
+ console.error(
1513
+ `codexhost Desktop Controller: ${error instanceof Error ? error.message : String(error)}`
1514
+ );
1515
+ process.exitCode = 1;
1516
+ } finally {
1517
+ process.removeListener("SIGINT", stop);
1518
+ process.removeListener("SIGTERM", stop);
1519
+ }