cxstatusline 0.1.1 → 0.3.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.
@@ -54793,7 +54793,7 @@ class SessionNameWidget {
54793
54793
  }
54794
54794
  // src/utils/terminal.ts
54795
54795
  import { execSync } from "child_process";
54796
- var __dirname = "/Users/nemesis/Projects/my-projects/cxstatusline/src/utils";
54796
+ var __dirname = "/Users/runner/work/cxstatusline/cxstatusline/src/utils";
54797
54797
  function probeTerminalWidth() {
54798
54798
  const overrideRaw = process.env.CCSTATUSLINE_WIDTH;
54799
54799
  if (overrideRaw) {
@@ -57046,10 +57046,9 @@ async function runRender(stdin, deps) {
57046
57046
  `, code: 2 };
57047
57047
  }
57048
57048
  }
57049
-
57050
57049
  // src/version-info.ts
57051
- var VERSION = "0.1.1";
57052
- var SOURCE_COMMIT = /^[0-9a-f]{40}$/.test("5a03216d4eae4d4565273e8206845658706dcb6b") ? "5a03216d4eae4d4565273e8206845658706dcb6b" : null;
57050
+ var VERSION = "0.3.0";
57051
+ var SOURCE_COMMIT = /^[0-9a-f]{40}$/.test("238a373fb9c429871859ab99d013fa69b456b23b") ? "238a373fb9c429871859ab99d013fa69b456b23b" : null;
57053
57052
  var SOURCE_DIRTY = false;
57054
57053
 
57055
57054
  // src/context.ts
@@ -57163,11 +57162,12 @@ function realContext(env3, io) {
57163
57162
  const deps = realDeps();
57164
57163
  const sccache = deps.which("sccache");
57165
57164
  const run = (cmd, args, opts) => {
57165
+ const baseEnv = opts?.env ? { ...process.env, ...opts.env } : process.env;
57166
57166
  const r = spawnSync2(cmd, args, {
57167
57167
  encoding: "utf8",
57168
57168
  cwd: opts?.cwd,
57169
57169
  stdio: opts?.interactive ? "inherit" : "pipe",
57170
- env: sccache && cmd === "cargo" ? { ...process.env, RUSTC_WRAPPER: sccache } : process.env,
57170
+ env: sccache && cmd === "cargo" ? { ...baseEnv, RUSTC_WRAPPER: sccache } : baseEnv,
57171
57171
  maxBuffer: 64 * 1024 * 1024,
57172
57172
  ...opts?.timeoutMs === undefined ? {} : { timeout: opts.timeoutMs, killSignal: "SIGKILL" }
57173
57173
  });
@@ -57190,7 +57190,7 @@ function realContext(env3, io) {
57190
57190
 
57191
57191
  // src/patch/run.ts
57192
57192
  import { appendFileSync, mkdirSync as mkdirSync7 } from "node:fs";
57193
- import { join as join14 } from "node:path";
57193
+ import { join as join15 } from "node:path";
57194
57194
 
57195
57195
  // src/lock.ts
57196
57196
  import { closeSync, existsSync as existsSync4, mkdirSync, openSync, readFileSync as readFileSync2, unlinkSync, writeSync } from "node:fs";
@@ -57244,89 +57244,10 @@ function acquireLock(file2, isAlive = pidAlive) {
57244
57244
  return null;
57245
57245
  }
57246
57246
 
57247
- // src/hook/install.ts
57248
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
57249
-
57250
- // src/atomic.ts
57251
- import { mkdirSync as mkdirSync2, renameSync, writeFileSync } from "node:fs";
57252
- import { randomBytes } from "node:crypto";
57253
- import { dirname as dirname5, join as join5 } from "node:path";
57254
- function writeFileAtomic(file2, text) {
57255
- mkdirSync2(dirname5(file2), { recursive: true });
57256
- const tmp = join5(dirname5(file2), `.${process.pid}.${randomBytes(4).toString("hex")}.tmp`);
57257
- writeFileSync(tmp, text);
57258
- renameSync(tmp, file2);
57259
- }
57260
-
57261
- // src/sh.ts
57262
- var sq = (s) => `'${s.replace(/'/g, `'"'"'`)}'`;
57263
-
57264
- // src/hook/install.ts
57265
- var HOOK_STATUS_MESSAGE = "cxstatusline: checking Codex version";
57266
- var HOOK_TIMEOUT_SEC = 10;
57267
- var hookCommand = (cxBin) => `${sq(cxBin)} hook`;
57268
- function hookEntry(cxBin) {
57269
- return { hooks: [{ type: "command", command: hookCommand(cxBin), timeout: HOOK_TIMEOUT_SEC, statusMessage: HOOK_STATUS_MESSAGE }] };
57270
- }
57271
- var isOurGroup = (g) => g.hooks.some((h) => h.statusMessage === HOOK_STATUS_MESSAGE);
57272
- var sameJson = (a, b) => JSON.stringify(a) === JSON.stringify(b);
57273
- function mergeHook(file2, cxBin) {
57274
- const base2 = file2 ?? { hooks: {} };
57275
- const entry = hookEntry(cxBin);
57276
- const groups = base2.hooks.SessionStart ?? [];
57277
- const idx = groups.findIndex(isOurGroup);
57278
- if (idx !== -1 && sameJson(groups[idx], entry))
57279
- return { file: base2, changed: false };
57280
- const next = idx === -1 ? [...groups, entry] : groups.map((g, i) => i === idx ? entry : g);
57281
- return { file: { ...base2, hooks: { ...base2.hooks, SessionStart: next } }, changed: true };
57282
- }
57283
- function removeHook(file2) {
57284
- const groups = file2.hooks.SessionStart ?? [];
57285
- const kept = groups.filter((g) => !isOurGroup(g));
57286
- if (kept.length === groups.length)
57287
- return { file: file2, changed: false };
57288
- const { SessionStart: _dropped, ...rest2 } = file2.hooks;
57289
- const hooks = kept.length === 0 ? rest2 : { ...rest2, SessionStart: kept };
57290
- return { file: { ...file2, hooks }, changed: true };
57291
- }
57292
- function readHooksFile(path) {
57293
- if (!existsSync5(path))
57294
- return null;
57295
- let raw;
57296
- try {
57297
- raw = JSON.parse(readFileSync3(path, "utf8"));
57298
- } catch {
57299
- throw new Error(`${path}: hooks.json is not valid JSON; fix it by hand before installing the hook`);
57300
- }
57301
- if (typeof raw !== "object" || raw === null || Array.isArray(raw))
57302
- throw new Error(`${path}: hooks.json must be a JSON object`);
57303
- const o = raw;
57304
- if (o.hooks !== undefined && (typeof o.hooks !== "object" || o.hooks === null))
57305
- throw new Error(`${path}: "hooks" must be an object`);
57306
- return { ...raw, hooks: o.hooks ?? {} };
57307
- }
57308
- function installHook(hooksFile, cxBin) {
57309
- const { file: file2, changed } = mergeHook(readHooksFile(hooksFile), cxBin);
57310
- if (!changed)
57311
- return "unchanged";
57312
- writeFileAtomic(hooksFile, `${JSON.stringify(file2, null, 2)}
57313
- `);
57314
- return "added";
57315
- }
57316
- function uninstallHook(hooksFile) {
57317
- const current = readHooksFile(hooksFile);
57318
- if (!current)
57319
- return "absent";
57320
- const { file: file2, changed } = removeHook(current);
57321
- if (!changed)
57322
- return "absent";
57323
- writeFileAtomic(hooksFile, `${JSON.stringify(file2, null, 2)}
57324
- `);
57325
- return "removed";
57326
- }
57327
-
57328
- // src/state.ts
57329
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
57247
+ // src/distribution/files.ts
57248
+ var GENERATION_EXECUTABLES = ["codex", "codex-code-mode-host"];
57249
+ var GENERATION_LEGAL_FILES = ["LICENSE", "NOTICE", "THIRD_PARTY_NOTICES.md"];
57250
+ var ARTIFACT_FILES = [...GENERATION_EXECUTABLES, ...GENERATION_LEGAL_FILES];
57330
57251
 
57331
57252
  // src/version.ts
57332
57253
  var POLICIES = ["every", "stable-minors", "manual"];
@@ -57376,173 +57297,19 @@ function behindWithinMinor(upstream, patched) {
57376
57297
  return upstream.major === patched.major && upstream.minor === patched.minor && upstream.pre === null && compareSemver(upstream, patched) === 1;
57377
57298
  }
57378
57299
 
57379
- // src/state.ts
57380
- var RELEASE_UNAVAILABLE = "release-unavailable";
57381
- var RELEASE_RETRY_AFTER_MS = 24 * 60 * 60 * 1000;
57382
- var DEFAULT_STATE = {
57383
- version: 1,
57384
- policy: "stable-minors",
57385
- patched_from: null,
57386
- upstream_bin: null,
57387
- launcher_restore: null,
57388
- last_attempt: null
57389
- };
57390
- var BACKUP_SUFFIX = ".bak";
57391
- var isObj2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
57392
- var strOrNull = (v) => v === null || typeof v === "string" ? v : undefined;
57393
- function restore(v) {
57394
- if (v === null)
57395
- return null;
57396
- if (!isObj2(v))
57397
- return;
57398
- if (v.kind === "none")
57399
- return { kind: "none" };
57400
- if (v.kind === "symlink" && typeof v.target === "string")
57401
- return { kind: "symlink", target: v.target };
57402
- return;
57403
- }
57404
- function attempt2(v) {
57405
- if (v === null)
57406
- return null;
57407
- if (!isObj2(v) || typeof v.at !== "string" || typeof v.ok !== "boolean" || typeof v.version !== "string")
57408
- return;
57409
- return typeof v.reason === "string" ? { at: v.at, ok: v.ok, version: v.version, reason: v.reason } : { at: v.at, ok: v.ok, version: v.version };
57410
- }
57411
- function validate3(raw) {
57412
- if (!isObj2(raw) || raw.version !== 1)
57413
- return null;
57414
- const policy = POLICIES.includes(String(raw.policy)) ? raw.policy : undefined;
57415
- const patched_from = strOrNull(raw.patched_from);
57416
- const upstream_bin = strOrNull(raw.upstream_bin);
57417
- const launcher_restore = restore(raw.launcher_restore);
57418
- const last_attempt = attempt2(raw.last_attempt);
57419
- if (policy === undefined || patched_from === undefined || upstream_bin === undefined || launcher_restore === undefined || last_attempt === undefined)
57420
- return null;
57421
- return { version: 1, policy, patched_from, upstream_bin, launcher_restore, last_attempt };
57422
- }
57423
- function readValidated(file2) {
57424
- if (!existsSync6(file2))
57425
- return null;
57426
- try {
57427
- return validate3(JSON.parse(readFileSync4(file2, "utf8")));
57428
- } catch {
57429
- return null;
57430
- }
57431
- }
57432
- function readState(file2) {
57433
- if (!existsSync6(file2) && !existsSync6(`${file2}${BACKUP_SUFFIX}`)) {
57434
- return { state: DEFAULT_STATE, corrupt: false };
57435
- }
57436
- const primary = readValidated(file2);
57437
- if (primary)
57438
- return { state: primary, corrupt: false };
57439
- const backup = readValidated(`${file2}${BACKUP_SUFFIX}`);
57440
- return backup ? { state: backup, corrupt: true } : { state: DEFAULT_STATE, corrupt: true };
57441
- }
57442
- function writeState(file2, state) {
57443
- const text = `${JSON.stringify(state, null, 2)}
57444
- `;
57445
- writeFileAtomic(file2, text);
57446
- writeFileAtomic(`${file2}${BACKUP_SUFFIX}`, text);
57447
- }
57448
-
57449
- // src/patch/acquire.ts
57450
- import { rmSync as rmSync5 } from "node:fs";
57451
-
57452
- // src/codex/upstream.ts
57453
- import { accessSync as accessSync2, constants as constants3, lstatSync, readlinkSync, realpathSync as realpathSync2, statSync } from "node:fs";
57454
- import { dirname as dirname6, isAbsolute, join as join6, resolve as resolve2 } from "node:path";
57455
- function isExecutable(p) {
57456
- try {
57457
- accessSync2(p, constants3.X_OK);
57458
- return statSync(p).isFile();
57459
- } catch {
57460
- return false;
57461
- }
57462
- }
57463
- function isManaged(p, paths, isOurWrapper) {
57464
- if (resolve2(p) === resolve2(paths.patchedBin) || isOurWrapper(p))
57465
- return true;
57466
- try {
57467
- return realpathSync2(p) === realpathSync2(paths.patchedBin);
57468
- } catch {
57469
- return false;
57470
- }
57471
- }
57472
- function inspectLauncher(paths, isOurWrapper) {
57473
- let st;
57474
- try {
57475
- st = lstatSync(paths.wrapperPath);
57476
- } catch {
57477
- return null;
57478
- }
57479
- if (st.isSymbolicLink()) {
57480
- const raw = readlinkSync(paths.wrapperPath);
57481
- const target = isAbsolute(raw) ? raw : resolve2(dirname6(paths.wrapperPath), raw);
57482
- if (isManaged(target, paths, isOurWrapper))
57483
- return null;
57484
- return { kind: "found", bin: target, launcher_restore: { kind: "symlink", target } };
57485
- }
57486
- if (isOurWrapper(paths.wrapperPath))
57487
- return null;
57488
- return { kind: "foreign", path: paths.wrapperPath };
57489
- }
57490
- function walkPath(paths, env3, isOurWrapper) {
57491
- for (const dir of (env3.PATH ?? "").split(":").filter(Boolean)) {
57492
- if (resolve2(dir) === resolve2(paths.binDir))
57493
- continue;
57494
- const candidate = join6(dir, "codex");
57495
- if (isExecutable(candidate) && !isManaged(candidate, paths, isOurWrapper))
57496
- return candidate;
57497
- }
57498
- return null;
57499
- }
57500
- function resolveUpstream(paths, env3, isOurWrapper, savedBin) {
57501
- const launcher = inspectLauncher(paths, isOurWrapper);
57502
- if (launcher)
57503
- return launcher;
57504
- const current = walkPath(paths, env3, isOurWrapper);
57505
- const bin = current ?? (savedBin && isExecutable(savedBin) && !isManaged(savedBin, paths, isOurWrapper) ? savedBin : null);
57506
- return bin ? { kind: "found", bin, launcher_restore: { kind: "none" } } : { kind: "not-found" };
57507
- }
57508
- function describeLookup(l) {
57509
- switch (l.kind) {
57510
- case "found":
57511
- return l.bin;
57512
- case "foreign":
57513
- return `${l.path} is a real file we did not write; refusing to overwrite it`;
57514
- case "not-found":
57515
- return "no upstream Codex found (nothing named codex on PATH outside ~/.local/bin, and the launcher there is not a symlink)";
57516
- }
57517
- }
57518
- function readUpstreamVersion(bin, run) {
57519
- const r = run(bin, ["--version"]);
57520
- if (r.status !== 0)
57521
- return null;
57522
- return parseCodexVersionOutput(r.stdout);
57523
- }
57524
- function preserveLauncherRestore(recorded, found) {
57525
- return found.kind === "symlink" ? found : recorded ?? found;
57526
- }
57527
-
57528
- // src/distribution/files.ts
57529
- var GENERATION_EXECUTABLES = ["codex", "codex-code-mode-host"];
57530
- var GENERATION_LEGAL_FILES = ["LICENSE", "NOTICE", "THIRD_PARTY_NOTICES.md"];
57531
- var ARTIFACT_FILES = [...GENERATION_EXECUTABLES, ...GENERATION_LEGAL_FILES];
57532
-
57533
57300
  // src/distribution/prebuilt.ts
57534
57301
  import { createHash as createHash3 } from "node:crypto";
57535
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, mkdtempSync, readFileSync as readFileSync6, rmSync as rmSync2 } from "node:fs";
57536
- import { join as join9 } from "node:path";
57302
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync4, rmSync as rmSync2 } from "node:fs";
57303
+ import { join as join7 } from "node:path";
57537
57304
 
57538
57305
  // src/patch/generation.ts
57539
- import { chmodSync, copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync, readlinkSync as readlinkSync2, realpathSync as realpathSync3, renameSync as renameSync2, rmSync, statSync as statSync2, symlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
57540
- import { createHash, randomBytes as randomBytes2 } from "node:crypto";
57541
- import { dirname as dirname7, isAbsolute as isAbsolute2, join as join7, resolve as resolve3, sep } from "node:path";
57306
+ import { chmodSync, copyFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync, readlinkSync, realpathSync as realpathSync2, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
57307
+ import { createHash, randomBytes } from "node:crypto";
57308
+ import { dirname as dirname5, isAbsolute, join as join5, resolve as resolve2, sep } from "node:path";
57542
57309
  var INSTALLATION_FILE = "installation.json";
57543
57310
  var SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
57544
57311
  function digestOf(file2) {
57545
- const buf = readFileSync5(file2);
57312
+ const buf = readFileSync3(file2);
57546
57313
  return { sha256: createHash("sha256").update(buf).digest("hex"), size: buf.length };
57547
57314
  }
57548
57315
  function stamp(installedAt) {
@@ -57554,10 +57321,10 @@ function nearestExisting2(p) {
57554
57321
  let cur = p;
57555
57322
  for (;; ) {
57556
57323
  try {
57557
- statSync2(cur);
57324
+ statSync(cur);
57558
57325
  return cur;
57559
57326
  } catch {
57560
- const parent = dirname7(cur);
57327
+ const parent = dirname5(cur);
57561
57328
  if (parent === cur)
57562
57329
  return cur;
57563
57330
  cur = parent;
@@ -57566,7 +57333,7 @@ function nearestExisting2(p) {
57566
57333
  }
57567
57334
  function realish(p) {
57568
57335
  const near = nearestExisting2(p);
57569
- return realpathSync3(near) + resolve3(p).slice(resolve3(near).length);
57336
+ return realpathSync2(near) + resolve2(p).slice(resolve2(near).length);
57570
57337
  }
57571
57338
  function insideGenerationsRoot(paths, candidate) {
57572
57339
  return realish(candidate).startsWith(`${realish(paths.generationsDir)}${sep}`);
@@ -57576,8 +57343,8 @@ function validateStaging(pair) {
57576
57343
  throw new Error(`prepared pair has an unusable codexVersion "${pair.codexVersion}"`);
57577
57344
  }
57578
57345
  for (const name of GENERATION_EXECUTABLES) {
57579
- const file2 = join7(pair.directory, name);
57580
- if (!existsSync7(file2))
57346
+ const file2 = join5(pair.directory, name);
57347
+ if (!existsSync5(file2))
57581
57348
  throw new Error(`staged pair is missing ${name}`);
57582
57349
  const want = pair.provenance.executables[name];
57583
57350
  const got = digestOf(file2);
@@ -57587,7 +57354,7 @@ function validateStaging(pair) {
57587
57354
  }
57588
57355
  if (pair.provenance.source === "prebuilt") {
57589
57356
  for (const name of GENERATION_LEGAL_FILES) {
57590
- if (!existsSync7(join7(pair.directory, name)))
57357
+ if (!existsSync5(join5(pair.directory, name)))
57591
57358
  throw new Error(`prebuilt pair is missing ${name}`);
57592
57359
  }
57593
57360
  }
@@ -57597,8 +57364,8 @@ function installationRecord(pair) {
57597
57364
  return record2;
57598
57365
  }
57599
57366
  function copyInto(from, into, name, mode, expected) {
57600
- const target = join7(into, name);
57601
- copyFileSync(join7(from, name), target);
57367
+ const target = join5(into, name);
57368
+ copyFileSync(join5(from, name), target);
57602
57369
  chmodSync(target, mode);
57603
57370
  if (!expected)
57604
57371
  return;
@@ -57608,9 +57375,9 @@ function copyInto(from, into, name, mode, expected) {
57608
57375
  }
57609
57376
  }
57610
57377
  function commitJson(file2, value, rename2) {
57611
- const tmp = join7(dirname7(file2), `.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`);
57378
+ const tmp = join5(dirname5(file2), `.${process.pid}.${randomBytes(4).toString("hex")}.tmp`);
57612
57379
  try {
57613
- writeFileSync2(tmp, `${JSON.stringify(value, null, 2)}
57380
+ writeFileSync(tmp, `${JSON.stringify(value, null, 2)}
57614
57381
  `);
57615
57382
  rename2(tmp, file2);
57616
57383
  } catch (e) {
@@ -57619,26 +57386,26 @@ function commitJson(file2, value, rename2) {
57619
57386
  }
57620
57387
  }
57621
57388
  function freshDirectory(pair, paths) {
57622
- mkdirSync3(paths.generationsDir, { recursive: true });
57389
+ mkdirSync2(paths.generationsDir, { recursive: true });
57623
57390
  for (;; ) {
57624
- const dir = join7(paths.generationsDir, `${pair.codexVersion}-${stamp(pair.provenance.installedAt)}-${randomBytes2(3).toString("hex")}`);
57625
- if (existsSync7(dir))
57391
+ const dir = join5(paths.generationsDir, `${pair.codexVersion}-${stamp(pair.provenance.installedAt)}-${randomBytes(3).toString("hex")}`);
57392
+ if (existsSync5(dir))
57626
57393
  continue;
57627
- mkdirSync3(dir);
57394
+ mkdirSync2(dir);
57628
57395
  return dir;
57629
57396
  }
57630
57397
  }
57631
- function createGeneration(pair, paths, rename2 = renameSync2) {
57398
+ function createGeneration(pair, paths, rename2 = renameSync) {
57632
57399
  validateStaging(pair);
57633
57400
  const dir = freshDirectory(pair, paths);
57634
57401
  try {
57635
57402
  for (const name of GENERATION_EXECUTABLES)
57636
57403
  copyInto(pair.directory, dir, name, 493, pair.provenance.executables[name]);
57637
57404
  for (const name of GENERATION_LEGAL_FILES) {
57638
- if (existsSync7(join7(pair.directory, name)))
57405
+ if (existsSync5(join5(pair.directory, name)))
57639
57406
  copyInto(pair.directory, dir, name, 420);
57640
57407
  }
57641
- commitJson(join7(dir, INSTALLATION_FILE), installationRecord(pair), rename2);
57408
+ commitJson(join5(dir, INSTALLATION_FILE), installationRecord(pair), rename2);
57642
57409
  } catch (e) {
57643
57410
  rmSync(dir, { recursive: true, force: true });
57644
57411
  throw e;
@@ -57646,24 +57413,24 @@ function createGeneration(pair, paths, rename2 = renameSync2) {
57646
57413
  return dir;
57647
57414
  }
57648
57415
  function isGenerationDir(dir) {
57649
- return existsSync7(join7(dir, INSTALLATION_FILE)) && existsSync7(join7(dir, "codex"));
57416
+ return existsSync5(join5(dir, INSTALLATION_FILE)) && existsSync5(join5(dir, "codex"));
57650
57417
  }
57651
57418
  function listGenerations(paths) {
57652
57419
  try {
57653
- return readdirSync(paths.generationsDir).map((name) => join7(paths.generationsDir, name)).sort();
57420
+ return readdirSync(paths.generationsDir).map((name) => join5(paths.generationsDir, name)).sort();
57654
57421
  } catch {
57655
57422
  return [];
57656
57423
  }
57657
57424
  }
57658
57425
  function readPointer(paths) {
57659
57426
  try {
57660
- return readlinkSync2(paths.currentGeneration);
57427
+ return readlinkSync(paths.currentGeneration);
57661
57428
  } catch {
57662
57429
  return null;
57663
57430
  }
57664
57431
  }
57665
- function swapPointer(paths, target, rename2 = renameSync2) {
57666
- const staged = `${paths.currentGeneration}.${randomBytes2(4).toString("hex")}`;
57432
+ function swapPointer(paths, target, rename2 = renameSync) {
57433
+ const staged = `${paths.currentGeneration}.${randomBytes(4).toString("hex")}`;
57667
57434
  symlinkSync(target, staged);
57668
57435
  try {
57669
57436
  rename2(staged, paths.currentGeneration);
@@ -57672,7 +57439,7 @@ function swapPointer(paths, target, rename2 = renameSync2) {
57672
57439
  throw e;
57673
57440
  }
57674
57441
  }
57675
- function restorePointer(paths, previous, rename2 = renameSync2) {
57442
+ function restorePointer(paths, previous, rename2 = renameSync) {
57676
57443
  if (previous === null) {
57677
57444
  rmSync(paths.currentGeneration, { force: true });
57678
57445
  return;
@@ -57683,11 +57450,11 @@ function activeGeneration(paths) {
57683
57450
  const target = readPointer(paths);
57684
57451
  if (target === null)
57685
57452
  return null;
57686
- const abs = isAbsolute2(target) ? target : resolve3(dirname7(paths.currentGeneration), target);
57453
+ const abs = isAbsolute(target) ? target : resolve2(dirname5(paths.currentGeneration), target);
57687
57454
  if (!insideGenerationsRoot(paths, abs))
57688
57455
  return null;
57689
57456
  try {
57690
- return statSync2(abs).isDirectory() ? abs : null;
57457
+ return statSync(abs).isDirectory() ? abs : null;
57691
57458
  } catch {
57692
57459
  return null;
57693
57460
  }
@@ -57745,7 +57512,7 @@ function readInstallation(paths) {
57745
57512
  if (dir === null)
57746
57513
  return null;
57747
57514
  try {
57748
- const raw = JSON.parse(readFileSync5(join7(dir, INSTALLATION_FILE), "utf8"));
57515
+ const raw = JSON.parse(readFileSync3(join5(dir, INSTALLATION_FILE), "utf8"));
57749
57516
  return checkedRecord(raw);
57750
57517
  } catch {
57751
57518
  return null;
@@ -57754,7 +57521,7 @@ function readInstallation(paths) {
57754
57521
 
57755
57522
  // src/distribution/archive.ts
57756
57523
  import { createReadStream, createWriteStream } from "node:fs";
57757
- import { join as join8 } from "node:path";
57524
+ import { join as join6 } from "node:path";
57758
57525
  import { createGunzip } from "node:zlib";
57759
57526
 
57760
57527
  // node_modules/tar/dist/esm/index.min.js
@@ -60985,7 +60752,7 @@ var EXTRACTED_TOTAL_LIMIT = 2 * 1024 * 1024 * 1024;
60985
60752
  var LEGAL_TOTAL_LIMIT = 16 * 1024 * 1024;
60986
60753
  var EXECUTABLE_MODE = 493;
60987
60754
  var LEGAL_MODE = 420;
60988
- function isExecutable2(name) {
60755
+ function isExecutable(name) {
60989
60756
  return GENERATION_EXECUTABLES.includes(name);
60990
60757
  }
60991
60758
  function quoteName(name) {
@@ -61009,7 +60776,7 @@ function rejectionReason(entry, seen, budget) {
61009
60776
  const size2 = entry.size;
61010
60777
  if (!Number.isSafeInteger(size2) || size2 < 0)
61011
60778
  return `archive entry ${quoteName(name)} declares an unusable size`;
61012
- const executable = isExecutable2(name);
60779
+ const executable = isExecutable(name);
61013
60780
  if (executable && size2 === 0)
61014
60781
  return `archive entry ${quoteName(name)} is an empty executable`;
61015
60782
  const mode = entry.mode ?? entry.header.mode ?? 0;
@@ -61028,9 +60795,9 @@ function rejectionReason(entry, seen, budget) {
61028
60795
  }
61029
60796
  function writeEntry(entry, staging) {
61030
60797
  const name = entry.path;
61031
- const mode = isExecutable2(name) ? EXECUTABLE_MODE : LEGAL_MODE;
61032
- const sink = createWriteStream(join8(staging, name), { mode, flags: "wx" });
61033
- const done = new Promise((resolve4, reject2) => {
60798
+ const mode = isExecutable(name) ? EXECUTABLE_MODE : LEGAL_MODE;
60799
+ const sink = createWriteStream(join6(staging, name), { mode, flags: "wx" });
60800
+ const done = new Promise((resolve3, reject2) => {
61034
60801
  sink.on("error", reject2);
61035
60802
  entry.on("error", reject2);
61036
60803
  sink.on("finish", () => {
@@ -61038,7 +60805,7 @@ function writeEntry(entry, staging) {
61038
60805
  reject2(new Error(`archive entry ${quoteName(name)} ended after ${sink.bytesWritten} of ${entry.size} bytes`));
61039
60806
  return;
61040
60807
  }
61041
- resolve4();
60808
+ resolve3();
61042
60809
  });
61043
60810
  entry.pipe(sink);
61044
60811
  });
@@ -61053,7 +60820,7 @@ function writeEntry(entry, staging) {
61053
60820
  function parseArchive(archivePath, staging, writes) {
61054
60821
  const seen = new Set;
61055
60822
  const budget = { total: 0, legal: 0 };
61056
- return new Promise((resolve4, reject2) => {
60823
+ return new Promise((resolve3, reject2) => {
61057
60824
  const source = createReadStream(archivePath);
61058
60825
  const gunzip = createGunzip();
61059
60826
  let settled = false;
@@ -61095,7 +60862,7 @@ function parseArchive(archivePath, staging, writes) {
61095
60862
  reject2(new Error(`archive is missing ${missing.join(", ")}`));
61096
60863
  return;
61097
60864
  }
61098
- resolve4();
60865
+ resolve3();
61099
60866
  });
61100
60867
  source.pipe(gunzip).pipe(parser);
61101
60868
  });
@@ -61113,9 +60880,9 @@ async function extractArchive(archivePath, staging) {
61113
60880
 
61114
60881
  // src/distribution/transport.ts
61115
60882
  import { createHash as createHash2 } from "node:crypto";
61116
- import { createReadStream as createReadStream2, existsSync as existsSync8 } from "node:fs";
60883
+ import { createReadStream as createReadStream2, existsSync as existsSync6 } from "node:fs";
61117
60884
  import { open as open2 } from "node:fs/promises";
61118
- import { dirname as dirname8 } from "node:path";
60885
+ import { dirname as dirname6 } from "node:path";
61119
60886
  var PUBLIC_BASE = "https://github.com/adrijshikhar/cxstatusline/releases/download";
61120
60887
  var REPO = "adrijshikhar/cxstatusline";
61121
60888
  var MAX_REDIRECTS = 5;
@@ -61157,7 +60924,7 @@ function nextUrl(current, location, allowHttp, asset) {
61157
60924
  }
61158
60925
  return resolved.toString();
61159
60926
  }
61160
- async function streamToFile(response, dest, maxBytes, asset) {
60927
+ async function streamToFile(response, dest, maxBytes, asset, onProgress) {
61161
60928
  const declared = response.headers.get("content-length");
61162
60929
  const expected = declared === null ? null : Number(declared);
61163
60930
  if (expected !== null && (!Number.isSafeInteger(expected) || expected < 0 || expected > maxBytes)) {
@@ -61180,6 +60947,7 @@ async function streamToFile(response, dest, maxBytes, asset) {
61180
60947
  throw new Error(`${asset} is larger than the ${maxBytes}-byte limit`);
61181
60948
  hash2.update(value);
61182
60949
  await handle.write(value);
60950
+ onProgress?.(size2, expected);
61183
60951
  }
61184
60952
  } finally {
61185
60953
  await handle.close();
@@ -61210,7 +60978,7 @@ async function httpDownload(start, asset, dest, maxBytes, opts) {
61210
60978
  return "not-found";
61211
60979
  if (!response.ok)
61212
60980
  throw new Error(`download of ${asset} failed with HTTP ${response.status}`);
61213
- return await streamToFile(response, dest, maxBytes, asset);
60981
+ return await streamToFile(response, dest, maxBytes, asset, opts.onProgress);
61214
60982
  }
61215
60983
  throw new Error(`download of ${asset} exceeded ${MAX_REDIRECTS} redirects`);
61216
60984
  }
@@ -61230,7 +60998,7 @@ async function ghDownload(ctx, tag, asset, dest, maxBytes) {
61230
60998
  if (ctx.which("gh") === null) {
61231
60999
  throw new GhMissingError(`${asset} is not public for release ${tag} and GitHub CLI (gh) is not installed`);
61232
61000
  }
61233
- const result2 = ctx.run("gh", ["release", "download", tag, "--repo", REPO, "--pattern", asset, "--dir", dirname8(dest)], { timeoutMs: GH_TIMEOUT_MS });
61001
+ const result2 = ctx.run("gh", ["release", "download", tag, "--repo", REPO, "--pattern", asset, "--dir", dirname6(dest)], { timeoutMs: GH_TIMEOUT_MS });
61234
61002
  if (result2.status !== 0) {
61235
61003
  const detail = sanitize(result2.stderr || result2.stdout);
61236
61004
  if (/auth|logged in|log in|login|credential/i.test(detail)) {
@@ -61238,7 +61006,7 @@ async function ghDownload(ctx, tag, asset, dest, maxBytes) {
61238
61006
  }
61239
61007
  throw new ReleaseUnavailableError(`release ${tag} not found or access denied: ${detail}`);
61240
61008
  }
61241
- if (!existsSync8(dest))
61009
+ if (!existsSync6(dest))
61242
61010
  throw new ReleaseUnavailableError(`release ${tag} not found or access denied: gh downloaded no ${asset}`);
61243
61011
  return await hashFile(dest, maxBytes, asset);
61244
61012
  }
@@ -61254,15 +61022,15 @@ async function downloadAsset(ctx, tag, asset, dest, maxBytes, opts = {}) {
61254
61022
  var MANIFEST_ASSET = "manifest.json";
61255
61023
  var PROBE_TIMEOUT_MS = 30000;
61256
61024
  function digestOf2(file2) {
61257
- const bytes = readFileSync6(file2);
61025
+ const bytes = readFileSync4(file2);
61258
61026
  return { sha256: createHash3("sha256").update(bytes).digest("hex"), size: bytes.length };
61259
61027
  }
61260
61028
  function sameDigest(a, b2) {
61261
61029
  return a.sha256 === b2.sha256 && a.size === b2.size;
61262
61030
  }
61263
61031
  function privateTemp(ctx, prefix) {
61264
- mkdirSync4(ctx.paths.libexecDir, { recursive: true });
61265
- const dir = mkdtempSync(join9(ctx.paths.libexecDir, prefix));
61032
+ mkdirSync3(ctx.paths.libexecDir, { recursive: true });
61033
+ const dir = mkdtempSync(join7(ctx.paths.libexecDir, prefix));
61266
61034
  chmodSync2(dir, 448);
61267
61035
  return dir;
61268
61036
  }
@@ -61288,7 +61056,7 @@ function unchangedPair(ctx, manifest, artifact, tag) {
61288
61056
  return null;
61289
61057
  for (const name of ARTIFACT_FILES) {
61290
61058
  try {
61291
- if (!sameDigest(digestOf2(join9(directory, name)), artifact.files[name]))
61059
+ if (!sameDigest(digestOf2(join7(directory, name)), artifact.files[name]))
61292
61060
  return null;
61293
61061
  } catch {
61294
61062
  return null;
@@ -61299,7 +61067,7 @@ function unchangedPair(ctx, manifest, artifact, tag) {
61299
61067
  function verifyStaged(staging, artifact) {
61300
61068
  const digests = {};
61301
61069
  for (const name of ARTIFACT_FILES) {
61302
- const got = digestOf2(join9(staging, name));
61070
+ const got = digestOf2(join7(staging, name));
61303
61071
  if (!sameDigest(got, artifact.files[name])) {
61304
61072
  throw new Error(`staged ${name} does not match the release manifest sha256`);
61305
61073
  }
@@ -61311,7 +61079,7 @@ function verifyStaged(staging, artifact) {
61311
61079
  };
61312
61080
  }
61313
61081
  function probeVersion(ctx, staging, codexVersion) {
61314
- const probe = ctx.run(join9(staging, "codex"), ["--version"], { timeoutMs: PROBE_TIMEOUT_MS });
61082
+ const probe = ctx.run(join7(staging, "codex"), ["--version"], { timeoutMs: PROBE_TIMEOUT_MS });
61315
61083
  const reported = sanitize((probe.stdout || probe.stderr).slice(0, 512));
61316
61084
  if (probe.status !== 0 || reported !== `codex-cli ${codexVersion}`) {
61317
61085
  throw new Error(`staged codex reported version "${reported}" instead of "codex-cli ${codexVersion}"`);
@@ -61338,15 +61106,20 @@ function stagedPair(ctx, staging, manifest, artifact, tag) {
61338
61106
  };
61339
61107
  }
61340
61108
  async function stageArchive(ctx, download, manifest, artifact, tag, opts) {
61341
- const archive = join9(download, artifact.filename);
61109
+ const archive = join7(download, artifact.filename);
61110
+ opts.onStatus?.("download", `Downloading prebuilt archive (${artifact.filename})...`);
61342
61111
  const got = await downloadAsset(ctx, tag, artifact.filename, archive, ARCHIVE_MAX_BYTES, opts);
61343
61112
  if (got.sha256 !== artifact.sha256 || got.size !== artifact.size) {
61344
61113
  throw new Error(`${artifact.filename} does not match the release manifest sha256`);
61345
61114
  }
61115
+ opts.onStatus?.("download-done", `Downloaded ${artifact.filename}`);
61346
61116
  const staging = privateTemp(ctx, "staging-");
61347
61117
  try {
61118
+ opts.onStatus?.("extract", "Extracting and verifying executables...");
61348
61119
  await extractArchive(archive, staging);
61349
- return stagedPair(ctx, staging, manifest, artifact, tag);
61120
+ const pair = stagedPair(ctx, staging, manifest, artifact, tag);
61121
+ opts.onStatus?.("extract-done", "Verified executables (codex, codex-code-mode-host)");
61122
+ return pair;
61350
61123
  } catch (e) {
61351
61124
  rmSync2(staging, { recursive: true, force: true });
61352
61125
  throw e;
@@ -61354,12 +61127,14 @@ async function stageArchive(ctx, download, manifest, artifact, tag, opts) {
61354
61127
  }
61355
61128
  async function preparePrebuilt(ctx, expected, opts = {}) {
61356
61129
  const tag = releaseTag(expected.cxVersion, expected.codexVersion);
61130
+ opts.onStatus?.("manifest", `Checking release ${tag} for ${expected.platform}...`);
61357
61131
  const download = privateTemp(ctx, "download-");
61358
61132
  try {
61359
- const manifestFile = join9(download, MANIFEST_ASSET);
61133
+ const manifestFile = join7(download, MANIFEST_ASSET);
61360
61134
  await downloadAsset(ctx, tag, MANIFEST_ASSET, manifestFile, MANIFEST_MAX_BYTES, opts);
61361
- const manifest = validateManifest(JSON.parse(readFileSync6(manifestFile, "utf8")), expected);
61135
+ const manifest = validateManifest(JSON.parse(readFileSync4(manifestFile, "utf8")), expected);
61362
61136
  const artifact = artifactFor(manifest, expected);
61137
+ opts.onStatus?.("manifest-done", `Found release for Codex ${manifest.codexVersion} (${artifact.platform})`);
61363
61138
  const unchanged = unchangedPair(ctx, manifest, artifact, tag);
61364
61139
  if (unchanged !== null)
61365
61140
  return { kind: "unchanged", pair: unchanged };
@@ -61474,67 +61249,354 @@ function validateManifest(raw, expected) {
61474
61249
  return structural.data;
61475
61250
  }
61476
61251
 
61477
- // src/patch/compile.ts
61478
- import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, mkdirSync as mkdirSync5, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
61479
- import { createHash as createHash4 } from "node:crypto";
61480
- import { join as join11 } from "node:path";
61481
-
61482
- // src/patch/build.ts
61483
- import { existsSync as existsSync9, lstatSync as lstatSync2, readFileSync as readFileSync7, statSync as statSync3 } from "node:fs";
61484
- import { join as join10 } from "node:path";
61485
-
61486
- class BuildError extends Error {
61487
- step;
61488
- name = "BuildError";
61489
- constructor(step, detail) {
61490
- super(`${step}: ${detail.trim() || "failed"}`);
61491
- this.step = step;
61252
+ // src/codex/upstream.ts
61253
+ import { accessSync as accessSync2, constants as constants3, lstatSync, readlinkSync as readlinkSync2, realpathSync as realpathSync3, statSync as statSync2 } from "node:fs";
61254
+ import { dirname as dirname7, isAbsolute as isAbsolute2, join as join8, resolve as resolve3 } from "node:path";
61255
+ function isExecutable2(p2) {
61256
+ try {
61257
+ accessSync2(p2, constants3.X_OK);
61258
+ return statSync2(p2).isFile();
61259
+ } catch {
61260
+ return false;
61492
61261
  }
61493
61262
  }
61494
- var REPO2 = "https://github.com/openai/codex";
61495
- var SNAPSHOT = "codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__cxstatusline_block_preserves_instruction_footer.snap";
61496
- var SNAPSHOT_CONTENT = [
61497
- "---",
61498
- "source: tui/src/bottom_pane/footer.rs",
61499
- "expression: terminal.backend()",
61500
- "---",
61501
- ...["status-0", "status-1", "status-2", "esc esc to edit previous message"].map((s3) => `" ${s3.padEnd(78)}"`),
61502
- ""
61503
- ].join(`
61504
- `);
61505
- var TARGETS = ["codex", "codex-code-mode-host"];
61506
- var HEX403 = /^[0-9a-f]{40}$/;
61507
- function must(step, run, log, cmd, args, cwd2) {
61508
- log(`$ ${[cmd, ...args].join(" ")}`);
61509
- const r = run(cmd, args, cwd2 ? { cwd: cwd2 } : undefined);
61510
- for (const line of `${r.stdout}${r.stderr}`.split(`
61511
- `).filter(Boolean))
61512
- log(line);
61513
- if (r.status !== 0)
61514
- throw new BuildError(step, r.stderr || r.stdout);
61515
- }
61516
- function runFocusedTests(run, log, which, crateDir) {
61517
- if (which("just")) {
61518
- log("running the focused Rust tests with just");
61519
- must("just test", run, log, "just", ["test", "--release", "-p", "codex-tui", "cxstatusline", "--retries", "0"], crateDir);
61520
- return;
61263
+ function isManaged(p2, paths, isOurWrapper) {
61264
+ if (resolve3(p2) === resolve3(paths.patchedBin) || isOurWrapper(p2))
61265
+ return true;
61266
+ try {
61267
+ return realpathSync3(p2) === realpathSync3(paths.patchedBin);
61268
+ } catch {
61269
+ return false;
61521
61270
  }
61522
- log("just is not on PATH; running the focused Rust tests with cargo");
61523
- must("cargo test", run, log, "cargo", ["test", "--release", "-p", "codex-tui", "cxstatusline"], crateDir);
61524
61271
  }
61525
- function headCommit(plan, run) {
61526
- const r = run("git", ["-C", plan.sourceDir, "rev-parse", "HEAD"]);
61527
- const commit = r.stdout.trim();
61528
- if (r.status !== 0 || !HEX403.test(commit)) {
61529
- throw new BuildError("rev-parse", `${plan.sourceDir} did not report a commit for ${plan.tag}`);
61272
+ function inspectLauncher(paths, isOurWrapper) {
61273
+ let st2;
61274
+ try {
61275
+ st2 = lstatSync(paths.wrapperPath);
61276
+ } catch {
61277
+ return null;
61530
61278
  }
61531
- return commit;
61532
- }
61533
- function prepareCheckout(plan, run, log) {
61279
+ if (st2.isSymbolicLink()) {
61280
+ const raw = readlinkSync2(paths.wrapperPath);
61281
+ const target = isAbsolute2(raw) ? raw : resolve3(dirname7(paths.wrapperPath), raw);
61282
+ if (isManaged(target, paths, isOurWrapper))
61283
+ return null;
61284
+ return { kind: "found", bin: target, launcher_restore: { kind: "symlink", target } };
61285
+ }
61286
+ if (isOurWrapper(paths.wrapperPath))
61287
+ return null;
61288
+ return { kind: "foreign", path: paths.wrapperPath };
61289
+ }
61290
+ function walkPath(paths, env3, isOurWrapper) {
61291
+ for (const dir of (env3.PATH ?? "").split(":").filter(Boolean)) {
61292
+ if (resolve3(dir) === resolve3(paths.binDir))
61293
+ continue;
61294
+ const candidate = join8(dir, "codex");
61295
+ if (isExecutable2(candidate) && !isManaged(candidate, paths, isOurWrapper))
61296
+ return candidate;
61297
+ }
61298
+ return null;
61299
+ }
61300
+ function resolveUpstream(paths, env3, isOurWrapper, savedBin) {
61301
+ const launcher = inspectLauncher(paths, isOurWrapper);
61302
+ if (launcher)
61303
+ return launcher;
61304
+ const current = walkPath(paths, env3, isOurWrapper);
61305
+ const bin = current ?? (savedBin && isExecutable2(savedBin) && !isManaged(savedBin, paths, isOurWrapper) ? savedBin : null);
61306
+ return bin ? { kind: "found", bin, launcher_restore: { kind: "none" } } : { kind: "not-found" };
61307
+ }
61308
+ function describeLookup(l) {
61309
+ switch (l.kind) {
61310
+ case "found":
61311
+ return l.bin;
61312
+ case "foreign":
61313
+ return `${l.path} is a real file we did not write; refusing to overwrite it`;
61314
+ case "not-found":
61315
+ return "no upstream Codex found (nothing named codex on PATH outside ~/.local/bin, and the launcher there is not a symlink)";
61316
+ }
61317
+ }
61318
+ function readUpstreamVersion(bin, run) {
61319
+ const r = run(bin, ["--version"]);
61320
+ if (r.status !== 0)
61321
+ return null;
61322
+ return parseCodexVersionOutput(r.stdout);
61323
+ }
61324
+ function preserveLauncherRestore(recorded, found) {
61325
+ return found.kind === "symlink" ? found : recorded ?? found;
61326
+ }
61327
+
61328
+ // src/hook/install.ts
61329
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
61330
+
61331
+ // src/atomic.ts
61332
+ import { mkdirSync as mkdirSync4, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
61333
+ import { randomBytes as randomBytes2 } from "node:crypto";
61334
+ import { dirname as dirname8, join as join9 } from "node:path";
61335
+ function writeFileAtomic(file2, text) {
61336
+ mkdirSync4(dirname8(file2), { recursive: true });
61337
+ const tmp = join9(dirname8(file2), `.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`);
61338
+ writeFileSync2(tmp, text);
61339
+ renameSync2(tmp, file2);
61340
+ }
61341
+
61342
+ // src/sh.ts
61343
+ var sq = (s3) => `'${s3.replace(/'/g, `'"'"'`)}'`;
61344
+
61345
+ // src/hook/install.ts
61346
+ var HOOK_STATUS_MESSAGE = "cxstatusline: checking Codex version";
61347
+ var HOOK_TIMEOUT_SEC = 10;
61348
+ var hookCommand = (cxBin) => `${sq(cxBin)} hook`;
61349
+ function hookEntry(cxBin) {
61350
+ return { hooks: [{ type: "command", command: hookCommand(cxBin), timeout: HOOK_TIMEOUT_SEC, statusMessage: HOOK_STATUS_MESSAGE }] };
61351
+ }
61352
+ var isOurGroup = (g2) => g2.hooks.some((h) => h.statusMessage === HOOK_STATUS_MESSAGE);
61353
+ var sameJson = (a, b2) => JSON.stringify(a) === JSON.stringify(b2);
61354
+ function mergeHook(file2, cxBin) {
61355
+ const base2 = file2 ?? { hooks: {} };
61356
+ const entry = hookEntry(cxBin);
61357
+ const groups = base2.hooks.SessionStart ?? [];
61358
+ const idx = groups.findIndex(isOurGroup);
61359
+ if (idx !== -1 && sameJson(groups[idx], entry))
61360
+ return { file: base2, changed: false };
61361
+ const next = idx === -1 ? [...groups, entry] : groups.map((g2, i) => i === idx ? entry : g2);
61362
+ return { file: { ...base2, hooks: { ...base2.hooks, SessionStart: next } }, changed: true };
61363
+ }
61364
+ function removeHook(file2) {
61365
+ const groups = file2.hooks.SessionStart ?? [];
61366
+ const kept = groups.filter((g2) => !isOurGroup(g2));
61367
+ if (kept.length === groups.length)
61368
+ return { file: file2, changed: false };
61369
+ const { SessionStart: _dropped, ...rest2 } = file2.hooks;
61370
+ const hooks = kept.length === 0 ? rest2 : { ...rest2, SessionStart: kept };
61371
+ return { file: { ...file2, hooks }, changed: true };
61372
+ }
61373
+ function readHooksFile(path) {
61374
+ if (!existsSync7(path))
61375
+ return null;
61376
+ let raw;
61377
+ try {
61378
+ raw = JSON.parse(readFileSync5(path, "utf8"));
61379
+ } catch {
61380
+ throw new Error(`${path}: hooks.json is not valid JSON; fix it by hand before installing the hook`);
61381
+ }
61382
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
61383
+ throw new Error(`${path}: hooks.json must be a JSON object`);
61384
+ const o = raw;
61385
+ if (o.hooks !== undefined && (typeof o.hooks !== "object" || o.hooks === null))
61386
+ throw new Error(`${path}: "hooks" must be an object`);
61387
+ return { ...raw, hooks: o.hooks ?? {} };
61388
+ }
61389
+ function installHook(hooksFile, cxBin) {
61390
+ const { file: file2, changed } = mergeHook(readHooksFile(hooksFile), cxBin);
61391
+ if (!changed)
61392
+ return "unchanged";
61393
+ writeFileAtomic(hooksFile, `${JSON.stringify(file2, null, 2)}
61394
+ `);
61395
+ return "added";
61396
+ }
61397
+ function uninstallHook(hooksFile) {
61398
+ const current = readHooksFile(hooksFile);
61399
+ if (!current)
61400
+ return "absent";
61401
+ const { file: file2, changed } = removeHook(current);
61402
+ if (!changed)
61403
+ return "absent";
61404
+ writeFileAtomic(hooksFile, `${JSON.stringify(file2, null, 2)}
61405
+ `);
61406
+ return "removed";
61407
+ }
61408
+
61409
+ // src/state.ts
61410
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
61411
+ var RELEASE_UNAVAILABLE = "release-unavailable";
61412
+ var RELEASE_RETRY_AFTER_MS = 24 * 60 * 60 * 1000;
61413
+ var DEFAULT_STATE = {
61414
+ version: 1,
61415
+ policy: "stable-minors",
61416
+ patched_from: null,
61417
+ upstream_bin: null,
61418
+ launcher_restore: null,
61419
+ last_attempt: null
61420
+ };
61421
+ var BACKUP_SUFFIX = ".bak";
61422
+ var isObj2 = (v2) => typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
61423
+ var strOrNull = (v2) => v2 === null || typeof v2 === "string" ? v2 : undefined;
61424
+ function restore(v2) {
61425
+ if (v2 === null)
61426
+ return null;
61427
+ if (!isObj2(v2))
61428
+ return;
61429
+ if (v2.kind === "none")
61430
+ return { kind: "none" };
61431
+ if (v2.kind === "symlink" && typeof v2.target === "string")
61432
+ return { kind: "symlink", target: v2.target };
61433
+ return;
61434
+ }
61435
+ function attempt2(v2) {
61436
+ if (v2 === null)
61437
+ return null;
61438
+ if (!isObj2(v2) || typeof v2.at !== "string" || typeof v2.ok !== "boolean" || typeof v2.version !== "string")
61439
+ return;
61440
+ return typeof v2.reason === "string" ? { at: v2.at, ok: v2.ok, version: v2.version, reason: v2.reason } : { at: v2.at, ok: v2.ok, version: v2.version };
61441
+ }
61442
+ function validate3(raw) {
61443
+ if (!isObj2(raw) || raw.version !== 1)
61444
+ return null;
61445
+ const policy = POLICIES.includes(String(raw.policy)) ? raw.policy : undefined;
61446
+ const patched_from = strOrNull(raw.patched_from);
61447
+ const upstream_bin = strOrNull(raw.upstream_bin);
61448
+ const launcher_restore = restore(raw.launcher_restore);
61449
+ const last_attempt = attempt2(raw.last_attempt);
61450
+ if (policy === undefined || patched_from === undefined || upstream_bin === undefined || launcher_restore === undefined || last_attempt === undefined)
61451
+ return null;
61452
+ return { version: 1, policy, patched_from, upstream_bin, launcher_restore, last_attempt };
61453
+ }
61454
+ function readValidated(file2) {
61455
+ if (!existsSync8(file2))
61456
+ return null;
61457
+ try {
61458
+ return validate3(JSON.parse(readFileSync6(file2, "utf8")));
61459
+ } catch {
61460
+ return null;
61461
+ }
61462
+ }
61463
+ function readState(file2) {
61464
+ if (!existsSync8(file2) && !existsSync8(`${file2}${BACKUP_SUFFIX}`)) {
61465
+ return { state: DEFAULT_STATE, corrupt: false };
61466
+ }
61467
+ const primary = readValidated(file2);
61468
+ if (primary)
61469
+ return { state: primary, corrupt: false };
61470
+ const backup = readValidated(`${file2}${BACKUP_SUFFIX}`);
61471
+ return backup ? { state: backup, corrupt: true } : { state: DEFAULT_STATE, corrupt: true };
61472
+ }
61473
+ function writeState(file2, state) {
61474
+ const text = `${JSON.stringify(state, null, 2)}
61475
+ `;
61476
+ writeFileAtomic(file2, text);
61477
+ writeFileAtomic(`${file2}${BACKUP_SUFFIX}`, text);
61478
+ }
61479
+
61480
+ // src/patch/acquire.ts
61481
+ import { rmSync as rmSync5 } from "node:fs";
61482
+
61483
+ // src/patch/compile.ts
61484
+ import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, mkdirSync as mkdirSync5, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
61485
+ import { createHash as createHash4 } from "node:crypto";
61486
+ import { join as join12 } from "node:path";
61487
+
61488
+ // src/patch/build.ts
61489
+ import { existsSync as existsSync10, lstatSync as lstatSync2, readFileSync as readFileSync7, statSync as statSync3 } from "node:fs";
61490
+ import { join as join11 } from "node:path";
61491
+
61492
+ // src/patch/v8.ts
61493
+ import { existsSync as existsSync9 } from "node:fs";
61494
+ import { join as join10 } from "node:path";
61495
+ function defaultV8Target() {
61496
+ const isArm = process.arch === "arm64";
61497
+ if (process.platform === "darwin")
61498
+ return isArm ? "aarch64-apple-darwin" : "x86_64-apple-darwin";
61499
+ if (process.platform === "linux")
61500
+ return isArm ? "aarch64-unknown-linux-gnu" : "x86_64-unknown-linux-gnu";
61501
+ return isArm ? "aarch64-pc-windows-msvc" : "x86_64-pc-windows-msvc";
61502
+ }
61503
+ var PYTHON_RESOLVE_SCRIPT = [
61504
+ "import json, os, sys",
61505
+ "repo = sys.argv[1]",
61506
+ "target = sys.argv[2]",
61507
+ "os.environ['CODEX_REPO_ROOT'] = repo",
61508
+ "sys.path.insert(0, repo)",
61509
+ "from scripts.codex_package.targets import TARGET_SPECS",
61510
+ "from scripts.codex_package.v8 import resolve_codex_v8_cargo_env",
61511
+ "spec = TARGET_SPECS.get(target)",
61512
+ "if spec:",
61513
+ " print(json.dumps(resolve_codex_v8_cargo_env(spec)))",
61514
+ "else:",
61515
+ " print('{}')"
61516
+ ].join(`
61517
+ `);
61518
+ function resolveV8Env(sourceDir, run, log) {
61519
+ if (process.env.RUSTY_V8_ARCHIVE && process.env.RUSTY_V8_SRC_BINDING_PATH) {
61520
+ return {
61521
+ RUSTY_V8_ARCHIVE: process.env.RUSTY_V8_ARCHIVE,
61522
+ RUSTY_V8_SRC_BINDING_PATH: process.env.RUSTY_V8_SRC_BINDING_PATH
61523
+ };
61524
+ }
61525
+ const v8Script = join10(sourceDir, "scripts", "codex_package", "v8.py");
61526
+ if (!existsSync9(v8Script)) {
61527
+ return {};
61528
+ }
61529
+ const target = defaultV8Target();
61530
+ log(`resolving Codex V8 dependencies for ${target}...`);
61531
+ for (const py of ["python3", "python"]) {
61532
+ const r = run(py, ["-c", PYTHON_RESOLVE_SCRIPT, sourceDir, target], { cwd: sourceDir });
61533
+ if (r.status === 0 && r.stdout.trim()) {
61534
+ try {
61535
+ const parsed = JSON.parse(r.stdout.trim());
61536
+ if (parsed.RUSTY_V8_ARCHIVE && parsed.RUSTY_V8_SRC_BINDING_PATH) {
61537
+ log(`using Codex V8 archive: ${parsed.RUSTY_V8_ARCHIVE}`);
61538
+ return parsed;
61539
+ }
61540
+ } catch {}
61541
+ }
61542
+ }
61543
+ log("warning: could not resolve Codex V8 dependencies via python; build may fail if V8 prebuilts are needed");
61544
+ return {};
61545
+ }
61546
+
61547
+ // src/patch/build.ts
61548
+ class BuildError extends Error {
61549
+ step;
61550
+ name = "BuildError";
61551
+ constructor(step, detail) {
61552
+ super(`${step}: ${detail.trim() || "failed"}`);
61553
+ this.step = step;
61554
+ }
61555
+ }
61556
+ var REPO2 = "https://github.com/openai/codex";
61557
+ var SNAPSHOT = "codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__cxstatusline_block_preserves_instruction_footer.snap";
61558
+ var SNAPSHOT_CONTENT = [
61559
+ "---",
61560
+ "source: tui/src/bottom_pane/footer.rs",
61561
+ "expression: terminal.backend()",
61562
+ "---",
61563
+ ...["status-0", "status-1", "status-2", "esc esc to edit previous message"].map((s3) => `" ${s3.padEnd(78)}"`),
61564
+ ""
61565
+ ].join(`
61566
+ `);
61567
+ var TARGETS = ["codex", "codex-code-mode-host"];
61568
+ var HEX403 = /^[0-9a-f]{40}$/;
61569
+ function must(step, run, log, cmd, args, cwd2, env3) {
61570
+ log(`$ ${[cmd, ...args].join(" ")}`);
61571
+ const r = run(cmd, args, { cwd: cwd2, env: env3 });
61572
+ for (const line of `${r.stdout}${r.stderr}`.split(`
61573
+ `).filter(Boolean))
61574
+ log(line);
61575
+ if (r.status !== 0)
61576
+ throw new BuildError(step, r.stderr || r.stdout);
61577
+ }
61578
+ function runFocusedTests(run, log, which, crateDir, env3) {
61579
+ if (which("just")) {
61580
+ log("running the focused Rust tests with just");
61581
+ must("just test", run, log, "just", ["test", "--release", "-p", "codex-tui", "cxstatusline", "--retries", "0"], crateDir, env3);
61582
+ return;
61583
+ }
61584
+ log("just is not on PATH; running the focused Rust tests with cargo");
61585
+ must("cargo test", run, log, "cargo", ["test", "--release", "-p", "codex-tui", "cxstatusline"], crateDir, env3);
61586
+ }
61587
+ function headCommit(plan, run) {
61588
+ const r = run("git", ["-C", plan.sourceDir, "rev-parse", "HEAD"]);
61589
+ const commit = r.stdout.trim();
61590
+ if (r.status !== 0 || !HEX403.test(commit)) {
61591
+ throw new BuildError("rev-parse", `${plan.sourceDir} did not report a commit for ${plan.tag}`);
61592
+ }
61593
+ return commit;
61594
+ }
61595
+ function prepareCheckout(plan, run, log) {
61534
61596
  const git = (step, ...args) => must(step, run, log, "git", ["-C", plan.sourceDir, ...args]);
61535
- if (existsSync9(join10(plan.sourceDir, ".git"))) {
61597
+ if (existsSync10(join11(plan.sourceDir, ".git"))) {
61536
61598
  git("fetch", "fetch", "--tags", "--filter=blob:none", "origin");
61537
- const snapshot = join10(plan.sourceDir, SNAPSHOT);
61599
+ const snapshot = join11(plan.sourceDir, SNAPSHOT);
61538
61600
  const st2 = lstatSync2(snapshot, { throwIfNoEntry: false });
61539
61601
  if (st2) {
61540
61602
  const tracked = run("git", ["-C", plan.sourceDir, "ls-files", "--error-unmatch", "--", SNAPSHOT]);
@@ -61551,22 +61613,38 @@ function prepareCheckout(plan, run, log) {
61551
61613
  }
61552
61614
  git("checkout", "checkout", "--detach", plan.tag);
61553
61615
  }
61554
- function buildPatched(plan, run, log, which) {
61616
+ function buildPatched(plan, run, log, which, onStatus) {
61555
61617
  const git = (step, ...args) => must(step, run, log, "git", ["-C", plan.sourceDir, ...args]);
61618
+ onStatus?.("checkout", `Preparing checkout for ${plan.tag}...`);
61556
61619
  prepareCheckout(plan, run, log);
61557
61620
  const upstreamCommit = headCommit(plan, run);
61621
+ onStatus?.("checkout-done", `Upstream source ready at ${upstreamCommit.slice(0, 10)}`);
61622
+ const patchBase = plan.patchFile.split("/").pop() ?? "patch";
61623
+ onStatus?.("patch", `Applying ${patchBase}...`);
61558
61624
  git("apply --check", "apply", "--index", "--check", plan.patchFile);
61559
61625
  git("apply", "apply", "--index", plan.patchFile);
61560
- const crateDir = join10(plan.sourceDir, "codex-rs");
61561
- must("cargo build", run, log, "cargo", ["build", "--release", "-p", "codex-cli", "--bin", "codex", "-p", "codex-code-mode-host", "--bin", "codex-code-mode-host"], crateDir);
61626
+ onStatus?.("patch-done", `Applied ${patchBase} cleanly`);
61627
+ const crateDir = join11(plan.sourceDir, "codex-rs");
61628
+ const v8Env = resolveV8Env(plan.sourceDir, run, log);
61629
+ onStatus?.("build", "Compiling release binaries (cargo build --release)... Note: this full compile typically takes 10 to 20 minutes.");
61630
+ const buildStart = Date.now();
61631
+ must("cargo build", run, log, "cargo", ["build", "--release", "-p", "codex-cli", "--bin", "codex", "-p", "codex-code-mode-host", "--bin", "codex-code-mode-host"], crateDir, v8Env);
61632
+ const buildElapsed = Math.round((Date.now() - buildStart) / 1000);
61633
+ const buildElapsedStr = buildElapsed >= 60 ? `${Math.floor(buildElapsed / 60)}m ${buildElapsed % 60}s` : `${buildElapsed}s`;
61634
+ onStatus?.("build-done", `Release binaries compiled (codex, codex-code-mode-host in ${buildElapsedStr})`);
61562
61635
  const [codex, codexCodeModeHost] = TARGETS.map((name) => {
61563
- const bin = join10(crateDir, "target", "release", name);
61564
- if (!existsSync9(bin) || statSync3(bin).size === 0) {
61636
+ const bin = join11(crateDir, "target", "release", name);
61637
+ if (!existsSync10(bin) || statSync3(bin).size === 0) {
61565
61638
  throw new BuildError("cargo build", `cargo exited 0 but ${bin} is missing or empty`);
61566
61639
  }
61567
61640
  return bin;
61568
61641
  });
61569
- runFocusedTests(run, log, which, crateDir);
61642
+ onStatus?.("test", "Running focused regression tests...");
61643
+ const testStart = Date.now();
61644
+ runFocusedTests(run, log, which, crateDir, v8Env);
61645
+ const testElapsed = Math.round((Date.now() - testStart) / 1000);
61646
+ const testElapsedStr = testElapsed >= 60 ? `${Math.floor(testElapsed / 60)}m ${testElapsed % 60}s` : `${testElapsed}s`;
61647
+ onStatus?.("test-done", `Focused regression tests passed (in ${testElapsedStr})`);
61570
61648
  return { codex, codexCodeModeHost, upstreamCommit };
61571
61649
  }
61572
61650
 
@@ -61584,7 +61662,7 @@ function platformLabel() {
61584
61662
  }
61585
61663
  function stage(ctx, built) {
61586
61664
  mkdirSync5(ctx.paths.libexecDir, { recursive: true });
61587
- const directory = mkdtempSync2(join11(ctx.paths.libexecDir, STAGING_PREFIX));
61665
+ const directory = mkdtempSync2(join12(ctx.paths.libexecDir, STAGING_PREFIX));
61588
61666
  chmodSync3(directory, 448);
61589
61667
  const sources = {
61590
61668
  codex: built.codex,
@@ -61593,7 +61671,7 @@ function stage(ctx, built) {
61593
61671
  const executables = {};
61594
61672
  try {
61595
61673
  for (const name of GENERATION_EXECUTABLES) {
61596
- const target = join11(directory, name);
61674
+ const target = join12(directory, name);
61597
61675
  copyFileSync2(sources[name], target);
61598
61676
  chmodSync3(target, 493);
61599
61677
  executables[name] = digestOf3(target);
@@ -61610,11 +61688,13 @@ function stage(ctx, built) {
61610
61688
  }
61611
61689
  };
61612
61690
  }
61613
- function prepareCompiled(ctx, upstream, ref) {
61614
- const patchFile = join11(ctx.patchesDir, ref.file);
61691
+ function prepareCompiled(ctx, upstream, ref, onStatus) {
61692
+ const patchFile = join12(ctx.patchesDir, ref.file);
61615
61693
  const patchSha256 = digestOf3(patchFile).sha256;
61616
- const built = buildPatched({ tag: ref.tag, sourceDir: ctx.paths.sourceDir, patchFile }, ctx.run, ctx.log, ctx.which);
61694
+ const built = buildPatched({ tag: ref.tag, sourceDir: ctx.paths.sourceDir, patchFile }, ctx.run, ctx.log, ctx.which, onStatus);
61695
+ onStatus?.("stage", "Staging generation and verifying binaries...");
61617
61696
  const staged = stage(ctx, built);
61697
+ onStatus?.("stage-done", "Generation staged and binaries verified");
61618
61698
  return {
61619
61699
  directory: staged.directory,
61620
61700
  codexVersion: upstream.raw,
@@ -61634,7 +61714,7 @@ function prepareCompiled(ctx, upstream, ref) {
61634
61714
 
61635
61715
  // src/patch/manifest.ts
61636
61716
  import { readFileSync as readFileSync9 } from "node:fs";
61637
- import { join as join12 } from "node:path";
61717
+ import { join as join13 } from "node:path";
61638
61718
  class ManifestError extends Error {
61639
61719
  name = "ManifestError";
61640
61720
  }
@@ -61653,7 +61733,7 @@ var isCandidate = (c) => {
61653
61733
  return parsed !== null && parsed.pre === null;
61654
61734
  };
61655
61735
  function loadManifest(patchesDir) {
61656
- const file2 = join12(patchesDir, "manifest.json");
61736
+ const file2 = join13(patchesDir, "manifest.json");
61657
61737
  try {
61658
61738
  const raw = JSON.parse(readFileSync9(file2, "utf8"));
61659
61739
  const m2 = raw;
@@ -61679,9 +61759,9 @@ function resolvePatch(m2, v2) {
61679
61759
  }
61680
61760
 
61681
61761
  // src/patch/wrapper.ts
61682
- import { chmodSync as chmodSync4, closeSync as closeSync2, existsSync as existsSync10, lstatSync as lstatSync3, mkdirSync as mkdirSync6, openSync as openSync2, readFileSync as readFileSync10, readSync, renameSync as renameSync3, rmSync as rmSync4, statSync as statSync5, writeFileSync as writeFileSync3 } from "node:fs";
61762
+ import { chmodSync as chmodSync4, closeSync as closeSync2, existsSync as existsSync11, lstatSync as lstatSync3, mkdirSync as mkdirSync6, openSync as openSync2, readFileSync as readFileSync10, readSync, renameSync as renameSync3, rmSync as rmSync4, statSync as statSync5, writeFileSync as writeFileSync3 } from "node:fs";
61683
61763
  import { randomBytes as randomBytes3 } from "node:crypto";
61684
- import { join as join13 } from "node:path";
61764
+ import { join as join14 } from "node:path";
61685
61765
  var WRAPPER_MARKER = "# cxstatusline-wrapper v1";
61686
61766
  var WRAPPER_MARKER_V2 = "# cxstatusline-wrapper v2";
61687
61767
  var WRAPPER_MARKERS = [WRAPPER_MARKER, WRAPPER_MARKER_V2];
@@ -61740,7 +61820,7 @@ function isSymlink(p2) {
61740
61820
  }
61741
61821
  }
61742
61822
  function stageExecutable(target, write) {
61743
- mkdirSync6(join13(target, ".."), { recursive: true });
61823
+ mkdirSync6(join14(target, ".."), { recursive: true });
61744
61824
  const tmp = `${target}.tmp-${process.pid}`;
61745
61825
  try {
61746
61826
  write(tmp);
@@ -61757,7 +61837,7 @@ function placeExecutable(target, write) {
61757
61837
  renameSync3(stageExecutable(target, write), target);
61758
61838
  }
61759
61839
  function foreignLauncher(target) {
61760
- return existsSync10(target) && !isSymlink(target) && !isOurWrapper(target) ? `${target} is a real file we did not write (not a symlink, no cxstatusline marker); refusing to replace it. Move it aside and re-run, or point PATH at a different bin dir.` : null;
61840
+ return existsSync11(target) && !isSymlink(target) && !isOurWrapper(target) ? `${target} is a real file we did not write (not a symlink, no cxstatusline marker); refusing to replace it. Move it aside and re-run, or point PATH at a different bin dir.` : null;
61761
61841
  }
61762
61842
  function assertLauncherReplaceable(paths) {
61763
61843
  const reason = foreignLauncher(paths.wrapperPath);
@@ -61895,7 +61975,7 @@ async function acquireLocked(ctx, initial2, opts, transport) {
61895
61975
  recordFailure(ctx, state, upstream.raw, reason);
61896
61976
  return { kind: "refused", reason };
61897
61977
  }
61898
- return opts.source === "compiled" ? compiledAcquisition(ctx, state, upstream) : prebuiltAcquisition(ctx, state, upstream, transport);
61978
+ return opts.source === "compiled" ? compiledAcquisition(ctx, state, upstream, transport.onStatus) : prebuiltAcquisition(ctx, state, upstream, transport);
61899
61979
  }
61900
61980
  function install(ctx, state, upstream, source, pair) {
61901
61981
  try {
@@ -61910,7 +61990,7 @@ function install(ctx, state, upstream, source, pair) {
61910
61990
  rmSync5(pair.directory, { recursive: true, force: true });
61911
61991
  }
61912
61992
  }
61913
- function compiledAcquisition(ctx, state, upstream) {
61993
+ function compiledAcquisition(ctx, state, upstream, onStatus) {
61914
61994
  let ref;
61915
61995
  try {
61916
61996
  ref = resolvePatch(loadManifest(ctx.patchesDir), upstream);
@@ -61932,7 +62012,7 @@ function compiledAcquisition(ctx, state, upstream) {
61932
62012
  }
61933
62013
  let pair;
61934
62014
  try {
61935
- pair = prepareCompiled(ctx, upstream, ref);
62015
+ pair = prepareCompiled(ctx, upstream, ref, onStatus);
61936
62016
  } catch (e) {
61937
62017
  const reason = reasonOf(e);
61938
62018
  recordFailure(ctx, state, upstream.raw, reason);
@@ -62004,6 +62084,288 @@ function describeOutcome(o) {
62004
62084
  }
62005
62085
  }
62006
62086
 
62087
+ // src/ui/box.ts
62088
+ var symbols = {
62089
+ ok: source_default.green("✔"),
62090
+ warn: source_default.yellow("▲"),
62091
+ fail: source_default.red("✖"),
62092
+ info: source_default.cyan("ℹ"),
62093
+ bullet: source_default.dim("●"),
62094
+ activeBullet: source_default.green("●"),
62095
+ dimBullet: source_default.dim("○"),
62096
+ pointer: source_default.cyan("▸"),
62097
+ diamond: source_default.magenta("◇")
62098
+ };
62099
+ function splitLines(lines) {
62100
+ return lines.flatMap((l) => l.split(`
62101
+ `));
62102
+ }
62103
+ function renderBox(lines, options = {}) {
62104
+ const border = options.borderColor ?? source_default.dim;
62105
+ const flatLines = splitLines(lines);
62106
+ const minWidth = options.minWidth ?? 60;
62107
+ const contentWidth = flatLines.reduce((max2, l) => Math.max(max2, getVisibleWidth(l)), 0);
62108
+ const titleWidth = options.title ? getVisibleWidth(options.title) + (options.badge ? getVisibleWidth(options.badge) + 2 : 0) : 0;
62109
+ const innerWidth = Math.max(minWidth, contentWidth, titleWidth);
62110
+ const topBorder = `${border("╭─")}${border("─".repeat(innerWidth + 2))}${border("─╮")}`;
62111
+ const bottomBorder = `${border("╰─")}${border("─".repeat(innerWidth + 2))}${border("─╯")}`;
62112
+ const renderedLines = [topBorder];
62113
+ if (options.title) {
62114
+ const title = options.title;
62115
+ const badge = options.badge ? ` ${options.badge}` : "";
62116
+ const headerVisible = getVisibleWidth(title) + getVisibleWidth(badge);
62117
+ const rightPad = " ".repeat(Math.max(0, innerWidth - headerVisible));
62118
+ renderedLines.push(`${border("│")} ${title}${rightPad}${badge} ${border("│")}`);
62119
+ renderedLines.push(`${border("├─")}${border("─".repeat(innerWidth + 2))}${border("─┤")}`);
62120
+ }
62121
+ for (const line of flatLines) {
62122
+ const pad3 = " ".repeat(Math.max(0, innerWidth - getVisibleWidth(line)));
62123
+ renderedLines.push(`${border("│")} ${line}${pad3} ${border("│")}`);
62124
+ }
62125
+ renderedLines.push(bottomBorder);
62126
+ return renderedLines.join(`
62127
+ `);
62128
+ }
62129
+ function renderHeader(title, subtitle, badge, minWidth = 68) {
62130
+ const border = source_default.cyan;
62131
+ const titleVis = getVisibleWidth(title);
62132
+ const badgeVis = badge ? getVisibleWidth(badge) : 0;
62133
+ const subVis = subtitle ? getVisibleWidth(subtitle) : 0;
62134
+ const innerWidth = Math.max(minWidth, titleVis + (badge ? badgeVis + 4 : 0), subVis);
62135
+ const topBorder = `${border("╭─")}${border("─".repeat(innerWidth + 2))}${border("─╮")}`;
62136
+ const bottomBorder = `${border("╰─")}${border("─".repeat(innerWidth + 2))}${border("─╯")}`;
62137
+ const lines = [topBorder];
62138
+ if (badge) {
62139
+ const space = " ".repeat(Math.max(2, innerWidth - titleVis - badgeVis));
62140
+ lines.push(`${border("│")} ${source_default.bold.white(title)}${space}${source_default.dim(badge)} ${border("│")}`);
62141
+ } else {
62142
+ const space = " ".repeat(Math.max(0, innerWidth - titleVis));
62143
+ lines.push(`${border("│")} ${source_default.bold.white(title)}${space} ${border("│")}`);
62144
+ }
62145
+ if (subtitle) {
62146
+ const space = " ".repeat(Math.max(0, innerWidth - subVis));
62147
+ lines.push(`${border("│")} ${source_default.dim(subtitle)}${space} ${border("│")}`);
62148
+ }
62149
+ lines.push(bottomBorder);
62150
+ return lines.join(`
62151
+ `);
62152
+ }
62153
+ function renderSectionTitle(title) {
62154
+ return ` ${symbols.pointer} ${source_default.bold.white(title)}`;
62155
+ }
62156
+
62157
+ // src/ui/install-format.ts
62158
+ function renderInstallHeader(compile3) {
62159
+ const subtitle = compile3 ? "Compiling patched Codex from source" : "Acquiring prebuilt statusline integration for OpenAI Codex";
62160
+ return renderHeader("cxstatusline install", subtitle, `v${VERSION}`);
62161
+ }
62162
+ function renderInstallSuccess(opts) {
62163
+ const sourceDesc = opts.source === "prebuilt" ? opts.reused ? "prebuilt release (reused existing generation)" : "prebuilt release" : "source build";
62164
+ const hookDesc = `hook ${opts.hookAction} in ${opts.hookFile}`;
62165
+ const lines = [
62166
+ `${symbols.ok} ${source_default.bold.green("Installation Complete")}`,
62167
+ "",
62168
+ ` ${source_default.dim("Codex Target:")} ${source_default.bold.white(opts.version)}`,
62169
+ ` ${source_default.dim("Source:")} ${source_default.cyan(sourceDesc)}`,
62170
+ ` ${source_default.dim("Hook:")} ${source_default.white(hookDesc)}`,
62171
+ "",
62172
+ source_default.white("Start Codex once and accept the cxstatusline hook when prompted.")
62173
+ ];
62174
+ return renderBox(lines, { borderColor: source_default.green, minWidth: 68 });
62175
+ }
62176
+ function renderInstallHookError(opts) {
62177
+ const lines = [
62178
+ `${symbols.warn} ${source_default.bold.yellow("Codex Installed (Hook Notice)")}`,
62179
+ "",
62180
+ `Codex ${opts.version} is installed, but the SessionStart hook could not be written: ${opts.error}`,
62181
+ "",
62182
+ `Fix ${opts.hookFile} by hand, then run \`cxstatusline hook install\`.`
62183
+ ];
62184
+ return renderBox(lines, { borderColor: source_default.yellow, minWidth: 68 });
62185
+ }
62186
+ function renderInstallFailure(outcome, advice) {
62187
+ const desc = describeOutcome(outcome);
62188
+ const lines = [
62189
+ `${symbols.fail} ${source_default.bold.red("Installation Incomplete")}`,
62190
+ "",
62191
+ source_default.white(desc)
62192
+ ];
62193
+ if (advice.length > 0) {
62194
+ lines.push("");
62195
+ lines.push(source_default.bold.cyan("Next Steps:"));
62196
+ for (const a of advice) {
62197
+ lines.push(` ${symbols.pointer} ${a}`);
62198
+ }
62199
+ }
62200
+ return renderBox(lines, { borderColor: source_default.red, minWidth: 68 });
62201
+ }
62202
+ // src/ui/progress.ts
62203
+ function formatBytes2(bytes) {
62204
+ if (bytes <= 0)
62205
+ return "0 B";
62206
+ if (bytes < 1024)
62207
+ return `${bytes} B`;
62208
+ if (bytes < 1024 * 1024)
62209
+ return `${(bytes / 1024).toFixed(1)} KB`;
62210
+ if (bytes < 1024 * 1024 * 1024)
62211
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
62212
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
62213
+ }
62214
+ function formatDurationMs(ms2) {
62215
+ if (ms2 < 1000)
62216
+ return `${ms2}ms`;
62217
+ const sec = Math.round(ms2 / 1000);
62218
+ if (sec < 60)
62219
+ return `${(ms2 / 1000).toFixed(1)}s`;
62220
+ const min2 = Math.floor(sec / 60);
62221
+ const remSec = sec % 60;
62222
+ return `${min2}m ${remSec}s`;
62223
+ }
62224
+ function renderProgressBar(ratio, width = 24) {
62225
+ const clamped = Math.max(0, Math.min(1, Number.isFinite(ratio) ? ratio : 0));
62226
+ const filled = Math.round(clamped * width);
62227
+ const empty = Math.max(0, width - filled);
62228
+ return `[${source_default.cyan("█".repeat(filled))}${source_default.dim("░".repeat(empty))}]`;
62229
+ }
62230
+ function createInstallProgressTracker(io2, baseTransport = {}) {
62231
+ const isTTY = io2.isTTY ?? (typeof process !== "undefined" && Boolean(process.stdout?.isTTY));
62232
+ let downloadStart = 0;
62233
+ let lastDownloadUpdate = 0;
62234
+ let hasProgressBar = false;
62235
+ let lastReportedPct = -1;
62236
+ const clearProgressLine = () => {
62237
+ if (hasProgressBar && isTTY) {
62238
+ io2.write("\r\x1B[2K");
62239
+ hasProgressBar = false;
62240
+ }
62241
+ };
62242
+ const onProgress = (loaded, total) => {
62243
+ const now2 = Date.now();
62244
+ if (!downloadStart)
62245
+ downloadStart = now2;
62246
+ if (isTTY && now2 - lastDownloadUpdate < 50 && total !== null && loaded < total) {
62247
+ return;
62248
+ }
62249
+ lastDownloadUpdate = now2;
62250
+ const elapsedSec = Math.max(0.001, (now2 - downloadStart) / 1000);
62251
+ const speed = loaded / elapsedSec;
62252
+ const speedStr = `${formatBytes2(speed)}/s`;
62253
+ if (total !== null && total > 0) {
62254
+ const ratio = Math.min(1, loaded / total);
62255
+ const pct = Math.floor(ratio * 100);
62256
+ if (isTTY) {
62257
+ const bar = renderProgressBar(ratio, 24);
62258
+ const loadedStr = formatBytes2(loaded);
62259
+ const totalStr = formatBytes2(total);
62260
+ const text = `\r ${bar} ${source_default.bold(String(pct).padStart(3))}% ${loadedStr} / ${totalStr} (${source_default.dim(speedStr)})`;
62261
+ io2.write(text);
62262
+ hasProgressBar = true;
62263
+ } else {
62264
+ const milestone = Math.floor(pct / 25) * 25;
62265
+ if (milestone > lastReportedPct && milestone > 0) {
62266
+ lastReportedPct = milestone;
62267
+ io2.say(` ${symbols.dimBullet} Downloaded ${milestone}% (${formatBytes2(loaded)} / ${formatBytes2(total)})`);
62268
+ }
62269
+ }
62270
+ } else {
62271
+ if (isTTY) {
62272
+ io2.write(`\r ${symbols.activeBullet} ${formatBytes2(loaded)} downloaded (${source_default.dim(speedStr)})`);
62273
+ hasProgressBar = true;
62274
+ }
62275
+ }
62276
+ };
62277
+ const onStatus = (phase, message) => {
62278
+ clearProgressLine();
62279
+ switch (phase) {
62280
+ case "manifest":
62281
+ io2.say(`
62282
+ ${symbols.pointer} ${source_default.bold("Release Metadata")}`);
62283
+ io2.say(` ${symbols.dimBullet} ${source_default.dim(message)}`);
62284
+ break;
62285
+ case "manifest-done":
62286
+ io2.say(` ${symbols.ok} ${source_default.white(message)}`);
62287
+ break;
62288
+ case "download":
62289
+ io2.say(`
62290
+ ${symbols.pointer} ${source_default.bold("Downloading Prebuilt Archive")}`);
62291
+ downloadStart = Date.now();
62292
+ lastDownloadUpdate = 0;
62293
+ lastReportedPct = -1;
62294
+ break;
62295
+ case "download-done":
62296
+ clearProgressLine();
62297
+ const duration3 = downloadStart ? ` in ${formatDurationMs(Date.now() - downloadStart)}` : "";
62298
+ io2.say(` ${symbols.ok} ${source_default.white(message)}${source_default.dim(duration3)}`);
62299
+ break;
62300
+ case "extract":
62301
+ io2.say(`
62302
+ ${symbols.pointer} ${source_default.bold("Verification & Staging")}`);
62303
+ io2.say(` ${symbols.dimBullet} ${source_default.dim(message)}`);
62304
+ break;
62305
+ case "extract-done":
62306
+ io2.say(` ${symbols.ok} ${source_default.white(message)}`);
62307
+ break;
62308
+ case "checkout":
62309
+ io2.say(`
62310
+ ${symbols.pointer} ${source_default.bold("Upstream Source")}`);
62311
+ io2.say(` ${symbols.dimBullet} ${source_default.dim(message)}`);
62312
+ break;
62313
+ case "checkout-done":
62314
+ io2.say(` ${symbols.ok} ${source_default.white(message)}`);
62315
+ break;
62316
+ case "patch":
62317
+ io2.say(`
62318
+ ${symbols.pointer} ${source_default.bold("Statusline Patch")}`);
62319
+ io2.say(` ${symbols.dimBullet} ${source_default.dim(message)}`);
62320
+ break;
62321
+ case "patch-done":
62322
+ io2.say(` ${symbols.ok} ${source_default.white(message)}`);
62323
+ break;
62324
+ case "build":
62325
+ io2.say(`
62326
+ ${symbols.pointer} ${source_default.bold("Compiling Release Binaries")}`);
62327
+ io2.say(` ${symbols.dimBullet} ${source_default.dim(message)}`);
62328
+ break;
62329
+ case "build-done":
62330
+ io2.say(` ${symbols.ok} ${source_default.white(message)}`);
62331
+ break;
62332
+ case "test":
62333
+ io2.say(`
62334
+ ${symbols.pointer} ${source_default.bold("Regression Tests")}`);
62335
+ io2.say(` ${symbols.dimBullet} ${source_default.dim(message)}`);
62336
+ break;
62337
+ case "test-done":
62338
+ io2.say(` ${symbols.ok} ${source_default.white(message)}`);
62339
+ break;
62340
+ case "stage":
62341
+ io2.say(`
62342
+ ${symbols.pointer} ${source_default.bold("Staging & Activation")}`);
62343
+ io2.say(` ${symbols.ok} ${source_default.white(message)}`);
62344
+ break;
62345
+ default:
62346
+ io2.say(` ${symbols.info} ${message}`);
62347
+ break;
62348
+ }
62349
+ };
62350
+ const transport = {
62351
+ ...baseTransport,
62352
+ onProgress: (loaded, total) => {
62353
+ onProgress(loaded, total);
62354
+ baseTransport.onProgress?.(loaded, total);
62355
+ },
62356
+ onStatus: (phase, msg) => {
62357
+ onStatus(phase, msg);
62358
+ baseTransport.onStatus?.(phase, msg);
62359
+ }
62360
+ };
62361
+ return {
62362
+ transport,
62363
+ finish: () => {
62364
+ clearProgressLine();
62365
+ }
62366
+ };
62367
+ }
62368
+
62007
62369
  // src/patch/run.ts
62008
62370
  var REPO3 = "adrijshikhar/cxstatusline";
62009
62371
  function simulateDrift(ctx, version2) {
@@ -62065,51 +62427,143 @@ function report(ctx, outcome) {
62065
62427
  for (const line of fallbackAdvice(ctx, outcome.version))
62066
62428
  ctx.say(line);
62067
62429
  }
62068
- function runPatch(ctx, opts) {
62069
- return runAcquisition(ctx, { source: "compiled", force: opts.force });
62430
+ function runPatch(ctx, opts, transport = {}) {
62431
+ ctx.say(renderInstallHeader(true));
62432
+ const progress = createInstallProgressTracker({
62433
+ isTTY: process.stdout?.isTTY,
62434
+ write: (s3) => process.stdout.write(s3),
62435
+ say: (l) => ctx.say(l)
62436
+ }, transport);
62437
+ return runAcquisition(ctx, { source: "compiled", force: opts.force }, progress.transport).finally(() => {
62438
+ progress.finish();
62439
+ });
62070
62440
  }
62071
62441
  async function runInstall(ctx, opts, transport = {}) {
62072
- const outcome = await runAcquisition(ctx, { source: opts.compile ? "compiled" : "prebuilt", force: true }, transport);
62073
- report(ctx, outcome);
62074
- if (outcome.kind !== "installed")
62442
+ ctx.say(renderInstallHeader(opts.compile));
62443
+ const progress = createInstallProgressTracker({
62444
+ isTTY: process.stdout?.isTTY,
62445
+ write: (s3) => process.stdout.write(s3),
62446
+ say: (l) => ctx.say(l)
62447
+ }, transport);
62448
+ let outcome;
62449
+ try {
62450
+ outcome = await runAcquisition(ctx, { source: opts.compile ? "compiled" : "prebuilt", force: true }, progress.transport);
62451
+ } finally {
62452
+ progress.finish();
62453
+ }
62454
+ if (outcome.kind !== "installed") {
62455
+ const advice = outcome.kind === "unavailable" ? fallbackAdvice(ctx, outcome.version) : [];
62456
+ ctx.say(renderInstallFailure(outcome, advice));
62075
62457
  return 1;
62458
+ }
62076
62459
  try {
62077
62460
  const r = installHook(ctx.paths.hooksFile, ctx.cxBin);
62078
- ctx.say(`hook ${r} in ${ctx.paths.hooksFile}`);
62079
- if (r === "added")
62080
- ctx.say("Start Codex once and accept the cxstatusline hook when prompted.");
62461
+ ctx.say(renderInstallSuccess({
62462
+ version: outcome.version,
62463
+ source: outcome.source,
62464
+ reused: outcome.reused,
62465
+ hookAction: r,
62466
+ hookFile: ctx.paths.hooksFile
62467
+ }));
62081
62468
  return 0;
62082
62469
  } catch (e) {
62083
- ctx.say(`Codex ${outcome.version} is installed, but the SessionStart hook could not be written: ${String(e)}`);
62084
- ctx.say(`Fix ${ctx.paths.hooksFile} by hand, then run \`cxstatusline hook install\`.`);
62470
+ ctx.say(renderInstallHookError({
62471
+ version: outcome.version,
62472
+ hookFile: ctx.paths.hooksFile,
62473
+ error: String(e)
62474
+ }));
62085
62475
  return 1;
62086
62476
  }
62087
62477
  }
62088
- async function runUpdate(ctx, transport = {}) {
62478
+ async function probeUpstreamLatest(fetchFn) {
62479
+ try {
62480
+ const headers = { accept: "application/vnd.github+json", "user-agent": "cxstatusline" };
62481
+ if (process.env.GITHUB_TOKEN)
62482
+ headers.authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
62483
+ const res = await fetchFn("https://api.github.com/repos/openai/codex/releases/latest", {
62484
+ headers,
62485
+ signal: AbortSignal.timeout(5000)
62486
+ });
62487
+ if (!res.ok)
62488
+ return null;
62489
+ const data = await res.json();
62490
+ if (typeof data.tag_name !== "string")
62491
+ return null;
62492
+ return data.tag_name.replace(/^rust-v/, "");
62493
+ } catch {
62494
+ return null;
62495
+ }
62496
+ }
62497
+ async function probePrebuiltExists(targetCodexVersion, cxVersion, fetchFn, baseUrl) {
62498
+ try {
62499
+ const tag = releaseTag(cxVersion, targetCodexVersion);
62500
+ const url2 = baseUrl ? `${baseUrl}/${tag}/manifest.json` : `https://github.com/adrijshikhar/cxstatusline/releases/download/${tag}/manifest.json`;
62501
+ const res = await fetchFn(url2, { method: "HEAD", signal: AbortSignal.timeout(5000) });
62502
+ return res.ok || res.status === 302 || res.status === 301;
62503
+ } catch {
62504
+ return false;
62505
+ }
62506
+ }
62507
+ async function runUpdate(ctx, optsOrTransport = {}, maybeTransport) {
62508
+ const opts = "compile" in optsOrTransport || "force" in optsOrTransport ? optsOrTransport : {};
62509
+ const transport = "baseUrl" in optsOrTransport || "fetch" in optsOrTransport ? optsOrTransport : maybeTransport ?? {};
62089
62510
  const state = loadState(ctx);
62090
62511
  const located = upstreamFor(ctx, state);
62091
62512
  if ("reason" in located) {
62092
62513
  ctx.say(`no upstream Codex to update: ${located.reason}`);
62093
62514
  return 1;
62094
62515
  }
62516
+ const current = readUpstreamVersion(located.bin, ctx.run);
62517
+ const fetchFn = transport.fetch ?? fetch;
62518
+ if (current && !opts.force && !opts.compile) {
62519
+ const latest = await probeUpstreamLatest(fetchFn);
62520
+ if (latest && latest !== current.raw) {
62521
+ const available = await probePrebuiltExists(latest, VERSION, fetchFn, transport.baseUrl);
62522
+ if (!available) {
62523
+ ctx.say(`Warning: Upstream Codex update available: ${current.raw} -> ${latest}.`);
62524
+ ctx.say(`However, cxstatusline has not yet published prebuilt binaries for Codex ${latest}.`);
62525
+ ctx.say("Updating now will replace your patched launcher with stock Codex.");
62526
+ ctx.say("");
62527
+ ctx.say("Options:");
62528
+ ctx.say(" - Wait until cxstatusline publishes prebuilt binaries for this version.");
62529
+ ctx.say(" - Update and compile from source: cxstatusline update --compile");
62530
+ ctx.say(" - Update to stock Codex anyway: cxstatusline update --force");
62531
+ return 1;
62532
+ }
62533
+ } else if (latest && latest === current.raw) {
62534
+ ctx.say(`Codex is already at the latest version (${current.raw}).`);
62535
+ return 0;
62536
+ }
62537
+ }
62095
62538
  ctx.say(`running upstream updater: ${located.bin} update`);
62096
62539
  const r = ctx.run(located.bin, ["update"], { interactive: true });
62097
62540
  if (r.status !== 0) {
62098
62541
  ctx.say(`upstream updater exited ${String(r.status)}; see its output above. Not installing.`);
62099
62542
  return 1;
62100
62543
  }
62101
- const outcome = await runAcquisition(ctx, { source: "prebuilt", force: true }, transport);
62544
+ const source = opts.compile ? "compiled" : "prebuilt";
62545
+ const progress = createInstallProgressTracker({
62546
+ isTTY: process.stdout?.isTTY,
62547
+ write: (s3) => process.stdout.write(s3),
62548
+ say: (l) => ctx.say(l)
62549
+ }, transport);
62550
+ let outcome;
62551
+ try {
62552
+ outcome = await runAcquisition(ctx, { source, force: true }, progress.transport);
62553
+ } finally {
62554
+ progress.finish();
62555
+ }
62102
62556
  report(ctx, outcome);
62103
62557
  return outcome.kind === "installed" ? 0 : 1;
62104
62558
  }
62105
62559
  function appendLog(file2, line) {
62106
- mkdirSync7(join14(file2, ".."), { recursive: true });
62560
+ mkdirSync7(join15(file2, ".."), { recursive: true });
62107
62561
  appendFileSync(file2, `${new Date().toISOString()} ${line}
62108
62562
  `);
62109
62563
  }
62110
62564
 
62111
62565
  // src/hook/run.ts
62112
- import { appendFileSync as appendFileSync2, closeSync as closeSync3, existsSync as existsSync11, mkdirSync as mkdirSync8, openSync as openSync3 } from "node:fs";
62566
+ import { appendFileSync as appendFileSync2, closeSync as closeSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, openSync as openSync3 } from "node:fs";
62113
62567
  import { spawn } from "node:child_process";
62114
62568
  import { dirname as dirname9 } from "node:path";
62115
62569
  var HOOK_SOURCES = ["startup", "resume", "clear"];
@@ -62147,11 +62601,11 @@ function locateUpstream(ctx, state) {
62147
62601
  }
62148
62602
  function maintainWrapper(ctx, messages) {
62149
62603
  if (readInstallation(ctx.paths) === null) {
62150
- if (!existsSync11(ctx.paths.patchedBin)) {
62604
+ if (!existsSync12(ctx.paths.patchedBin)) {
62151
62605
  messages.push(`cxstatusline: the patched binary ${ctx.paths.patchedBin} is missing; Codex is running unpatched. Run \`cxstatusline install\`.`);
62152
62606
  return;
62153
62607
  }
62154
- if (!existsSync11(ctx.paths.patchedCodeModeHost)) {
62608
+ if (!existsSync12(ctx.paths.patchedCodeModeHost)) {
62155
62609
  messages.push(`cxstatusline: the required Code Mode host ${ctx.paths.patchedCodeModeHost} is missing; run \`cxstatusline install\` to restore it.`);
62156
62610
  return;
62157
62611
  }
@@ -62248,12 +62702,12 @@ function realHookDeps() {
62248
62702
  }
62249
62703
 
62250
62704
  // src/commands/doctor.ts
62251
- import { existsSync as existsSync13, lstatSync as lstatSync4, readFileSync as readFileSync12, readlinkSync as readlinkSync3 } from "node:fs";
62705
+ import { existsSync as existsSync14, lstatSync as lstatSync4, readFileSync as readFileSync12, readlinkSync as readlinkSync3 } from "node:fs";
62252
62706
 
62253
62707
  // src/commands/doctor-generation.ts
62254
62708
  import { createHash as createHash5 } from "node:crypto";
62255
- import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
62256
- import { dirname as dirname10, isAbsolute as isAbsolute3, join as join15, resolve as resolve4 } from "node:path";
62709
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
62710
+ import { dirname as dirname10, isAbsolute as isAbsolute3, join as join16, resolve as resolve4 } from "node:path";
62257
62711
  var line = (key, value, ok = null) => ({ key, value, ok });
62258
62712
  var NO_GENERATION = "n/a (no active generation)";
62259
62713
  function resolvePointerTarget(paths, raw) {
@@ -62298,7 +62752,9 @@ function cxVersionLine(record2) {
62298
62752
  if (!record2)
62299
62753
  return line("cx_version", NO_GENERATION, null);
62300
62754
  const match = record2.provenance.cxVersion === VERSION;
62301
- return line("cx_version", `running ${VERSION} vs installed ${record2.provenance.cxVersion}`, match);
62755
+ if (match)
62756
+ return line("cx_version", VERSION, true);
62757
+ return line("cx_version", `cli ${VERSION} vs generation ${record2.provenance.cxVersion}`, false);
62302
62758
  }
62303
62759
  function releaseLine(record2) {
62304
62760
  if (!record2)
@@ -62330,8 +62786,8 @@ function upstreamCommitLine(record2) {
62330
62786
  function digestLine(key, label, record2, dir) {
62331
62787
  if (!record2 || !dir)
62332
62788
  return line(label, NO_GENERATION, null);
62333
- const file2 = join15(dir, key);
62334
- if (!existsSync12(file2))
62789
+ const file2 = join16(dir, key);
62790
+ if (!existsSync13(file2))
62335
62791
  return line(label, "missing", false);
62336
62792
  let buf;
62337
62793
  try {
@@ -62348,8 +62804,8 @@ function digestLine(key, label, record2, dir) {
62348
62804
  function codexVersionLine(ctx, record2, dir) {
62349
62805
  if (!record2 || !dir)
62350
62806
  return line("codex_version", NO_GENERATION, null);
62351
- const bin = join15(dir, "codex");
62352
- if (!existsSync12(bin))
62807
+ const bin = join16(dir, "codex");
62808
+ if (!existsSync13(bin))
62353
62809
  return line("codex_version", "missing", false);
62354
62810
  let result2;
62355
62811
  try {
@@ -62371,7 +62827,7 @@ function legalLine(record2, dir) {
62371
62827
  return line("legal", "n/a (compiled build)", null);
62372
62828
  if (!dir)
62373
62829
  return line("legal", NO_GENERATION, null);
62374
- const missing = GENERATION_LEGAL_FILES.filter((f2) => !existsSync12(join15(dir, f2)));
62830
+ const missing = GENERATION_LEGAL_FILES.filter((f2) => !existsSync13(join16(dir, f2)));
62375
62831
  return missing.length === 0 ? line("legal", "present", true) : line("legal", `missing: ${missing.join(", ")}`, false);
62376
62832
  }
62377
62833
  function generationDetailLines(ctx, status) {
@@ -62404,11 +62860,136 @@ function bookkeepingLine(state, record2) {
62404
62860
  function legacyLine(paths, record2) {
62405
62861
  if (record2 !== null)
62406
62862
  return null;
62407
- if (!existsSync12(paths.patchedBin) && !existsSync12(paths.patchedCodeModeHost))
62863
+ if (!existsSync13(paths.patchedBin) && !existsSync13(paths.patchedCodeModeHost))
62408
62864
  return null;
62409
62865
  return line("legacy", "legacy layout: run cxstatusline revert then cxstatusline install", false);
62410
62866
  }
62411
62867
 
62868
+ // src/commands/doctor-format.ts
62869
+ var SECTIONS = [
62870
+ {
62871
+ title: "Core & Environment",
62872
+ keys: ["renderer", "settings", "platform", "toolchain"]
62873
+ },
62874
+ {
62875
+ title: "Codex Integration",
62876
+ keys: ["upstream", "wrapper", "hook", "policy", "drift"]
62877
+ },
62878
+ {
62879
+ title: "Active Generation & Binaries",
62880
+ keys: [
62881
+ "active",
62882
+ "generation",
62883
+ "cx_version",
62884
+ "release",
62885
+ "patch",
62886
+ "source_commit",
62887
+ "upstream_commit",
62888
+ "codex_digest",
62889
+ "host_digest",
62890
+ "codex_version",
62891
+ "legal",
62892
+ "bookkeeping",
62893
+ "legacy"
62894
+ ]
62895
+ },
62896
+ {
62897
+ title: "State & Locks",
62898
+ keys: ["state", "patched_from", "last_attempt", "lock"]
62899
+ }
62900
+ ];
62901
+ function formatValue(raw, ok) {
62902
+ const value = String(raw ?? "");
62903
+ if (ok === false)
62904
+ return source_default.red(value);
62905
+ if (value === "absent" || value === "none" || value === "never" || value.startsWith("n/a")) {
62906
+ return source_default.dim(value);
62907
+ }
62908
+ if (value === "free" || value === "ours" || value === "darwin-arm64" || value === "darwin-x64") {
62909
+ return source_default.green(value);
62910
+ }
62911
+ return source_default.white(value);
62912
+ }
62913
+ function statusBullet(ok) {
62914
+ if (ok === true)
62915
+ return symbols.ok;
62916
+ if (ok === false)
62917
+ return symbols.fail;
62918
+ return symbols.dimBullet;
62919
+ }
62920
+ function renderSummary(lines) {
62921
+ const failures = lines.filter((l) => l.ok === false);
62922
+ const driftLine = lines.find((l) => l.key === "drift");
62923
+ if (failures.length > 0) {
62924
+ const count = failures.length;
62925
+ const word = count === 1 ? "issue" : "issues";
62926
+ const detail = driftLine && driftLine.ok === false ? "Run `cxstatusline install` to update to the current Codex version." : "Check the failed items above for instructions.";
62927
+ return renderBox([
62928
+ `${symbols.fail} ${source_default.bold.red(`${count} ${word} detected`)}`,
62929
+ ` ${source_default.dim(detail)}`
62930
+ ], { borderColor: source_default.red, minWidth: 68 });
62931
+ }
62932
+ return renderBox([
62933
+ `${symbols.ok} ${source_default.bold.green("All systems operational")}`,
62934
+ ` ${source_default.dim("Statusline is configured and active for OpenAI Codex.")}`
62935
+ ], { borderColor: source_default.green, minWidth: 68 });
62936
+ }
62937
+ function singleLineSummary(text) {
62938
+ const lines = text.split(`
62939
+ `).map((l) => l.trim()).filter(Boolean);
62940
+ if (lines.length <= 1)
62941
+ return text;
62942
+ const match = /^(failed\s+\S+\s+at\s+[0-9T:.-]+Z?):\s*(.*)/.exec(lines[0]);
62943
+ const prefix = match ? match[1] : lines[0].split(":")[0] ?? "failed";
62944
+ const errorLine = lines.find((l) => /^error(\[[A-Z0-9]+\])?:/i.test(l)) ?? lines.find((l) => /^failed to/i.test(l)) ?? lines.find((l) => /panic/i.test(l)) ?? lines[lines.length - 1];
62945
+ return `${prefix}: ${errorLine}`;
62946
+ }
62947
+ function formatDiagnosticLine(item, maxKeyWidth) {
62948
+ const bullet = statusBullet(item.ok);
62949
+ const key = source_default.cyan(item.key.padEnd(maxKeyWidth));
62950
+ const summary = singleLineSummary(String(item.value ?? ""));
62951
+ const val = formatValue(summary, item.ok);
62952
+ return ` ${bullet} ${key} ${val}`;
62953
+ }
62954
+ function formatDoctorPretty(lines) {
62955
+ const lineMap = new Map(lines.map((l) => [l.key, l]));
62956
+ const seen = new Set;
62957
+ const maxKeyWidth = lines.reduce((max2, l) => Math.max(max2, l.key.length), 0);
62958
+ const parts = [
62959
+ renderHeader("cxstatusline doctor", "Diagnostics & System Health", `v${VERSION}`),
62960
+ ""
62961
+ ];
62962
+ for (const section of SECTIONS) {
62963
+ const sectionLines = [];
62964
+ for (const key of section.keys) {
62965
+ const entry = lineMap.get(key);
62966
+ if (entry) {
62967
+ sectionLines.push(entry);
62968
+ seen.add(key);
62969
+ }
62970
+ }
62971
+ if (sectionLines.length === 0)
62972
+ continue;
62973
+ parts.push(renderSectionTitle(section.title));
62974
+ for (const item of sectionLines) {
62975
+ parts.push(formatDiagnosticLine(item, maxKeyWidth));
62976
+ }
62977
+ parts.push("");
62978
+ }
62979
+ const extra = lines.filter((l) => !seen.has(l.key));
62980
+ if (extra.length > 0) {
62981
+ parts.push(renderSectionTitle("Other Diagnostics"));
62982
+ for (const item of extra) {
62983
+ parts.push(formatDiagnosticLine(item, maxKeyWidth));
62984
+ }
62985
+ parts.push("");
62986
+ }
62987
+ parts.push(renderSummary(lines));
62988
+ return `${parts.join(`
62989
+ `)}
62990
+ `;
62991
+ }
62992
+
62412
62993
  // src/commands/doctor.ts
62413
62994
  var line2 = (key, value, ok = null) => ({ key, value, ok });
62414
62995
  function wrapperLine(ctx) {
@@ -62424,7 +63005,7 @@ function wrapperLine(ctx) {
62424
63005
  return isOurWrapper(p2) ? line2("wrapper", "ours", true) : line2("wrapper", "foreign file! not ours, not a symlink", false);
62425
63006
  }
62426
63007
  function hookLine(ctx) {
62427
- if (!existsSync13(ctx.paths.hooksFile))
63008
+ if (!existsSync14(ctx.paths.hooksFile))
62428
63009
  return line2("hook", "absent");
62429
63010
  try {
62430
63011
  const f2 = JSON.parse(readFileSync12(ctx.paths.hooksFile, "utf8"));
@@ -62480,7 +63061,7 @@ function doctorReport(ctx) {
62480
63061
  };
62481
63062
  const lines = [
62482
63063
  line2("renderer", `${ctx.cxBin} (${VERSION})`),
62483
- line2("settings", `${ctx.paths.settingsFile} ${existsSync13(ctx.paths.settingsFile) ? "present" : "absent (written on first render)"}`),
63064
+ line2("settings", `${ctx.paths.settingsFile} ${existsSync14(ctx.paths.settingsFile) ? "present" : "absent (written on first render)"}`),
62484
63065
  upstreamLine(ctx, upstreamBin, upstream?.raw ?? null, lookupNote),
62485
63066
  line2("state", corrupt ? `${ctx.paths.stateFile} CORRUPT (recovered from ${ctx.paths.stateBackupFile} where possible)` : ctx.paths.stateFile, corrupt ? false : null),
62486
63067
  line2("patched_from", state.patched_from ?? "never"),
@@ -62500,16 +63081,12 @@ function doctorReport(ctx) {
62500
63081
  return lines.filter((l) => l !== null);
62501
63082
  }
62502
63083
  function formatDoctor(lines) {
62503
- const mark = (ok) => ok === null ? " " : ok ? "✓" : "✗";
62504
- const width = Math.max(...lines.map((l) => l.key.length));
62505
- return `${lines.map((l) => `${mark(l.ok)} ${l.key.padEnd(width)} ${l.value}`).join(`
62506
- `)}
62507
- `;
63084
+ return formatDoctorPretty(lines);
62508
63085
  }
62509
63086
 
62510
63087
  // src/commands/revert.ts
62511
- import { existsSync as existsSync14, lstatSync as lstatSync5, readdirSync as readdirSync2, rmSync as rmSync6, symlinkSync as symlinkSync2 } from "node:fs";
62512
- import { join as join16 } from "node:path";
63088
+ import { existsSync as existsSync15, lstatSync as lstatSync5, readdirSync as readdirSync2, rmSync as rmSync6, symlinkSync as symlinkSync2 } from "node:fs";
63089
+ import { join as join17 } from "node:path";
62513
63090
  function safeIsSymlink(p2) {
62514
63091
  try {
62515
63092
  return lstatSync5(p2).isSymbolicLink();
@@ -62530,11 +63107,11 @@ function revert(ctx) {
62530
63107
  }
62531
63108
  function removeGenerations(ctx, actions) {
62532
63109
  const { currentGeneration, generationsDir } = ctx.paths;
62533
- if (existsSync14(currentGeneration) || safeIsSymlink(currentGeneration)) {
63110
+ if (existsSync15(currentGeneration) || safeIsSymlink(currentGeneration)) {
62534
63111
  rmSync6(currentGeneration, { recursive: true, force: true });
62535
63112
  actions.push(`removed generation pointer ${currentGeneration}`);
62536
63113
  }
62537
- if (!existsSync14(generationsDir))
63114
+ if (!existsSync15(generationsDir))
62538
63115
  return;
62539
63116
  let removed = 0;
62540
63117
  for (const dir of listGenerations(ctx.paths)) {
@@ -62552,11 +63129,11 @@ function removeGenerations(ctx, actions) {
62552
63129
  var OWNED_LIBEXEC_PREFIXES = ["current.", "staging-", "download-", "compiled-"];
62553
63130
  function removeStagingDebris(ctx, actions) {
62554
63131
  const { libexecDir } = ctx.paths;
62555
- if (!existsSync14(libexecDir))
63132
+ if (!existsSync15(libexecDir))
62556
63133
  return;
62557
63134
  const debris = readdirSync2(libexecDir).filter((n) => OWNED_LIBEXEC_PREFIXES.some((p2) => n.startsWith(p2)));
62558
63135
  for (const name of debris)
62559
- rmSync6(join16(libexecDir, name), { recursive: true, force: true });
63136
+ rmSync6(join17(libexecDir, name), { recursive: true, force: true });
62560
63137
  if (debris.length > 0) {
62561
63138
  actions.push(`removed ${debris.length} leftover staging entr${debris.length === 1 ? "y" : "ies"} from ${libexecDir}`);
62562
63139
  }
@@ -62568,7 +63145,7 @@ function revertLocked(ctx) {
62568
63145
  if (corrupt)
62569
63146
  actions.push(`state.json was corrupt; used ${ctx.paths.stateBackupFile} where possible`);
62570
63147
  const w2 = ctx.paths.wrapperPath;
62571
- if (existsSync14(w2) || safeIsSymlink(w2)) {
63148
+ if (existsSync15(w2) || safeIsSymlink(w2)) {
62572
63149
  if (isOurWrapper(w2)) {
62573
63150
  rmSync6(w2);
62574
63151
  actions.push(`removed wrapper ${w2}`);
@@ -62584,11 +63161,11 @@ function revertLocked(ctx) {
62584
63161
  }
62585
63162
  removeGenerations(ctx, actions);
62586
63163
  removeStagingDebris(ctx, actions);
62587
- if (existsSync14(ctx.paths.patchedBin)) {
63164
+ if (existsSync15(ctx.paths.patchedBin)) {
62588
63165
  rmSync6(ctx.paths.patchedBin);
62589
63166
  actions.push(`removed patched binary ${ctx.paths.patchedBin}`);
62590
63167
  }
62591
- if (existsSync14(ctx.paths.patchedCodeModeHost)) {
63168
+ if (existsSync15(ctx.paths.patchedCodeModeHost)) {
62592
63169
  rmSync6(ctx.paths.patchedCodeModeHost);
62593
63170
  actions.push(`removed Code Mode host ${ctx.paths.patchedCodeModeHost}`);
62594
63171
  }
@@ -64038,9 +64615,9 @@ function MainMenu({ onSelect, initialSelection = 0, hasChanges = false }) {
64038
64615
  }
64039
64616
 
64040
64617
  // src/tui/components/PowerlineSetup.tsx
64041
- import { existsSync as existsSync15, readdirSync as readdirSync3 } from "node:fs";
64618
+ import { existsSync as existsSync16, readdirSync as readdirSync3 } from "node:fs";
64042
64619
  import { homedir as homedir2, platform as platform2 } from "node:os";
64043
- import { join as join17 } from "node:path";
64620
+ import { join as join18 } from "node:path";
64044
64621
  var import_react40 = __toESM(require_react(), 1);
64045
64622
 
64046
64623
  // src/utils/powerline-settings.ts
@@ -64425,9 +65002,9 @@ function buildPowerlineSetupMenuItems(powerline) {
64425
65002
  ];
64426
65003
  }
64427
65004
  function detectPowerlineFont() {
64428
- const dirs = platform2() === "darwin" ? [join17(homedir2(), "Library", "Fonts"), "/Library/Fonts"] : platform2() === "win32" ? [join17(homedir2(), "AppData", "Local", "Microsoft", "Windows", "Fonts"), "C:\\Windows\\Fonts"] : [join17(homedir2(), ".local", "share", "fonts"), join17(homedir2(), ".fonts"), "/usr/share/fonts"];
65005
+ const dirs = platform2() === "darwin" ? [join18(homedir2(), "Library", "Fonts"), "/Library/Fonts"] : platform2() === "win32" ? [join18(homedir2(), "AppData", "Local", "Microsoft", "Windows", "Fonts"), "C:\\Windows\\Fonts"] : [join18(homedir2(), ".local", "share", "fonts"), join18(homedir2(), ".fonts"), "/usr/share/fonts"];
64429
65006
  try {
64430
- return dirs.some((dir) => existsSync15(dir) && readdirSync3(dir).some((file2) => /powerline|nerd font|meslo.*lg/i.test(file2)));
65007
+ return dirs.some((dir) => existsSync16(dir) && readdirSync3(dir).some((file2) => /powerline|nerd font|meslo.*lg/i.test(file2)));
64431
65008
  } catch {
64432
65009
  return false;
64433
65010
  }
@@ -64950,7 +65527,7 @@ var USAGE = `usage: cxstatusline [command]
64950
65527
  install [--compile] install the published Codex pair (--compile builds it from source)
64951
65528
  patch [--force] build and install the patched Codex from source
64952
65529
  patch --simulate-drift <v> record <v> as the installed version so the next session sees drift
64953
- update run upstream's own updater, then install the pair for it
65530
+ update [--compile|--force] run upstream's own updater, then install the pair for it
64954
65531
  hook [install|uninstall] manage the SessionStart entry; bare 'hook' is what Codex runs
64955
65532
  doctor report toolchain, drift, hook and wrapper state
64956
65533
  revert restore stock Codex; keep settings
@@ -64999,7 +65576,19 @@ async function installCommand(argv, io2, deps) {
64999
65576
  io2.stderr(USAGE);
65000
65577
  return 2;
65001
65578
  }
65002
- return runInstall(contextFor(io2, deps), { compile: flags[0] === "--compile" });
65579
+ return runInstall(contextFor(io2, deps), { compile: flags[0] === "--compile" }, deps.transport);
65580
+ }
65581
+ async function updateCommand(argv, io2, deps) {
65582
+ const flags = argv.slice(1);
65583
+ const allowed = new Set(["--compile", "--force", "-y", "--yes"]);
65584
+ if (flags.some((f2) => !allowed.has(f2))) {
65585
+ io2.stderr(USAGE);
65586
+ return 2;
65587
+ }
65588
+ return runUpdate(contextFor(io2, deps), {
65589
+ compile: flags.includes("--compile"),
65590
+ force: flags.includes("--force") || flags.includes("-y") || flags.includes("--yes")
65591
+ }, deps.transport);
65003
65592
  }
65004
65593
  async function hookAcquireCommand(io2, deps) {
65005
65594
  const ctx = contextFor(io2, deps);
@@ -65058,7 +65647,7 @@ async function main(argv, io2, deps = {}) {
65058
65647
  if (cmd === "patch")
65059
65648
  return patchCommand(argv, io2, deps);
65060
65649
  if (cmd === "update")
65061
- return runUpdate(contextFor(io2, deps));
65650
+ return updateCommand(argv, io2, deps);
65062
65651
  if (cmd === "install")
65063
65652
  return installCommand(argv, io2, deps);
65064
65653
  if (cmd === "hook" && (argv[1] === "install" || argv[1] === "uninstall")) {