@codexhost/cli-linux-arm64 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  // packages/desktop-control/src/production-controller.ts
2
2
  import { readFile } from "node:fs/promises";
3
- import path from "node:path";
3
+ import path2 from "node:path";
4
4
 
5
5
  // packages/desktop-control/src/controller-attachment-server.ts
6
6
  import { createServer } from "node:net";
@@ -87,7 +87,106 @@ async function startControllerAttachmentServer(options) {
87
87
  };
88
88
  }
89
89
 
90
+ // packages/desktop-control/src/controller-status.ts
91
+ import { randomBytes } from "node:crypto";
92
+ import { mkdir, rename, rm, writeFile } from "node:fs/promises";
93
+ import os from "node:os";
94
+ import path from "node:path";
95
+ var DESKTOP_CONTROLLER_STATUS_FILE = "desktop-controller-v1.json";
96
+ var ERROR_MAX_LENGTH = 1e3;
97
+ function defaultControllerStatusPath(environment = process.env) {
98
+ const dataDirectory = environment.CODEXHOST_DATA_DIR ? path.resolve(environment.CODEXHOST_DATA_DIR) : path.join(os.homedir(), ".codexhost");
99
+ return path.join(dataDirectory, "diagnostics", DESKTOP_CONTROLLER_STATUS_FILE);
100
+ }
101
+ function errorMessage(error) {
102
+ const parts = [];
103
+ let current = error;
104
+ for (let depth = 0; depth < 4 && current !== void 0; depth += 1) {
105
+ parts.push(current instanceof Error ? current.message : String(current));
106
+ current = current instanceof Error ? current.cause : void 0;
107
+ }
108
+ return parts.join(": ").slice(0, ERROR_MAX_LENGTH) || "Renderer installation failed";
109
+ }
110
+ function createRendererStatusReporter(publish, now = Date.now, pid = process.pid) {
111
+ const startedAt = now();
112
+ let current;
113
+ const update = (next) => {
114
+ if (current && current.state === next.state && current.error === next.error && current.failures === next.failures) {
115
+ return;
116
+ }
117
+ current = { ...next, updatedAt: now() };
118
+ publish({ schemaVersion: 1, pid, startedAt, renderer: current });
119
+ };
120
+ return {
121
+ installing() {
122
+ update({
123
+ state: "installing",
124
+ error: current?.error ?? null,
125
+ failures: current?.failures ?? 0,
126
+ lastError: current?.lastError ?? null,
127
+ lastFailedAt: current?.lastFailedAt ?? null,
128
+ lastInstalledAt: current?.lastInstalledAt ?? null
129
+ });
130
+ },
131
+ installed() {
132
+ if (current?.state === "installed") return;
133
+ update({
134
+ state: "installed",
135
+ error: null,
136
+ failures: current?.failures ?? 0,
137
+ lastError: current?.lastError ?? null,
138
+ lastFailedAt: current?.lastFailedAt ?? null,
139
+ lastInstalledAt: now()
140
+ });
141
+ },
142
+ failed(error) {
143
+ const message = errorMessage(error);
144
+ update({
145
+ state: "unavailable",
146
+ error: message,
147
+ failures: (current?.failures ?? 0) + 1,
148
+ lastError: message,
149
+ lastFailedAt: now(),
150
+ lastInstalledAt: current?.lastInstalledAt ?? null
151
+ });
152
+ }
153
+ };
154
+ }
155
+ async function writeControllerStatusFile(filePath, document) {
156
+ const directory = path.dirname(filePath);
157
+ await mkdir(directory, { recursive: true, mode: 448 });
158
+ const temporaryPath = path.join(
159
+ directory,
160
+ `.${path.basename(filePath)}.${randomBytes(6).toString("hex")}.tmp`
161
+ );
162
+ try {
163
+ await writeFile(temporaryPath, `${JSON.stringify(document)}
164
+ `, {
165
+ encoding: "utf8",
166
+ mode: 384,
167
+ flag: "wx"
168
+ });
169
+ await rename(temporaryPath, filePath);
170
+ } catch (error) {
171
+ await rm(temporaryPath, { force: true });
172
+ throw error;
173
+ }
174
+ }
175
+ function createControllerStatusPublisher(filePath = defaultControllerStatusPath()) {
176
+ let queue = Promise.resolve();
177
+ return (document) => {
178
+ queue = queue.then(() => writeControllerStatusFile(filePath, document)).catch(() => void 0);
179
+ };
180
+ }
181
+
90
182
  // packages/desktop-control/src/cdp-client.ts
183
+ function cdpExceptionMessage(details, fallback) {
184
+ const exception = details.exception;
185
+ if (isRecord(exception) && typeof exception.description === "string" && exception.description) {
186
+ return exception.description;
187
+ }
188
+ return typeof details.text === "string" && details.text ? details.text : fallback;
189
+ }
91
190
  function isRecord(value) {
92
191
  return typeof value === "object" && value !== null && !Array.isArray(value);
93
192
  }
@@ -244,8 +343,7 @@ var CdpClient = class _CdpClient {
244
343
  });
245
344
  if (!isRecord(response)) throw new Error("Runtime.evaluate returned an invalid result");
246
345
  if (isRecord(response.exceptionDetails)) {
247
- const text = typeof response.exceptionDetails.text === "string" ? response.exceptionDetails.text : "Renderer evaluation failed";
248
- throw new Error(text);
346
+ throw new Error(cdpExceptionMessage(response.exceptionDetails, "Renderer evaluation failed"));
249
347
  }
250
348
  if (!isRecord(response.result) || !("value" in response.result)) {
251
349
  throw new Error("Runtime.evaluate did not return a value");
@@ -298,14 +396,25 @@ var CdpClient = class _CdpClient {
298
396
 
299
397
  // packages/desktop-control/src/renderer-react-ownership.ts
300
398
  function committedReactAncestors(value) {
301
- const MAX_VISITED_FIBERS = 2e4;
399
+ const MAX_VISITED_FIBERS = 2e5;
302
400
  const fiber = (value2) => typeof value2 === "object" && value2 !== null ? value2 : null;
401
+ const exhausted = () => {
402
+ const target = globalThis;
403
+ if (typeof target.dispatchEvent === "function" && typeof CustomEvent === "function")
404
+ target.dispatchEvent(
405
+ new CustomEvent("codexhost:react-fiber-walk-limit", {
406
+ detail: { limit: MAX_VISITED_FIBERS }
407
+ })
408
+ );
409
+ return [];
410
+ };
303
411
  const first = fiber(value);
304
412
  if (!first) return [];
305
413
  const previous = [];
306
414
  const seen = /* @__PURE__ */ new Set();
307
415
  for (let node = first; node; node = fiber(node.return)) {
308
- if (seen.has(node) || seen.size >= MAX_VISITED_FIBERS) return [];
416
+ if (seen.has(node)) return [];
417
+ if (seen.size >= MAX_VISITED_FIBERS) return exhausted();
309
418
  seen.add(node);
310
419
  previous.push(node);
311
420
  }
@@ -313,8 +422,30 @@ function committedReactAncestors(value) {
313
422
  if (!rootState || !("current" in rootState)) return previous;
314
423
  const current = fiber(rootState.current);
315
424
  if (!current) return [];
316
- const stack = [{ node: current, parent: null }];
317
425
  const alternate = fiber(first.alternate);
426
+ const path3 = [current];
427
+ seen.clear();
428
+ seen.add(current);
429
+ for (let index = previous.length - 2; index >= 0; index -= 1) {
430
+ const expected = previous[index];
431
+ const expectedAlternate = fiber(expected?.alternate);
432
+ let child = fiber(path3.at(-1)?.child);
433
+ let matched = false;
434
+ while (child && !seen.has(child) && seen.size < MAX_VISITED_FIBERS) {
435
+ seen.add(child);
436
+ if (child === expected || child === expectedAlternate) {
437
+ matched = true;
438
+ break;
439
+ }
440
+ child = fiber(child.sibling);
441
+ }
442
+ if (!matched || !child) break;
443
+ path3.push(child);
444
+ }
445
+ if (path3.length === previous.length && (path3.at(-1) === first || path3.at(-1) === alternate)) {
446
+ return path3.reverse();
447
+ }
448
+ const stack = [{ node: current, parent: null }];
318
449
  seen.clear();
319
450
  while (stack.length > 0 && seen.size < MAX_VISITED_FIBERS) {
320
451
  const entry = stack.pop();
@@ -332,7 +463,7 @@ function committedReactAncestors(value) {
332
463
  const child = fiber(entry.node.child);
333
464
  if (child) stack.push({ node: child, parent: entry });
334
465
  }
335
- return [];
466
+ return stack.length > 0 ? exhausted() : [];
336
467
  }
337
468
 
338
469
  // packages/desktop-control/src/renderer-host-response-ownership.ts
@@ -1253,8 +1384,9 @@ async function evaluateSource(renderer, source) {
1253
1384
  });
1254
1385
  if (!isRecord2(response)) throw new Error("Renderer source evaluation returned an invalid result");
1255
1386
  if (isRecord2(response.exceptionDetails)) {
1256
- const text = typeof response.exceptionDetails.text === "string" ? response.exceptionDetails.text : "Renderer source evaluation failed";
1257
- throw new Error(text);
1387
+ throw new Error(
1388
+ cdpExceptionMessage(response.exceptionDetails, "Renderer source evaluation failed")
1389
+ );
1258
1390
  }
1259
1391
  }
1260
1392
  async function readBinding(renderer) {
@@ -1454,6 +1586,7 @@ var defaultDependencies = {
1454
1586
  process.stdout.write(`${serializeDesktopControllerReadiness(readiness)}
1455
1587
  `);
1456
1588
  },
1589
+ publishStatus: createControllerStatusPublisher(),
1457
1590
  sleep: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
1458
1591
  monitorIntervalMs: 500
1459
1592
  };
@@ -1485,8 +1618,8 @@ function parseDesktopControllerArguments(arguments_) {
1485
1618
  if (argument === "--renderer") {
1486
1619
  if (rendererPath !== void 0) throw new Error("--renderer may only be provided once");
1487
1620
  if (!value) throw new Error("--renderer requires a value");
1488
- if (!path.isAbsolute(value)) throw new Error("--renderer must be an absolute path");
1489
- rendererPath = path.normalize(value);
1621
+ if (!path2.isAbsolute(value)) throw new Error("--renderer must be an absolute path");
1622
+ rendererPath = path2.normalize(value);
1490
1623
  index += 1;
1491
1624
  continue;
1492
1625
  }
@@ -1568,15 +1701,22 @@ async function runDesktopController(options, signal, dependencies = defaultDepen
1568
1701
  let session;
1569
1702
  let nextRecoveryAt = 0;
1570
1703
  let recoveryDelayMs = RECOVERY_RETRY_INITIAL_MS;
1571
- const recordRecoveryFailure = () => {
1704
+ const status = createRendererStatusReporter(
1705
+ (document) => dependencies.publishStatus?.(document),
1706
+ now
1707
+ );
1708
+ const recordRecoveryFailure = (error) => {
1572
1709
  nextRecoveryAt = now() + recoveryDelayMs;
1573
1710
  recoveryDelayMs = Math.min(recoveryDelayMs * 2, RECOVERY_RETRY_MAX_MS);
1711
+ status.failed(error);
1574
1712
  };
1575
1713
  const recordRecoverySuccess = () => {
1576
1714
  nextRecoveryAt = 0;
1577
1715
  recoveryDelayMs = RECOVERY_RETRY_INITIAL_MS;
1716
+ status.installed();
1578
1717
  };
1579
1718
  const createSession = async () => {
1719
+ status.installing();
1580
1720
  startupTrace("reading Renderer bundle");
1581
1721
  const rendererSource = await dependencies.readRenderer(options.rendererPath);
1582
1722
  if (rendererSource.trim().length === 0) throw new Error("production Renderer Bundle is empty");
@@ -1619,7 +1759,7 @@ ${rendererSource}`,
1619
1759
  } catch (error) {
1620
1760
  startupTrace("initial Renderer Session unavailable", error);
1621
1761
  session = void 0;
1622
- recordRecoveryFailure();
1762
+ recordRecoveryFailure(error);
1623
1763
  }
1624
1764
  let operation = Promise.resolve(void 0);
1625
1765
  const useSession = (callback) => {
@@ -1646,7 +1786,7 @@ ${rendererSource}`,
1646
1786
  return current;
1647
1787
  } catch (error) {
1648
1788
  resetSession();
1649
- recordRecoveryFailure();
1789
+ recordRecoveryFailure(error);
1650
1790
  throw error;
1651
1791
  }
1652
1792
  };