@codexhost/cli-darwin-arm64 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -7,7 +7,7 @@ Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install -g @codexhost/cli@0.1.1
10
+ npm install -g @codexhost/cli@0.1.3
11
11
  ```
12
12
 
13
13
  Do not install this package directly. npm selects it through the optional dependencies of `@codexhost/cli`.
@@ -18,6 +18,7 @@ This package is platform-specific (`os=darwin`, `cpu=arm64`).
18
18
 
19
19
  ```bash
20
20
  codexhost
21
+ codexhost --version
21
22
  codexhost inspect
22
23
  codexhost launch --agent pi
23
24
  codexhost launch --agent codex
@@ -1 +1 @@
1
- {"schemaVersion":1,"version":"0.1.1","distribution":"npm","target":"macos-arm64"}
1
+ {"schemaVersion":1,"version":"0.1.3","distribution":"npm","target":"macos-arm64"}
@@ -1344,12 +1344,15 @@ function isTransientElectronInstallError(error) {
1344
1344
  }
1345
1345
  return false;
1346
1346
  }
1347
+ function shouldRetryInstallError(error) {
1348
+ return !(error instanceof RendererCompatibilityError) || isTransientElectronInstallError(error);
1349
+ }
1347
1350
  async function installProductionSession(options, dependencies) {
1348
1351
  for (let attempt = 1; attempt <= TRANSIENT_INSTALL_ATTEMPTS; attempt += 1) {
1349
1352
  try {
1350
1353
  return await dependencies.install(options);
1351
1354
  } catch (error) {
1352
- if (attempt === TRANSIENT_INSTALL_ATTEMPTS || !isTransientElectronInstallError(error)) {
1355
+ if (attempt === TRANSIENT_INSTALL_ATTEMPTS || !shouldRetryInstallError(error)) {
1353
1356
  throw error;
1354
1357
  }
1355
1358
  await dependencies.sleep(TRANSIENT_INSTALL_RETRY_MS);
@@ -15006,6 +15006,7 @@ var updateSemanticVersionSchema = external_exports.string().regex(UPDATE_SEMVER_
15006
15006
  var updateInstallationSchema = external_exports.enum(["npm", "windows-installer", "macos-dmg"]);
15007
15007
  var updatePhaseSchema = external_exports.enum([
15008
15008
  "prepared",
15009
+ "downloading",
15009
15010
  "waiting-for-exit",
15010
15011
  "installing",
15011
15012
  "restarting",
@@ -15017,12 +15018,23 @@ var updateStatusSchema = external_exports.strictObject({
15017
15018
  installation: updateInstallationSchema,
15018
15019
  phase: updatePhaseSchema,
15019
15020
  updatedAt: external_exports.number().int().nonnegative(),
15021
+ downloadedBytes: external_exports.number().int().nonnegative().optional(),
15022
+ totalBytes: external_exports.number().int().positive().optional(),
15020
15023
  error: external_exports.string().min(1).max(UPDATE_ERROR_MAX_LENGTH).nullable()
15024
+ }).superRefine((status, context) => {
15025
+ if (status.downloadedBytes !== void 0 && status.totalBytes !== void 0 && status.downloadedBytes > status.totalBytes) {
15026
+ context.addIssue({
15027
+ code: "custom",
15028
+ path: ["downloadedBytes"],
15029
+ message: "downloadedBytes must not exceed totalBytes"
15030
+ });
15031
+ }
15021
15032
  });
15022
15033
  var updateEmptyParamsSchema = external_exports.strictObject({});
15023
15034
  var githubReleaseNotesUrlSchema = external_exports.string().max(300).regex(/^https:\/\/github\.com\/BytePioneer-AI\/codex-host\/releases\/tag\/v[0-9A-Za-z.+-]+$/u, "release notes URL must identify a codexhost GitHub Release");
15024
15035
  var updateCheckResultSchema = external_exports.strictObject({
15025
15036
  currentVersion: updateSemanticVersionSchema,
15037
+ installation: updateInstallationSchema.nullable(),
15026
15038
  latestVersion: updateSemanticVersionSchema.nullable(),
15027
15039
  updateAvailable: external_exports.boolean(),
15028
15040
  installationAvailable: external_exports.boolean(),
@@ -52364,11 +52376,28 @@ function parseUpdateStatus(value) {
52364
52376
  throw new Error("background update status must be an object");
52365
52377
  }
52366
52378
  const status = value;
52367
- const allowed2 = ["error", "installation", "phase", "schemaVersion", "updatedAt", "version"];
52379
+ const allowed2 = [
52380
+ "downloadedBytes",
52381
+ "error",
52382
+ "installation",
52383
+ "phase",
52384
+ "schemaVersion",
52385
+ "totalBytes",
52386
+ "updatedAt",
52387
+ "version"
52388
+ ];
52368
52389
  if (Object.keys(status).some((key) => !allowed2.includes(key))) {
52369
52390
  throw new Error("background update status contains unknown fields");
52370
52391
  }
52371
- if (status.schemaVersion !== STATUS_SCHEMA_VERSION || typeof status.version !== "string" || !SEMVER_PATTERN.test(status.version) || !["npm", "windows-installer", "macos-dmg"].includes(String(status.installation)) || !["prepared", "waiting-for-exit", "installing", "restarting", "succeeded", "failed"].includes(String(status.phase)) || !Number.isSafeInteger(status.updatedAt) || status.error !== void 0 && typeof status.error !== "string") {
52392
+ if (status.schemaVersion !== STATUS_SCHEMA_VERSION || typeof status.version !== "string" || !SEMVER_PATTERN.test(status.version) || !["npm", "windows-installer", "macos-dmg"].includes(String(status.installation)) || ![
52393
+ "prepared",
52394
+ "downloading",
52395
+ "waiting-for-exit",
52396
+ "installing",
52397
+ "restarting",
52398
+ "succeeded",
52399
+ "failed"
52400
+ ].includes(String(status.phase)) || !Number.isSafeInteger(status.updatedAt) || status.downloadedBytes !== void 0 && (typeof status.downloadedBytes !== "number" || !Number.isSafeInteger(status.downloadedBytes) || status.downloadedBytes < 0) || status.totalBytes !== void 0 && (typeof status.totalBytes !== "number" || !Number.isSafeInteger(status.totalBytes) || status.totalBytes <= 0) || typeof status.downloadedBytes === "number" && typeof status.totalBytes === "number" && status.downloadedBytes > status.totalBytes || status.error !== void 0 && typeof status.error !== "string") {
52372
52401
  throw new Error("background update status is invalid");
52373
52402
  }
52374
52403
  return status;
@@ -52665,6 +52694,11 @@ async function regularFile(filePath) {
52665
52694
  throw error52;
52666
52695
  }
52667
52696
  }
52697
+ async function isUpdateOperationActive(stateDirectory) {
52698
+ if (!path9.isAbsolute(stateDirectory))
52699
+ throw new Error("update state directory must be absolute");
52700
+ return regularFile(path9.join(stateDirectory, LOCK_FILE));
52701
+ }
52668
52702
  async function discoverLatestUpdateStatus(stateDirectory) {
52669
52703
  if (!path9.isAbsolute(stateDirectory))
52670
52704
  throw new Error("update state directory must be absolute");
@@ -52755,13 +52789,25 @@ async function acquireUpdateOperationLock(stateDirectory) {
52755
52789
  }
52756
52790
  };
52757
52791
  }
52792
+ function processIsAlive(processId) {
52793
+ if (!Number.isSafeInteger(processId) || processId <= 0)
52794
+ return false;
52795
+ try {
52796
+ process.kill(processId, 0);
52797
+ return true;
52798
+ } catch (error52) {
52799
+ return error52.code === "EPERM";
52800
+ }
52801
+ }
52758
52802
  async function recoverUpdateOperationLock(stateDirectory) {
52759
52803
  const lockPath = path9.join(stateDirectory, LOCK_FILE);
52760
52804
  if (!await regularFile(lockPath))
52761
52805
  return;
52806
+ let ownerPid;
52762
52807
  let statusPath;
52763
52808
  try {
52764
52809
  const value = JSON.parse(await readFile3(lockPath, "utf8"));
52810
+ ownerPid = value.ownerPid;
52765
52811
  statusPath = value.statusPath;
52766
52812
  } catch {
52767
52813
  return;
@@ -52770,8 +52816,9 @@ async function recoverUpdateOperationLock(stateDirectory) {
52770
52816
  return;
52771
52817
  try {
52772
52818
  const status = parseUpdateStatus(JSON.parse(await readFile3(statusPath, "utf8")));
52773
- if (TERMINAL_PHASES.has(status.phase))
52819
+ if (TERMINAL_PHASES.has(status.phase) || typeof ownerPid !== "number" || !processIsAlive(ownerPid)) {
52774
52820
  await rm3(lockPath, { force: true });
52821
+ }
52775
52822
  } catch {
52776
52823
  }
52777
52824
  }
@@ -52810,7 +52857,7 @@ async function writeChunk(file2, chunk) {
52810
52857
  offset += result.bytesWritten;
52811
52858
  }
52812
52859
  }
52813
- async function downloadArtifact(source, destination) {
52860
+ async function downloadArtifact(source, destination, onProgress) {
52814
52861
  const response = await fetch(source.url, {
52815
52862
  redirect: "follow",
52816
52863
  headers: { "accept-encoding": "identity" }
@@ -52836,6 +52883,7 @@ async function downloadArtifact(source, destination) {
52836
52883
  throw new Error(`update artifact exceeds ${MAX_ARTIFACT_BYTES} bytes`);
52837
52884
  }
52838
52885
  await writeChunk(file2, item.value);
52886
+ await onProgress?.({ downloadedBytes: bytes, totalBytes: source.size });
52839
52887
  }
52840
52888
  await file2.sync();
52841
52889
  } finally {
@@ -52920,6 +52968,59 @@ function createBackgroundUpdateManager(dependencies = {}) {
52920
52968
  const spawnUpdater = dependencies.spawnUpdater ?? defaultSpawnUpdater;
52921
52969
  const now = dependencies.now ?? Date.now;
52922
52970
  const preparedRequests = /* @__PURE__ */ new Set();
52971
+ async function writeStatusSnapshot(statusPath, status) {
52972
+ const temporaryPath = path10.join(path10.dirname(statusPath), `.update-status-${randomId()}.tmp`);
52973
+ try {
52974
+ await writeFile2(temporaryPath, `${JSON.stringify(status)}
52975
+ `, {
52976
+ encoding: "utf8",
52977
+ mode: 384,
52978
+ flag: "wx"
52979
+ });
52980
+ await rename2(temporaryPath, statusPath);
52981
+ } catch (error52) {
52982
+ await rm4(temporaryPath, { force: true });
52983
+ throw error52;
52984
+ }
52985
+ }
52986
+ function statusSnapshot(version2, installation, phase, progress, error52) {
52987
+ return {
52988
+ schemaVersion: 1,
52989
+ version: version2,
52990
+ installation,
52991
+ phase,
52992
+ updatedAt: Math.floor(now() / 1e3),
52993
+ ...progress?.downloadedBytes === void 0 ? {} : { downloadedBytes: progress.downloadedBytes },
52994
+ ...progress?.totalBytes === void 0 ? {} : { totalBytes: progress.totalBytes },
52995
+ ...error52 === void 0 ? {} : { error: errorMessage4(error52).slice(0, 500) }
52996
+ };
52997
+ }
52998
+ async function writeFailedStatus(statusPath, version2, installation, error52) {
52999
+ await writeStatusSnapshot(statusPath, statusSnapshot(version2, installation, "failed", void 0, error52)).catch(() => void 0);
53000
+ }
53001
+ function progressReporter(statusPath, version2, installation, totalBytes) {
53002
+ let lastProgress = { downloadedBytes: 0, totalBytes };
53003
+ let lastWriteAt = 0;
53004
+ let queued = Promise.resolve();
53005
+ const enqueue = (progress, force = false) => {
53006
+ lastProgress = progress;
53007
+ const currentTime = Date.now();
53008
+ if (!force && currentTime - lastWriteAt < 250)
53009
+ return;
53010
+ lastWriteAt = currentTime;
53011
+ queued = queued.then(() => writeStatusSnapshot(statusPath, statusSnapshot(version2, installation, "downloading", lastProgress))).catch(() => void 0);
53012
+ };
53013
+ enqueue(lastProgress, true);
53014
+ return {
53015
+ update(progress) {
53016
+ enqueue(progress);
53017
+ },
53018
+ async flush() {
53019
+ enqueue(lastProgress, true);
53020
+ await queued;
53021
+ }
53022
+ };
53023
+ }
52923
53024
  async function prepareCommon(options, installation) {
52924
53025
  const version2 = requireSemanticVersion(options.version);
52925
53026
  const launcherPid = requireLauncherPid(options.launcherPid);
@@ -52937,6 +53038,7 @@ function createBackgroundUpdateManager(dependencies = {}) {
52937
53038
  const requestPath = path10.join(workDirectory, "request-v1.json");
52938
53039
  const statusPath = path10.join(workDirectory, "status-v1.json");
52939
53040
  await writePrivateJson(statusPath, preparedStatus(version2, installation, now()));
53041
+ await options.onPrepared?.({ version: version2, installation, statusPath });
52940
53042
  return {
52941
53043
  version: version2,
52942
53044
  launcherPid,
@@ -52947,12 +53049,15 @@ function createBackgroundUpdateManager(dependencies = {}) {
52947
53049
  statusPath
52948
53050
  };
52949
53051
  }
52950
- async function prepareArtifact(sourceValue, workDirectory, fileName) {
53052
+ async function prepareArtifact(common, installation, sourceValue, fileName) {
52951
53053
  const source = validateArtifact(sourceValue);
52952
- const temporaryPath = path10.join(workDirectory, `.${fileName}.download`);
52953
- const artifactPath = path10.join(workDirectory, fileName);
53054
+ const temporaryPath = path10.join(common.workDirectory, `.${fileName}.download`);
53055
+ const artifactPath = path10.join(common.workDirectory, fileName);
53056
+ const progress = progressReporter(common.statusPath, common.version, installation, source.size);
52954
53057
  try {
52955
- const result = await download(source, temporaryPath);
53058
+ const result = await download(source, temporaryPath, progress.update);
53059
+ progress.update({ downloadedBytes: result.bytes, totalBytes: source.size });
53060
+ await progress.flush();
52956
53061
  const finalUrl = new URL(result.finalUrl);
52957
53062
  if (finalUrl.protocol !== "https:" || finalUrl.username || finalUrl.password) {
52958
53063
  throw new Error("update artifact downloader returned a non-HTTPS URL");
@@ -52962,6 +53067,8 @@ function createBackgroundUpdateManager(dependencies = {}) {
52962
53067
  return { source, artifactPath };
52963
53068
  } catch (error52) {
52964
53069
  await rm4(temporaryPath, { force: true });
53070
+ await progress.flush();
53071
+ await writeFailedStatus(common.statusPath, common.version, installation, error52);
52965
53072
  throw error52;
52966
53073
  }
52967
53074
  }
@@ -52975,6 +53082,7 @@ function createBackgroundUpdateManager(dependencies = {}) {
52975
53082
  installation
52976
53083
  };
52977
53084
  await writePrivateJson(common.requestPath, request);
53085
+ await writeStatusSnapshot(common.statusPath, statusSnapshot(common.version, installation.kind, "prepared"));
52978
53086
  preparedRequests.add(common.requestPath);
52979
53087
  return Object.freeze({
52980
53088
  version: common.version,
@@ -52988,19 +53096,24 @@ function createBackgroundUpdateManager(dependencies = {}) {
52988
53096
  return Object.freeze({
52989
53097
  async prepareNpm(options) {
52990
53098
  const common = await prepareCommon(options, "npm");
52991
- return finalize2(common, {
52992
- kind: "npm",
52993
- node_path: await requireRegularFile(options.nodePath, "npm Node.js executable"),
52994
- npm_cli_path: await requireRegularFile(options.npmCliPath, "npm CLI"),
52995
- npm_launcher_path: await requireRegularFile(options.npmLauncherPath, "npm codexhost launcher"),
52996
- package_root: requireAbsolutePath(options.packageRoot, "npm platform package root")
52997
- });
53099
+ try {
53100
+ return await finalize2(common, {
53101
+ kind: "npm",
53102
+ node_path: await requireRegularFile(options.nodePath, "npm Node.js executable"),
53103
+ npm_cli_path: await requireRegularFile(options.npmCliPath, "npm CLI"),
53104
+ npm_launcher_path: await requireRegularFile(options.npmLauncherPath, "npm codexhost launcher"),
53105
+ package_root: requireAbsolutePath(options.packageRoot, "npm platform package root")
53106
+ });
53107
+ } catch (error52) {
53108
+ await writeFailedStatus(common.statusPath, common.version, "npm", error52);
53109
+ throw error52;
53110
+ }
52998
53111
  },
52999
53112
  async prepareWindowsInstaller(options) {
53000
53113
  if (platform !== "win32")
53001
53114
  throw new Error("Windows installer updates require Windows");
53002
53115
  const common = await prepareCommon(options, "windows-installer");
53003
- const artifact = await prepareArtifact(options.artifact, common.workDirectory, "update.exe");
53116
+ const artifact = await prepareArtifact(common, "windows-installer", options.artifact, "update.exe");
53004
53117
  return finalize2(common, {
53005
53118
  kind: "windows-installer",
53006
53119
  installer_path: artifact.artifactPath,
@@ -53016,7 +53129,7 @@ function createBackgroundUpdateManager(dependencies = {}) {
53016
53129
  if (path10.extname(appPath) !== ".app") {
53017
53130
  throw new Error("macOS application path must end in .app");
53018
53131
  }
53019
- const artifact = await prepareArtifact(options.artifact, common.workDirectory, "update.dmg");
53132
+ const artifact = await prepareArtifact(common, "macos-dmg", options.artifact, "update.dmg");
53020
53133
  return finalize2(common, {
53021
53134
  kind: "macos-dmg",
53022
53135
  dmg_path: artifact.artifactPath,
@@ -53049,6 +53162,8 @@ function createBackgroundUpdateManager(dependencies = {}) {
53049
53162
  // packages/host-runtime/src/update-coordinator.ts
53050
53163
  var ERROR_MAX_LENGTH = 500;
53051
53164
  var CONTROLLER_TIMEOUT_MS = 5e3;
53165
+ var UPDATER_READY_TIMEOUT_MS = 1e4;
53166
+ var UPDATER_READY_POLL_MS = 20;
53052
53167
  async function startCompatibilityUpdate(coordinator) {
53053
53168
  try {
53054
53169
  const check2 = await coordinator.check();
@@ -53067,12 +53182,38 @@ function boundedError(error52) {
53067
53182
  const message3 = error52 instanceof Error ? error52.message : String(error52);
53068
53183
  return message3.slice(0, ERROR_MAX_LENGTH) || "Update operation failed";
53069
53184
  }
53185
+ function delay4(milliseconds) {
53186
+ return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
53187
+ }
53188
+ async function waitForUpdaterReady(manager, statusPath) {
53189
+ const deadline = Date.now() + UPDATER_READY_TIMEOUT_MS;
53190
+ while (Date.now() < deadline) {
53191
+ const status = await manager.readStatus(statusPath);
53192
+ switch (status?.phase) {
53193
+ case "waiting-for-exit":
53194
+ case "installing":
53195
+ case "restarting":
53196
+ case "succeeded":
53197
+ return;
53198
+ case "failed":
53199
+ throw new Error(status.error ?? "Background Updater failed before Desktop shutdown");
53200
+ case "prepared":
53201
+ break;
53202
+ default:
53203
+ throw new Error("Background Updater reported an unexpected startup phase");
53204
+ }
53205
+ await delay4(UPDATER_READY_POLL_MS);
53206
+ }
53207
+ throw new Error("Background Updater did not become ready before Desktop shutdown");
53208
+ }
53070
53209
  function publicStatus(status) {
53071
53210
  return {
53072
53211
  version: status.version,
53073
53212
  installation: status.installation,
53074
53213
  phase: status.phase,
53075
53214
  updatedAt: status.updatedAt,
53215
+ ...status.downloadedBytes === void 0 ? {} : { downloadedBytes: status.downloadedBytes },
53216
+ ...status.totalBytes === void 0 ? {} : { totalBytes: status.totalBytes },
53076
53217
  error: status.error?.slice(0, ERROR_MAX_LENGTH) ?? null
53077
53218
  };
53078
53219
  }
@@ -53105,7 +53246,8 @@ function requestControllerShutdown(controller) {
53105
53246
  });
53106
53247
  }
53107
53248
  function createHostUpdateCoordinator(options) {
53108
- const manager = options.manager ?? createBackgroundUpdateManager(options.platform ? { platform: options.platform } : {});
53249
+ const platform = options.platform ?? process.platform;
53250
+ const manager = options.manager ?? createBackgroundUpdateManager({ platform });
53109
53251
  const contextPromise = resolveInstalledUpdateContext({
53110
53252
  hostRuntimePath: options.hostRuntimePath,
53111
53253
  ...options.environment ? { environment: options.environment } : {},
@@ -53116,9 +53258,20 @@ function createHostUpdateCoordinator(options) {
53116
53258
  const shutdown = options.shutdown ?? requestControllerShutdown;
53117
53259
  let candidate = null;
53118
53260
  let shutdownPending = null;
53261
+ let shutdownRequested = false;
53262
+ const scheduleShutdown = () => {
53263
+ if (!shutdownRequested || !shutdownPending) return;
53264
+ const controller = shutdownPending;
53265
+ shutdownPending = null;
53266
+ setTimeout(() => void shutdown(controller).catch(() => void 0), 50).unref();
53267
+ };
53119
53268
  async function latestStatus(context) {
53120
53269
  const discovered = await discoverLatestUpdateStatus(context.common.stateDirectory);
53121
- return discovered ? publicStatus(discovered.status) : null;
53270
+ if (!discovered) return null;
53271
+ if (discovered.status.phase !== "succeeded" && discovered.status.phase !== "failed" && !await isUpdateOperationActive(context.common.stateDirectory)) {
53272
+ return null;
53273
+ }
53274
+ return publicStatus(discovered.status);
53122
53275
  }
53123
53276
  async function installable(context, release) {
53124
53277
  if (context.metadata.distribution === "npm") return true;
@@ -53139,6 +53292,7 @@ function createHostUpdateCoordinator(options) {
53139
53292
  } catch (error52) {
53140
53293
  return {
53141
53294
  currentVersion: "0.0.0",
53295
+ installation: null,
53142
53296
  latestVersion: null,
53143
53297
  updateAvailable: false,
53144
53298
  installationAvailable: false,
@@ -53163,6 +53317,7 @@ function createHostUpdateCoordinator(options) {
53163
53317
  }
53164
53318
  return {
53165
53319
  currentVersion: context.metadata.version,
53320
+ installation: context.installation.kind,
53166
53321
  latestVersion: release.version,
53167
53322
  updateAvailable,
53168
53323
  installationAvailable,
@@ -53174,6 +53329,7 @@ function createHostUpdateCoordinator(options) {
53174
53329
  } catch (error52) {
53175
53330
  return {
53176
53331
  currentVersion: context.metadata.version,
53332
+ installation: context.installation.kind,
53177
53333
  latestVersion: null,
53178
53334
  updateAvailable: false,
53179
53335
  installationAvailable: false,
@@ -53193,34 +53349,60 @@ function createHostUpdateCoordinator(options) {
53193
53349
  if (existing) return { status: existing };
53194
53350
  throw new Error("Another update operation is already active");
53195
53351
  }
53352
+ let resolvePrepared;
53353
+ let rejectPrepared;
53354
+ const preparedReady = new Promise((resolve2, reject) => {
53355
+ resolvePrepared = resolve2;
53356
+ rejectPrepared = reject;
53357
+ });
53196
53358
  try {
53197
53359
  const release = await fetchLatest();
53198
53360
  if (compareSemanticVersions(context.metadata.version, release.version) >= 0 || candidate && candidate.version !== release.version) {
53199
53361
  throw new Error("The selected update is no longer the current GitHub Release");
53200
53362
  }
53201
- let prepared;
53202
- if (context.installation.kind === "npm") {
53203
- prepared = await manager.prepareNpm({
53204
- ...context.installation.options,
53205
- version: release.version
53206
- });
53207
- } else {
53208
- const artifact = selectInstallerReleaseArtifact(release, context.metadata.target).source;
53209
- prepared = context.installation.kind === "windows-installer" ? await manager.prepareWindowsInstaller({
53210
- ...context.installation.options,
53211
- version: release.version,
53212
- artifact
53213
- }) : await manager.prepareMacOsDmg({
53214
- ...context.installation.options,
53215
- version: release.version,
53216
- artifact
53217
- });
53218
- }
53219
- await lock.setStatusPath(prepared.statusPath);
53220
- manager.start(prepared);
53363
+ const onPrepared = async (info) => {
53364
+ await lock.setStatusPath(info.statusPath);
53365
+ resolvePrepared(info);
53366
+ };
53367
+ const prepareAndStart = async () => {
53368
+ try {
53369
+ let prepared2;
53370
+ if (context.installation.kind === "npm") {
53371
+ prepared2 = await manager.prepareNpm({
53372
+ ...context.installation.options,
53373
+ version: release.version,
53374
+ onPrepared
53375
+ });
53376
+ } else {
53377
+ const artifact = selectInstallerReleaseArtifact(
53378
+ release,
53379
+ context.metadata.target
53380
+ ).source;
53381
+ prepared2 = context.installation.kind === "windows-installer" ? await manager.prepareWindowsInstaller({
53382
+ ...context.installation.options,
53383
+ version: release.version,
53384
+ artifact,
53385
+ onPrepared
53386
+ }) : await manager.prepareMacOsDmg({
53387
+ ...context.installation.options,
53388
+ version: release.version,
53389
+ artifact,
53390
+ onPrepared
53391
+ });
53392
+ }
53393
+ if (platform !== "darwin") manager.start(prepared2);
53394
+ await waitForUpdaterReady(manager, prepared2.statusPath);
53395
+ shutdownPending = context.controller;
53396
+ scheduleShutdown();
53397
+ } catch (error52) {
53398
+ await lock.release();
53399
+ rejectPrepared(error52);
53400
+ }
53401
+ };
53402
+ void prepareAndStart();
53403
+ const prepared = await preparedReady;
53221
53404
  const status = await manager.readStatus(prepared.statusPath);
53222
- if (!status) throw new Error("Background Updater did not create status");
53223
- shutdownPending = context.controller;
53405
+ if (!status) throw new Error("Background update did not create status");
53224
53406
  return { status: publicStatus(status) };
53225
53407
  } catch (error52) {
53226
53408
  await lock.release();
@@ -53237,10 +53419,8 @@ function createHostUpdateCoordinator(options) {
53237
53419
  }
53238
53420
  },
53239
53421
  requestShutdown() {
53240
- const controller = shutdownPending;
53241
- shutdownPending = null;
53242
- if (!controller) return;
53243
- setTimeout(() => void shutdown(controller).catch(() => void 0), 50).unref();
53422
+ shutdownRequested = true;
53423
+ scheduleShutdown();
53244
53424
  }
53245
53425
  });
53246
53426
  }
@@ -15242,6 +15242,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15242
15242
  var updateInstallationSchema = external_exports.enum(["npm", "windows-installer", "macos-dmg"]);
15243
15243
  var updatePhaseSchema = external_exports.enum([
15244
15244
  "prepared",
15245
+ "downloading",
15245
15246
  "waiting-for-exit",
15246
15247
  "installing",
15247
15248
  "restarting",
@@ -15253,12 +15254,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15253
15254
  installation: updateInstallationSchema,
15254
15255
  phase: updatePhaseSchema,
15255
15256
  updatedAt: external_exports.number().int().nonnegative(),
15257
+ downloadedBytes: external_exports.number().int().nonnegative().optional(),
15258
+ totalBytes: external_exports.number().int().positive().optional(),
15256
15259
  error: external_exports.string().min(1).max(UPDATE_ERROR_MAX_LENGTH).nullable()
15260
+ }).superRefine((status, context) => {
15261
+ if (status.downloadedBytes !== void 0 && status.totalBytes !== void 0 && status.downloadedBytes > status.totalBytes) {
15262
+ context.addIssue({
15263
+ code: "custom",
15264
+ path: ["downloadedBytes"],
15265
+ message: "downloadedBytes must not exceed totalBytes"
15266
+ });
15267
+ }
15257
15268
  });
15258
15269
  var updateEmptyParamsSchema = external_exports.strictObject({});
15259
15270
  var githubReleaseNotesUrlSchema = external_exports.string().max(300).regex(/^https:\/\/github\.com\/BytePioneer-AI\/codex-host\/releases\/tag\/v[0-9A-Za-z.+-]+$/u, "release notes URL must identify a codexhost GitHub Release");
15260
15271
  var updateCheckResultSchema = external_exports.strictObject({
15261
15272
  currentVersion: updateSemanticVersionSchema,
15273
+ installation: updateInstallationSchema.nullable(),
15262
15274
  latestVersion: updateSemanticVersionSchema.nullable(),
15263
15275
  updateAvailable: external_exports.boolean(),
15264
15276
  installationAvailable: external_exports.boolean(),
@@ -18101,16 +18113,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18101
18113
  settingsButtonTitle: "codexhost settings",
18102
18114
  settingsUnavailableTitle: "codexhost settings unavailable",
18103
18115
  updateCurrentVersion: "Current version",
18116
+ updateInstallation: "Installation method",
18117
+ updateInstallationNpm: "npm",
18118
+ updateInstallationWindowsInstaller: "Windows installer",
18119
+ updateInstallationMacOsDmg: "macOS DMG",
18120
+ updateInstallationUnknown: "Unknown",
18104
18121
  updateLatestVersion: "Latest version",
18105
18122
  updateUpToDate: "You are up to date.",
18106
18123
  updateAvailable: "A new version is available.",
18107
18124
  updateAndRestart: "Update and restart",
18125
+ updateChecking: "Checking for updates...",
18126
+ updateDownloading: "Downloading update...",
18108
18127
  updatePreparing: "Preparing update...",
18128
+ updateRequestTimeout: "The update service did not respond. Try again.",
18109
18129
  updateRestarting: "Restarting to finish the update...",
18110
18130
  updateSucceeded: "Update installed successfully.",
18111
18131
  updateFailed: "Update failed.",
18112
18132
  updateRetry: "Retry",
18113
- updateViewRelease: "View release notes",
18133
+ updateDownloadFromReleases: "Download from GitHub Releases",
18114
18134
  pageLabels: Object.freeze({
18115
18135
  connections: "Connections",
18116
18136
  "model-pool": "Model Pool",
@@ -18138,16 +18158,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18138
18158
  settingsButtonTitle: "codexhost \u8BBE\u7F6E",
18139
18159
  settingsUnavailableTitle: "codexhost \u8BBE\u7F6E\u4E0D\u53EF\u7528",
18140
18160
  updateCurrentVersion: "\u5F53\u524D\u7248\u672C",
18161
+ updateInstallation: "\u5B89\u88C5\u65B9\u5F0F",
18162
+ updateInstallationNpm: "npm",
18163
+ updateInstallationWindowsInstaller: "Windows \u5B89\u88C5\u7A0B\u5E8F",
18164
+ updateInstallationMacOsDmg: "macOS DMG",
18165
+ updateInstallationUnknown: "\u672A\u77E5",
18141
18166
  updateLatestVersion: "\u6700\u65B0\u7248\u672C",
18142
18167
  updateUpToDate: "\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002",
18143
18168
  updateAvailable: "\u6709\u65B0\u7248\u672C\u53EF\u7528\u3002",
18144
18169
  updateAndRestart: "\u66F4\u65B0\u5E76\u91CD\u542F",
18170
+ updateChecking: "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0...",
18171
+ updateDownloading: "\u6B63\u5728\u4E0B\u8F7D\u66F4\u65B0...",
18145
18172
  updatePreparing: "\u6B63\u5728\u51C6\u5907\u66F4\u65B0...",
18173
+ updateRequestTimeout: "\u66F4\u65B0\u670D\u52A1\u672A\u54CD\u5E94\uFF0C\u8BF7\u91CD\u8BD5\u3002",
18146
18174
  updateRestarting: "\u6B63\u5728\u91CD\u542F\u4EE5\u5B8C\u6210\u66F4\u65B0...",
18147
18175
  updateSucceeded: "\u66F4\u65B0\u5B89\u88C5\u6210\u529F\u3002",
18148
18176
  updateFailed: "\u66F4\u65B0\u5931\u8D25\u3002",
18149
18177
  updateRetry: "\u91CD\u8BD5",
18150
- updateViewRelease: "\u67E5\u770B\u66F4\u65B0\u65E5\u5FD7",
18178
+ updateDownloadFromReleases: "\u524D\u5F80 GitHub Releases \u4E0B\u8F7D",
18151
18179
  pageLabels: Object.freeze({
18152
18180
  connections: "\u8FDE\u63A5",
18153
18181
  "model-pool": "\u6A21\u578B\u6C60",
@@ -18449,7 +18477,51 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18449
18477
  }
18450
18478
  };
18451
18479
 
18480
+ // src/settings/update-request.ts
18481
+ var RENDERER_UPDATE_REQUEST_TIMEOUT_MS = 15e3;
18482
+ var RendererUpdateRequestTimeoutError = class extends Error {
18483
+ constructor() {
18484
+ super("Update request timed out");
18485
+ this.name = "RendererUpdateRequestTimeoutError";
18486
+ }
18487
+ };
18488
+ function runBoundedRendererUpdateRequest(operation, signal, timeoutMs = RENDERER_UPDATE_REQUEST_TIMEOUT_MS) {
18489
+ return new Promise((resolve, reject) => {
18490
+ let settled = false;
18491
+ const settle = (handler) => {
18492
+ if (settled) return;
18493
+ settled = true;
18494
+ clearTimeout(timeout);
18495
+ signal.removeEventListener("abort", abort);
18496
+ handler();
18497
+ };
18498
+ const timeout = setTimeout(() => {
18499
+ settle(() => reject(new RendererUpdateRequestTimeoutError()));
18500
+ }, timeoutMs);
18501
+ const abort = () => {
18502
+ settle(() => reject(new Error("Update request was aborted")));
18503
+ };
18504
+ const rejectRequest = (error51) => {
18505
+ settle(() => reject(error51));
18506
+ };
18507
+ if (signal.aborted) {
18508
+ abort();
18509
+ return;
18510
+ }
18511
+ signal.addEventListener("abort", abort, { once: true });
18512
+ try {
18513
+ void operation().then(
18514
+ (value) => settle(() => resolve(value)),
18515
+ (error51) => rejectRequest(error51)
18516
+ );
18517
+ } catch (error51) {
18518
+ rejectRequest(error51);
18519
+ }
18520
+ });
18521
+ }
18522
+
18452
18523
  // src/settings/pages.ts
18524
+ var CODEXHOST_RELEASES_LATEST_URL = "https://github.com/BytePioneer-AI/codex-host/releases/latest";
18453
18525
  var DEFAULT_RENDERER_SETTINGS_PAGE_IDS = [
18454
18526
  "connections",
18455
18527
  "model-pool",
@@ -18493,6 +18565,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18493
18565
  row.append(name, value);
18494
18566
  return row;
18495
18567
  }
18568
+ function installationLabel(installation, messages) {
18569
+ if (installation === "npm") return messages.updateInstallationNpm;
18570
+ if (installation === "windows-installer") {
18571
+ return messages.updateInstallationWindowsInstaller;
18572
+ }
18573
+ if (installation === "macos-dmg") return messages.updateInstallationMacOsDmg;
18574
+ return messages.updateInstallationUnknown;
18575
+ }
18496
18576
  function isPendingStatus(status) {
18497
18577
  return status !== null && status.phase !== "succeeded" && status.phase !== "failed";
18498
18578
  }
@@ -18501,8 +18581,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18501
18581
  if (status.phase === "succeeded") return messages.updateSucceeded;
18502
18582
  if (status.phase === "failed") return status.error ?? messages.updateFailed;
18503
18583
  if (status.phase === "restarting") return messages.updateRestarting;
18584
+ if (status.phase === "downloading") return messages.updateDownloading;
18504
18585
  return messages.updatePreparing;
18505
18586
  }
18587
+ function formatUpdateBytes(value) {
18588
+ if (value < 1024) return `${value} B`;
18589
+ const units = ["KB", "MB", "GB"];
18590
+ let scaled = value;
18591
+ let unit = "B";
18592
+ for (const nextUnit of units) {
18593
+ scaled /= 1024;
18594
+ unit = nextUnit;
18595
+ if (scaled < 1024 || nextUnit === units.at(-1)) break;
18596
+ }
18597
+ return `${scaled.toFixed(scaled >= 10 ? 0 : 1)} ${unit}`;
18598
+ }
18506
18599
  function updatesPage(messages, getClient) {
18507
18600
  return Object.freeze({
18508
18601
  id: "updates",
@@ -18513,10 +18606,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18513
18606
  const heading = document2.createElement("div");
18514
18607
  heading.className = "settings-section-label";
18515
18608
  heading.textContent = messages.pageLabels.updates;
18609
+ const metadata = document2.createElement("div");
18610
+ metadata.className = "settings-update-metadata";
18611
+ const currentVersion = document2.createElement("div");
18612
+ currentVersion.className = "settings-update-metadata__item";
18613
+ const currentVersionLabel = document2.createElement("span");
18614
+ currentVersionLabel.textContent = messages.updateCurrentVersion;
18615
+ const currentVersionValue = document2.createElement("strong");
18616
+ currentVersionValue.textContent = "-";
18617
+ currentVersion.append(currentVersionLabel, currentVersionValue);
18618
+ const installation = document2.createElement("div");
18619
+ installation.className = "settings-update-metadata__item";
18620
+ const installationName = document2.createElement("span");
18621
+ installationName.textContent = messages.updateInstallation;
18622
+ const installationValue = document2.createElement("strong");
18623
+ installationValue.textContent = "-";
18624
+ installation.append(installationName, installationValue);
18625
+ metadata.append(currentVersion, installation);
18516
18626
  const panel = document2.createElement("section");
18517
18627
  panel.className = "settings-update-panel";
18518
18628
  panel.setAttribute("aria-live", "polite");
18519
- context.content.append(heading, panel);
18629
+ const actions = document2.createElement("div");
18630
+ actions.className = "settings-update-actions";
18631
+ const releaseLink = document2.createElement("a");
18632
+ releaseLink.className = "settings-update-link";
18633
+ releaseLink.href = CODEXHOST_RELEASES_LATEST_URL;
18634
+ releaseLink.target = "_blank";
18635
+ releaseLink.rel = "noopener noreferrer";
18636
+ releaseLink.append(
18637
+ messages.updateDownloadFromReleases,
18638
+ createRendererSettingsIcon("external-link", 14)
18639
+ );
18640
+ actions.append(releaseLink);
18641
+ context.content.append(heading, metadata, panel, actions);
18520
18642
  let pollTimer;
18521
18643
  let pollAttempts = 0;
18522
18644
  let pending = false;
@@ -18535,22 +18657,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18535
18657
  copy.textContent = detail;
18536
18658
  panel.append(title, copy);
18537
18659
  };
18660
+ const renderRequestFailure = (error51) => {
18661
+ renderPendingStatus(
18662
+ null,
18663
+ error51 instanceof RendererUpdateRequestTimeoutError ? messages.updateRequestTimeout : error51 instanceof Error ? error51.message : messages.updateFailed,
18664
+ "failed"
18665
+ );
18666
+ };
18538
18667
  const scheduleStatusPoll = (client, resetAttempts = false) => {
18539
18668
  clearPoll();
18540
18669
  if (resetAttempts) pollAttempts = 0;
18541
- if (pollAttempts >= 320) return;
18670
+ if (pollAttempts >= 320) {
18671
+ renderPendingStatus(null, messages.updateRequestTimeout, "failed");
18672
+ return;
18673
+ }
18542
18674
  pollAttempts += 1;
18543
18675
  pollTimer = document2.defaultView?.setTimeout(() => {
18544
- void context.runLatest(() => client.readUpdateStatus(), {
18545
- success(result) {
18546
- const message = statusMessage(result.status, messages);
18547
- if (isPendingStatus(result.status)) scheduleStatusPoll(client);
18548
- if (message) renderPendingStatus(result.status, message);
18549
- },
18550
- failure(error51) {
18551
- renderUnavailable(error51 instanceof Error ? error51.message : messages.updateFailed);
18676
+ void context.runLatest(
18677
+ (signal) => runBoundedRendererUpdateRequest(() => client.readUpdateStatus(), signal),
18678
+ {
18679
+ success(result) {
18680
+ const message = statusMessage(result.status, messages);
18681
+ if (isPendingStatus(result.status)) scheduleStatusPoll(client);
18682
+ if (message) renderPendingStatus(result.status, message);
18683
+ },
18684
+ failure(error51) {
18685
+ renderRequestFailure(error51);
18686
+ }
18552
18687
  }
18553
- });
18688
+ );
18554
18689
  }, 750);
18555
18690
  };
18556
18691
  const renderPendingStatus = (status, message, viewPhase = status?.phase ?? "pending") => {
@@ -18559,6 +18694,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18559
18694
  const state = document2.createElement("strong");
18560
18695
  state.textContent = message;
18561
18696
  panel.append(state);
18697
+ if (status?.phase === "downloading" && status.totalBytes !== void 0 && status.downloadedBytes !== void 0) {
18698
+ const progress = document2.createElement("progress");
18699
+ progress.className = "settings-update-progress";
18700
+ progress.max = status.totalBytes;
18701
+ progress.value = Math.min(status.downloadedBytes, status.totalBytes);
18702
+ progress.setAttribute("aria-label", messages.updateDownloading);
18703
+ const detail = document2.createElement("span");
18704
+ detail.className = "settings-update-progress-detail";
18705
+ const percent = Math.min(
18706
+ 100,
18707
+ Math.round(status.downloadedBytes / status.totalBytes * 1e3) / 10
18708
+ );
18709
+ detail.textContent = `${percent}% \xB7 ${formatUpdateBytes(status.downloadedBytes)} / ${formatUpdateBytes(status.totalBytes)}`;
18710
+ panel.append(progress, detail);
18711
+ }
18562
18712
  if (viewPhase === "failed") {
18563
18713
  const retry = document2.createElement("button");
18564
18714
  retry.type = "button";
@@ -18572,26 +18722,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18572
18722
  if (pending) return;
18573
18723
  pending = true;
18574
18724
  renderPendingStatus(null, messages.updatePreparing);
18575
- void context.runLatest(() => client.startUpdate(), {
18576
- success(result) {
18577
- pending = false;
18578
- renderPendingStatus(
18579
- result.status,
18580
- statusMessage(result.status, messages) ?? messages.updatePreparing
18581
- );
18582
- if (isPendingStatus(result.status)) scheduleStatusPoll(client, true);
18583
- },
18584
- failure(error51) {
18585
- pending = false;
18586
- renderPendingStatus(
18587
- null,
18588
- error51 instanceof Error ? error51.message : messages.updateFailed,
18589
- "failed"
18590
- );
18725
+ void context.runLatest(
18726
+ (signal) => runBoundedRendererUpdateRequest(() => client.startUpdate(), signal),
18727
+ {
18728
+ success(result) {
18729
+ pending = false;
18730
+ renderPendingStatus(
18731
+ result.status,
18732
+ statusMessage(result.status, messages) ?? messages.updatePreparing
18733
+ );
18734
+ if (isPendingStatus(result.status)) scheduleStatusPoll(client, true);
18735
+ },
18736
+ failure(error51) {
18737
+ pending = false;
18738
+ renderRequestFailure(error51);
18739
+ }
18591
18740
  }
18592
- });
18741
+ );
18593
18742
  };
18594
18743
  const renderCheck = (result, client) => {
18744
+ currentVersionValue.textContent = `v${result.currentVersion}`;
18745
+ installationValue.textContent = installationLabel(result.installation, messages);
18746
+ if (result.releaseNotesUrl) releaseLink.href = result.releaseNotesUrl;
18595
18747
  const operationMessage = statusMessage(result.status, messages);
18596
18748
  if (isPendingStatus(result.status)) {
18597
18749
  renderPendingStatus(result.status, operationMessage ?? messages.updatePreparing);
@@ -18599,9 +18751,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18599
18751
  return;
18600
18752
  }
18601
18753
  panel.dataset.updateState = result.error ? "error" : result.updateAvailable ? "available" : "current";
18602
- panel.replaceChildren(
18603
- versionRow(context, messages.updateCurrentVersion, result.currentVersion)
18604
- );
18754
+ panel.replaceChildren();
18605
18755
  if (result.latestVersion) {
18606
18756
  panel.append(versionRow(context, messages.updateLatestVersion, result.latestVersion));
18607
18757
  }
@@ -18629,15 +18779,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18629
18779
  update.addEventListener("click", () => start(client));
18630
18780
  panel.append(update);
18631
18781
  }
18632
- if (result.releaseNotesUrl) {
18633
- const notes = document2.createElement("a");
18634
- notes.className = "settings-update-link";
18635
- notes.href = result.releaseNotesUrl;
18636
- notes.target = "_blank";
18637
- notes.rel = "noopener noreferrer";
18638
- notes.append(messages.updateViewRelease, createRendererSettingsIcon("external-link", 14));
18639
- panel.append(notes);
18640
- }
18641
18782
  if (result.error) {
18642
18783
  const error51 = document2.createElement("p");
18643
18784
  error51.className = "settings-update-error";
@@ -18657,17 +18798,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18657
18798
  return Promise.resolve();
18658
18799
  }
18659
18800
  pending = true;
18660
- renderPendingStatus(null, messages.updatePreparing);
18661
- return context.runLatest(() => client.checkUpdate(), {
18662
- success(result) {
18663
- pending = false;
18664
- renderCheck(result, client);
18665
- },
18666
- failure(error51) {
18667
- pending = false;
18668
- renderUnavailable(error51 instanceof Error ? error51.message : messages.updateFailed);
18801
+ renderPendingStatus(null, messages.updateChecking);
18802
+ return context.runLatest(
18803
+ (signal) => runBoundedRendererUpdateRequest(() => client.checkUpdate(), signal),
18804
+ {
18805
+ success(result) {
18806
+ pending = false;
18807
+ renderCheck(result, client);
18808
+ },
18809
+ failure(error51) {
18810
+ pending = false;
18811
+ renderRequestFailure(error51);
18812
+ }
18669
18813
  }
18670
- });
18814
+ );
18671
18815
  };
18672
18816
  void load();
18673
18817
  return clearPoll;
@@ -18690,7 +18834,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18690
18834
  }
18691
18835
 
18692
18836
  // src/settings/shell.css
18693
- var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-notes {\n max-height: 240px;\n padding: 14px;\n overflow: auto;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(255 255 255 / 4%);\n border: 1px solid var(--settings-divider);\n border-radius: 6px;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
18837
+ var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-notes {\n max-height: 240px;\n padding: 14px;\n overflow: auto;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(255 255 255 / 4%);\n border: 1px solid var(--settings-divider);\n border-radius: 6px;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-update-actions {\n display: flex;\n min-width: 0;\n margin-top: 16px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
18694
18838
 
18695
18839
  // src/settings/shell.ts
18696
18840
  var SETTINGS_SHELL_ATTRIBUTE = "data-codexhost-settings-shell";
@@ -19126,6 +19270,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19126
19270
  }
19127
19271
 
19128
19272
  // src/renderer-settings-lifecycle.ts
19273
+ var UPDATE_CHECK_TIMEOUT_MS = 5e3;
19274
+ var UPDATE_RETRY_DELAYS_MS = [1e3, 3e3, 1e4, 3e4];
19129
19275
  function installRendererSettingsLifecycle(ownerWindow = window, options = {}) {
19130
19276
  const lifecycleController = new AbortController();
19131
19277
  let locale = resolveRendererSettingsLocale(ownerWindow.navigator.languages);
@@ -19133,6 +19279,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19133
19279
  let trigger = null;
19134
19280
  let localeRequest = null;
19135
19281
  let checkedUpdateClient = null;
19282
+ let retryUpdateClient = null;
19283
+ let updateRetryTimer = null;
19284
+ let updateRetryAttempt = 0;
19136
19285
  let updateCheckGeneration = 0;
19137
19286
  let updateAvailable = false;
19138
19287
  let openGeneration = 0;
@@ -19195,16 +19344,65 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19195
19344
  localeRequest = request;
19196
19345
  return request;
19197
19346
  };
19347
+ const clearUpdateRetry = () => {
19348
+ if (updateRetryTimer === null) return;
19349
+ ownerWindow.clearTimeout(updateRetryTimer);
19350
+ updateRetryTimer = null;
19351
+ };
19352
+ const scheduleUpdateRetry = (client) => {
19353
+ if (disposed || updateRetryTimer !== null) return;
19354
+ const delay = UPDATE_RETRY_DELAYS_MS[updateRetryAttempt];
19355
+ if (delay === void 0) return;
19356
+ updateRetryAttempt += 1;
19357
+ updateRetryTimer = ownerWindow.setTimeout(() => {
19358
+ updateRetryTimer = null;
19359
+ if (disposed || options.getUpdateClient?.() !== client) return;
19360
+ refreshUpdateIndicator();
19361
+ }, delay);
19362
+ };
19363
+ const checkUpdateWithTimeout = (client) => new Promise((resolve, reject) => {
19364
+ const timeout = ownerWindow.setTimeout(
19365
+ () => reject(new Error("Update indicator check timed out")),
19366
+ UPDATE_CHECK_TIMEOUT_MS
19367
+ );
19368
+ void client.checkUpdate().then(
19369
+ (result) => {
19370
+ ownerWindow.clearTimeout(timeout);
19371
+ resolve(result);
19372
+ },
19373
+ (error51) => {
19374
+ ownerWindow.clearTimeout(timeout);
19375
+ reject(error51);
19376
+ }
19377
+ );
19378
+ });
19198
19379
  const refreshUpdateIndicator = () => {
19199
19380
  const client = options.getUpdateClient?.() ?? null;
19200
19381
  if (!client || checkedUpdateClient === client) return;
19382
+ if (retryUpdateClient !== client) {
19383
+ clearUpdateRetry();
19384
+ retryUpdateClient = client;
19385
+ updateRetryAttempt = 0;
19386
+ }
19201
19387
  checkedUpdateClient = client;
19202
19388
  const generation = ++updateCheckGeneration;
19203
- void client.checkUpdate().then((result) => {
19389
+ void checkUpdateWithTimeout(client).then((result) => {
19204
19390
  if (disposed || generation !== updateCheckGeneration) return;
19205
19391
  updateAvailable = result.updateAvailable;
19206
19392
  trigger?.setUpdateAvailable(updateAvailable);
19393
+ if (result.error === null) {
19394
+ updateRetryAttempt = 0;
19395
+ clearUpdateRetry();
19396
+ return;
19397
+ }
19398
+ checkedUpdateClient = null;
19399
+ scheduleUpdateRetry(client);
19207
19400
  }).catch(() => {
19401
+ if (disposed || generation !== updateCheckGeneration || checkedUpdateClient !== client) {
19402
+ return;
19403
+ }
19404
+ checkedUpdateClient = null;
19405
+ scheduleUpdateRetry(client);
19208
19406
  });
19209
19407
  };
19210
19408
  mount();
@@ -19225,6 +19423,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19225
19423
  openGeneration += 1;
19226
19424
  updateCheckGeneration += 1;
19227
19425
  lifecycleController.abort();
19426
+ clearUpdateRetry();
19228
19427
  trigger?.dispose();
19229
19428
  shell?.dispose();
19230
19429
  trigger = null;
@@ -20488,6 +20687,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20488
20687
  applyAdapterAgent = applyAgent ?? null;
20489
20688
  modelControl = nextModelControl ?? null;
20490
20689
  adapterStatus = status;
20690
+ const installedModelControl = modelControl;
20691
+ queueMicrotask(() => {
20692
+ if (disposed || modelControl !== installedModelControl) return;
20693
+ try {
20694
+ settingsLifecycle.refresh();
20695
+ } catch {
20696
+ }
20697
+ });
20491
20698
  sidebarAgentIcons.refresh();
20492
20699
  void refreshHarnessAvailability();
20493
20700
  const connected = connectedComposers();
package/bin/codexhost CHANGED
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexhost/cli-darwin-arm64",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.",
5
5
  "type": "module",
6
6
  "files": [