cxstatusline 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47725,6 +47725,7 @@ var HIDE_KEYBIND = {
47725
47725
  };
47726
47726
  var NO_GIT_HIDEABLE_STATE = { key: "no-git", label: "when not in a git repo" };
47727
47727
  var ZERO_HIDEABLE_STATE = { key: "zero", label: "when value is zero" };
47728
+ var MERGE_TARGET_HIDDEN_HIDEABLE_STATE = { key: "merge-target-hidden", label: "when merge target is hidden" };
47728
47729
  function parseHideStates(value) {
47729
47730
  if (value === undefined) {
47730
47731
  return [];
@@ -47836,7 +47837,7 @@ class ClaudeSessionIdWidget {
47836
47837
  return "Shows the current Codex session ID";
47837
47838
  }
47838
47839
  getDisplayName() {
47839
- return "Claude Session ID";
47840
+ return "Codex Session ID";
47840
47841
  }
47841
47842
  getCategory() {
47842
47843
  return "Core";
@@ -48597,8 +48598,8 @@ class ContextBarWidget {
48597
48598
  if (total === null || used === null || total <= 0)
48598
48599
  return null;
48599
48600
  const percent = Math.max(0, Math.min(100, used / total * 100));
48600
- const usedText = formatTokens(used, tokenFormat);
48601
- const totalText = formatTokens(total, tokenFormat);
48601
+ const usedText = formatTokens(used, tokenFormat, 0);
48602
+ const totalText = formatTokens(total, tokenFormat, 0);
48602
48603
  const percentText = formatPercent(percent, percentFormat, 0);
48603
48604
  if (sliderMode(mode)) {
48604
48605
  const slider = makeSliderBar(percent);
@@ -54502,6 +54503,9 @@ class CustomSymbolWidget {
54502
54503
  action: "edit-symbol"
54503
54504
  }];
54504
54505
  }
54506
+ getHideableStates() {
54507
+ return [MERGE_TARGET_HIDDEN_HIDEABLE_STATE];
54508
+ }
54505
54509
  renderEditor(props) {
54506
54510
  return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(CustomSymbolEditor, {
54507
54511
  ...props
@@ -54594,6 +54598,9 @@ class CustomTextWidget {
54594
54598
  action: "edit-text"
54595
54599
  }];
54596
54600
  }
54601
+ getHideableStates() {
54602
+ return [MERGE_TARGET_HIDDEN_HIDEABLE_STATE];
54603
+ }
54597
54604
  renderEditor(props) {
54598
54605
  return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(CustomTextEditor, {
54599
54606
  ...props
@@ -55787,6 +55794,9 @@ class FiveHourUsageWidget {
55787
55794
  modifierText: getUsageDisplayModifierText(item, { showUsageDirection: true })
55788
55795
  };
55789
55796
  }
55797
+ getHideableStates() {
55798
+ return [USAGE_NO_DATA_HIDEABLE_STATE];
55799
+ }
55790
55800
  handleEditorAction(action, item) {
55791
55801
  if (action === "toggle-progress") {
55792
55802
  return cycleUsageDisplayMode(item, [], true, true);
@@ -55822,8 +55832,9 @@ class FiveHourUsageWidget {
55822
55832
  }
55823
55833
  const data = context.usageData ?? {};
55824
55834
  if (data.fiveHourUsage === undefined) {
55825
- if (data.error)
55826
- return getUsageErrorMessage(data.error);
55835
+ if (data.error) {
55836
+ return isHideStateEnabled(item, USAGE_NO_DATA_HIDEABLE_STATE) ? null : getUsageErrorMessage(data.error);
55837
+ }
55827
55838
  return null;
55828
55839
  }
55829
55840
  const percent = Math.max(0, Math.min(100, data.fiveHourUsage));
@@ -56772,6 +56783,7 @@ class SandboxStatusWidget {
56772
56783
  }
56773
56784
  }
56774
56785
  // src/widgets/SessionClock.ts
56786
+ var ZERO_HIDEABLE_STATE2 = { key: "zero", label: "when under 1 minute" };
56775
56787
  function formatDuration(ms) {
56776
56788
  const minutes = Math.floor(Math.max(0, ms) / 60000);
56777
56789
  if (minutes < 1)
@@ -56799,11 +56811,17 @@ class SessionClockWidget {
56799
56811
  getEditorDisplay(_item) {
56800
56812
  return { displayText: this.getDisplayName() };
56801
56813
  }
56814
+ getHideableStates() {
56815
+ return [ZERO_HIDEABLE_STATE2];
56816
+ }
56802
56817
  render(item, context, _settings) {
56803
56818
  const started = context.isPreview ? Date.parse("2026-09-02T09:45:00Z") : Date.parse(context.data.session?.started_at ?? "");
56804
56819
  if (!Number.isFinite(started))
56805
56820
  return null;
56806
- const value = formatDuration(context.now.getTime() - started);
56821
+ const elapsedMs = context.now.getTime() - started;
56822
+ if (elapsedMs < 60000 && isHideStateEnabled(item, ZERO_HIDEABLE_STATE2))
56823
+ return null;
56824
+ const value = formatDuration(elapsedMs);
56807
56825
  return item.rawValue ? value : "Session: " + value;
56808
56826
  }
56809
56827
  supportsRawValue() {
@@ -57051,12 +57069,19 @@ class TokensCachedWidget {
57051
57069
  getEditorDisplay(_item) {
57052
57070
  return { displayText: this.getDisplayName() };
57053
57071
  }
57072
+ getHideableStates() {
57073
+ return [ZERO_HIDEABLE_STATE];
57074
+ }
57054
57075
  render(item, context, settings) {
57055
57076
  const format = resolveNumberFormat("token", item, settings);
57056
57077
  if (context.isPreview)
57057
57078
  return formatRawOrLabeledValue(item, "Cached: ", formatTokens(12000, format, 0));
57058
57079
  const value = context.data.usage?.cached_input_tokens ?? null;
57059
- return value === null ? null : formatRawOrLabeledValue(item, "Cached: ", formatTokens(value, format));
57080
+ if (value === null)
57081
+ return null;
57082
+ if (value === 0 && isHideStateEnabled(item, ZERO_HIDEABLE_STATE))
57083
+ return null;
57084
+ return formatRawOrLabeledValue(item, "Cached: ", formatTokens(value, format));
57060
57085
  }
57061
57086
  supportsRawValue() {
57062
57087
  return true;
@@ -57085,12 +57110,19 @@ class TokensInputWidget {
57085
57110
  getEditorDisplay(_item) {
57086
57111
  return { displayText: this.getDisplayName() };
57087
57112
  }
57113
+ getHideableStates() {
57114
+ return [ZERO_HIDEABLE_STATE];
57115
+ }
57088
57116
  render(item, context, settings) {
57089
57117
  const format = resolveNumberFormat("token", item, settings);
57090
57118
  if (context.isPreview)
57091
57119
  return formatRawOrLabeledValue(item, "In: ", formatTokens(15200, format));
57092
57120
  const value = context.data.usage?.input_tokens ?? null;
57093
- return value === null ? null : formatRawOrLabeledValue(item, "In: ", formatTokens(value, format));
57121
+ if (value === null)
57122
+ return null;
57123
+ if (value === 0 && isHideStateEnabled(item, ZERO_HIDEABLE_STATE))
57124
+ return null;
57125
+ return formatRawOrLabeledValue(item, "In: ", formatTokens(value, format));
57094
57126
  }
57095
57127
  supportsRawValue() {
57096
57128
  return true;
@@ -57119,12 +57151,19 @@ class TokensOutputWidget {
57119
57151
  getEditorDisplay(_item) {
57120
57152
  return { displayText: this.getDisplayName() };
57121
57153
  }
57154
+ getHideableStates() {
57155
+ return [ZERO_HIDEABLE_STATE];
57156
+ }
57122
57157
  render(item, context, settings) {
57123
57158
  const format = resolveNumberFormat("token", item, settings);
57124
57159
  if (context.isPreview)
57125
57160
  return formatRawOrLabeledValue(item, "Out: ", formatTokens(3400, format));
57126
57161
  const value = context.data.usage?.output_tokens ?? null;
57127
- return value === null ? null : formatRawOrLabeledValue(item, "Out: ", formatTokens(value, format));
57162
+ if (value === null)
57163
+ return null;
57164
+ if (value === 0 && isHideStateEnabled(item, ZERO_HIDEABLE_STATE))
57165
+ return null;
57166
+ return formatRawOrLabeledValue(item, "Out: ", formatTokens(value, format));
57128
57167
  }
57129
57168
  supportsRawValue() {
57130
57169
  return true;
@@ -57153,12 +57192,19 @@ class TokensTotalWidget {
57153
57192
  getEditorDisplay(_item) {
57154
57193
  return { displayText: this.getDisplayName() };
57155
57194
  }
57195
+ getHideableStates() {
57196
+ return [ZERO_HIDEABLE_STATE];
57197
+ }
57156
57198
  render(item, context, settings) {
57157
57199
  const format = resolveNumberFormat("token", item, settings);
57158
57200
  if (context.isPreview)
57159
57201
  return formatRawOrLabeledValue(item, "Total: ", formatTokens(30600, format));
57160
57202
  const value = context.data.usage && context.data.usage.input_tokens !== undefined && context.data.usage.output_tokens !== undefined ? context.data.usage.input_tokens + context.data.usage.output_tokens : null;
57161
- return value === null ? null : formatRawOrLabeledValue(item, "Total: ", formatTokens(value, format));
57203
+ if (value === null)
57204
+ return null;
57205
+ if (value === 0 && isHideStateEnabled(item, ZERO_HIDEABLE_STATE))
57206
+ return null;
57207
+ return formatRawOrLabeledValue(item, "Total: ", formatTokens(value, format));
57162
57208
  }
57163
57209
  supportsRawValue() {
57164
57210
  return true;
@@ -57454,6 +57500,9 @@ class WeeklyUsageWidget {
57454
57500
  modifierText: getUsageDisplayModifierText(item, { showUsageDirection: true })
57455
57501
  };
57456
57502
  }
57503
+ getHideableStates() {
57504
+ return [USAGE_NO_DATA_HIDEABLE_STATE];
57505
+ }
57457
57506
  handleEditorAction(action, item) {
57458
57507
  if (action === "toggle-progress") {
57459
57508
  return cycleUsageDisplayMode(item, [], true, true);
@@ -57489,8 +57538,9 @@ class WeeklyUsageWidget {
57489
57538
  }
57490
57539
  const data = context.usageData ?? {};
57491
57540
  if (data.weeklyUsage === undefined) {
57492
- if (data.error)
57493
- return getUsageErrorMessage(data.error);
57541
+ if (data.error) {
57542
+ return isHideStateEnabled(item, USAGE_NO_DATA_HIDEABLE_STATE) ? null : getUsageErrorMessage(data.error);
57543
+ }
57494
57544
  return null;
57495
57545
  }
57496
57546
  const percent = Math.max(0, Math.min(100, data.weeklyUsage));
@@ -57568,6 +57618,7 @@ var WIDGET_MANIFEST = [
57568
57618
  { type: "session-clock", create: () => new SessionClockWidget },
57569
57619
  { type: "session-name", create: () => new SessionNameWidget },
57570
57620
  { type: "claude-session-id", create: () => new ClaudeSessionIdWidget },
57621
+ { type: "codex-session-id", create: () => new ClaudeSessionIdWidget },
57571
57622
  { type: "version", create: () => new VersionWidget },
57572
57623
  { type: "current-working-dir", create: () => new CurrentWorkingDirWidget },
57573
57624
  { type: "sandbox-status", create: () => new SandboxStatusWidget },
@@ -58190,6 +58241,46 @@ function countPowerlineStartCapSlots(widgets, preRenderedWidgets) {
58190
58241
  }
58191
58242
  return renderedSegmentCount;
58192
58243
  }
58244
+ var DECORATIVE_WIDGET_TYPES = new Set(["custom-text", "custom-symbol"]);
58245
+ function isSeparatorType(type) {
58246
+ return type === "separator" || type === "flex-separator";
58247
+ }
58248
+ function applyMergeTargetHiding(preRenderedLine) {
58249
+ let chainStart = 0;
58250
+ for (let i = 0;i <= preRenderedLine.length; i++) {
58251
+ const element = preRenderedLine[i];
58252
+ if (element && !isSeparatorType(element.widget.type)) {
58253
+ continue;
58254
+ }
58255
+ applyMergeTargetHidingToSegment(preRenderedLine.slice(chainStart, i));
58256
+ chainStart = i + 1;
58257
+ }
58258
+ }
58259
+ function applyMergeTargetHidingToSegment(segment) {
58260
+ let chainStart = 0;
58261
+ for (let i = 0;i < segment.length; i++) {
58262
+ const linksToNext = Boolean(segment[i]?.widget.merge) && i < segment.length - 1;
58263
+ if (linksToNext) {
58264
+ continue;
58265
+ }
58266
+ const chain = segment.slice(chainStart, i + 1);
58267
+ chainStart = i + 1;
58268
+ if (chain.length < 2) {
58269
+ continue;
58270
+ }
58271
+ for (let position = 0;position < chain.length; position++) {
58272
+ const element = chain[position];
58273
+ if (!element || !DECORATIVE_WIDGET_TYPES.has(element.widget.type) || !isHideStateEnabled(element.widget, MERGE_TARGET_HIDDEN_HIDEABLE_STATE)) {
58274
+ continue;
58275
+ }
58276
+ const target = chain.slice(position + 1).find((candidate) => !DECORATIVE_WIDGET_TYPES.has(candidate.widget.type)) ?? chain.slice(0, position).reverse().find((candidate) => !DECORATIVE_WIDGET_TYPES.has(candidate.widget.type));
58277
+ if (target?.content === "") {
58278
+ element.content = "";
58279
+ element.plainLength = 0;
58280
+ }
58281
+ }
58282
+ }
58283
+ }
58193
58284
  function preRenderAllWidgets(allLinesWidgets, settings, context) {
58194
58285
  const preRenderedLines = [];
58195
58286
  for (const lineWidgets of allLinesWidgets) {
@@ -58227,6 +58318,7 @@ function preRenderAllWidgets(allLinesWidgets, settings, context) {
58227
58318
  styled
58228
58319
  });
58229
58320
  }
58321
+ applyMergeTargetHiding(preRenderedLine);
58230
58322
  preRenderedLines.push(preRenderedLine);
58231
58323
  }
58232
58324
  return preRenderedLines;
@@ -58589,6 +58681,7 @@ var CANONICAL_WIDGET_TYPES = [
58589
58681
  "session-clock",
58590
58682
  "session-name",
58591
58683
  "claude-session-id",
58684
+ "codex-session-id",
58592
58685
  "version",
58593
58686
  "current-working-dir",
58594
58687
  "sandbox-status",
@@ -58912,8 +59005,8 @@ function runRefreshCommand(cacheKey2, deps) {
58912
59005
  return 0;
58913
59006
  }
58914
59007
  // src/version-info.ts
58915
- var VERSION = "0.7.0";
58916
- var SOURCE_COMMIT = /^[0-9a-f]{40}$/.test("e1daa573762212753a6a35fec0e3cc621cadb614") ? "e1daa573762212753a6a35fec0e3cc621cadb614" : null;
59008
+ var VERSION = "0.8.0";
59009
+ var SOURCE_COMMIT = /^[0-9a-f]{40}$/.test("ac126bc0e09a58856e40452e31bfc62c294eee36") ? "ac126bc0e09a58856e40452e31bfc62c294eee36" : null;
58917
59010
  var SOURCE_DIRTY = false;
58918
59011
 
58919
59012
  // src/context.ts
@@ -63161,6 +63254,47 @@ async function preparePrebuilt(ctx, expected, opts = {}) {
63161
63254
  rmSync2(download, { recursive: true, force: true });
63162
63255
  }
63163
63256
  }
63257
+ async function fetchPublishedPrebuiltVersions(fetchFn, repo = "adrijshikhar/cxstatusline") {
63258
+ const fetcher = fetchFn ?? globalThis.fetch;
63259
+ try {
63260
+ const headers = {
63261
+ accept: "application/vnd.github+json",
63262
+ "user-agent": "cxstatusline"
63263
+ };
63264
+ if (process.env.GITHUB_TOKEN) {
63265
+ headers.authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
63266
+ }
63267
+ const res = await fetcher(`https://api.github.com/repos/${repo}/releases?per_page=100`, {
63268
+ headers,
63269
+ signal: AbortSignal.timeout(5000)
63270
+ });
63271
+ if (!res.ok)
63272
+ return [];
63273
+ const data = await res.json();
63274
+ if (!Array.isArray(data))
63275
+ return [];
63276
+ const versions2 = new Set;
63277
+ for (const item of data) {
63278
+ if (typeof item === "object" && item !== null) {
63279
+ const tag = item.tag_name;
63280
+ const draft = item.draft;
63281
+ if (typeof tag === "string" && !draft && tag.startsWith("codex-v")) {
63282
+ const v2 = tag.slice("codex-v".length);
63283
+ if (parseSemver(v2)) {
63284
+ versions2.add(v2);
63285
+ }
63286
+ }
63287
+ }
63288
+ }
63289
+ return Array.from(versions2).sort((a, b2) => {
63290
+ const semA = parseSemver(a);
63291
+ const semB = parseSemver(b2);
63292
+ return compareSemver(semB, semA);
63293
+ });
63294
+ } catch {
63295
+ return [];
63296
+ }
63297
+ }
63164
63298
 
63165
63299
  // src/distribution.ts
63166
63300
  var PLATFORMS = [
@@ -63494,6 +63628,79 @@ function writeState(file2, state) {
63494
63628
  writeFileAtomic(`${file2}${BACKUP_SUFFIX}`, text);
63495
63629
  }
63496
63630
 
63631
+ // src/ui/prompt-version.ts
63632
+ import { createInterface } from "node:readline";
63633
+ async function promptCodexVersion(options) {
63634
+ const { supportedVersions, prebuiltVersions, isTTY = false, say = (s3) => console.log(s3) } = options;
63635
+ if (supportedVersions.length === 0) {
63636
+ throw new Error("No supported Codex versions available.");
63637
+ }
63638
+ let defaultVersion;
63639
+ if (options.defaultVersion && supportedVersions.includes(options.defaultVersion)) {
63640
+ defaultVersion = options.defaultVersion;
63641
+ } else if (prebuiltVersions && prebuiltVersions.length > 0) {
63642
+ defaultVersion = supportedVersions.find((v2) => prebuiltVersions.includes(v2)) ?? supportedVersions[0];
63643
+ } else {
63644
+ defaultVersion = supportedVersions[0];
63645
+ }
63646
+ if (!isTTY) {
63647
+ const isSourceOnly = Boolean(prebuiltVersions && !prebuiltVersions.includes(defaultVersion));
63648
+ return { version: defaultVersion, compile: isSourceOnly };
63649
+ }
63650
+ const defaultIndex = supportedVersions.indexOf(defaultVersion) + 1;
63651
+ say(`
63652
+ Select Codex version to install:`);
63653
+ supportedVersions.forEach((v2, idx) => {
63654
+ const isDef = v2 === defaultVersion;
63655
+ let tag = "";
63656
+ if (prebuiltVersions) {
63657
+ tag = prebuiltVersions.includes(v2) ? " [prebuilt available]" : " [compile from source - prebuilt pending]";
63658
+ }
63659
+ say(` ${idx + 1}) ${v2}${tag}${isDef ? " (recommended / default)" : ""}`);
63660
+ });
63661
+ const askFn = options.ask ?? defaultAsk;
63662
+ while (true) {
63663
+ const raw = (await askFn(`Enter choice [1-${supportedVersions.length}] (default ${defaultIndex}): `)).trim();
63664
+ let selectedVersion = null;
63665
+ if (raw === "") {
63666
+ selectedVersion = defaultVersion;
63667
+ } else {
63668
+ const num2 = Number.parseInt(raw, 10);
63669
+ if (!Number.isNaN(num2) && num2 >= 1 && num2 <= supportedVersions.length) {
63670
+ selectedVersion = supportedVersions[num2 - 1];
63671
+ } else if (supportedVersions.includes(raw)) {
63672
+ selectedVersion = raw;
63673
+ }
63674
+ }
63675
+ if (!selectedVersion) {
63676
+ say(`Invalid selection "${raw}". Please enter a number between 1 and ${supportedVersions.length}.`);
63677
+ continue;
63678
+ }
63679
+ const hasPrebuilt = !prebuiltVersions || prebuiltVersions.includes(selectedVersion);
63680
+ if (!hasPrebuilt) {
63681
+ const confirm = (await askFn(`No prebuilt binary is published for Codex ${selectedVersion} yet. Would you like to compile from source instead? [y/N]: `)).trim().toLowerCase();
63682
+ if (confirm === "y" || confirm === "yes") {
63683
+ return { version: selectedVersion, compile: true };
63684
+ }
63685
+ say("Please select a version with prebuilt binaries available, or run with --compile.");
63686
+ continue;
63687
+ }
63688
+ return { version: selectedVersion, compile: false };
63689
+ }
63690
+ }
63691
+ function defaultAsk(question) {
63692
+ const rl = createInterface({
63693
+ input: process.stdin,
63694
+ output: process.stdout
63695
+ });
63696
+ return new Promise((resolve4) => {
63697
+ rl.question(question, (answer) => {
63698
+ rl.close();
63699
+ resolve4(answer);
63700
+ });
63701
+ });
63702
+ }
63703
+
63497
63704
  // src/patch/acquire.ts
63498
63705
  import { rmSync as rmSync5 } from "node:fs";
63499
63706
 
@@ -64677,8 +64884,9 @@ async function probeRemoteCandidate(targetCodexVersion, fetchFnOrBaseUrl, baseUr
64677
64884
  }
64678
64885
  }
64679
64886
  async function runUpdate(ctx, optsOrTransport = {}, maybeTransport) {
64680
- const opts = "compile" in optsOrTransport || "force" in optsOrTransport ? optsOrTransport : {};
64681
- const transport = "baseUrl" in optsOrTransport || "fetch" in optsOrTransport ? optsOrTransport : maybeTransport ?? {};
64887
+ const isOpts = typeof optsOrTransport === "object" && optsOrTransport !== null && (("compile" in optsOrTransport) || ("force" in optsOrTransport) || ("isTTY" in optsOrTransport) || ("ask" in optsOrTransport) || ("fetchPrebuilts" in optsOrTransport));
64888
+ let activeOpts = isOpts ? optsOrTransport : {};
64889
+ const transport = !isOpts && (("baseUrl" in optsOrTransport) || ("fetch" in optsOrTransport)) ? optsOrTransport : maybeTransport ?? {};
64682
64890
  const state = loadState(ctx);
64683
64891
  const located = upstreamFor(ctx, state);
64684
64892
  if ("reason" in located) {
@@ -64687,20 +64895,106 @@ async function runUpdate(ctx, optsOrTransport = {}, maybeTransport) {
64687
64895
  }
64688
64896
  const current = readUpstreamVersion(located.bin, ctx.run);
64689
64897
  const fetchFn = transport.fetch ?? fetch;
64690
- if (current && !opts.force && !opts.compile) {
64898
+ if (current && !activeOpts.force && !activeOpts.compile) {
64691
64899
  const latest = await probeUpstreamLatest(fetchFn);
64692
64900
  if (latest && latest !== current.raw) {
64693
64901
  const available = await probePrebuiltExists(latest, fetchFn, transport.baseUrl);
64694
64902
  if (!available) {
64695
- ctx.say(`Warning: Upstream Codex update available: ${current.raw} -> ${latest}.`);
64696
- ctx.say(`However, cxstatusline has not yet published prebuilt binaries for Codex ${latest}.`);
64697
- ctx.say("Updating now will replace your patched launcher with stock Codex.");
64698
- ctx.say("");
64699
- ctx.say("Options:");
64700
- ctx.say(" - Wait until cxstatusline publishes prebuilt binaries for this version.");
64701
- ctx.say(" - Update and compile from source: cxstatusline update --compile");
64702
- ctx.say(" - Update to stock Codex anyway: cxstatusline update --force");
64703
- return 1;
64903
+ const prebuiltsFetcher = activeOpts.fetchPrebuilts ?? fetchPublishedPrebuiltVersions;
64904
+ const publishedPrebuilts = await prebuiltsFetcher(fetchFn);
64905
+ const currentSem = parseSemver(current.raw);
64906
+ const newerPrebuilts = currentSem ? publishedPrebuilts.filter((v2) => {
64907
+ const s3 = parseSemver(v2);
64908
+ return s3 !== null && compareSemver(s3, currentSem) > 0;
64909
+ }) : [];
64910
+ const highestAvailable = newerPrebuilts[0];
64911
+ if (highestAvailable) {
64912
+ ctx.say(`Warning: Upstream Codex update available: ${current.raw} -> ${latest}.`);
64913
+ ctx.say(`However, cxstatusline has not yet published prebuilt binaries for Codex ${latest}.`);
64914
+ ctx.say(`Newer prebuilt available: Codex ${highestAvailable} is published and ready to install.`);
64915
+ ctx.say("");
64916
+ if (!activeOpts.isTTY) {
64917
+ ctx.say("Options:");
64918
+ ctx.say(` - Install latest available prebuilt: cxstatusline install --codex-version ${highestAvailable}`);
64919
+ ctx.say(` - Wait until cxstatusline publishes prebuilt binaries for ${latest}.`);
64920
+ ctx.say(" - Update and compile from source: cxstatusline update --compile");
64921
+ ctx.say(" - Update to stock Codex anyway: cxstatusline update --force");
64922
+ return 1;
64923
+ }
64924
+ ctx.say("How would you like to proceed?");
64925
+ ctx.say(` 1) Install latest available prebuilt (Codex ${highestAvailable}) [recommended]`);
64926
+ ctx.say(` 2) Compile Codex ${latest} from source`);
64927
+ ctx.say(` 3) Update to stock Codex ${latest} anyway`);
64928
+ ctx.say(" 4) Cancel");
64929
+ const askFn = activeOpts.ask ?? defaultAsk;
64930
+ let selectedAction = null;
64931
+ while (!selectedAction) {
64932
+ const choice = (await askFn("Enter choice [1-4] (default 1): ")).trim();
64933
+ if (choice === "1" || choice === "") {
64934
+ selectedAction = "prebuilt";
64935
+ } else if (choice === "2") {
64936
+ selectedAction = "compile";
64937
+ } else if (choice === "3") {
64938
+ selectedAction = "stock";
64939
+ } else if (choice === "4") {
64940
+ selectedAction = "cancel";
64941
+ } else {
64942
+ ctx.say(`Invalid choice "${choice}". Please select 1, 2, 3, or 4.`);
64943
+ }
64944
+ }
64945
+ if (selectedAction === "prebuilt") {
64946
+ ctx.say(`Installing prebuilt binaries for Codex ${highestAvailable}...`);
64947
+ return runInstall(ctx, { compile: false, codexVersion: highestAvailable }, transport);
64948
+ }
64949
+ if (selectedAction === "cancel") {
64950
+ ctx.say("Update cancelled.");
64951
+ return 0;
64952
+ }
64953
+ if (selectedAction === "compile") {
64954
+ activeOpts = { ...activeOpts, compile: true };
64955
+ } else if (selectedAction === "stock") {
64956
+ activeOpts = { ...activeOpts, force: true };
64957
+ }
64958
+ } else {
64959
+ ctx.say(`Warning: Upstream Codex update available: ${current.raw} -> ${latest}.`);
64960
+ ctx.say(`However, cxstatusline has not yet published prebuilt binaries for Codex ${latest}.`);
64961
+ ctx.say("Updating now will replace your patched launcher with stock Codex.");
64962
+ ctx.say("");
64963
+ if (!activeOpts.isTTY) {
64964
+ ctx.say("Options:");
64965
+ ctx.say(" - Wait until cxstatusline publishes prebuilt binaries for this version.");
64966
+ ctx.say(" - Update and compile from source: cxstatusline update --compile");
64967
+ ctx.say(" - Update to stock Codex anyway: cxstatusline update --force");
64968
+ return 1;
64969
+ }
64970
+ ctx.say("How would you like to proceed?");
64971
+ ctx.say(` 1) Compile Codex ${latest} from source`);
64972
+ ctx.say(` 2) Update to stock Codex ${latest} anyway`);
64973
+ ctx.say(" 3) Cancel");
64974
+ const askFn = activeOpts.ask ?? defaultAsk;
64975
+ let selectedAction = null;
64976
+ while (!selectedAction) {
64977
+ const choice = (await askFn("Enter choice [1-3] (default 3): ")).trim();
64978
+ if (choice === "1") {
64979
+ selectedAction = "compile";
64980
+ } else if (choice === "2") {
64981
+ selectedAction = "stock";
64982
+ } else if (choice === "3" || choice === "") {
64983
+ selectedAction = "cancel";
64984
+ } else {
64985
+ ctx.say(`Invalid choice "${choice}". Please select 1, 2, or 3.`);
64986
+ }
64987
+ }
64988
+ if (selectedAction === "cancel") {
64989
+ ctx.say("Update cancelled.");
64990
+ return 0;
64991
+ }
64992
+ if (selectedAction === "compile") {
64993
+ activeOpts = { ...activeOpts, compile: true };
64994
+ } else if (selectedAction === "stock") {
64995
+ activeOpts = { ...activeOpts, force: true };
64996
+ }
64997
+ }
64704
64998
  }
64705
64999
  } else if (latest && latest === current.raw) {
64706
65000
  ctx.say(`Codex is already at the latest version (${current.raw}).`);
@@ -64713,7 +65007,7 @@ async function runUpdate(ctx, optsOrTransport = {}, maybeTransport) {
64713
65007
  ctx.say(`upstream updater exited ${String(r.status)}; see its output above. Not installing.`);
64714
65008
  return 1;
64715
65009
  }
64716
- const source = opts.compile ? "compiled" : "prebuilt";
65010
+ const source = activeOpts.compile ? "compiled" : "prebuilt";
64717
65011
  const progress = createInstallProgressTracker({
64718
65012
  isTTY: process.stdout?.isTTY,
64719
65013
  write: (s3) => process.stdout.write(s3),
@@ -64734,53 +65028,6 @@ function appendLog(file2, line) {
64734
65028
  `);
64735
65029
  }
64736
65030
 
64737
- // src/ui/prompt-version.ts
64738
- import { createInterface } from "node:readline";
64739
- async function promptCodexVersion(options) {
64740
- const { supportedVersions, isTTY = false, say = (s3) => console.log(s3) } = options;
64741
- if (supportedVersions.length === 0) {
64742
- throw new Error("No supported Codex versions available.");
64743
- }
64744
- const defaultVersion = options.defaultVersion && supportedVersions.includes(options.defaultVersion) ? options.defaultVersion : supportedVersions[0];
64745
- if (!isTTY) {
64746
- return defaultVersion;
64747
- }
64748
- const defaultIndex = supportedVersions.indexOf(defaultVersion) + 1;
64749
- say(`
64750
- Select Codex version to install:`);
64751
- supportedVersions.forEach((v2, idx) => {
64752
- const isDef = v2 === defaultVersion;
64753
- say(` ${idx + 1}) ${v2}${isDef ? " (recommended / default)" : ""}`);
64754
- });
64755
- const askFn = options.ask ?? defaultAsk;
64756
- while (true) {
64757
- const raw = (await askFn(`Enter choice [1-${supportedVersions.length}] (default ${defaultIndex}): `)).trim();
64758
- if (raw === "") {
64759
- return defaultVersion;
64760
- }
64761
- const num2 = Number.parseInt(raw, 10);
64762
- if (!Number.isNaN(num2) && num2 >= 1 && num2 <= supportedVersions.length) {
64763
- return supportedVersions[num2 - 1];
64764
- }
64765
- if (supportedVersions.includes(raw)) {
64766
- return raw;
64767
- }
64768
- say(`Invalid selection "${raw}". Please enter a number between 1 and ${supportedVersions.length}.`);
64769
- }
64770
- }
64771
- function defaultAsk(question) {
64772
- const rl = createInterface({
64773
- input: process.stdin,
64774
- output: process.stdout
64775
- });
64776
- return new Promise((resolve4) => {
64777
- rl.question(question, (answer) => {
64778
- rl.close();
64779
- resolve4(answer);
64780
- });
64781
- });
64782
- }
64783
-
64784
65031
  // src/hook/run.ts
64785
65032
  import { appendFileSync as appendFileSync2, closeSync as closeSync3, existsSync as existsSync12, mkdirSync as mkdirSync9, openSync as openSync3 } from "node:fs";
64786
65033
  import { spawn as spawn2 } from "node:child_process";
@@ -65351,6 +65598,25 @@ async function codexTargetLine(ctx, effectivePatchedFrom, opts) {
65351
65598
  return line2("codex_target", `${target} available (active: ${effectivePatchedFrom}; prebuilt live on GitHub; run cxstatusline install to update)`, null);
65352
65599
  }
65353
65600
  if (remote?.available === false) {
65601
+ let intermediate = null;
65602
+ if (opts?.fetchPrebuilts || opts?.probe !== false && opts?.probeFn === undefined) {
65603
+ try {
65604
+ const fetchFn = (url2, init) => fetch(url2, { ...init, signal: AbortSignal.timeout(2000) });
65605
+ const prebuilts = await (opts?.fetchPrebuilts ?? fetchPublishedPrebuiltVersions)(fetchFn);
65606
+ const intermediatePrebuilt = prebuilts.find((v2) => {
65607
+ const s3 = parseSemver(v2);
65608
+ return s3 !== null && parsedActive !== null && parsedTarget !== null && compareSemver(s3, parsedActive) > 0 && compareSemver(s3, parsedTarget) < 0;
65609
+ });
65610
+ if (intermediatePrebuilt) {
65611
+ intermediate = intermediatePrebuilt;
65612
+ }
65613
+ } catch {
65614
+ intermediate = null;
65615
+ }
65616
+ }
65617
+ if (intermediate) {
65618
+ return line2("codex_target", `${target} supported (active: ${effectivePatchedFrom}; prebuilt pending; ${intermediate} prebuilt available on GitHub)`, null);
65619
+ }
65354
65620
  return line2("codex_target", `${target} supported (active: ${effectivePatchedFrom}; prebuilt pending; run cxstatusline install --compile)`, null);
65355
65621
  }
65356
65622
  return line2("codex_target", `${target} supported (active: ${effectivePatchedFrom}; run cxstatusline install to update)`, null);
@@ -68239,6 +68505,14 @@ async function installCommand(argv, io2, deps) {
68239
68505
  const manifest = loadManifest(ctx.patchesDir);
68240
68506
  const supported = supportedCodexVersions(manifest);
68241
68507
  if (supported.length > 0) {
68508
+ let prebuiltVersions;
68509
+ if (!compile3) {
68510
+ try {
68511
+ prebuiltVersions = await fetchPublishedPrebuiltVersions(deps.transport?.fetch);
68512
+ } catch {
68513
+ prebuiltVersions = undefined;
68514
+ }
68515
+ }
68242
68516
  let defaultVersion;
68243
68517
  try {
68244
68518
  const located = upstreamFor(ctx, loadState(ctx));
@@ -68247,19 +68521,28 @@ async function installCommand(argv, io2, deps) {
68247
68521
  if (upstream) {
68248
68522
  const semverStr = `${upstream.major}.${upstream.minor}.${upstream.patch}`;
68249
68523
  if (supported.includes(semverStr)) {
68250
- defaultVersion = semverStr;
68524
+ if (compile3 || !prebuiltVersions || prebuiltVersions.includes(semverStr)) {
68525
+ defaultVersion = semverStr;
68526
+ }
68251
68527
  }
68252
68528
  }
68253
68529
  }
68254
68530
  } catch {}
68531
+ if (!defaultVersion && prebuiltVersions && prebuiltVersions.length > 0) {
68532
+ defaultVersion = supported.find((v2) => prebuiltVersions.includes(v2));
68533
+ }
68255
68534
  const prompter = deps.promptVersion ?? promptCodexVersion;
68256
- codexVersion = await prompter({
68535
+ const result2 = await prompter({
68257
68536
  supportedVersions: supported,
68537
+ prebuiltVersions,
68258
68538
  defaultVersion,
68259
68539
  isTTY: true,
68260
68540
  say: (l) => io2.stdout(`${l}
68261
68541
  `)
68262
68542
  });
68543
+ const selection = typeof result2 === "string" ? { version: result2, compile: false } : result2;
68544
+ codexVersion = selection.version;
68545
+ compile3 = compile3 || selection.compile;
68263
68546
  }
68264
68547
  } catch {}
68265
68548
  }
@@ -68274,7 +68557,10 @@ async function updateCommand(argv, io2, deps) {
68274
68557
  }
68275
68558
  return runUpdate(contextFor(io2, deps), {
68276
68559
  compile: flags.includes("--compile"),
68277
- force: flags.includes("--force") || flags.includes("-y") || flags.includes("--yes")
68560
+ force: flags.includes("--force") || flags.includes("-y") || flags.includes("--yes"),
68561
+ isTTY: io2.isTTY,
68562
+ ask: deps.ask,
68563
+ fetchPrebuilts: deps.fetchPrebuilts
68278
68564
  }, deps.transport);
68279
68565
  }
68280
68566
  async function hookAcquireCommand(io2, deps) {