@codexhost/cli-darwin-x64 0.1.0 → 0.1.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.
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.0
10
+ npm install -g @codexhost/cli@0.1.2
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=x64`).
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.0","distribution":"npm","target":"macos-x64"}
1
+ {"schemaVersion":1,"version":"0.1.2","distribution":"npm","target":"macos-x64"}
@@ -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");
@@ -52810,7 +52844,7 @@ async function writeChunk(file2, chunk) {
52810
52844
  offset += result.bytesWritten;
52811
52845
  }
52812
52846
  }
52813
- async function downloadArtifact(source, destination) {
52847
+ async function downloadArtifact(source, destination, onProgress) {
52814
52848
  const response = await fetch(source.url, {
52815
52849
  redirect: "follow",
52816
52850
  headers: { "accept-encoding": "identity" }
@@ -52836,6 +52870,7 @@ async function downloadArtifact(source, destination) {
52836
52870
  throw new Error(`update artifact exceeds ${MAX_ARTIFACT_BYTES} bytes`);
52837
52871
  }
52838
52872
  await writeChunk(file2, item.value);
52873
+ await onProgress?.({ downloadedBytes: bytes, totalBytes: source.size });
52839
52874
  }
52840
52875
  await file2.sync();
52841
52876
  } finally {
@@ -52920,6 +52955,59 @@ function createBackgroundUpdateManager(dependencies = {}) {
52920
52955
  const spawnUpdater = dependencies.spawnUpdater ?? defaultSpawnUpdater;
52921
52956
  const now = dependencies.now ?? Date.now;
52922
52957
  const preparedRequests = /* @__PURE__ */ new Set();
52958
+ async function writeStatusSnapshot(statusPath, status) {
52959
+ const temporaryPath = path10.join(path10.dirname(statusPath), `.update-status-${randomId()}.tmp`);
52960
+ try {
52961
+ await writeFile2(temporaryPath, `${JSON.stringify(status)}
52962
+ `, {
52963
+ encoding: "utf8",
52964
+ mode: 384,
52965
+ flag: "wx"
52966
+ });
52967
+ await rename2(temporaryPath, statusPath);
52968
+ } catch (error52) {
52969
+ await rm4(temporaryPath, { force: true });
52970
+ throw error52;
52971
+ }
52972
+ }
52973
+ function statusSnapshot(version2, installation, phase, progress, error52) {
52974
+ return {
52975
+ schemaVersion: 1,
52976
+ version: version2,
52977
+ installation,
52978
+ phase,
52979
+ updatedAt: Math.floor(now() / 1e3),
52980
+ ...progress?.downloadedBytes === void 0 ? {} : { downloadedBytes: progress.downloadedBytes },
52981
+ ...progress?.totalBytes === void 0 ? {} : { totalBytes: progress.totalBytes },
52982
+ ...error52 === void 0 ? {} : { error: errorMessage4(error52).slice(0, 500) }
52983
+ };
52984
+ }
52985
+ async function writeFailedStatus(statusPath, version2, installation, error52) {
52986
+ await writeStatusSnapshot(statusPath, statusSnapshot(version2, installation, "failed", void 0, error52)).catch(() => void 0);
52987
+ }
52988
+ function progressReporter(statusPath, version2, installation, totalBytes) {
52989
+ let lastProgress = { downloadedBytes: 0, totalBytes };
52990
+ let lastWriteAt = 0;
52991
+ let queued = Promise.resolve();
52992
+ const enqueue = (progress, force = false) => {
52993
+ lastProgress = progress;
52994
+ const currentTime = Date.now();
52995
+ if (!force && currentTime - lastWriteAt < 250)
52996
+ return;
52997
+ lastWriteAt = currentTime;
52998
+ queued = queued.then(() => writeStatusSnapshot(statusPath, statusSnapshot(version2, installation, "downloading", lastProgress))).catch(() => void 0);
52999
+ };
53000
+ enqueue(lastProgress, true);
53001
+ return {
53002
+ update(progress) {
53003
+ enqueue(progress);
53004
+ },
53005
+ async flush() {
53006
+ enqueue(lastProgress, true);
53007
+ await queued;
53008
+ }
53009
+ };
53010
+ }
52923
53011
  async function prepareCommon(options, installation) {
52924
53012
  const version2 = requireSemanticVersion(options.version);
52925
53013
  const launcherPid = requireLauncherPid(options.launcherPid);
@@ -52937,6 +53025,7 @@ function createBackgroundUpdateManager(dependencies = {}) {
52937
53025
  const requestPath = path10.join(workDirectory, "request-v1.json");
52938
53026
  const statusPath = path10.join(workDirectory, "status-v1.json");
52939
53027
  await writePrivateJson(statusPath, preparedStatus(version2, installation, now()));
53028
+ await options.onPrepared?.({ version: version2, installation, statusPath });
52940
53029
  return {
52941
53030
  version: version2,
52942
53031
  launcherPid,
@@ -52947,12 +53036,15 @@ function createBackgroundUpdateManager(dependencies = {}) {
52947
53036
  statusPath
52948
53037
  };
52949
53038
  }
52950
- async function prepareArtifact(sourceValue, workDirectory, fileName) {
53039
+ async function prepareArtifact(common, installation, sourceValue, fileName) {
52951
53040
  const source = validateArtifact(sourceValue);
52952
- const temporaryPath = path10.join(workDirectory, `.${fileName}.download`);
52953
- const artifactPath = path10.join(workDirectory, fileName);
53041
+ const temporaryPath = path10.join(common.workDirectory, `.${fileName}.download`);
53042
+ const artifactPath = path10.join(common.workDirectory, fileName);
53043
+ const progress = progressReporter(common.statusPath, common.version, installation, source.size);
52954
53044
  try {
52955
- const result = await download(source, temporaryPath);
53045
+ const result = await download(source, temporaryPath, progress.update);
53046
+ progress.update({ downloadedBytes: result.bytes, totalBytes: source.size });
53047
+ await progress.flush();
52956
53048
  const finalUrl = new URL(result.finalUrl);
52957
53049
  if (finalUrl.protocol !== "https:" || finalUrl.username || finalUrl.password) {
52958
53050
  throw new Error("update artifact downloader returned a non-HTTPS URL");
@@ -52962,6 +53054,8 @@ function createBackgroundUpdateManager(dependencies = {}) {
52962
53054
  return { source, artifactPath };
52963
53055
  } catch (error52) {
52964
53056
  await rm4(temporaryPath, { force: true });
53057
+ await progress.flush();
53058
+ await writeFailedStatus(common.statusPath, common.version, installation, error52);
52965
53059
  throw error52;
52966
53060
  }
52967
53061
  }
@@ -52975,6 +53069,7 @@ function createBackgroundUpdateManager(dependencies = {}) {
52975
53069
  installation
52976
53070
  };
52977
53071
  await writePrivateJson(common.requestPath, request);
53072
+ await writeStatusSnapshot(common.statusPath, statusSnapshot(common.version, installation.kind, "prepared"));
52978
53073
  preparedRequests.add(common.requestPath);
52979
53074
  return Object.freeze({
52980
53075
  version: common.version,
@@ -52988,19 +53083,24 @@ function createBackgroundUpdateManager(dependencies = {}) {
52988
53083
  return Object.freeze({
52989
53084
  async prepareNpm(options) {
52990
53085
  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
- });
53086
+ try {
53087
+ return await finalize2(common, {
53088
+ kind: "npm",
53089
+ node_path: await requireRegularFile(options.nodePath, "npm Node.js executable"),
53090
+ npm_cli_path: await requireRegularFile(options.npmCliPath, "npm CLI"),
53091
+ npm_launcher_path: await requireRegularFile(options.npmLauncherPath, "npm codexhost launcher"),
53092
+ package_root: requireAbsolutePath(options.packageRoot, "npm platform package root")
53093
+ });
53094
+ } catch (error52) {
53095
+ await writeFailedStatus(common.statusPath, common.version, "npm", error52);
53096
+ throw error52;
53097
+ }
52998
53098
  },
52999
53099
  async prepareWindowsInstaller(options) {
53000
53100
  if (platform !== "win32")
53001
53101
  throw new Error("Windows installer updates require Windows");
53002
53102
  const common = await prepareCommon(options, "windows-installer");
53003
- const artifact = await prepareArtifact(options.artifact, common.workDirectory, "update.exe");
53103
+ const artifact = await prepareArtifact(common, "windows-installer", options.artifact, "update.exe");
53004
53104
  return finalize2(common, {
53005
53105
  kind: "windows-installer",
53006
53106
  installer_path: artifact.artifactPath,
@@ -53016,7 +53116,7 @@ function createBackgroundUpdateManager(dependencies = {}) {
53016
53116
  if (path10.extname(appPath) !== ".app") {
53017
53117
  throw new Error("macOS application path must end in .app");
53018
53118
  }
53019
- const artifact = await prepareArtifact(options.artifact, common.workDirectory, "update.dmg");
53119
+ const artifact = await prepareArtifact(common, "macos-dmg", options.artifact, "update.dmg");
53020
53120
  return finalize2(common, {
53021
53121
  kind: "macos-dmg",
53022
53122
  dmg_path: artifact.artifactPath,
@@ -53073,6 +53173,8 @@ function publicStatus(status) {
53073
53173
  installation: status.installation,
53074
53174
  phase: status.phase,
53075
53175
  updatedAt: status.updatedAt,
53176
+ ...status.downloadedBytes === void 0 ? {} : { downloadedBytes: status.downloadedBytes },
53177
+ ...status.totalBytes === void 0 ? {} : { totalBytes: status.totalBytes },
53076
53178
  error: status.error?.slice(0, ERROR_MAX_LENGTH) ?? null
53077
53179
  };
53078
53180
  }
@@ -53116,9 +53218,20 @@ function createHostUpdateCoordinator(options) {
53116
53218
  const shutdown = options.shutdown ?? requestControllerShutdown;
53117
53219
  let candidate = null;
53118
53220
  let shutdownPending = null;
53221
+ let shutdownRequested = false;
53222
+ const scheduleShutdown = () => {
53223
+ if (!shutdownRequested || !shutdownPending) return;
53224
+ const controller = shutdownPending;
53225
+ shutdownPending = null;
53226
+ setTimeout(() => void shutdown(controller).catch(() => void 0), 50).unref();
53227
+ };
53119
53228
  async function latestStatus(context) {
53120
53229
  const discovered = await discoverLatestUpdateStatus(context.common.stateDirectory);
53121
- return discovered ? publicStatus(discovered.status) : null;
53230
+ if (!discovered) return null;
53231
+ if (discovered.status.phase !== "succeeded" && discovered.status.phase !== "failed" && !await isUpdateOperationActive(context.common.stateDirectory)) {
53232
+ return null;
53233
+ }
53234
+ return publicStatus(discovered.status);
53122
53235
  }
53123
53236
  async function installable(context, release) {
53124
53237
  if (context.metadata.distribution === "npm") return true;
@@ -53139,6 +53252,7 @@ function createHostUpdateCoordinator(options) {
53139
53252
  } catch (error52) {
53140
53253
  return {
53141
53254
  currentVersion: "0.0.0",
53255
+ installation: null,
53142
53256
  latestVersion: null,
53143
53257
  updateAvailable: false,
53144
53258
  installationAvailable: false,
@@ -53163,6 +53277,7 @@ function createHostUpdateCoordinator(options) {
53163
53277
  }
53164
53278
  return {
53165
53279
  currentVersion: context.metadata.version,
53280
+ installation: context.installation.kind,
53166
53281
  latestVersion: release.version,
53167
53282
  updateAvailable,
53168
53283
  installationAvailable,
@@ -53174,6 +53289,7 @@ function createHostUpdateCoordinator(options) {
53174
53289
  } catch (error52) {
53175
53290
  return {
53176
53291
  currentVersion: context.metadata.version,
53292
+ installation: context.installation.kind,
53177
53293
  latestVersion: null,
53178
53294
  updateAvailable: false,
53179
53295
  installationAvailable: false,
@@ -53193,34 +53309,59 @@ function createHostUpdateCoordinator(options) {
53193
53309
  if (existing) return { status: existing };
53194
53310
  throw new Error("Another update operation is already active");
53195
53311
  }
53312
+ let resolvePrepared;
53313
+ let rejectPrepared;
53314
+ const preparedReady = new Promise((resolve2, reject) => {
53315
+ resolvePrepared = resolve2;
53316
+ rejectPrepared = reject;
53317
+ });
53196
53318
  try {
53197
53319
  const release = await fetchLatest();
53198
53320
  if (compareSemanticVersions(context.metadata.version, release.version) >= 0 || candidate && candidate.version !== release.version) {
53199
53321
  throw new Error("The selected update is no longer the current GitHub Release");
53200
53322
  }
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);
53323
+ const onPrepared = async (info) => {
53324
+ await lock.setStatusPath(info.statusPath);
53325
+ resolvePrepared(info);
53326
+ };
53327
+ const prepareAndStart = async () => {
53328
+ try {
53329
+ let prepared2;
53330
+ if (context.installation.kind === "npm") {
53331
+ prepared2 = await manager.prepareNpm({
53332
+ ...context.installation.options,
53333
+ version: release.version,
53334
+ onPrepared
53335
+ });
53336
+ } else {
53337
+ const artifact = selectInstallerReleaseArtifact(
53338
+ release,
53339
+ context.metadata.target
53340
+ ).source;
53341
+ prepared2 = context.installation.kind === "windows-installer" ? await manager.prepareWindowsInstaller({
53342
+ ...context.installation.options,
53343
+ version: release.version,
53344
+ artifact,
53345
+ onPrepared
53346
+ }) : await manager.prepareMacOsDmg({
53347
+ ...context.installation.options,
53348
+ version: release.version,
53349
+ artifact,
53350
+ onPrepared
53351
+ });
53352
+ }
53353
+ manager.start(prepared2);
53354
+ shutdownPending = context.controller;
53355
+ scheduleShutdown();
53356
+ } catch (error52) {
53357
+ await lock.release();
53358
+ rejectPrepared(error52);
53359
+ }
53360
+ };
53361
+ void prepareAndStart();
53362
+ const prepared = await preparedReady;
53221
53363
  const status = await manager.readStatus(prepared.statusPath);
53222
- if (!status) throw new Error("Background Updater did not create status");
53223
- shutdownPending = context.controller;
53364
+ if (!status) throw new Error("Background update did not create status");
53224
53365
  return { status: publicStatus(status) };
53225
53366
  } catch (error52) {
53226
53367
  await lock.release();
@@ -53237,10 +53378,8 @@ function createHostUpdateCoordinator(options) {
53237
53378
  }
53238
53379
  },
53239
53380
  requestShutdown() {
53240
- const controller = shutdownPending;
53241
- shutdownPending = null;
53242
- if (!controller) return;
53243
- setTimeout(() => void shutdown(controller).catch(() => void 0), 50).unref();
53381
+ shutdownRequested = true;
53382
+ scheduleShutdown();
53244
53383
  }
53245
53384
  });
53246
53385
  }
@@ -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(),
@@ -17309,6 +17321,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17309
17321
  publish();
17310
17322
  return true;
17311
17323
  }
17324
+ var DRAFT_PREWARM_POLICY_WAIT_TIMEOUT_MS = 1e4;
17325
+ var DRAFT_PREWARM_POLICY_POLL_INTERVAL_MS = 25;
17312
17326
  function transportModelIdForAgent(agent) {
17313
17327
  if (agent === "pi") return PI_TRANSPORT_MODEL_ID;
17314
17328
  if (agent === "claude-code") return CLAUDE_CODE_TRANSPORT_MODEL_ID;
@@ -17639,6 +17653,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17639
17653
  function isDraftPrewarmPolicyReady(value) {
17640
17654
  return isRecord4(value) && value.state === "ready" && typeof value.clear === "function";
17641
17655
  }
17656
+ async function waitForRendererDraftPrewarmPolicy(target) {
17657
+ const deadline = Date.now() + DRAFT_PREWARM_POLICY_WAIT_TIMEOUT_MS;
17658
+ while (true) {
17659
+ const policy = target.__codexhostDraftPrewarmPolicyV1;
17660
+ if (isDraftPrewarmPolicyReady(policy)) return policy;
17661
+ const remaining = deadline - Date.now();
17662
+ if (remaining <= 0) throw new Error("Renderer draft prewarm policy is unavailable");
17663
+ await new Promise((resolve) => {
17664
+ target.setTimeout(resolve, Math.min(DRAFT_PREWARM_POLICY_POLL_INTERVAL_MS, remaining));
17665
+ });
17666
+ }
17667
+ }
17642
17668
  function modelSelectionForAgent(officialSelection, reasoningEffort, agent, model, thinkingOptionId, permissionModeId) {
17643
17669
  const transportModelId = agent === "pi" ? piTransportModelId(model, thinkingOptionId) : agent === "claude-code" ? claudeTransportModelId(model, permissionModeId, thinkingOptionId) : transportModelIdForAgent(agent);
17644
17670
  return transportModelId ? { model: transportModelId, reasoningEffort } : officialSelection;
@@ -18087,11 +18113,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18087
18113
  settingsButtonTitle: "codexhost settings",
18088
18114
  settingsUnavailableTitle: "codexhost settings unavailable",
18089
18115
  updateCurrentVersion: "Current version",
18116
+ updateInstallation: "Installation method",
18117
+ updateInstallationNpm: "npm",
18118
+ updateInstallationWindowsInstaller: "Windows installer",
18119
+ updateInstallationMacOsDmg: "macOS DMG",
18120
+ updateInstallationUnknown: "Unknown",
18090
18121
  updateLatestVersion: "Latest version",
18091
18122
  updateUpToDate: "You are up to date.",
18092
18123
  updateAvailable: "A new version is available.",
18093
18124
  updateAndRestart: "Update and restart",
18125
+ updateChecking: "Checking for updates...",
18126
+ updateDownloading: "Downloading update...",
18094
18127
  updatePreparing: "Preparing update...",
18128
+ updateRequestTimeout: "The update service did not respond. Try again.",
18095
18129
  updateRestarting: "Restarting to finish the update...",
18096
18130
  updateSucceeded: "Update installed successfully.",
18097
18131
  updateFailed: "Update failed.",
@@ -18124,11 +18158,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18124
18158
  settingsButtonTitle: "codexhost \u8BBE\u7F6E",
18125
18159
  settingsUnavailableTitle: "codexhost \u8BBE\u7F6E\u4E0D\u53EF\u7528",
18126
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",
18127
18166
  updateLatestVersion: "\u6700\u65B0\u7248\u672C",
18128
18167
  updateUpToDate: "\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002",
18129
18168
  updateAvailable: "\u6709\u65B0\u7248\u672C\u53EF\u7528\u3002",
18130
18169
  updateAndRestart: "\u66F4\u65B0\u5E76\u91CD\u542F",
18170
+ updateChecking: "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0...",
18171
+ updateDownloading: "\u6B63\u5728\u4E0B\u8F7D\u66F4\u65B0...",
18131
18172
  updatePreparing: "\u6B63\u5728\u51C6\u5907\u66F4\u65B0...",
18173
+ updateRequestTimeout: "\u66F4\u65B0\u670D\u52A1\u672A\u54CD\u5E94\uFF0C\u8BF7\u91CD\u8BD5\u3002",
18132
18174
  updateRestarting: "\u6B63\u5728\u91CD\u542F\u4EE5\u5B8C\u6210\u66F4\u65B0...",
18133
18175
  updateSucceeded: "\u66F4\u65B0\u5B89\u88C5\u6210\u529F\u3002",
18134
18176
  updateFailed: "\u66F4\u65B0\u5931\u8D25\u3002",
@@ -18435,6 +18477,49 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18435
18477
  }
18436
18478
  };
18437
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
+
18438
18523
  // src/settings/pages.ts
18439
18524
  var DEFAULT_RENDERER_SETTINGS_PAGE_IDS = [
18440
18525
  "connections",
@@ -18479,6 +18564,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18479
18564
  row.append(name, value);
18480
18565
  return row;
18481
18566
  }
18567
+ function installationLabel(installation, messages) {
18568
+ if (installation === "npm") return messages.updateInstallationNpm;
18569
+ if (installation === "windows-installer") {
18570
+ return messages.updateInstallationWindowsInstaller;
18571
+ }
18572
+ if (installation === "macos-dmg") return messages.updateInstallationMacOsDmg;
18573
+ return messages.updateInstallationUnknown;
18574
+ }
18482
18575
  function isPendingStatus(status) {
18483
18576
  return status !== null && status.phase !== "succeeded" && status.phase !== "failed";
18484
18577
  }
@@ -18487,8 +18580,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18487
18580
  if (status.phase === "succeeded") return messages.updateSucceeded;
18488
18581
  if (status.phase === "failed") return status.error ?? messages.updateFailed;
18489
18582
  if (status.phase === "restarting") return messages.updateRestarting;
18583
+ if (status.phase === "downloading") return messages.updateDownloading;
18490
18584
  return messages.updatePreparing;
18491
18585
  }
18586
+ function formatUpdateBytes(value) {
18587
+ if (value < 1024) return `${value} B`;
18588
+ const units = ["KB", "MB", "GB"];
18589
+ let scaled = value;
18590
+ let unit = "B";
18591
+ for (const nextUnit of units) {
18592
+ scaled /= 1024;
18593
+ unit = nextUnit;
18594
+ if (scaled < 1024 || nextUnit === units.at(-1)) break;
18595
+ }
18596
+ return `${scaled.toFixed(scaled >= 10 ? 0 : 1)} ${unit}`;
18597
+ }
18492
18598
  function updatesPage(messages, getClient) {
18493
18599
  return Object.freeze({
18494
18600
  id: "updates",
@@ -18499,10 +18605,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18499
18605
  const heading = document2.createElement("div");
18500
18606
  heading.className = "settings-section-label";
18501
18607
  heading.textContent = messages.pageLabels.updates;
18608
+ const metadata = document2.createElement("div");
18609
+ metadata.className = "settings-update-metadata";
18610
+ const currentVersion = document2.createElement("div");
18611
+ currentVersion.className = "settings-update-metadata__item";
18612
+ const currentVersionLabel = document2.createElement("span");
18613
+ currentVersionLabel.textContent = messages.updateCurrentVersion;
18614
+ const currentVersionValue = document2.createElement("strong");
18615
+ currentVersionValue.textContent = "-";
18616
+ currentVersion.append(currentVersionLabel, currentVersionValue);
18617
+ const installation = document2.createElement("div");
18618
+ installation.className = "settings-update-metadata__item";
18619
+ const installationName = document2.createElement("span");
18620
+ installationName.textContent = messages.updateInstallation;
18621
+ const installationValue = document2.createElement("strong");
18622
+ installationValue.textContent = "-";
18623
+ installation.append(installationName, installationValue);
18624
+ metadata.append(currentVersion, installation);
18502
18625
  const panel = document2.createElement("section");
18503
18626
  panel.className = "settings-update-panel";
18504
18627
  panel.setAttribute("aria-live", "polite");
18505
- context.content.append(heading, panel);
18628
+ context.content.append(heading, metadata, panel);
18506
18629
  let pollTimer;
18507
18630
  let pollAttempts = 0;
18508
18631
  let pending = false;
@@ -18521,22 +18644,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18521
18644
  copy.textContent = detail;
18522
18645
  panel.append(title, copy);
18523
18646
  };
18647
+ const renderRequestFailure = (error51) => {
18648
+ renderPendingStatus(
18649
+ null,
18650
+ error51 instanceof RendererUpdateRequestTimeoutError ? messages.updateRequestTimeout : error51 instanceof Error ? error51.message : messages.updateFailed,
18651
+ "failed"
18652
+ );
18653
+ };
18524
18654
  const scheduleStatusPoll = (client, resetAttempts = false) => {
18525
18655
  clearPoll();
18526
18656
  if (resetAttempts) pollAttempts = 0;
18527
- if (pollAttempts >= 320) return;
18657
+ if (pollAttempts >= 320) {
18658
+ renderPendingStatus(null, messages.updateRequestTimeout, "failed");
18659
+ return;
18660
+ }
18528
18661
  pollAttempts += 1;
18529
18662
  pollTimer = document2.defaultView?.setTimeout(() => {
18530
- void context.runLatest(() => client.readUpdateStatus(), {
18531
- success(result) {
18532
- const message = statusMessage(result.status, messages);
18533
- if (isPendingStatus(result.status)) scheduleStatusPoll(client);
18534
- if (message) renderPendingStatus(result.status, message);
18535
- },
18536
- failure(error51) {
18537
- renderUnavailable(error51 instanceof Error ? error51.message : messages.updateFailed);
18663
+ void context.runLatest(
18664
+ (signal) => runBoundedRendererUpdateRequest(() => client.readUpdateStatus(), signal),
18665
+ {
18666
+ success(result) {
18667
+ const message = statusMessage(result.status, messages);
18668
+ if (isPendingStatus(result.status)) scheduleStatusPoll(client);
18669
+ if (message) renderPendingStatus(result.status, message);
18670
+ },
18671
+ failure(error51) {
18672
+ renderRequestFailure(error51);
18673
+ }
18538
18674
  }
18539
- });
18675
+ );
18540
18676
  }, 750);
18541
18677
  };
18542
18678
  const renderPendingStatus = (status, message, viewPhase = status?.phase ?? "pending") => {
@@ -18545,6 +18681,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18545
18681
  const state = document2.createElement("strong");
18546
18682
  state.textContent = message;
18547
18683
  panel.append(state);
18684
+ if (status?.phase === "downloading" && status.totalBytes !== void 0 && status.downloadedBytes !== void 0) {
18685
+ const progress = document2.createElement("progress");
18686
+ progress.className = "settings-update-progress";
18687
+ progress.max = status.totalBytes;
18688
+ progress.value = Math.min(status.downloadedBytes, status.totalBytes);
18689
+ progress.setAttribute("aria-label", messages.updateDownloading);
18690
+ const detail = document2.createElement("span");
18691
+ detail.className = "settings-update-progress-detail";
18692
+ const percent = Math.min(
18693
+ 100,
18694
+ Math.round(status.downloadedBytes / status.totalBytes * 1e3) / 10
18695
+ );
18696
+ detail.textContent = `${percent}% \xB7 ${formatUpdateBytes(status.downloadedBytes)} / ${formatUpdateBytes(status.totalBytes)}`;
18697
+ panel.append(progress, detail);
18698
+ }
18548
18699
  if (viewPhase === "failed") {
18549
18700
  const retry = document2.createElement("button");
18550
18701
  retry.type = "button";
@@ -18558,26 +18709,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18558
18709
  if (pending) return;
18559
18710
  pending = true;
18560
18711
  renderPendingStatus(null, messages.updatePreparing);
18561
- void context.runLatest(() => client.startUpdate(), {
18562
- success(result) {
18563
- pending = false;
18564
- renderPendingStatus(
18565
- result.status,
18566
- statusMessage(result.status, messages) ?? messages.updatePreparing
18567
- );
18568
- if (isPendingStatus(result.status)) scheduleStatusPoll(client, true);
18569
- },
18570
- failure(error51) {
18571
- pending = false;
18572
- renderPendingStatus(
18573
- null,
18574
- error51 instanceof Error ? error51.message : messages.updateFailed,
18575
- "failed"
18576
- );
18712
+ void context.runLatest(
18713
+ (signal) => runBoundedRendererUpdateRequest(() => client.startUpdate(), signal),
18714
+ {
18715
+ success(result) {
18716
+ pending = false;
18717
+ renderPendingStatus(
18718
+ result.status,
18719
+ statusMessage(result.status, messages) ?? messages.updatePreparing
18720
+ );
18721
+ if (isPendingStatus(result.status)) scheduleStatusPoll(client, true);
18722
+ },
18723
+ failure(error51) {
18724
+ pending = false;
18725
+ renderRequestFailure(error51);
18726
+ }
18577
18727
  }
18578
- });
18728
+ );
18579
18729
  };
18580
18730
  const renderCheck = (result, client) => {
18731
+ currentVersionValue.textContent = `v${result.currentVersion}`;
18732
+ installationValue.textContent = installationLabel(result.installation, messages);
18581
18733
  const operationMessage = statusMessage(result.status, messages);
18582
18734
  if (isPendingStatus(result.status)) {
18583
18735
  renderPendingStatus(result.status, operationMessage ?? messages.updatePreparing);
@@ -18585,9 +18737,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18585
18737
  return;
18586
18738
  }
18587
18739
  panel.dataset.updateState = result.error ? "error" : result.updateAvailable ? "available" : "current";
18588
- panel.replaceChildren(
18589
- versionRow(context, messages.updateCurrentVersion, result.currentVersion)
18590
- );
18740
+ panel.replaceChildren();
18591
18741
  if (result.latestVersion) {
18592
18742
  panel.append(versionRow(context, messages.updateLatestVersion, result.latestVersion));
18593
18743
  }
@@ -18643,17 +18793,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18643
18793
  return Promise.resolve();
18644
18794
  }
18645
18795
  pending = true;
18646
- renderPendingStatus(null, messages.updatePreparing);
18647
- return context.runLatest(() => client.checkUpdate(), {
18648
- success(result) {
18649
- pending = false;
18650
- renderCheck(result, client);
18651
- },
18652
- failure(error51) {
18653
- pending = false;
18654
- renderUnavailable(error51 instanceof Error ? error51.message : messages.updateFailed);
18796
+ renderPendingStatus(null, messages.updateChecking);
18797
+ return context.runLatest(
18798
+ (signal) => runBoundedRendererUpdateRequest(() => client.checkUpdate(), signal),
18799
+ {
18800
+ success(result) {
18801
+ pending = false;
18802
+ renderCheck(result, client);
18803
+ },
18804
+ failure(error51) {
18805
+ pending = false;
18806
+ renderRequestFailure(error51);
18807
+ }
18655
18808
  }
18656
- });
18809
+ );
18657
18810
  };
18658
18811
  void load();
18659
18812
  return clearPoll;
@@ -18676,7 +18829,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18676
18829
  }
18677
18830
 
18678
18831
  // src/settings/shell.css
18679
- 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';
18832
+ 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-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';
18680
18833
 
18681
18834
  // src/settings/shell.ts
18682
18835
  var SETTINGS_SHELL_ATTRIBUTE = "data-codexhost-settings-shell";
@@ -18994,31 +19147,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18994
19147
  updateButton.setAttribute("aria-haspopup", "dialog");
18995
19148
  updateButton.title = messages.updateAvailable;
18996
19149
  updateButton.style.display = "none";
18997
- updateButton.style.position = "relative";
18998
19150
  updateButton.style.alignItems = "center";
18999
19151
  updateButton.style.justifyContent = "center";
19000
- updateButton.style.width = "28px";
19001
19152
  updateButton.style.height = "28px";
19002
- updateButton.style.padding = "0";
19003
- updateButton.style.border = "0";
19004
- updateButton.style.borderRadius = "8px";
19005
- updateButton.style.background = "transparent";
19006
- updateButton.style.color = "inherit";
19153
+ updateButton.style.padding = "0 10px";
19154
+ updateButton.style.gap = "6px";
19155
+ updateButton.style.border = "1px solid #1d4ed8";
19156
+ updateButton.style.borderRadius = "7px";
19157
+ updateButton.style.background = "#2563eb";
19158
+ updateButton.style.color = "#ffffff";
19007
19159
  updateButton.style.cursor = available ? "pointer" : "not-allowed";
19160
+ updateButton.style.opacity = available ? "1" : "0.5";
19161
+ updateButton.style.boxShadow = "0 1px 2px rgba(15, 23, 42, 0.18)";
19008
19162
  updateButton.style.outlineOffset = "2px";
19009
19163
  updateButton.style.setProperty("-webkit-app-region", "no-drag");
19010
- updateButton.append(createRendererSettingsIcon("updates", 16));
19011
- const updateIndicator = ownerDocument.createElement("span");
19012
- updateIndicator.setAttribute("aria-hidden", "true");
19013
- updateIndicator.style.position = "absolute";
19014
- updateIndicator.style.top = "3px";
19015
- updateIndicator.style.right = "3px";
19016
- updateIndicator.style.width = "6px";
19017
- updateIndicator.style.height = "6px";
19018
- updateIndicator.style.background = "#ef4444";
19019
- updateIndicator.style.border = "1px solid currentColor";
19020
- updateIndicator.style.borderRadius = "50%";
19021
- updateButton.append(updateIndicator);
19164
+ updateButton.append(createRendererSettingsIcon("updates", 15));
19165
+ const updateLabel = ownerDocument.createElement("span");
19166
+ updateLabel.textContent = messages.pageLabels.updates;
19167
+ updateLabel.style.fontSize = "12px";
19168
+ updateLabel.style.fontWeight = "600";
19169
+ updateLabel.style.lineHeight = "1";
19170
+ updateLabel.style.whiteSpace = "nowrap";
19171
+ updateButton.append(updateLabel);
19022
19172
  const onPointerEnter = () => {
19023
19173
  if (!button.disabled) button.style.background = "rgba(127, 127, 127, 0.16)";
19024
19174
  };
@@ -19030,10 +19180,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19030
19180
  if (!button.disabled) onOpen(button);
19031
19181
  };
19032
19182
  const onUpdatePointerEnter = () => {
19033
- if (!updateButton.disabled) updateButton.style.background = "rgba(127, 127, 127, 0.16)";
19183
+ if (!updateButton.disabled) {
19184
+ updateButton.style.background = "#1d4ed8";
19185
+ updateButton.style.boxShadow = "0 2px 4px rgba(15, 23, 42, 0.22)";
19186
+ }
19034
19187
  };
19035
19188
  const onUpdatePointerLeave = () => {
19036
- updateButton.style.background = "transparent";
19189
+ updateButton.style.background = "#2563eb";
19190
+ updateButton.style.boxShadow = "0 1px 2px rgba(15, 23, 42, 0.18)";
19037
19191
  };
19038
19192
  const onUpdateClick = (event) => {
19039
19193
  event.stopPropagation();
@@ -19462,10 +19616,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19462
19616
  ) === true && isPermissionModeControlReady(mounted.permissionModeView);
19463
19617
  };
19464
19618
  const clearDraftPrewarm = async () => {
19465
- const policy = window.__codexhostDraftPrewarmPolicyV1;
19466
- if (!isDraftPrewarmPolicyReady(policy)) {
19467
- throw new Error("Renderer draft prewarm policy is unavailable");
19468
- }
19619
+ const policy = await waitForRendererDraftPrewarmPolicy(window);
19469
19620
  await policy.clear();
19470
19621
  };
19471
19622
  const applyExternalConfiguration = (mounted, agent, model, thinkingOptionId, permissionModeId) => {
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-x64",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.",
5
5
  "type": "module",
6
6
  "files": [