cxstatusline 0.10.1 → 0.13.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.
@@ -27133,7 +27133,7 @@ var require_jsx_dev_runtime = __commonJS(function(exports, module) {
27133
27133
  });
27134
27134
 
27135
27135
  // src/cli.ts
27136
- import { readFileSync as readFileSync15 } from "node:fs";
27136
+ import { readFileSync as readFileSync16 } from "node:fs";
27137
27137
 
27138
27138
  // src/main.ts
27139
27139
  import { freemem, platform as platform3, totalmem } from "node:os";
@@ -56307,7 +56307,14 @@ function resolveLiveSpeedMetrics(context, windowSeconds) {
56307
56307
  totalTokens: 0,
56308
56308
  requestCount: 0
56309
56309
  },
56310
- lastActiveTimeMs: nowMs
56310
+ lastActiveTimeMs: nowMs,
56311
+ sessionCumulativeMetrics: {
56312
+ totalDurationMs: 0,
56313
+ inputTokens: 0,
56314
+ outputTokens: 0,
56315
+ totalTokens: 0,
56316
+ requestCount: 0
56317
+ }
56311
56318
  };
56312
56319
  writeCache(context.commandCacheDir, initialCache);
56313
56320
  return {
@@ -56329,18 +56336,29 @@ function resolveLiveSpeedMetrics(context, windowSeconds) {
56329
56336
  });
56330
56337
  let nextLastActiveMetrics = { ...cached2.lastActiveMetrics };
56331
56338
  let nextLastActiveTimeMs = cached2.lastActiveTimeMs;
56339
+ let nextSessionCumulative = cached2.sessionCumulativeMetrics ? { ...cached2.sessionCumulativeMetrics } : { ...cached2.lastActiveMetrics };
56332
56340
  let nextLastSample = cached2.lastSample;
56333
56341
  if ((deltaInput > 0 || deltaOutput > 0) && elapsedSinceLastSec >= 0.2) {
56334
56342
  const instInput = deltaInput > 0 ? deltaInput / elapsedSinceLastSec : 0;
56335
56343
  const instOutput = deltaOutput > 0 ? deltaOutput / elapsedSinceLastSec : 0;
56336
56344
  if (instOutput <= MAX_PLAUSIBLE_OUTPUT_SPEED && instInput <= MAX_PLAUSIBLE_INPUT_SPEED) {
56345
+ const deltaDurationMs = Math.round(elapsedSinceLastSec * 1000);
56346
+ const validDeltaIn = Math.max(0, deltaInput);
56347
+ const validDeltaOut = Math.max(0, deltaOutput);
56337
56348
  nextLastActiveMetrics = {
56338
- totalDurationMs: Math.round(elapsedSinceLastSec * 1000),
56339
- inputTokens: Math.max(0, deltaInput),
56340
- outputTokens: Math.max(0, deltaOutput),
56341
- totalTokens: Math.max(0, deltaInput) + Math.max(0, deltaOutput),
56349
+ totalDurationMs: deltaDurationMs,
56350
+ inputTokens: validDeltaIn,
56351
+ outputTokens: validDeltaOut,
56352
+ totalTokens: validDeltaIn + validDeltaOut,
56342
56353
  requestCount: cached2.lastActiveMetrics.requestCount + 1
56343
56354
  };
56355
+ nextSessionCumulative = {
56356
+ totalDurationMs: nextSessionCumulative.totalDurationMs + deltaDurationMs,
56357
+ inputTokens: nextSessionCumulative.inputTokens + validDeltaIn,
56358
+ outputTokens: nextSessionCumulative.outputTokens + validDeltaOut,
56359
+ totalTokens: nextSessionCumulative.totalTokens + validDeltaIn + validDeltaOut,
56360
+ requestCount: nextSessionCumulative.requestCount + 1
56361
+ };
56344
56362
  nextLastActiveTimeMs = nowMs;
56345
56363
  nextLastSample = {
56346
56364
  timeMs: nowMs,
@@ -56348,13 +56366,20 @@ function resolveLiveSpeedMetrics(context, windowSeconds) {
56348
56366
  outputTokens: rawOutput
56349
56367
  };
56350
56368
  }
56369
+ } else if (deltaInput === 0 && deltaOutput === 0 && (session?.run_state === "ready" || elapsedSinceLastSec >= 5)) {
56370
+ nextLastSample = {
56371
+ timeMs: nowMs,
56372
+ inputTokens: rawInput,
56373
+ outputTokens: rawOutput
56374
+ };
56351
56375
  }
56352
56376
  const updatedCache = {
56353
56377
  ...cached2,
56354
56378
  lastSample: nextLastSample,
56355
56379
  samples,
56356
56380
  lastActiveMetrics: nextLastActiveMetrics,
56357
- lastActiveTimeMs: nextLastActiveTimeMs
56381
+ lastActiveTimeMs: nextLastActiveTimeMs,
56382
+ sessionCumulativeMetrics: nextSessionCumulative
56358
56383
  };
56359
56384
  writeCache(context.commandCacheDir, updatedCache);
56360
56385
  if (windowSeconds && windowSeconds > 0) {
@@ -56382,7 +56407,10 @@ function resolveLiveSpeedMetrics(context, windowSeconds) {
56382
56407
  requestCount: 0
56383
56408
  };
56384
56409
  }
56385
- if (nextLastActiveTimeMs && nowMs - nextLastActiveTimeMs <= 60000 && nextLastActiveMetrics.totalDurationMs > 0) {
56410
+ if (nextSessionCumulative.totalDurationMs > 0) {
56411
+ return nextSessionCumulative;
56412
+ }
56413
+ if (nextLastActiveMetrics.totalDurationMs > 0) {
56386
56414
  return nextLastActiveMetrics;
56387
56415
  }
56388
56416
  return {
@@ -59007,8 +59035,8 @@ function runRefreshCommand(cacheKey2, deps) {
59007
59035
  return 0;
59008
59036
  }
59009
59037
  // src/version-info.ts
59010
- var VERSION = "0.10.1";
59011
- var SOURCE_COMMIT = /^[0-9a-f]{40}$/.test("cb2f6799d53ce95398331bae1cea4f96d2c9641e") ? "cb2f6799d53ce95398331bae1cea4f96d2c9641e" : null;
59038
+ var VERSION = "0.13.0";
59039
+ var SOURCE_COMMIT = /^[0-9a-f]{40}$/.test("4e5293425d1f37dde096fb5a49ebfd3b67fd62bb") ? "4e5293425d1f37dde096fb5a49ebfd3b67fd62bb" : null;
59012
59040
  var SOURCE_DIRTY = false;
59013
59041
 
59014
59042
  // src/context.ts
@@ -59018,8 +59046,81 @@ import { fileURLToPath } from "node:url";
59018
59046
 
59019
59047
  // src/patch/preflight.ts
59020
59048
  import { accessSync, constants as constants2, existsSync as existsSync2, statfsSync } from "node:fs";
59021
- import { dirname as dirname4, join as join7 } from "node:path";
59049
+ import { dirname as dirname4, join as join8 } from "node:path";
59022
59050
  import { spawnSync as spawnSync3 } from "node:child_process";
59051
+
59052
+ // src/patch/package-assemble.ts
59053
+ import { join as join7 } from "node:path";
59054
+ var PYTHON_CANDIDATES = ["python3.13", "python3.12", "python3.11", "python3.10", "python3"];
59055
+ var PYTHON_VERSION_CHECK = "import sys; sys.exit(sys.version_info < (3, 10))";
59056
+ function assembleArgs(input2) {
59057
+ const args = [
59058
+ join7(input2.upstream, "scripts", "build_codex_package.py"),
59059
+ "--variant",
59060
+ "codex",
59061
+ "--target",
59062
+ input2.target,
59063
+ "--package-version",
59064
+ input2.codexVersion,
59065
+ "--entrypoint-bin",
59066
+ input2.codex,
59067
+ "--code-mode-host-bin",
59068
+ input2.codeModeHost,
59069
+ "--cargo",
59070
+ input2.cargo,
59071
+ "--cargo-profile",
59072
+ "release",
59073
+ "--package-dir",
59074
+ input2.packageDir
59075
+ ];
59076
+ if (input2.bwrapBin)
59077
+ args.push("--bwrap-bin", input2.bwrapBin);
59078
+ if (input2.force)
59079
+ args.push("--force");
59080
+ return args;
59081
+ }
59082
+ function assembleEnv(input2, base2 = process.env) {
59083
+ const keep = ["PATH", "HOME", "CARGO_HOME", "RUSTUP_HOME", "HTTPS_PROXY", "HTTP_PROXY", "NO_PROXY", "https_proxy", "http_proxy", "no_proxy", "SSL_CERT_FILE", "RUSTC_WRAPPER", "SCCACHE_DIR"];
59084
+ const env3 = {};
59085
+ for (const key of keep)
59086
+ if (base2[key] !== undefined)
59087
+ env3[key] = base2[key];
59088
+ env3.CODEX_REPO_ROOT = input2.upstream;
59089
+ env3.TMPDIR = input2.tmpDir;
59090
+ return env3;
59091
+ }
59092
+ function assemblePackage(input2, run, log) {
59093
+ const args = assembleArgs(input2);
59094
+ log(`$ ${[input2.python, ...args].join(" ")}`);
59095
+ const r = run(input2.python, args, { cwd: input2.upstream, env: assembleEnv(input2) });
59096
+ for (const line of `${r.stdout}${r.stderr}`.split(`
59097
+ `).filter(Boolean))
59098
+ log(line);
59099
+ if (r.status !== 0) {
59100
+ throw new Error(`build_codex_package.py failed (exit ${String(r.status)}): ${(r.stderr || r.stdout).trim().split(`
59101
+ `).slice(-12).join(`
59102
+ `)}`);
59103
+ }
59104
+ }
59105
+ function findPython(which, run) {
59106
+ for (const candidate of PYTHON_CANDIDATES) {
59107
+ const bin = which(candidate);
59108
+ if (bin === null)
59109
+ continue;
59110
+ if (run(bin, ["-c", PYTHON_VERSION_CHECK]).status === 0)
59111
+ return bin;
59112
+ }
59113
+ return null;
59114
+ }
59115
+ function rustcHostTarget(run) {
59116
+ const r = run("rustc", ["-vV"]);
59117
+ const host = /^host:\s*(\S+)\s*$/m.exec(r.stdout)?.[1];
59118
+ if (r.status !== 0 || !host)
59119
+ throw new Error(`rustc -vV did not report a host target: ${(r.stderr || r.stdout).trim().slice(0, 200)}`);
59120
+ return host;
59121
+ }
59122
+
59123
+ // src/patch/preflight.ts
59023
59124
  var MIN_FREE_BYTES = 20 * 1024 ** 3;
59024
59125
  var MIN_STAGING_FREE_BYTES = 2 * 1024 ** 3;
59025
59126
  var REQUIRED_TOOLCHAIN = "1.95.0";
@@ -59056,6 +59157,15 @@ function preflight(deps, shareDir) {
59056
59157
  const toolchain = checkToolchain(deps);
59057
59158
  if (!toolchain.ok)
59058
59159
  return toolchain;
59160
+ if (findPython(deps.which, deps.run) === null) {
59161
+ return no("Python >= 3.10 is not on PATH; Codex's package is assembled by upstream's Python packager", "brew install python (or apt install python3), then reopen your shell");
59162
+ }
59163
+ if (process.platform === "linux" && !deps.which("bwrap")) {
59164
+ const libcap = deps.run("pkg-config", ["--exists", "libcap"]);
59165
+ if (!deps.which("pkg-config") || libcap.status !== 0) {
59166
+ return no("bubblewrap is not on PATH and libcap-dev is not installed; Codex's Linux package needs one of them for its sandbox", "apt install bubblewrap (or apt install libcap-dev pkg-config), then re-run");
59167
+ }
59168
+ }
59059
59169
  const free = deps.freeBytes(shareDir);
59060
59170
  if (free < MIN_FREE_BYTES) {
59061
59171
  return no(`${gib(free)} free at ${shareDir}; a Codex release build needs 20 GiB`, "free disk space or point XDG_DATA_HOME elsewhere");
@@ -59079,7 +59189,7 @@ function prebuiltPreflight(deps, paths) {
59079
59189
  }
59080
59190
  function whichReal(cmd) {
59081
59191
  for (const dir of (process.env.PATH ?? "").split(":").filter(Boolean)) {
59082
- const p = join7(dir, cmd);
59192
+ const p = join8(dir, cmd);
59083
59193
  try {
59084
59194
  accessSync(p, constants2.X_OK);
59085
59195
  return p;
@@ -59149,8 +59259,8 @@ function realContext(env3, io) {
59149
59259
  }
59150
59260
 
59151
59261
  // src/patch/run.ts
59152
- import { appendFileSync, mkdirSync as mkdirSync8, rmSync as rmSync6 } from "node:fs";
59153
- import { join as join18 } from "node:path";
59262
+ import { appendFileSync, mkdirSync as mkdirSync9, rmSync as rmSync6 } from "node:fs";
59263
+ import { join as join19 } from "node:path";
59154
59264
 
59155
59265
  // src/lock.ts
59156
59266
  import { closeSync, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync4, unlinkSync as unlinkSync3, writeSync } from "node:fs";
@@ -59204,11 +59314,6 @@ function acquireLock(file2, isAlive = pidAlive) {
59204
59314
  return null;
59205
59315
  }
59206
59316
 
59207
- // src/distribution/files.ts
59208
- var GENERATION_EXECUTABLES = ["codex", "codex-code-mode-host"];
59209
- var GENERATION_LEGAL_FILES = ["LICENSE", "NOTICE", "THIRD_PARTY_NOTICES.md"];
59210
- var ARTIFACT_FILES = [...GENERATION_EXECUTABLES, ...GENERATION_LEGAL_FILES];
59211
-
59212
59317
  // src/version.ts
59213
59318
  var POLICIES = ["every", "stable-minors", "manual"];
59214
59319
  var SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;
@@ -59258,20 +59363,95 @@ function behindWithinMinor(upstream, patched) {
59258
59363
  }
59259
59364
 
59260
59365
  // src/distribution/prebuilt.ts
59261
- import { createHash as createHash4 } from "node:crypto";
59262
- import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, mkdirSync as mkdirSync5, mkdtempSync, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync2 } from "node:fs";
59263
- import { join as join11 } from "node:path";
59366
+ import { chmodSync as chmodSync2, copyFileSync, mkdirSync as mkdirSync6, mkdtempSync, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync2 } from "node:fs";
59367
+ import { join as join12 } from "node:path";
59264
59368
 
59265
59369
  // src/patch/generation.ts
59266
- import { chmodSync, copyFileSync, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, readdirSync as readdirSync2, readlinkSync, realpathSync as realpathSync2, renameSync as renameSync2, rmSync, statSync, symlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
59267
- import { createHash as createHash2, randomBytes as randomBytes2 } from "node:crypto";
59268
- import { dirname as dirname6, isAbsolute, join as join8, resolve as resolve2, sep } from "node:path";
59269
- var INSTALLATION_FILE = "installation.json";
59270
- var SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
59370
+ import { chmodSync, cpSync, existsSync as existsSync5, lstatSync as lstatSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, readlinkSync, realpathSync as realpathSync2, renameSync as renameSync2, rmSync, statSync, symlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
59371
+ import { randomBytes as randomBytes2 } from "node:crypto";
59372
+ import { dirname as dirname6, isAbsolute, join as join9, relative, resolve as resolve2, sep } from "node:path";
59373
+
59374
+ // src/digest.ts
59375
+ import { createHash as createHash2 } from "node:crypto";
59376
+ import { createReadStream, readFileSync as readFileSync5 } from "node:fs";
59271
59377
  function digestOf(file2) {
59272
59378
  const buf = readFileSync5(file2);
59273
59379
  return { sha256: createHash2("sha256").update(buf).digest("hex"), size: buf.length };
59274
59380
  }
59381
+
59382
+ // src/patch/generation.ts
59383
+ var INSTALLATION_FILE = "installation.json";
59384
+ var GENERATION_EXECUTABLES = ["codex", "codex-code-mode-host"];
59385
+ var GENERATION_LEGAL_FILES = ["LICENSE", "NOTICE", "THIRD_PARTY_NOTICES.md"];
59386
+ var SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
59387
+ var ROOT_ALIAS = "codex";
59388
+ function isPackageLayout(dir) {
59389
+ return existsSync5(join9(dir, "bin", "codex")) && existsSync5(join9(dir, "codex-package.json"));
59390
+ }
59391
+ function executablePath(dir, name) {
59392
+ return isPackageLayout(dir) ? join9(dir, "bin", name) : join9(dir, name);
59393
+ }
59394
+ var PackageMetadata = exports_external.object({
59395
+ version: exports_external.string(),
59396
+ target: exports_external.string(),
59397
+ entrypoint: exports_external.string()
59398
+ }).passthrough();
59399
+ function verifyPackage(dir, files, expect) {
59400
+ for (const [name, want] of Object.entries(files)) {
59401
+ const file2 = join9(dir, name);
59402
+ let st;
59403
+ try {
59404
+ st = lstatSync3(file2);
59405
+ } catch {
59406
+ return `${name} is missing`;
59407
+ }
59408
+ if (!st.isFile())
59409
+ return `${name} is not a regular file`;
59410
+ const executable = (st.mode & 73) !== 0;
59411
+ if (executable !== (modeFor(name) === 493))
59412
+ return `${name} has the wrong executable bit`;
59413
+ const got = digestOf(file2);
59414
+ if (got.sha256 !== want.sha256 || got.size !== want.size)
59415
+ return `${name} does not match its recorded digest`;
59416
+ }
59417
+ let metadata;
59418
+ try {
59419
+ metadata = PackageMetadata.parse(JSON.parse(readFileSync6(join9(dir, "codex-package.json"), "utf8")));
59420
+ } catch (e) {
59421
+ return `codex-package.json is unreadable: ${e instanceof Error ? e.message.split(`
59422
+ `)[0] : String(e)}`;
59423
+ }
59424
+ if (metadata.entrypoint !== "bin/codex")
59425
+ return `codex-package.json entrypoint is ${JSON.stringify(metadata.entrypoint)}, not "bin/codex"`;
59426
+ if (metadata.target !== expect.target)
59427
+ return `codex-package.json target is ${metadata.target}, expected ${expect.target}`;
59428
+ if (metadata.version !== expect.codexVersion)
59429
+ return `codex-package.json version is ${metadata.version}, expected ${expect.codexVersion}`;
59430
+ return null;
59431
+ }
59432
+ function packageFiles(dir) {
59433
+ const out = {};
59434
+ const walk = (current) => {
59435
+ for (const name of readdirSync2(current).sort()) {
59436
+ const path = join9(current, name);
59437
+ const rel = relative(dir, path).split(sep).join("/");
59438
+ const st = lstatSync3(path);
59439
+ if (rel === INSTALLATION_FILE || rel === ROOT_ALIAS && st.isSymbolicLink())
59440
+ continue;
59441
+ if (st.isSymbolicLink())
59442
+ throw new Error(`${rel} is a symbolic link; a Codex package may only contain regular files`);
59443
+ if (st.isDirectory()) {
59444
+ walk(path);
59445
+ continue;
59446
+ }
59447
+ if (!st.isFile())
59448
+ throw new Error(`${rel} is not a regular file`);
59449
+ out[rel] = digestOf(path);
59450
+ }
59451
+ };
59452
+ walk(dir);
59453
+ return out;
59454
+ }
59275
59455
  function stamp(installedAt) {
59276
59456
  const parsed = new Date(installedAt);
59277
59457
  const when = Number.isNaN(parsed.getTime()) ? new Date : parsed;
@@ -59302,40 +59482,31 @@ function validateStaging(pair) {
59302
59482
  if (!SAFE_SEGMENT.test(pair.codexVersion)) {
59303
59483
  throw new Error(`prepared pair has an unusable codexVersion "${pair.codexVersion}"`);
59304
59484
  }
59305
- for (const name of GENERATION_EXECUTABLES) {
59306
- const file2 = join8(pair.directory, name);
59307
- if (!existsSync5(file2))
59308
- throw new Error(`staged pair is missing ${name}`);
59309
- const want = pair.provenance.executables[name];
59310
- const got = digestOf(file2);
59311
- if (got.sha256 !== want.sha256 || got.size !== want.size) {
59312
- throw new Error(`staged ${name} does not match its recorded digest; refusing to install it`);
59313
- }
59314
- }
59485
+ const problem = verifyPackage(pair.directory, pair.provenance.files, { target: pair.provenance.target, codexVersion: pair.codexVersion });
59486
+ if (problem !== null)
59487
+ throw new Error(`staged package is not the one its provenance describes: ${problem}; refusing to install it`);
59315
59488
  if (pair.provenance.source === "prebuilt") {
59316
59489
  for (const name of GENERATION_LEGAL_FILES) {
59317
- if (!existsSync5(join8(pair.directory, name)))
59490
+ if (!(name in pair.provenance.files) || !existsSync5(join9(pair.directory, name)))
59318
59491
  throw new Error(`prebuilt pair is missing ${name}`);
59319
59492
  }
59320
59493
  }
59494
+ for (const name of GENERATION_EXECUTABLES) {
59495
+ const want = pair.provenance.files[`bin/${name}`];
59496
+ if (!want)
59497
+ throw new Error(`staged pair is missing bin/${name}`);
59498
+ const got = digestOf(join9(pair.directory, "bin", name));
59499
+ if (got.sha256 !== want.sha256 || got.size !== want.size) {
59500
+ throw new Error(`staged bin/${name} does not match its recorded digest; refusing to install it`);
59501
+ }
59502
+ }
59321
59503
  }
59322
59504
  function installationRecord(pair) {
59323
59505
  const { directory: _staging, ...record2 } = pair;
59324
59506
  return record2;
59325
59507
  }
59326
- function copyInto(from, into, name, mode, expected) {
59327
- const target = join8(into, name);
59328
- copyFileSync(join8(from, name), target);
59329
- chmodSync(target, mode);
59330
- if (!expected)
59331
- return;
59332
- const got = digestOf(target);
59333
- if (got.sha256 !== expected.sha256 || got.size !== expected.size) {
59334
- throw new Error(`${name} did not survive the copy into the new generation intact`);
59335
- }
59336
- }
59337
59508
  function commitJson(file2, value, rename2) {
59338
- const tmp = join8(dirname6(file2), `.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`);
59509
+ const tmp = join9(dirname6(file2), `.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`);
59339
59510
  try {
59340
59511
  writeFileSync2(tmp, `${JSON.stringify(value, null, 2)}
59341
59512
  `);
@@ -59345,27 +59516,30 @@ function commitJson(file2, value, rename2) {
59345
59516
  throw e;
59346
59517
  }
59347
59518
  }
59348
- function freshDirectory(pair, paths) {
59519
+ var GENERATION_NAME = /^\d+\.\d+\.\d+-\d{8}T\d{6}-[0-9a-f]{6}$/;
59520
+ function freshName(pair, paths) {
59349
59521
  mkdirSync4(paths.generationsDir, { recursive: true });
59350
59522
  for (;; ) {
59351
- const dir = join8(paths.generationsDir, `${pair.codexVersion}-${stamp(pair.provenance.installedAt)}-${randomBytes2(3).toString("hex")}`);
59352
- if (existsSync5(dir))
59353
- continue;
59354
- mkdirSync4(dir);
59355
- return dir;
59523
+ const dir = join9(paths.generationsDir, `${pair.codexVersion}-${stamp(pair.provenance.installedAt)}-${randomBytes2(3).toString("hex")}`);
59524
+ if (!existsSync5(dir))
59525
+ return dir;
59356
59526
  }
59357
59527
  }
59358
59528
  function createGeneration(pair, paths, rename2 = renameSync2) {
59359
59529
  validateStaging(pair);
59360
- const dir = freshDirectory(pair, paths);
59530
+ const dir = freshName(pair, paths);
59361
59531
  try {
59362
- for (const name of GENERATION_EXECUTABLES)
59363
- copyInto(pair.directory, dir, name, 493, pair.provenance.executables[name]);
59364
- for (const name of GENERATION_LEGAL_FILES) {
59365
- if (existsSync5(join8(pair.directory, name)))
59366
- copyInto(pair.directory, dir, name, 420);
59367
- }
59368
- commitJson(join8(dir, INSTALLATION_FILE), installationRecord(pair), rename2);
59532
+ rename2(pair.directory, dir);
59533
+ } catch (e) {
59534
+ if (e.code !== "EXDEV")
59535
+ throw e;
59536
+ cpSync(pair.directory, dir, { recursive: true, verbatimSymlinks: true });
59537
+ rmSync(pair.directory, { recursive: true, force: true });
59538
+ }
59539
+ try {
59540
+ chmodSync(dir, 493);
59541
+ symlinkSync("bin/codex", join9(dir, ROOT_ALIAS));
59542
+ commitJson(join9(dir, INSTALLATION_FILE), installationRecord(pair), rename2);
59369
59543
  } catch (e) {
59370
59544
  rmSync(dir, { recursive: true, force: true });
59371
59545
  throw e;
@@ -59373,11 +59547,11 @@ function createGeneration(pair, paths, rename2 = renameSync2) {
59373
59547
  return dir;
59374
59548
  }
59375
59549
  function isGenerationDir(dir) {
59376
- return existsSync5(join8(dir, INSTALLATION_FILE)) && existsSync5(join8(dir, "codex"));
59550
+ return existsSync5(join9(dir, INSTALLATION_FILE)) && isPackageLayout(dir);
59377
59551
  }
59378
59552
  function listGenerations(paths) {
59379
59553
  try {
59380
- return readdirSync2(paths.generationsDir).map((name) => join8(paths.generationsDir, name)).sort();
59554
+ return readdirSync2(paths.generationsDir).map((name) => join9(paths.generationsDir, name)).sort();
59381
59555
  } catch {
59382
59556
  return [];
59383
59557
  }
@@ -59441,7 +59615,9 @@ var RecordSchema = exports_external.object({
59441
59615
  sourceCommit: exports_external.string().regex(HEX40, "sourceCommit must be 40 lowercase hex chars").nullable(),
59442
59616
  sourceDirty: exports_external.boolean(),
59443
59617
  installedAt: exports_external.string(),
59444
- executables: exports_external.object({ codex: DigestSchema, "codex-code-mode-host": DigestSchema }),
59618
+ target: exports_external.string().optional(),
59619
+ files: exports_external.record(exports_external.string(), DigestSchema).optional(),
59620
+ executables: exports_external.object({ codex: DigestSchema, "codex-code-mode-host": DigestSchema }).optional(),
59445
59621
  release: exports_external.object({
59446
59622
  tag: exports_external.string(),
59447
59623
  archiveSha256: exports_external.string().regex(HEX64, "archiveSha256 must be 64 lowercase hex chars"),
@@ -59475,7 +59651,7 @@ function readInstallation(paths) {
59475
59651
  if (dir === null)
59476
59652
  return null;
59477
59653
  try {
59478
- const raw = JSON.parse(readFileSync5(join8(dir, INSTALLATION_FILE), "utf8"));
59654
+ const raw = JSON.parse(readFileSync6(join9(dir, INSTALLATION_FILE), "utf8"));
59479
59655
  return checkedRecord(raw);
59480
59656
  } catch {
59481
59657
  return null;
@@ -59483,8 +59659,8 @@ function readInstallation(paths) {
59483
59659
  }
59484
59660
 
59485
59661
  // src/distribution/archive.ts
59486
- import { createReadStream, createWriteStream } from "node:fs";
59487
- import { join as join9 } from "node:path";
59662
+ import { createReadStream as createReadStream2, createWriteStream, mkdirSync as mkdirSync5 } from "node:fs";
59663
+ import { dirname as dirname7, join as join10 } from "node:path";
59488
59664
  import { createGunzip } from "node:zlib";
59489
59665
 
59490
59666
  // node_modules/tar/dist/esm/index.min.js
@@ -62710,13 +62886,12 @@ var To = (s3) => {
62710
62886
  };
62711
62887
 
62712
62888
  // src/distribution/archive.ts
62713
- var ALLOWED = ARTIFACT_FILES;
62714
62889
  var EXTRACTED_TOTAL_LIMIT = 2 * 1024 * 1024 * 1024;
62715
62890
  var LEGAL_TOTAL_LIMIT = 16 * 1024 * 1024;
62716
62891
  var EXECUTABLE_MODE = 493;
62717
62892
  var LEGAL_MODE = 420;
62718
62893
  function isExecutable(name) {
62719
- return GENERATION_EXECUTABLES.includes(name);
62894
+ return modeFor(name) === 493;
62720
62895
  }
62721
62896
  function quoteName(name) {
62722
62897
  return JSON.stringify(name.replace(/[\u0000-\u001f\u007f]/g, "?").slice(0, 120));
@@ -62724,13 +62899,13 @@ function quoteName(name) {
62724
62899
  function toError(cause) {
62725
62900
  return cause instanceof Error ? cause : new Error(String(cause));
62726
62901
  }
62727
- function rejectionReason(entry, seen, budget) {
62902
+ function rejectionReason(entry, seen, budget, expected) {
62728
62903
  const name = entry.path;
62729
62904
  if (entry.type !== "File") {
62730
62905
  return `archive entry ${quoteName(name)} is a ${entry.type}, not a regular file`;
62731
62906
  }
62732
- if (!ALLOWED.includes(name) || entry.header.path !== name) {
62733
- return `archive entry ${quoteName(name)} is not one of the five expected files`;
62907
+ if (!Object.hasOwn(expected, name) || entry.header.path !== name) {
62908
+ return `archive entry ${quoteName(name)} is not listed in the release manifest`;
62734
62909
  }
62735
62910
  if (seen.has(name))
62736
62911
  return `archive contains ${quoteName(name)} more than once`;
@@ -62739,6 +62914,9 @@ function rejectionReason(entry, seen, budget) {
62739
62914
  const size2 = entry.size;
62740
62915
  if (!Number.isSafeInteger(size2) || size2 < 0)
62741
62916
  return `archive entry ${quoteName(name)} declares an unusable size`;
62917
+ if (size2 !== expected[name].size) {
62918
+ return `archive entry ${quoteName(name)} declares ${size2} bytes but the manifest records ${expected[name].size}`;
62919
+ }
62742
62920
  const executable = isExecutable(name);
62743
62921
  if (executable && size2 === 0)
62744
62922
  return `archive entry ${quoteName(name)} is an empty executable`;
@@ -62756,10 +62934,13 @@ function rejectionReason(entry, seen, budget) {
62756
62934
  budget.legal += size2;
62757
62935
  return null;
62758
62936
  }
62759
- function writeEntry(entry, staging) {
62937
+ function writeEntry(entry, staging, expected) {
62760
62938
  const name = entry.path;
62761
62939
  const mode = isExecutable(name) ? EXECUTABLE_MODE : LEGAL_MODE;
62762
- const sink = createWriteStream(join9(staging, name), { mode, flags: "wx" });
62940
+ const target = join10(staging, name);
62941
+ if (name.includes("/"))
62942
+ mkdirSync5(dirname7(target), { recursive: true });
62943
+ const sink = createWriteStream(target, { mode, flags: "wx" });
62763
62944
  const done = new Promise((resolve3, reject2) => {
62764
62945
  sink.on("error", reject2);
62765
62946
  entry.on("error", reject2);
@@ -62780,11 +62961,11 @@ function writeEntry(entry, staging) {
62780
62961
  }
62781
62962
  };
62782
62963
  }
62783
- function parseArchive(archivePath, staging, writes) {
62964
+ function parseArchive(archivePath, staging, writes, expected) {
62784
62965
  const seen = new Set;
62785
62966
  const budget = { total: 0, legal: 0 };
62786
62967
  return new Promise((resolve3, reject2) => {
62787
- const source = createReadStream(archivePath);
62968
+ const source = createReadStream2(archivePath);
62788
62969
  const gunzip = createGunzip();
62789
62970
  let settled = false;
62790
62971
  const fail = (cause) => {
@@ -62804,14 +62985,14 @@ function parseArchive(archivePath, staging, writes) {
62804
62985
  entry.resume();
62805
62986
  return;
62806
62987
  }
62807
- const reason = rejectionReason(entry, seen, budget);
62988
+ const reason = rejectionReason(entry, seen, budget, expected);
62808
62989
  if (reason !== null) {
62809
62990
  fail(new Error(reason));
62810
62991
  entry.resume();
62811
62992
  return;
62812
62993
  }
62813
62994
  seen.add(entry.path);
62814
- writes.push(writeEntry(entry, staging));
62995
+ writes.push(writeEntry(entry, staging, expected));
62815
62996
  });
62816
62997
  parser.on("error", fail);
62817
62998
  gunzip.on("error", fail);
@@ -62820,7 +63001,7 @@ function parseArchive(archivePath, staging, writes) {
62820
63001
  if (settled)
62821
63002
  return;
62822
63003
  settled = true;
62823
- const missing = ALLOWED.filter((name) => !seen.has(name));
63004
+ const missing = Object.keys(expected).filter((name) => !seen.has(name));
62824
63005
  if (missing.length > 0) {
62825
63006
  reject2(new Error(`archive is missing ${missing.join(", ")}`));
62826
63007
  return;
@@ -62830,10 +63011,11 @@ function parseArchive(archivePath, staging, writes) {
62830
63011
  source.pipe(gunzip).pipe(parser);
62831
63012
  });
62832
63013
  }
62833
- async function extractArchive(archivePath, staging) {
63014
+ async function extractArchive(archivePath, staging, files) {
63015
+ const expected = files;
62834
63016
  const writes = [];
62835
63017
  try {
62836
- await parseArchive(archivePath, staging, writes);
63018
+ await parseArchive(archivePath, staging, writes, expected);
62837
63019
  } catch (e) {
62838
63020
  await Promise.allSettled(writes.map((w2) => w2.done));
62839
63021
  throw e;
@@ -62843,9 +63025,9 @@ async function extractArchive(archivePath, staging) {
62843
63025
 
62844
63026
  // src/distribution/transport.ts
62845
63027
  import { createHash as createHash3 } from "node:crypto";
62846
- import { createReadStream as createReadStream2, existsSync as existsSync6, renameSync as renameSync3 } from "node:fs";
63028
+ import { createReadStream as createReadStream3, existsSync as existsSync6, renameSync as renameSync3 } from "node:fs";
62847
63029
  import { open as open2 } from "node:fs/promises";
62848
- import { dirname as dirname7, join as join10 } from "node:path";
63030
+ import { dirname as dirname8, join as join11 } from "node:path";
62849
63031
  var PUBLIC_BASE = "https://github.com/adrijshikhar/cxstatusline/releases/download";
62850
63032
  var REPO = "adrijshikhar/cxstatusline";
62851
63033
  var MAX_REDIRECTS = 5;
@@ -62893,7 +63075,7 @@ function nextUrl(current, location, allowHttp, asset) {
62893
63075
  async function hashExistingFile(file2, maxBytes, asset) {
62894
63076
  const hash2 = createHash3("sha256");
62895
63077
  let size2 = 0;
62896
- for await (const chunk2 of createReadStream2(file2)) {
63078
+ for await (const chunk2 of createReadStream3(file2)) {
62897
63079
  const buf = chunk2;
62898
63080
  size2 += buf.length;
62899
63081
  if (size2 > maxBytes)
@@ -63091,7 +63273,7 @@ async function ghDownload(ctx, tag, asset, dest, maxBytes) {
63091
63273
  if (ctx.which("gh") === null) {
63092
63274
  throw new GhMissingError(`${asset} is not public for release ${tag} and GitHub CLI (gh) is not installed`);
63093
63275
  }
63094
- const targetDir = dirname7(dest);
63276
+ const targetDir = dirname8(dest);
63095
63277
  const result2 = ctx.run("gh", ["release", "download", tag, "--repo", REPO, "--pattern", asset, "--dir", targetDir], { timeoutMs: GH_TIMEOUT_MS });
63096
63278
  if (result2.status !== 0) {
63097
63279
  const detail = sanitize(result2.stderr || result2.stdout);
@@ -63100,7 +63282,7 @@ async function ghDownload(ctx, tag, asset, dest, maxBytes) {
63100
63282
  }
63101
63283
  throw new ReleaseUnavailableError(`release ${tag} not found or access denied: ${detail}`);
63102
63284
  }
63103
- const downloadedFile = join10(targetDir, asset);
63285
+ const downloadedFile = join11(targetDir, asset);
63104
63286
  if (!existsSync6(downloadedFile) && !existsSync6(dest)) {
63105
63287
  throw new ReleaseUnavailableError(`release ${tag} not found or access denied: gh downloaded no ${asset}`);
63106
63288
  }
@@ -63120,16 +63302,9 @@ async function downloadAsset(ctx, tag, asset, dest, maxBytes, opts = {}) {
63120
63302
  // src/distribution/prebuilt.ts
63121
63303
  var MANIFEST_ASSET = "manifest.json";
63122
63304
  var PROBE_TIMEOUT_MS = 30000;
63123
- function digestOf2(file2) {
63124
- const bytes = readFileSync6(file2);
63125
- return { sha256: createHash4("sha256").update(bytes).digest("hex"), size: bytes.length };
63126
- }
63127
- function sameDigest(a, b2) {
63128
- return a.sha256 === b2.sha256 && a.size === b2.size;
63129
- }
63130
63305
  function privateTemp(ctx, prefix) {
63131
- mkdirSync5(ctx.paths.libexecDir, { recursive: true });
63132
- const dir = mkdtempSync(join11(ctx.paths.libexecDir, prefix));
63306
+ mkdirSync6(ctx.paths.libexecDir, { recursive: true });
63307
+ const dir = mkdtempSync(join12(ctx.paths.libexecDir, prefix));
63133
63308
  chmodSync2(dir, 448);
63134
63309
  return dir;
63135
63310
  }
@@ -63153,39 +63328,29 @@ function unchangedPair(ctx, manifest, artifact, tag) {
63153
63328
  const directory = activeGeneration(ctx.paths);
63154
63329
  if (directory === null)
63155
63330
  return null;
63156
- for (const name of ARTIFACT_FILES) {
63157
- try {
63158
- if (!sameDigest(digestOf2(join11(directory, name)), artifact.files[name]))
63159
- return null;
63160
- } catch {
63161
- return null;
63162
- }
63163
- }
63331
+ if (record2.provenance.files === undefined || !isPackageLayout(directory))
63332
+ return null;
63333
+ if (verifyPackage(directory, artifact.files, { target: codexTarget(artifact.platform), codexVersion: manifest.codexVersion }) !== null)
63334
+ return null;
63164
63335
  return { directory, codexVersion: record2.codexVersion, provenance: record2.provenance };
63165
63336
  }
63166
- function verifyStaged(staging, artifact) {
63167
- const digests = {};
63168
- for (const name of ARTIFACT_FILES) {
63169
- const got = digestOf2(join11(staging, name));
63170
- if (!sameDigest(got, artifact.files[name])) {
63171
- throw new Error(`staged ${name} does not match the release manifest sha256`);
63172
- }
63173
- digests[name] = got;
63337
+ function verifyStaged(staging, manifest, artifact) {
63338
+ if (manifest.schema !== 3) {
63339
+ throw new Error(`release ${releaseTag(manifest.codexVersion)} predates cxstatusline 0.11 and has not been republished yet; use --compile or wait`);
63174
63340
  }
63175
- return {
63176
- codex: digests.codex,
63177
- "codex-code-mode-host": digests["codex-code-mode-host"]
63178
- };
63341
+ const problem = verifyPackage(staging, artifact.files, { target: codexTarget(artifact.platform), codexVersion: manifest.codexVersion });
63342
+ if (problem !== null)
63343
+ throw new Error(`staged package does not match the release manifest: ${problem}`);
63179
63344
  }
63180
63345
  function probeVersion(ctx, staging, codexVersion) {
63181
- const probe = ctx.run(join11(staging, "codex"), ["--version"], { timeoutMs: PROBE_TIMEOUT_MS });
63346
+ const probe = ctx.run(join12(staging, "bin", "codex"), ["--version"], { timeoutMs: PROBE_TIMEOUT_MS });
63182
63347
  const reported = sanitize((probe.stdout || probe.stderr).slice(0, 512));
63183
63348
  if (probe.status !== 0 || reported !== `codex-cli ${codexVersion}`) {
63184
63349
  throw new Error(`staged codex reported version "${reported}" instead of "codex-cli ${codexVersion}"`);
63185
63350
  }
63186
63351
  }
63187
63352
  function stagedPair(ctx, staging, manifest, artifact, tag) {
63188
- const executables = verifyStaged(staging, artifact);
63353
+ verifyStaged(staging, manifest, artifact);
63189
63354
  probeVersion(ctx, staging, manifest.codexVersion);
63190
63355
  return {
63191
63356
  directory: staging,
@@ -63194,13 +63359,14 @@ function stagedPair(ctx, staging, manifest, artifact, tag) {
63194
63359
  source: "prebuilt",
63195
63360
  cxVersion: manifest.cxVersion ?? VERSION,
63196
63361
  platform: artifact.platform,
63362
+ target: codexTarget(artifact.platform),
63197
63363
  patchVersion: manifest.patchVersion,
63198
63364
  patchSha256: manifest.patchSha256,
63199
63365
  upstreamCommit: manifest.upstreamCommit,
63200
63366
  sourceCommit: manifest.sourceCommit,
63201
63367
  sourceDirty: false,
63202
63368
  installedAt: ctx.now().toISOString(),
63203
- executables,
63369
+ files: artifact.files,
63204
63370
  release: { tag, archiveSha256: artifact.sha256, manifest }
63205
63371
  }
63206
63372
  };
@@ -63210,7 +63376,7 @@ function moveFile(src, dst) {
63210
63376
  renameSync4(src, dst);
63211
63377
  } catch (e) {
63212
63378
  if (typeof e === "object" && e !== null && e.code === "EXDEV") {
63213
- copyFileSync2(src, dst);
63379
+ copyFileSync(src, dst);
63214
63380
  rmSync2(src, { force: true });
63215
63381
  } else {
63216
63382
  throw e;
@@ -63219,9 +63385,9 @@ function moveFile(src, dst) {
63219
63385
  }
63220
63386
  async function stageArchive(ctx, download, manifest, artifact, tag, opts) {
63221
63387
  const cacheDir = ctx.paths.downloadCacheDir;
63222
- mkdirSync5(cacheDir, { recursive: true });
63223
- const partFile = join11(cacheDir, `${tag}-${artifact.filename}.part`);
63224
- const archive = join11(download, artifact.filename);
63388
+ mkdirSync6(cacheDir, { recursive: true });
63389
+ const partFile = join12(cacheDir, `${tag}-${artifact.filename}.part`);
63390
+ const archive = join12(download, artifact.filename);
63225
63391
  opts.onStatus?.("download", `Downloading prebuilt archive (${artifact.filename})...`);
63226
63392
  const got = await downloadAsset(ctx, tag, artifact.filename, partFile, ARCHIVE_MAX_BYTES, opts);
63227
63393
  if (got.sha256 !== artifact.sha256 || got.size !== artifact.size) {
@@ -63233,7 +63399,7 @@ async function stageArchive(ctx, download, manifest, artifact, tag, opts) {
63233
63399
  const staging = privateTemp(ctx, "staging-");
63234
63400
  try {
63235
63401
  opts.onStatus?.("extract", "Extracting and verifying executables...");
63236
- await extractArchive(archive, staging);
63402
+ await extractArchive(archive, staging, artifact.files);
63237
63403
  const pair = stagedPair(ctx, staging, manifest, artifact, tag);
63238
63404
  opts.onStatus?.("extract-done", "Verified executables (codex, codex-code-mode-host)");
63239
63405
  return pair;
@@ -63247,9 +63413,9 @@ async function preparePrebuilt(ctx, expected, opts = {}) {
63247
63413
  opts.onStatus?.("manifest", `Checking release ${tag} for ${expected.platform}...`);
63248
63414
  const download = privateTemp(ctx, "download-");
63249
63415
  try {
63250
- const manifestFile = join11(download, MANIFEST_ASSET);
63416
+ const manifestFile = join12(download, MANIFEST_ASSET);
63251
63417
  await downloadAsset(ctx, tag, MANIFEST_ASSET, manifestFile, MANIFEST_MAX_BYTES, opts);
63252
- const manifest = validateManifest(JSON.parse(readFileSync6(manifestFile, "utf8")), expected);
63418
+ const manifest = validateManifest(JSON.parse(readFileSync7(manifestFile, "utf8")), expected);
63253
63419
  const artifact = artifactFor(manifest, expected);
63254
63420
  opts.onStatus?.("manifest-done", `Found release for Codex ${manifest.codexVersion} (${artifact.platform})`);
63255
63421
  const unchanged = unchangedPair(ctx, manifest, artifact, tag);
@@ -63316,6 +63482,28 @@ var PLATFORMS = [
63316
63482
  "linux-arm64"
63317
63483
  ];
63318
63484
  var ARCHIVE_PREFIX = "cxstatusline-codex";
63485
+ function codexTarget(platform2) {
63486
+ switch (platform2) {
63487
+ case "darwin-arm64":
63488
+ return "aarch64-apple-darwin";
63489
+ case "darwin-x64":
63490
+ return "x86_64-apple-darwin";
63491
+ case "linux-x64":
63492
+ return "x86_64-unknown-linux-gnu";
63493
+ case "linux-arm64":
63494
+ return "aarch64-unknown-linux-gnu";
63495
+ }
63496
+ }
63497
+ var PACKAGE_PATH = /^(bin|codex-path|codex-resources)(\/[A-Za-z0-9][A-Za-z0-9._+-]*){1,4}$/;
63498
+ var PACKAGE_ROOT_FILES = ["codex-package.json", "LICENSE", "NOTICE", "THIRD_PARTY_NOTICES.md"];
63499
+ var PACKAGE_MAX_FILES = 64;
63500
+ function packageRequiredFiles(platform2) {
63501
+ const base2 = ["codex-package.json", "bin/codex", "bin/codex-code-mode-host", "codex-path/rg", "LICENSE", "NOTICE", "THIRD_PARTY_NOTICES.md"];
63502
+ return platform2.startsWith("linux-") ? [...base2, "codex-resources/bwrap"] : base2;
63503
+ }
63504
+ function modeFor(path) {
63505
+ return /^(bin|codex-path|codex-resources)\//.test(path) ? 493 : 420;
63506
+ }
63319
63507
  var HEX402 = /^[0-9a-f]{40}$/;
63320
63508
  var HEX642 = /^[0-9a-f]{64}$/;
63321
63509
  var ISO_8601 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
@@ -63347,8 +63535,7 @@ var FileDigestSchema = exports_external.object({
63347
63535
  sha256: exports_external.string().regex(HEX642, "sha256 must be 64 lowercase hex chars"),
63348
63536
  size: exports_external.number().refine(safePositiveInt, "size must be a finite positive safe integer")
63349
63537
  }).strict();
63350
- var filesShape = Object.fromEntries(ARTIFACT_FILES.map((k2) => [k2, FileDigestSchema]));
63351
- var FilesSchema = exports_external.object(filesShape).strict();
63538
+ var FilesSchema = exports_external.record(exports_external.string(), FileDigestSchema);
63352
63539
  var ArtifactSchema = exports_external.object({
63353
63540
  platform: exports_external.enum(["darwin-arm64", "darwin-x64", "linux-x64", "linux-arm64"]),
63354
63541
  filename: exports_external.string(),
@@ -63357,8 +63544,8 @@ var ArtifactSchema = exports_external.object({
63357
63544
  files: FilesSchema
63358
63545
  }).strict();
63359
63546
  var ManifestShapeSchema = exports_external.object({
63360
- schema: exports_external.union([exports_external.literal(1), exports_external.literal(2)]),
63361
- patchVersion: exports_external.number().refine(safePositiveInt).optional(),
63547
+ schema: exports_external.literal(3),
63548
+ patchVersion: exports_external.number().refine(safePositiveInt),
63362
63549
  cxVersion: exports_external.string().optional(),
63363
63550
  codexVersion: exports_external.string(),
63364
63551
  upstreamTag: exports_external.string(),
@@ -63370,12 +63557,38 @@ var ManifestShapeSchema = exports_external.object({
63370
63557
  createdAt: exports_external.string().regex(ISO_8601, "createdAt must be an ISO 8601 timestamp"),
63371
63558
  artifacts: exports_external.array(ArtifactSchema).min(1).max(4)
63372
63559
  }).strict();
63560
+ function checkPackageFiles(platform2, files) {
63561
+ const keys2 = Object.keys(files);
63562
+ if (keys2.length > PACKAGE_MAX_FILES)
63563
+ return `files lists more than ${PACKAGE_MAX_FILES} members`;
63564
+ for (const key of keys2) {
63565
+ if (!PACKAGE_PATH.test(key) && !PACKAGE_ROOT_FILES.includes(key))
63566
+ return `files has unexpected member ${key}`;
63567
+ }
63568
+ for (const name of packageRequiredFiles(platform2))
63569
+ if (!(name in files))
63570
+ return `files is missing ${name}`;
63571
+ const lower = new Set;
63572
+ for (const key of keys2) {
63573
+ const folded = key.toLowerCase();
63574
+ if (lower.has(folded))
63575
+ return `files has members that differ only by case: ${key}`;
63576
+ lower.add(folded);
63577
+ for (const other of keys2)
63578
+ if (other !== key && other.startsWith(`${key}/`))
63579
+ return `files member ${key} is also a directory of ${other}`;
63580
+ }
63581
+ return null;
63582
+ }
63373
63583
  function checkArtifacts(m2, expected) {
63374
63584
  const seen = new Set;
63375
63585
  for (const artifact of m2.artifacts) {
63376
63586
  if (seen.has(artifact.platform))
63377
63587
  return "duplicate platform in artifacts";
63378
63588
  seen.add(artifact.platform);
63589
+ const files = checkPackageFiles(artifact.platform, artifact.files);
63590
+ if (files)
63591
+ return files;
63379
63592
  if (artifact.filename.includes("/") || artifact.filename.includes("\\") || artifact.filename.includes("..")) {
63380
63593
  return "artifact filename contains illegal path characters";
63381
63594
  }
@@ -63388,10 +63601,6 @@ function checkArtifacts(m2, expected) {
63388
63601
  return null;
63389
63602
  }
63390
63603
  function checkBusinessRules(m2, expected) {
63391
- if (m2.schema === 2 && m2.patchVersion === undefined)
63392
- return "patchVersion is required for schema 2";
63393
- if (m2.schema === 1 && m2.patchVersion !== undefined)
63394
- return "patchVersion requires schema 2";
63395
63604
  if (m2.cxVersion !== undefined && !isStableVersion(m2.cxVersion)) {
63396
63605
  return "cxVersion is not a valid semver";
63397
63606
  }
@@ -63400,10 +63609,8 @@ function checkBusinessRules(m2, expected) {
63400
63609
  }
63401
63610
  if (m2.upstreamTag !== `rust-v${m2.codexVersion}`)
63402
63611
  return "upstreamTag does not match codexVersion";
63403
- if (m2.schema === 2 && !/^[a-zA-Z0-9][a-zA-Z0-9._-]*\.patch$/.test(m2.patchFile))
63612
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*\.patch$/.test(m2.patchFile))
63404
63613
  return "patchFile must be a patch basename";
63405
- if (m2.schema === 1 && m2.patchFile !== `codex-${m2.codexVersion}.patch`)
63406
- return "patchFile does not match codexVersion";
63407
63614
  if (Number.isNaN(Date.parse(m2.createdAt)))
63408
63615
  return "createdAt is not a valid timestamp";
63409
63616
  return checkArtifacts(m2, expected);
@@ -63418,6 +63625,13 @@ function validateManifest(raw, expected) {
63418
63625
  if (!PLATFORMS.includes(expected.platform)) {
63419
63626
  throw new Error("validateManifest: expected release platform is not supported");
63420
63627
  }
63628
+ const rawSchema = typeof raw === "object" && raw !== null ? raw.schema : undefined;
63629
+ if (typeof rawSchema === "number" && rawSchema > 3) {
63630
+ throw new Error(`this release needs a newer cxstatusline (manifest schema ${rawSchema}); run npm i -g cxstatusline`);
63631
+ }
63632
+ if (typeof rawSchema === "number" && rawSchema < 3) {
63633
+ throw new Error(`release ${releaseTag(expected.codexVersion)} predates cxstatusline 0.11 and has not been republished yet; use --compile or wait`);
63634
+ }
63421
63635
  const structural = ManifestShapeSchema.safeParse(raw);
63422
63636
  if (!structural.success) {
63423
63637
  const issue2 = structural.error.issues[0];
@@ -63431,7 +63645,7 @@ function validateManifest(raw, expected) {
63431
63645
  }
63432
63646
 
63433
63647
  // src/hook/install.ts
63434
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
63648
+ import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
63435
63649
 
63436
63650
  // src/sh.ts
63437
63651
  var sq = (s3) => `'${s3.replace(/'/g, `'"'"'`)}'`;
@@ -63469,7 +63683,7 @@ function readHooksFile(path) {
63469
63683
  return null;
63470
63684
  let raw;
63471
63685
  try {
63472
- raw = JSON.parse(readFileSync7(path, "utf8"));
63686
+ raw = JSON.parse(readFileSync8(path, "utf8"));
63473
63687
  } catch {
63474
63688
  throw new Error(`${path}: hooks.json is not valid JSON; fix it by hand before installing the hook`);
63475
63689
  }
@@ -63501,7 +63715,7 @@ function uninstallHook(hooksFile) {
63501
63715
  }
63502
63716
 
63503
63717
  // src/state.ts
63504
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
63718
+ import { existsSync as existsSync8, readFileSync as readFileSync9 } from "node:fs";
63505
63719
  var RELEASE_UNAVAILABLE = "release-unavailable";
63506
63720
  var RELEASE_RETRY_AFTER_MS = 24 * 60 * 60 * 1000;
63507
63721
  var DEFAULT_STATE = {
@@ -63549,7 +63763,7 @@ function readValidated(file2) {
63549
63763
  if (!existsSync8(file2))
63550
63764
  return null;
63551
63765
  try {
63552
- return validate3(JSON.parse(readFileSync8(file2, "utf8")));
63766
+ return validate3(JSON.parse(readFileSync9(file2, "utf8")));
63553
63767
  } catch {
63554
63768
  return null;
63555
63769
  }
@@ -63800,8 +64014,8 @@ function promptUpdate(options) {
63800
64014
  import { rmSync as rmSync5 } from "node:fs";
63801
64015
 
63802
64016
  // src/codex/upstream.ts
63803
- import { accessSync as accessSync2, constants as constants3, lstatSync as lstatSync3, readlinkSync as readlinkSync2, realpathSync as realpathSync3, statSync as statSync2 } from "node:fs";
63804
- import { dirname as dirname8, isAbsolute as isAbsolute2, join as join12, resolve as resolve3 } from "node:path";
64017
+ import { accessSync as accessSync2, constants as constants3, lstatSync as lstatSync4, readlinkSync as readlinkSync2, realpathSync as realpathSync3, statSync as statSync2 } from "node:fs";
64018
+ import { dirname as dirname9, isAbsolute as isAbsolute2, join as join13, resolve as resolve3 } from "node:path";
63805
64019
  function isExecutable2(p2) {
63806
64020
  try {
63807
64021
  accessSync2(p2, constants3.X_OK);
@@ -63822,13 +64036,13 @@ function isManaged(p2, paths, isOurWrapper) {
63822
64036
  function inspectLauncher(paths, isOurWrapper) {
63823
64037
  let st2;
63824
64038
  try {
63825
- st2 = lstatSync3(paths.wrapperPath);
64039
+ st2 = lstatSync4(paths.wrapperPath);
63826
64040
  } catch {
63827
64041
  return null;
63828
64042
  }
63829
64043
  if (st2.isSymbolicLink()) {
63830
64044
  const raw = readlinkSync2(paths.wrapperPath);
63831
- const target = isAbsolute2(raw) ? raw : resolve3(dirname8(paths.wrapperPath), raw);
64045
+ const target = isAbsolute2(raw) ? raw : resolve3(dirname9(paths.wrapperPath), raw);
63832
64046
  if (isManaged(target, paths, isOurWrapper))
63833
64047
  return null;
63834
64048
  return { kind: "found", bin: target, launcher_restore: { kind: "symlink", target } };
@@ -63841,7 +64055,7 @@ function walkPath(paths, env3, isOurWrapper) {
63841
64055
  for (const dir of (env3.PATH ?? "").split(":").filter(Boolean)) {
63842
64056
  if (resolve3(dir) === resolve3(paths.binDir))
63843
64057
  continue;
63844
- const candidate = join12(dir, "codex");
64058
+ const candidate = join13(dir, "codex");
63845
64059
  if (isExecutable2(candidate) && !isManaged(candidate, paths, isOurWrapper))
63846
64060
  return candidate;
63847
64061
  }
@@ -63876,17 +64090,16 @@ function preserveLauncherRestore(recorded, found) {
63876
64090
  }
63877
64091
 
63878
64092
  // src/patch/compile.ts
63879
- import { chmodSync as chmodSync3, copyFileSync as copyFileSync3, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync2, readFileSync as readFileSync10, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
63880
- import { createHash as createHash5 } from "node:crypto";
63881
- import { join as join15 } from "node:path";
64093
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "node:fs";
64094
+ import { join as join16 } from "node:path";
63882
64095
 
63883
64096
  // src/patch/build.ts
63884
- import { existsSync as existsSync10, lstatSync as lstatSync4, readFileSync as readFileSync9, statSync as statSync3 } from "node:fs";
63885
- import { join as join14 } from "node:path";
64097
+ import { existsSync as existsSync10, lstatSync as lstatSync5, readFileSync as readFileSync10, statSync as statSync3 } from "node:fs";
64098
+ import { join as join15 } from "node:path";
63886
64099
 
63887
64100
  // src/patch/v8.ts
63888
64101
  import { existsSync as existsSync9 } from "node:fs";
63889
- import { join as join13 } from "node:path";
64102
+ import { join as join14 } from "node:path";
63890
64103
  function defaultV8Target() {
63891
64104
  const isArm = process.arch === "arm64";
63892
64105
  if (process.platform === "darwin")
@@ -63917,7 +64130,7 @@ function resolveV8Env(sourceDir, run, log) {
63917
64130
  RUSTY_V8_SRC_BINDING_PATH: process.env.RUSTY_V8_SRC_BINDING_PATH
63918
64131
  };
63919
64132
  }
63920
- const v8Script = join13(sourceDir, "scripts", "codex_package", "v8.py");
64133
+ const v8Script = join14(sourceDir, "scripts", "codex_package", "v8.py");
63921
64134
  if (!existsSync9(v8Script)) {
63922
64135
  return {};
63923
64136
  }
@@ -63989,14 +64202,14 @@ function headCommit(plan, run) {
63989
64202
  }
63990
64203
  function prepareCheckout(plan, run, log) {
63991
64204
  const git = (step, ...args) => must(step, run, log, "git", ["-C", plan.sourceDir, ...args]);
63992
- if (existsSync10(join14(plan.sourceDir, ".git"))) {
64205
+ if (existsSync10(join15(plan.sourceDir, ".git"))) {
63993
64206
  git("fetch", "fetch", "--tags", "--filter=blob:none", "origin");
63994
- const snapshot = join14(plan.sourceDir, SNAPSHOT);
63995
- const st2 = lstatSync4(snapshot, { throwIfNoEntry: false });
64207
+ const snapshot = join15(plan.sourceDir, SNAPSHOT);
64208
+ const st2 = lstatSync5(snapshot, { throwIfNoEntry: false });
63996
64209
  if (st2) {
63997
64210
  const tracked = run("git", ["-C", plan.sourceDir, "ls-files", "--error-unmatch", "--", SNAPSHOT]);
63998
64211
  if (tracked.status !== 0) {
63999
- if (!st2.isFile() || readFileSync9(snapshot, "utf8") !== SNAPSHOT_CONTENT) {
64212
+ if (!st2.isFile() || readFileSync10(snapshot, "utf8") !== SNAPSHOT_CONTENT) {
64000
64213
  throw new BuildError("snapshot", `conflicting untracked snapshot ${snapshot}; move it aside before retrying`);
64001
64214
  }
64002
64215
  git("stage snapshot", "add", "--", SNAPSHOT);
@@ -64019,7 +64232,7 @@ function buildPatched(plan, run, log, which, onStatus) {
64019
64232
  git("apply --check", "apply", "--index", "--check", plan.patchFile);
64020
64233
  git("apply", "apply", "--index", plan.patchFile);
64021
64234
  onStatus?.("patch-done", `Applied ${patchBase} cleanly`);
64022
- const crateDir = join14(plan.sourceDir, "codex-rs");
64235
+ const crateDir = join15(plan.sourceDir, "codex-rs");
64023
64236
  const v8Env = resolveV8Env(plan.sourceDir, run, log);
64024
64237
  onStatus?.("build", "Compiling release binaries (cargo build --release)... Note: this full compile typically takes 10 to 20 minutes.");
64025
64238
  const buildStart = Date.now();
@@ -64028,7 +64241,7 @@ function buildPatched(plan, run, log, which, onStatus) {
64028
64241
  const buildElapsedStr = buildElapsed >= 60 ? `${Math.floor(buildElapsed / 60)}m ${buildElapsed % 60}s` : `${buildElapsed}s`;
64029
64242
  onStatus?.("build-done", `Release binaries compiled (codex, codex-code-mode-host in ${buildElapsedStr})`);
64030
64243
  const [codex, codexCodeModeHost] = TARGETS.map((name) => {
64031
- const bin = join14(crateDir, "target", "release", name);
64244
+ const bin = join15(crateDir, "target", "release", name);
64032
64245
  if (!existsSync10(bin) || statSync3(bin).size === 0) {
64033
64246
  throw new BuildError("cargo build", `cargo exited 0 but ${bin} is missing or empty`);
64034
64247
  }
@@ -64045,9 +64258,6 @@ function buildPatched(plan, run, log, which, onStatus) {
64045
64258
 
64046
64259
  // src/patch/compile.ts
64047
64260
  var STAGING_PREFIX = "compiled-";
64048
- function digestOf3(file2) {
64049
- return { sha256: createHash5("sha256").update(readFileSync10(file2)).digest("hex"), size: statSync4(file2).size };
64050
- }
64051
64261
  function platformLabel() {
64052
64262
  try {
64053
64263
  return platformFor(process.platform, process.arch);
@@ -64055,62 +64265,68 @@ function platformLabel() {
64055
64265
  return `${process.platform}-${process.arch}`;
64056
64266
  }
64057
64267
  }
64058
- function stage(ctx, built) {
64059
- mkdirSync6(ctx.paths.libexecDir, { recursive: true });
64060
- const directory = mkdtempSync2(join15(ctx.paths.libexecDir, STAGING_PREFIX));
64268
+ function prepareCompiled(ctx, upstream, ref, onStatus) {
64269
+ const patchFile = join16(ctx.patchesDir, ref.file);
64270
+ const patchSha256 = digestOf(patchFile).sha256;
64271
+ const built = buildPatched({ tag: ref.tag, sourceDir: ctx.paths.sourceDir, patchFile }, ctx.run, ctx.log, ctx.which, onStatus);
64272
+ onStatus?.("stage", "Assembling the Codex package with upstream's packager...");
64273
+ const python = findPython(ctx.which, ctx.run);
64274
+ if (python === null)
64275
+ throw new Error("Python >= 3.10 is required to assemble the Codex package (brew install python / apt install python3)");
64276
+ const cargo = ctx.which("cargo");
64277
+ if (cargo === null)
64278
+ throw new Error("cargo is not on PATH");
64279
+ mkdirSync7(ctx.paths.libexecDir, { recursive: true });
64280
+ const directory = mkdtempSync2(join16(ctx.paths.libexecDir, STAGING_PREFIX));
64061
64281
  chmodSync3(directory, 448);
64062
- const sources = {
64063
- codex: built.codex,
64064
- "codex-code-mode-host": built.codexCodeModeHost
64065
- };
64066
- const executables = {};
64282
+ const tmpDir = mkdtempSync2(join16(ctx.paths.libexecDir, "download-"));
64283
+ chmodSync3(tmpDir, 448);
64284
+ let target;
64067
64285
  try {
64068
- for (const name of GENERATION_EXECUTABLES) {
64069
- const target = join15(directory, name);
64070
- copyFileSync3(sources[name], target);
64071
- chmodSync3(target, 493);
64072
- executables[name] = digestOf3(target);
64073
- }
64286
+ target = rustcHostTarget(ctx.run);
64287
+ assemblePackage({
64288
+ upstream: ctx.paths.sourceDir,
64289
+ target,
64290
+ codexVersion: upstream.raw,
64291
+ codex: built.codex,
64292
+ codeModeHost: built.codexCodeModeHost,
64293
+ packageDir: directory,
64294
+ python,
64295
+ cargo,
64296
+ bwrapBin: process.platform === "linux" ? ctx.which("bwrap") : null,
64297
+ tmpDir,
64298
+ force: false
64299
+ }, ctx.run, ctx.log);
64074
64300
  } catch (e) {
64075
64301
  rmSync3(directory, { recursive: true, force: true });
64076
64302
  throw e;
64303
+ } finally {
64304
+ rmSync3(tmpDir, { recursive: true, force: true });
64077
64305
  }
64306
+ const files = packageFiles(directory);
64307
+ onStatus?.("stage-done", "Codex package assembled and verified");
64078
64308
  return {
64079
64309
  directory,
64080
- executables: {
64081
- codex: executables.codex,
64082
- "codex-code-mode-host": executables["codex-code-mode-host"]
64083
- }
64084
- };
64085
- }
64086
- function prepareCompiled(ctx, upstream, ref, onStatus) {
64087
- const patchFile = join15(ctx.patchesDir, ref.file);
64088
- const patchSha256 = digestOf3(patchFile).sha256;
64089
- const built = buildPatched({ tag: ref.tag, sourceDir: ctx.paths.sourceDir, patchFile }, ctx.run, ctx.log, ctx.which, onStatus);
64090
- onStatus?.("stage", "Staging generation and verifying binaries...");
64091
- const staged = stage(ctx, built);
64092
- onStatus?.("stage-done", "Generation staged and binaries verified");
64093
- return {
64094
- directory: staged.directory,
64095
64310
  codexVersion: upstream.raw,
64096
64311
  provenance: {
64097
64312
  source: "compiled",
64098
64313
  cxVersion: VERSION,
64099
64314
  platform: platformLabel(),
64315
+ target,
64100
64316
  patchVersion: ref.patchVersion,
64101
64317
  patchSha256,
64102
64318
  upstreamCommit: built.upstreamCommit,
64103
64319
  sourceCommit: SOURCE_COMMIT,
64104
64320
  sourceDirty: SOURCE_DIRTY,
64105
64321
  installedAt: ctx.now().toISOString(),
64106
- executables: staged.executables
64322
+ files
64107
64323
  }
64108
64324
  };
64109
64325
  }
64110
64326
 
64111
64327
  // src/patch/manifest.ts
64112
64328
  import { readFileSync as readFileSync11 } from "node:fs";
64113
- import { join as join16 } from "node:path";
64329
+ import { join as join17 } from "node:path";
64114
64330
  class ManifestError extends Error {
64115
64331
  name = "ManifestError";
64116
64332
  }
@@ -64129,7 +64345,7 @@ var isCandidate = (c) => {
64129
64345
  return parsed !== null && parsed.pre === null;
64130
64346
  };
64131
64347
  function loadManifest(patchesDir) {
64132
- const file2 = join16(patchesDir, "manifest.json");
64348
+ const file2 = join17(patchesDir, "manifest.json");
64133
64349
  try {
64134
64350
  const raw = JSON.parse(readFileSync11(file2, "utf8"));
64135
64351
  const m2 = raw;
@@ -64190,9 +64406,9 @@ function isCodexVersionSupported(m2, version2) {
64190
64406
  }
64191
64407
 
64192
64408
  // src/patch/wrapper.ts
64193
- import { chmodSync as chmodSync4, closeSync as closeSync2, existsSync as existsSync11, lstatSync as lstatSync5, mkdirSync as mkdirSync7, openSync as openSync2, readFileSync as readFileSync12, readSync, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync5, writeFileSync as writeFileSync3 } from "node:fs";
64409
+ import { chmodSync as chmodSync4, closeSync as closeSync2, existsSync as existsSync11, lstatSync as lstatSync6, mkdirSync as mkdirSync8, openSync as openSync2, readFileSync as readFileSync12, readSync, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync3 } from "node:fs";
64194
64410
  import { randomBytes as randomBytes3 } from "node:crypto";
64195
- import { join as join17 } from "node:path";
64411
+ import { join as join18 } from "node:path";
64196
64412
  var WRAPPER_MARKER = "# cxstatusline-wrapper v1";
64197
64413
  var WRAPPER_MARKER_V2 = "# cxstatusline-wrapper v2";
64198
64414
  var WRAPPER_MARKERS = [WRAPPER_MARKER, WRAPPER_MARKER_V2];
@@ -64222,6 +64438,9 @@ function generationWrapperScript(currentGeneration, cxBin) {
64222
64438
  `CXSTATUSLINE_COMMAND=${sq(`${sq(cxBin)} render`)}`,
64223
64439
  "export CXSTATUSLINE_COMMAND",
64224
64440
  `generation=$(CDPATH= cd -P -- ${sq(currentGeneration)} && pwd -P) || exit 1`,
64441
+ `if [ -x "$generation/bin/codex" ]; then`,
64442
+ ` exec "$generation/bin/codex" "$@"`,
64443
+ "fi",
64225
64444
  `exec "$generation/codex" "$@"`,
64226
64445
  ""
64227
64446
  ].join(`
@@ -64245,18 +64464,18 @@ function isOurWrapper(path) {
64245
64464
  }
64246
64465
  function isSymlink(p2) {
64247
64466
  try {
64248
- return lstatSync5(p2).isSymbolicLink();
64467
+ return lstatSync6(p2).isSymbolicLink();
64249
64468
  } catch {
64250
64469
  return false;
64251
64470
  }
64252
64471
  }
64253
64472
  function stageExecutable(target, write) {
64254
- mkdirSync7(join17(target, ".."), { recursive: true });
64473
+ mkdirSync8(join18(target, ".."), { recursive: true });
64255
64474
  const tmp = `${target}.tmp-${process.pid}`;
64256
64475
  try {
64257
64476
  write(tmp);
64258
64477
  chmodSync4(tmp, 493);
64259
- if (statSync5(tmp).size === 0)
64478
+ if (statSync4(tmp).size === 0)
64260
64479
  throw new Error(`${target} staged as an empty file`);
64261
64480
  return tmp;
64262
64481
  } catch (e) {
@@ -64310,7 +64529,7 @@ function preserveStockLauncher(ctx) {
64310
64529
  }
64311
64530
  function stageWrapper(paths, cxBin) {
64312
64531
  const want = generationWrapperScript(paths.currentGeneration, cxBin);
64313
- mkdirSync7(paths.binDir, { recursive: true });
64532
+ mkdirSync8(paths.binDir, { recursive: true });
64314
64533
  const tmp = `${paths.wrapperPath}.tmp-${process.pid}-${randomBytes3(4).toString("hex")}`;
64315
64534
  try {
64316
64535
  writeFileSync3(tmp, want);
@@ -65048,7 +65267,7 @@ async function runUpdate(ctx, opts = {}, transport = {}) {
65048
65267
  return runInstall(ctx, { compile: opts.compile ?? false, codexVersion: target }, transport);
65049
65268
  }
65050
65269
  function appendLog(file2, line) {
65051
- mkdirSync8(join18(file2, ".."), { recursive: true });
65270
+ mkdirSync9(join19(file2, ".."), { recursive: true });
65052
65271
  appendFileSync(file2, `${new Date().toISOString()} ${line}
65053
65272
  `);
65054
65273
  }
@@ -65203,9 +65422,9 @@ async function promptCodexVersion(options) {
65203
65422
  }
65204
65423
 
65205
65424
  // src/hook/run.ts
65206
- import { appendFileSync as appendFileSync2, closeSync as closeSync3, existsSync as existsSync12, mkdirSync as mkdirSync9, openSync as openSync3 } from "node:fs";
65425
+ import { appendFileSync as appendFileSync2, closeSync as closeSync3, existsSync as existsSync12, mkdirSync as mkdirSync10, openSync as openSync3 } from "node:fs";
65207
65426
  import { spawn as spawn2 } from "node:child_process";
65208
- import { dirname as dirname9 } from "node:path";
65427
+ import { dirname as dirname10 } from "node:path";
65209
65428
  var HOOK_SOURCES = ["startup", "resume", "clear"];
65210
65429
  function parseHookInput(stdin) {
65211
65430
  try {
@@ -65298,7 +65517,9 @@ async function runHook(ctx, stdin, deps) {
65298
65517
  if (effectiveInstalled === null)
65299
65518
  return done();
65300
65519
  const patched = parseSemver(effectiveInstalled);
65301
- const drift = needsRepatch(upstream, patched, state.policy);
65520
+ const active = activeGeneration(ctx.paths);
65521
+ const flat = active !== null && !isPackageLayout(active);
65522
+ const drift = needsRepatch(upstream, patched, state.policy) || flat;
65302
65523
  if (!drift)
65303
65524
  maintainWrapper(ctx, messages);
65304
65525
  if (drift) {
@@ -65323,7 +65544,7 @@ async function runHook(ctx, stdin, deps) {
65323
65544
  return done();
65324
65545
  if (drift) {
65325
65546
  deps.spawnDetached(ctx.cxBin, ["hook", "acquire"], ctx.paths.patchLog);
65326
- messages.push(`cxstatusline: Codex updated to ${upstream.raw} (installed pair is from ${effectiveInstalled}). Installing the new pair in the background - reopen Codex in a few minutes. Log: ${ctx.paths.patchLog}`);
65547
+ messages.push(flat && !needsRepatch(upstream, patched, state.policy) ? `cxstatusline: this Codex was installed by an older cxstatusline; rebuilding it in the background - reopen Codex in a few minutes. Log: ${ctx.paths.patchLog}` : `cxstatusline: Codex updated to ${upstream.raw} (installed pair is from ${effectiveInstalled}). Installing the new pair in the background - reopen Codex in a few minutes. Log: ${ctx.paths.patchLog}`);
65327
65548
  }
65328
65549
  return done();
65329
65550
  }
@@ -65332,7 +65553,7 @@ function realHookDeps() {
65332
65553
  spawnDetached(cxBin, args, logFile) {
65333
65554
  let fd;
65334
65555
  try {
65335
- mkdirSync9(dirname9(logFile), { recursive: true });
65556
+ mkdirSync10(dirname10(logFile), { recursive: true });
65336
65557
  fd = openSync3(logFile, "a");
65337
65558
  const child = spawn2(cxBin, args, { detached: true, stdio: ["ignore", fd, fd], env: process.env });
65338
65559
  child.on("error", (err) => {
@@ -65355,8 +65576,8 @@ function realHookDeps() {
65355
65576
  }
65356
65577
 
65357
65578
  // src/commands/doctor.ts
65358
- import { existsSync as existsSync14, lstatSync as lstatSync6, readFileSync as readFileSync14, readlinkSync as readlinkSync3, readdirSync as readdirSync3 } from "node:fs";
65359
- import { join as join20 } from "node:path";
65579
+ import { existsSync as existsSync14, lstatSync as lstatSync7, readFileSync as readFileSync14, readlinkSync as readlinkSync4, readdirSync as readdirSync3 } from "node:fs";
65580
+ import { join as join21 } from "node:path";
65360
65581
 
65361
65582
  // src/utils/format.ts
65362
65583
  function formatDuration2(ms2) {
@@ -65372,18 +65593,17 @@ function formatDuration2(ms2) {
65372
65593
  }
65373
65594
 
65374
65595
  // src/commands/doctor-generation.ts
65375
- import { createHash as createHash6 } from "node:crypto";
65376
- import { existsSync as existsSync13, readFileSync as readFileSync13 } from "node:fs";
65377
- import { dirname as dirname10, isAbsolute as isAbsolute3, join as join19, resolve as resolve4 } from "node:path";
65596
+ import { existsSync as existsSync13, readFileSync as readFileSync13, readlinkSync as readlinkSync3 } from "node:fs";
65597
+ import { dirname as dirname11, isAbsolute as isAbsolute3, join as join20, resolve as resolve4 } from "node:path";
65378
65598
  var line = (key, value, ok = null) => ({ key, value, ok });
65379
65599
  var NO_GENERATION = "n/a (no active generation)";
65380
65600
  function resolvePointerTarget(paths, raw) {
65381
- return isAbsolute3(raw) ? raw : resolve4(dirname10(paths.currentGeneration), raw);
65601
+ return isAbsolute3(raw) ? raw : resolve4(dirname11(paths.currentGeneration), raw);
65382
65602
  }
65383
65603
  function computeGenerationLine(paths, pointer) {
65384
65604
  const dir = activeGeneration(paths);
65385
65605
  if (dir !== null) {
65386
- if (!isGenerationDir(dir))
65606
+ if (!isGenerationDir(dir) && !existsSync13(join20(dir, INSTALLATION_FILE)))
65387
65607
  return { line: line("generation", "foreign", false), dir: null };
65388
65608
  return { line: line("generation", dir, true), dir };
65389
65609
  }
@@ -65453,25 +65673,26 @@ function upstreamCommitLine(record2) {
65453
65673
  function digestLine(key, label, record2, dir) {
65454
65674
  if (!record2 || !dir)
65455
65675
  return line(label, NO_GENERATION, null);
65456
- const file2 = join19(dir, key);
65676
+ const file2 = executablePath(dir, key);
65457
65677
  if (!existsSync13(file2))
65458
65678
  return line(label, "missing", false);
65459
- let buf;
65679
+ let got;
65460
65680
  try {
65461
- buf = readFileSync13(file2);
65681
+ got = digestOf(file2);
65462
65682
  } catch (e) {
65463
65683
  return line(label, `unreadable: ${e.message.split(`
65464
65684
  `)[0]}`, false);
65465
65685
  }
65466
- const got = { sha256: createHash6("sha256").update(buf).digest("hex"), size: buf.length };
65467
- const want = record2.provenance.executables[key];
65686
+ const want = record2.provenance.files?.[`bin/${key}`] ?? record2.provenance.executables?.[key];
65687
+ if (!want)
65688
+ return line(label, "no digest recorded", false);
65468
65689
  const verified = got.sha256 === want.sha256 && got.size === want.size;
65469
65690
  return line(label, verified ? "verified" : "MISMATCH", verified);
65470
65691
  }
65471
65692
  function codexVersionLine(ctx, record2, dir) {
65472
65693
  if (!record2 || !dir)
65473
65694
  return line("codex_version", NO_GENERATION, null);
65474
- const bin = join19(dir, "codex");
65695
+ const bin = executablePath(dir, "codex");
65475
65696
  if (!existsSync13(bin))
65476
65697
  return line("codex_version", "missing", false);
65477
65698
  let result2;
@@ -65494,9 +65715,41 @@ function legalLine(record2, dir) {
65494
65715
  return line("legal", "n/a (compiled build)", null);
65495
65716
  if (!dir)
65496
65717
  return line("legal", NO_GENERATION, null);
65497
- const missing = GENERATION_LEGAL_FILES.filter((f2) => !existsSync13(join19(dir, f2)));
65718
+ const missing = GENERATION_LEGAL_FILES.filter((f2) => !existsSync13(join20(dir, f2)));
65498
65719
  return missing.length === 0 ? line("legal", "present", true) : line("legal", `missing: ${missing.join(", ")}`, false);
65499
65720
  }
65721
+ var DAEMON_SINCE = parseSemver("0.156.0");
65722
+ function packageLayoutLine(record2, dir) {
65723
+ if (!record2 || !dir)
65724
+ return line("package_layout", NO_GENERATION, null);
65725
+ if (!isPackageLayout(dir) || record2.provenance.files === undefined || record2.provenance.target === undefined) {
65726
+ const parsed = parseSemver(record2.codexVersion);
65727
+ const needed = parsed !== null && compareSemver(parsed, DAEMON_SINCE) >= 0;
65728
+ return line("package_layout", "flat layout from an older cxstatusline: run cxstatusline upgrade", needed ? false : null);
65729
+ }
65730
+ const problem = verifyPackage(dir, record2.provenance.files, { target: record2.provenance.target, codexVersion: record2.codexVersion });
65731
+ return problem === null ? line("package_layout", `ok (${record2.provenance.target})`, true) : line("package_layout", problem, false);
65732
+ }
65733
+ function daemonPackageLine(paths, record2) {
65734
+ const current = join20(paths.codexHome, "packages", "app-server-daemon", "current");
65735
+ let target;
65736
+ try {
65737
+ target = readlinkSync3(current);
65738
+ } catch {
65739
+ return line("daemon_package", "none", null);
65740
+ }
65741
+ const dir = isAbsolute3(target) ? target : resolve4(dirname11(current), target);
65742
+ let version2 = "unknown";
65743
+ try {
65744
+ const meta3 = JSON.parse(readFileSync13(join20(dir, "codex-package.json"), "utf8"));
65745
+ if (typeof meta3.version === "string")
65746
+ version2 = meta3.version;
65747
+ } catch {}
65748
+ if (record2 && version2 !== "unknown" && version2 !== record2.codexVersion) {
65749
+ return line("daemon_package", `daemon ${version2} differs from active ${record2.codexVersion} (${dir}); run codex app-server daemon restart`, false);
65750
+ }
65751
+ return line("daemon_package", `${version2} (${dir})`, null);
65752
+ }
65500
65753
  function generationDetailLines(ctx, status) {
65501
65754
  const { record: record2, dir } = status;
65502
65755
  return [
@@ -65509,7 +65762,9 @@ function generationDetailLines(ctx, status) {
65509
65762
  digestLine("codex", "codex_digest", record2, dir),
65510
65763
  digestLine("codex-code-mode-host", "host_digest", record2, dir),
65511
65764
  codexVersionLine(ctx, record2, dir),
65512
- legalLine(record2, dir)
65765
+ legalLine(record2, dir),
65766
+ packageLayoutLine(record2, dir),
65767
+ daemonPackageLine(ctx.paths, record2)
65513
65768
  ];
65514
65769
  }
65515
65770
  function toolchainLine(ctx, source) {
@@ -65663,12 +65918,12 @@ function wrapperLine(ctx) {
65663
65918
  const p2 = ctx.paths.wrapperPath;
65664
65919
  let st2;
65665
65920
  try {
65666
- st2 = lstatSync6(p2);
65921
+ st2 = lstatSync7(p2);
65667
65922
  } catch {
65668
65923
  return line2("wrapper", "absent");
65669
65924
  }
65670
65925
  if (st2.isSymbolicLink())
65671
- return line2("wrapper", `upstream symlink -> ${readlinkSync3(p2)}`);
65926
+ return line2("wrapper", `upstream symlink -> ${readlinkSync4(p2)}`);
65672
65927
  return isOurWrapper(p2) ? line2("wrapper", "ours", true) : line2("wrapper", "foreign file! not ours, not a symlink", false);
65673
65928
  }
65674
65929
  function hookLine(ctx) {
@@ -65718,7 +65973,7 @@ function commandCacheLine(ctx) {
65718
65973
  let oldestMtime = Infinity;
65719
65974
  for (const f2 of entries) {
65720
65975
  try {
65721
- const st2 = lstatSync6(join20(dir, f2));
65976
+ const st2 = lstatSync7(join21(dir, f2));
65722
65977
  if (st2.mtimeMs < oldestMtime)
65723
65978
  oldestMtime = st2.mtimeMs;
65724
65979
  } catch {}
@@ -65814,7 +66069,7 @@ async function doctorReport(ctx, opts) {
65814
66069
  return line2("drift", `behind within minor: ${patched.raw} < ${upstream.raw} (held by policy; \`patch --force\` to pick up)`);
65815
66070
  return line2("drift", "none", true);
65816
66071
  };
65817
- const codexTarget = await codexTargetLine(ctx, effectivePatchedFrom, opts);
66072
+ const codexTarget2 = await codexTargetLine(ctx, effectivePatchedFrom, opts);
65818
66073
  const lines = [
65819
66074
  line2("renderer", `${ctx.cxBin} (${VERSION})`),
65820
66075
  line2("settings", `${ctx.paths.settingsFile} ${existsSync14(ctx.paths.settingsFile) ? "present" : "absent (written on first render)"}`),
@@ -65823,7 +66078,7 @@ async function doctorReport(ctx, opts) {
65823
66078
  line2("patch_version", status.record?.provenance.patchVersion === undefined ? "unknown (legacy)" : `v${status.record.provenance.patchVersion}`),
65824
66079
  line2("patched_from", state.patched_from ?? "never"),
65825
66080
  line2("policy", state.policy),
65826
- codexTarget,
66081
+ codexTarget2,
65827
66082
  drift(),
65828
66083
  bookkeepingLine(state, status.record),
65829
66084
  wrapperLine(ctx),
@@ -65889,11 +66144,11 @@ function runPolicy(ctx, argv, io2) {
65889
66144
  }
65890
66145
 
65891
66146
  // src/commands/revert.ts
65892
- import { existsSync as existsSync15, lstatSync as lstatSync7, readdirSync as readdirSync4, rmSync as rmSync7, symlinkSync as symlinkSync2 } from "node:fs";
65893
- import { join as join21 } from "node:path";
66147
+ import { existsSync as existsSync15, lstatSync as lstatSync8, readFileSync as readFileSync15, readdirSync as readdirSync4, readlinkSync as readlinkSync5, rmSync as rmSync7, symlinkSync as symlinkSync2 } from "node:fs";
66148
+ import { basename as basename2, dirname as dirname12, isAbsolute as isAbsolute4, join as join22, resolve as resolve5 } from "node:path";
65894
66149
  function safeIsSymlink(p2) {
65895
66150
  try {
65896
- return lstatSync7(p2).isSymbolicLink();
66151
+ return lstatSync8(p2).isSymbolicLink();
65897
66152
  } catch {
65898
66153
  return false;
65899
66154
  }
@@ -65919,7 +66174,7 @@ function removeGenerations(ctx, actions) {
65919
66174
  return;
65920
66175
  let removed = 0;
65921
66176
  for (const dir of listGenerations(ctx.paths)) {
65922
- if (!isGenerationDir(dir)) {
66177
+ if (!GENERATION_NAME.test(basename2(dir)) || safeIsSymlink(dir)) {
65923
66178
  actions.push(`${dir} is not a cxstatusline generation; left in place`);
65924
66179
  continue;
65925
66180
  }
@@ -65937,11 +66192,32 @@ function removeStagingDebris(ctx, actions) {
65937
66192
  return;
65938
66193
  const debris = readdirSync4(libexecDir).filter((n) => OWNED_LIBEXEC_PREFIXES.some((p2) => n.startsWith(p2)));
65939
66194
  for (const name of debris)
65940
- rmSync7(join21(libexecDir, name), { recursive: true, force: true });
66195
+ rmSync7(join22(libexecDir, name), { recursive: true, force: true });
65941
66196
  if (debris.length > 0) {
65942
66197
  actions.push(`removed ${debris.length} leftover staging entr${debris.length === 1 ? "y" : "ies"} from ${libexecDir}`);
65943
66198
  }
65944
66199
  }
66200
+ function daemonCopyNotice(ctx, actions) {
66201
+ const root = join22(ctx.paths.codexHome, "packages", "app-server-daemon");
66202
+ const current = join22(root, "current");
66203
+ let target;
66204
+ try {
66205
+ target = readlinkSync5(current);
66206
+ } catch {
66207
+ return;
66208
+ }
66209
+ const dir = isAbsolute4(target) ? target : resolve5(dirname12(current), target);
66210
+ let ours = false;
66211
+ try {
66212
+ const record2 = JSON.parse(readFileSync15(join22(dir, INSTALLATION_FILE), "utf8"));
66213
+ ours = typeof record2?.provenance?.cxVersion === "string";
66214
+ } catch {
66215
+ return;
66216
+ }
66217
+ if (!ours)
66218
+ return;
66219
+ actions.push(`the Codex daemon still runs a copy of the removed generation (${dir}); stop it and remove the copy: codex app-server daemon stop && rm -rf ${root}`);
66220
+ }
65945
66221
  function revertLocked(ctx) {
65946
66222
  const actions = [];
65947
66223
  let code = 0;
@@ -65964,6 +66240,7 @@ function revertLocked(ctx) {
65964
66240
  }
65965
66241
  }
65966
66242
  removeGenerations(ctx, actions);
66243
+ daemonCopyNotice(ctx, actions);
65967
66244
  removeStagingDebris(ctx, actions);
65968
66245
  if (existsSync15(ctx.paths.patchedBin)) {
65969
66246
  rmSync7(ctx.paths.patchedBin);
@@ -67588,7 +67865,7 @@ function MainMenu({ onSelect, initialSelection = 0, hasChanges = false }) {
67588
67865
  // src/tui/components/PowerlineSetup.tsx
67589
67866
  import { existsSync as existsSync16, readdirSync as readdirSync5 } from "node:fs";
67590
67867
  import { homedir as homedir2, platform as platform2 } from "node:os";
67591
- import { join as join22 } from "node:path";
67868
+ import { join as join23 } from "node:path";
67592
67869
  var import_react51 = __toESM(require_react(), 1);
67593
67870
 
67594
67871
  // src/utils/powerline-settings.ts
@@ -67973,7 +68250,7 @@ function buildPowerlineSetupMenuItems(powerline) {
67973
68250
  ];
67974
68251
  }
67975
68252
  function detectPowerlineFont() {
67976
- const dirs = platform2() === "darwin" ? [join22(homedir2(), "Library", "Fonts"), "/Library/Fonts"] : platform2() === "win32" ? [join22(homedir2(), "AppData", "Local", "Microsoft", "Windows", "Fonts"), "C:\\Windows\\Fonts"] : [join22(homedir2(), ".local", "share", "fonts"), join22(homedir2(), ".fonts"), "/usr/share/fonts"];
68253
+ const dirs = platform2() === "darwin" ? [join23(homedir2(), "Library", "Fonts"), "/Library/Fonts"] : platform2() === "win32" ? [join23(homedir2(), "AppData", "Local", "Microsoft", "Windows", "Fonts"), "C:\\Windows\\Fonts"] : [join23(homedir2(), ".local", "share", "fonts"), join23(homedir2(), ".fonts"), "/usr/share/fonts"];
67977
68254
  try {
67978
68255
  return dirs.some((dir) => existsSync16(dir) && readdirSync5(dir).some((file2) => /powerline|nerd font|meslo.*lg/i.test(file2)));
67979
68256
  } catch {
@@ -68494,7 +68771,7 @@ function readMemoryUsage(deps, nowMs) {
68494
68771
  // src/commands/update-tool.ts
68495
68772
  import { spawnSync as spawnSync5 } from "node:child_process";
68496
68773
  import { existsSync as existsSync17, realpathSync as realpathSync4 } from "node:fs";
68497
- import { dirname as dirname11, join as join23 } from "node:path";
68774
+ import { dirname as dirname13, join as join24 } from "node:path";
68498
68775
  async function defaultFetchLatestVersion() {
68499
68776
  const controller = new AbortController;
68500
68777
  const timer = setTimeout(() => controller.abort(), 5000);
@@ -68521,12 +68798,12 @@ function detectInstallType(binaryPath = process.argv[1]) {
68521
68798
  try {
68522
68799
  resolved = realpathSync4(binaryPath);
68523
68800
  } catch {}
68524
- let dir = dirname11(resolved);
68801
+ let dir = dirname13(resolved);
68525
68802
  for (let i = 0;i < 5; i++) {
68526
- if (existsSync17(join23(dir, ".git")) && existsSync17(join23(dir, "package.json"))) {
68803
+ if (existsSync17(join24(dir, ".git")) && existsSync17(join24(dir, "package.json"))) {
68527
68804
  return { type: "git", repoDir: dir };
68528
68805
  }
68529
- const parent = dirname11(dir);
68806
+ const parent = dirname13(dir);
68530
68807
  if (parent === dir)
68531
68808
  break;
68532
68809
  dir = parent;
@@ -68730,14 +69007,16 @@ async function installCommand(argv, io2, deps) {
68730
69007
  }
68731
69008
  }
68732
69009
  const ctx = contextFor(io2, deps);
68733
- if (io2.isTTY === true && !yes && !codexVersion) {
69010
+ if (!codexVersion) {
68734
69011
  let manifest;
68735
69012
  try {
68736
69013
  manifest = loadManifest(ctx.patchesDir);
68737
69014
  } catch (e) {
68738
- io2.stderr(`Cannot load supported Codex versions: ${e instanceof Error ? e.message : String(e)}
69015
+ if (io2.isTTY === true && !yes) {
69016
+ io2.stderr(`Cannot load supported Codex versions: ${e instanceof Error ? e.message : String(e)}
68739
69017
  `);
68740
- return 1;
69018
+ return 1;
69019
+ }
68741
69020
  }
68742
69021
  if (manifest) {
68743
69022
  const supported = supportedCodexVersions(manifest);
@@ -68750,42 +69029,29 @@ async function installCommand(argv, io2, deps) {
68750
69029
  prebuiltVersions = undefined;
68751
69030
  }
68752
69031
  }
68753
- let defaultVersion;
68754
- try {
68755
- const located = upstreamFor(ctx, loadState(ctx));
68756
- if ("bin" in located) {
68757
- const upstream = readUpstreamVersion(located.bin, ctx.run);
68758
- if (upstream) {
68759
- const semverStr = `${upstream.major}.${upstream.minor}.${upstream.patch}`;
68760
- if (supported.includes(semverStr)) {
68761
- if (compile3 || !prebuiltVersions || prebuiltVersions.includes(semverStr)) {
68762
- defaultVersion = semverStr;
68763
- }
68764
- }
68765
- }
68766
- }
68767
- } catch {}
68768
- if (!defaultVersion && prebuiltVersions && prebuiltVersions.length > 0) {
68769
- defaultVersion = supported.find((v2) => prebuiltVersions.includes(v2));
68770
- }
68771
- const prompter = deps.promptVersion ?? promptCodexVersion;
68772
- const result2 = await prompter({
68773
- supportedVersions: supported,
68774
- prebuiltVersions,
68775
- defaultVersion,
68776
- compile: compile3,
68777
- isTTY: true,
68778
- say: (l) => io2.stdout(`${l}
69032
+ const defaultVersion = compile3 ? supported[0] : prebuiltVersions && prebuiltVersions.length > 0 ? supported.find((v2) => prebuiltVersions.includes(v2)) ?? supported[0] : supported[0];
69033
+ if (io2.isTTY === true && !yes) {
69034
+ const prompter = deps.promptVersion ?? promptCodexVersion;
69035
+ const result2 = await prompter({
69036
+ supportedVersions: supported,
69037
+ prebuiltVersions,
69038
+ defaultVersion,
69039
+ compile: compile3,
69040
+ isTTY: true,
69041
+ say: (l) => io2.stdout(`${l}
68779
69042
  `)
68780
- });
68781
- if (result2 === null) {
68782
- io2.stdout(`Installation cancelled.
69043
+ });
69044
+ if (result2 === null) {
69045
+ io2.stdout(`Installation cancelled.
68783
69046
  `);
68784
- return 0;
69047
+ return 0;
69048
+ }
69049
+ const selection = typeof result2 === "string" ? { version: result2, compile: false } : result2;
69050
+ codexVersion = selection.version;
69051
+ compile3 = compile3 || selection.compile;
69052
+ } else {
69053
+ codexVersion = defaultVersion;
68785
69054
  }
68786
- const selection = typeof result2 === "string" ? { version: result2, compile: false } : result2;
68787
- codexVersion = selection.version;
68788
- compile3 = compile3 || selection.compile;
68789
69055
  }
68790
69056
  }
68791
69057
  }
@@ -68938,7 +69204,7 @@ main(argv, {
68938
69204
  isTTY: canPrompt(process.stdin, process.stdout),
68939
69205
  stdin: () => {
68940
69206
  try {
68941
- return readFileSync15(0, "utf8");
69207
+ return readFileSync16(0, "utf8");
68942
69208
  } catch {
68943
69209
  return "";
68944
69210
  }