@roamcode.ai/server 1.0.19 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2466,12 +2466,12 @@ type CodexExecutableProbe = {
2466
2466
  state: "failed";
2467
2467
  };
2468
2468
  interface CodexExecutableResolution {
2469
- /** Executable used by every Codex terminal and auxiliary service for this server process. */
2469
+ /** Stable user-selected command used by Codex terminals and auxiliary services. */
2470
2470
  executable: string;
2471
- /** User-selected executable before a managed macOS recovery copy is considered. */
2471
+ /** Resolved source installation inspected during boot. */
2472
2472
  sourceExecutable: string;
2473
2473
  provenance: CodexInstallProvenance;
2474
- /** True only when the verified private copy is active. The source installation is never modified. */
2474
+ /** True only when a blocked OpenAI-signed Homebrew executable was atomically repaired in place. */
2475
2475
  recovered: boolean;
2476
2476
  }
2477
2477
  interface CodexExecutableDeps {
@@ -2489,10 +2489,9 @@ interface ResolveCodexExecutableOptions {
2489
2489
  }
2490
2490
  declare function defaultProbeCodexExecutable(executable: string, env: NodeJS.ProcessEnv): Promise<CodexExecutableProbe>;
2491
2491
  /**
2492
- * macOS 26 can indefinitely block a valid OpenAI-signed Codex Homebrew Cask binary in `_dyld_start`.
2493
- * A byte-identical executable at a fresh, non-quarantined path starts normally. On an observed timeout,
2494
- * this resolver creates that private copy only after checking the embedded official OpenAI signature.
2495
- * It never modifies the user's Codex installation and never applies the workaround to an unsigned binary.
2492
+ * Probe the configured Codex CLI once at boot. Only a timed-out, officially signed Homebrew installation is
2493
+ * repaired, and the configured command remains the stable launch path so future Homebrew upgrades are not pinned
2494
+ * to an old Caskroom version.
2496
2495
  */
2497
2496
  declare function resolveCodexExecutable(options: ResolveCodexExecutableOptions): Promise<CodexExecutableResolution>;
2498
2497
 
package/dist/index.js CHANGED
@@ -2268,16 +2268,16 @@ async function smokeServer(serverEntry, dataDir, nodePath, log) {
2268
2268
  void dataDir;
2269
2269
  }
2270
2270
  }
2271
- function replaceSymlink(link, target) {
2272
- const temp = `${link}.${process.pid}.${randomUUID2()}.new`;
2271
+ function replaceSymlink(link2, target) {
2272
+ const temp = `${link2}.${process.pid}.${randomUUID2()}.new`;
2273
2273
  try {
2274
2274
  unlinkSync(temp);
2275
2275
  } catch {
2276
2276
  }
2277
- const parent = realpathSync(dirname2(link));
2277
+ const parent = realpathSync(dirname2(link2));
2278
2278
  const canonicalTarget = realpathSync(target);
2279
2279
  symlinkSync(relative(parent, canonicalTarget), temp, "dir");
2280
- renameSync(temp, link);
2280
+ renameSync(temp, link2);
2281
2281
  }
2282
2282
  function trimLog(log) {
2283
2283
  return log.split("\n").slice(-20).join("\n").slice(-8e3);
@@ -2429,7 +2429,7 @@ async function installManagedRelease(opts) {
2429
2429
  }
2430
2430
 
2431
2431
  // src/updater.ts
2432
- var RUNNING_VERSION = "1.0.19" ? "1.0.19".replace(/^v/, "") : packageVersion();
2432
+ var RUNNING_VERSION = "1.0.21" ? "1.0.21".replace(/^v/, "") : packageVersion();
2433
2433
  var RUNNING_BUILD = RUNNING_VERSION;
2434
2434
  var RELEASES_API = "https://api.github.com/repos/burakgon/roamcode/releases?per_page=100";
2435
2435
  var RELEASE_MANIFEST_ASSET = "roamcode-release.json";
@@ -8588,29 +8588,16 @@ var CodexLatestService = class {
8588
8588
  // src/providers/codex-executable.ts
8589
8589
  import { execFile } from "child_process";
8590
8590
  import { constants as constants2 } from "fs";
8591
- import {
8592
- access,
8593
- chmod,
8594
- copyFile,
8595
- lstat as lstat2,
8596
- mkdir as mkdir2,
8597
- readFile as readFile3,
8598
- realpath as realpath3,
8599
- rename as rename2,
8600
- rm,
8601
- stat as stat3,
8602
- writeFile
8603
- } from "fs/promises";
8591
+ import { access, chmod, copyFile, link, realpath as realpath3, rename as rename2, rm, rmdir as rmdir2, stat as stat3 } from "fs/promises";
8604
8592
  import { randomUUID as randomUUID7 } from "crypto";
8605
- import { delimiter, isAbsolute as isAbsolute5, join as join12, sep as sep5 } from "path";
8593
+ import { basename as basename3, delimiter, dirname as dirname5, isAbsolute as isAbsolute5, join as join12, sep as sep5 } from "path";
8606
8594
  var CODEX_EXECUTABLE_PROBE_TIMEOUT_MS = 5e3;
8607
8595
  var OPENAI_CODE_SIGNING_TEAM_ID = "2DC432GLL2";
8608
- var STATE_SCHEMA_VERSION = 1;
8609
- var MAX_MANAGED_CODEX_BYTES = 1024 * 1024 * 1024;
8610
- var MANAGED_DIRECTORY = "provider-bin";
8611
- var MANAGED_EXECUTABLE = "codex-macos";
8612
- var STATE_FILE = "codex-macos-source.json";
8596
+ var MAX_CODEX_BYTES = 1024 * 1024 * 1024;
8613
8597
  var MAX_PROCESS_OUTPUT_BYTES = 16 * 1024;
8598
+ var LEGACY_MANAGED_DIRECTORY = "provider-bin";
8599
+ var LEGACY_MANAGED_EXECUTABLE = "codex-macos";
8600
+ var LEGACY_MANAGED_STATE = "codex-macos-source.json";
8614
8601
  function runBounded(executable, args, options) {
8615
8602
  return new Promise((resolve7) => {
8616
8603
  execFile(
@@ -8696,37 +8683,60 @@ function provenanceFor(executable) {
8696
8683
  if (/\/node_modules\/(?:@openai\/codex|@openai\/codex-[^/]+)\//i.test(normalized)) return "npm";
8697
8684
  return "unknown";
8698
8685
  }
8699
- function identityFor(sourcePath, sourceStat) {
8700
- return {
8701
- sourcePath,
8702
- device: String(sourceStat.dev),
8703
- inode: String(sourceStat.ino),
8704
- size: sourceStat.size,
8705
- modifiedAtMs: sourceStat.mtimeMs
8706
- };
8686
+ function validSource(sourceStat) {
8687
+ return sourceStat.isFile() && sourceStat.size > 0 && sourceStat.size <= MAX_CODEX_BYTES;
8707
8688
  }
8708
- function parseManagedState(raw) {
8709
- try {
8710
- const value = JSON.parse(raw);
8711
- if (value.schemaVersion !== STATE_SCHEMA_VERSION || typeof value.sourcePath !== "string" || typeof value.device !== "string" || typeof value.inode !== "string" || typeof value.size !== "number" || !Number.isSafeInteger(value.size) || typeof value.modifiedAtMs !== "number" || !Number.isFinite(value.modifiedAtMs)) {
8712
- return void 0;
8713
- }
8714
- return value;
8715
- } catch {
8716
- return void 0;
8717
- }
8689
+ function sameSource(left, right) {
8690
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs;
8718
8691
  }
8719
- function stateMatches(state, identity) {
8720
- return state.sourcePath === identity.sourcePath && state.device === identity.device && state.inode === identity.inode && state.size === identity.size && state.modifiedAtMs === identity.modifiedAtMs;
8692
+ async function removeLegacyManagedCopy(dataDir) {
8693
+ const directory = join12(dataDir, LEGACY_MANAGED_DIRECTORY);
8694
+ await Promise.all([
8695
+ rm(join12(directory, LEGACY_MANAGED_EXECUTABLE), { force: true }).catch(() => void 0),
8696
+ rm(join12(directory, LEGACY_MANAGED_STATE), { force: true }).catch(() => void 0)
8697
+ ]);
8698
+ await rmdir2(directory).catch(() => void 0);
8721
8699
  }
8722
- async function usableManagedExecutable(target, env, deps) {
8700
+ async function repairHomebrewExecutable(source, sourceStat, env, deps) {
8701
+ if (!await deps.verifyOfficialSignature(source)) return false;
8702
+ const nonce = randomUUID7();
8703
+ const directory = dirname5(source);
8704
+ const name = basename3(source);
8705
+ const temporary = join12(directory, `.${name}.roamcode-repair-${nonce}`);
8706
+ const backup = join12(directory, `.${name}.roamcode-backup-${nonce}`);
8707
+ let backupCreated = false;
8708
+ let sourceReplaced = false;
8709
+ let committed = false;
8710
+ let preserveBackup = false;
8723
8711
  try {
8724
- const targetStat = await lstat2(target);
8725
- if (!targetStat.isFile() || targetStat.isSymbolicLink()) return false;
8726
- if (!await deps.verifyOfficialSignature(target)) return false;
8727
- return (await deps.probe(target, env)).state === "ready";
8712
+ await copyFile(source, temporary, constants2.COPYFILE_EXCL);
8713
+ await chmod(temporary, sourceStat.mode & 511);
8714
+ if (!await deps.clearExtendedAttributes(temporary)) return false;
8715
+ if (!await deps.verifyOfficialSignature(temporary)) return false;
8716
+ if ((await deps.probe(temporary, env)).state !== "ready") return false;
8717
+ const currentStat = await stat3(source);
8718
+ if (!sameSource(sourceStat, currentStat)) return false;
8719
+ await link(source, backup);
8720
+ backupCreated = true;
8721
+ await rename2(temporary, source);
8722
+ sourceReplaced = true;
8723
+ if (!await deps.verifyOfficialSignature(source)) return false;
8724
+ if ((await deps.probe(source, env)).state !== "ready") return false;
8725
+ committed = true;
8726
+ return true;
8728
8727
  } catch {
8729
8728
  return false;
8729
+ } finally {
8730
+ if (sourceReplaced && !committed && backupCreated) {
8731
+ try {
8732
+ await rename2(backup, source);
8733
+ backupCreated = false;
8734
+ } catch {
8735
+ preserveBackup = true;
8736
+ }
8737
+ }
8738
+ await rm(temporary, { force: true }).catch(() => void 0);
8739
+ if (!preserveBackup) await rm(backup, { force: true }).catch(() => void 0);
8730
8740
  }
8731
8741
  }
8732
8742
  async function resolveCodexExecutable(options) {
@@ -8743,7 +8753,7 @@ async function resolveCodexExecutable(options) {
8743
8753
  if (!source) return unresolved;
8744
8754
  const provenance = provenanceFor(source);
8745
8755
  const ordinary = {
8746
- executable: source,
8756
+ executable: options.codexBin,
8747
8757
  sourceExecutable: source,
8748
8758
  provenance,
8749
8759
  recovered: false
@@ -8754,47 +8764,16 @@ async function resolveCodexExecutable(options) {
8754
8764
  } catch {
8755
8765
  return ordinary;
8756
8766
  }
8757
- if (!sourceStat.isFile() || sourceStat.size <= 0 || sourceStat.size > MAX_MANAGED_CODEX_BYTES) return ordinary;
8758
- const identity = identityFor(source, sourceStat);
8759
- const managedDir = join12(options.dataDir, MANAGED_DIRECTORY);
8760
- const target = join12(managedDir, MANAGED_EXECUTABLE);
8761
- const statePath = join12(managedDir, STATE_FILE);
8762
- try {
8763
- const state = parseManagedState(await readFile3(statePath, "utf8"));
8764
- if (state && stateMatches(state, identity) && await usableManagedExecutable(target, env, deps)) {
8765
- return { executable: target, sourceExecutable: source, provenance, recovered: true };
8766
- }
8767
- } catch {
8768
- }
8767
+ if (!validSource(sourceStat)) return ordinary;
8769
8768
  const sourceProbe = await deps.probe(source, env);
8770
- if (sourceProbe.state !== "timeout") return ordinary;
8771
- if (!await deps.verifyOfficialSignature(source)) return ordinary;
8772
- const nonce = randomUUID7();
8773
- const temporaryExecutable = join12(managedDir, `.codex-${nonce}.tmp`);
8774
- const temporaryState = join12(managedDir, `.codex-state-${nonce}.tmp`);
8775
- try {
8776
- await mkdir2(managedDir, { recursive: true, mode: 448 });
8777
- await chmod(managedDir, 448);
8778
- await copyFile(source, temporaryExecutable, constants2.COPYFILE_EXCL);
8779
- await chmod(temporaryExecutable, 448);
8780
- if (!await deps.clearExtendedAttributes(temporaryExecutable)) return ordinary;
8781
- if (!await deps.verifyOfficialSignature(temporaryExecutable)) return ordinary;
8782
- if ((await deps.probe(temporaryExecutable, env)).state !== "ready") return ordinary;
8783
- await rename2(temporaryExecutable, target);
8784
- if (!await usableManagedExecutable(target, env, deps)) return ordinary;
8785
- const state = { schemaVersion: STATE_SCHEMA_VERSION, ...identity };
8786
- await writeFile(temporaryState, `${JSON.stringify(state)}
8787
- `, { encoding: "utf8", mode: 384, flag: "wx" });
8788
- await rename2(temporaryState, statePath);
8789
- return { executable: target, sourceExecutable: source, provenance, recovered: true };
8790
- } catch {
8769
+ if (sourceProbe.state === "ready") {
8770
+ await removeLegacyManagedCopy(options.dataDir);
8791
8771
  return ordinary;
8792
- } finally {
8793
- await Promise.all([
8794
- rm(temporaryExecutable, { force: true }).catch(() => void 0),
8795
- rm(temporaryState, { force: true }).catch(() => void 0)
8796
- ]);
8797
8772
  }
8773
+ if (sourceProbe.state !== "timeout" || provenance !== "homebrew") return ordinary;
8774
+ if (!await repairHomebrewExecutable(source, sourceStat, env, deps)) return ordinary;
8775
+ await removeLegacyManagedCopy(options.dataDir);
8776
+ return { ...ordinary, recovered: true };
8798
8777
  }
8799
8778
 
8800
8779
  // src/start.ts
@@ -8857,7 +8836,7 @@ async function startServer(env = process.env) {
8857
8836
  const codexBin = codexExecutable.executable;
8858
8837
  if (codexExecutable.recovered) {
8859
8838
  console.warn(
8860
- "[roamcode] Recovered a macOS-blocked Codex CLI using a private verified copy of the official OpenAI-signed binary; the original installation was left unchanged."
8839
+ "[roamcode] Repaired a macOS-blocked Homebrew Codex CLI in place using a verified, byte-identical OpenAI-signed replacement; no private binary was retained."
8861
8840
  );
8862
8841
  }
8863
8842
  const store = openSessionStore({ dbPath: join13(config.dataDir, "sessions.db") });
package/dist/start.js CHANGED
@@ -871,7 +871,7 @@ function readPreviousVersion(root) {
871
871
  }
872
872
 
873
873
  // src/updater.ts
874
- var RUNNING_VERSION = "1.0.19" ? "1.0.19".replace(/^v/, "") : packageVersion();
874
+ var RUNNING_VERSION = "1.0.21" ? "1.0.21".replace(/^v/, "") : packageVersion();
875
875
  var RELEASES_API = "https://api.github.com/repos/burakgon/roamcode/releases?per_page=100";
876
876
  var RELEASE_MANIFEST_ASSET = "roamcode-release.json";
877
877
  var CHECK_CACHE_MS = 15 * 6e4;
@@ -8049,29 +8049,16 @@ var CodexLatestService = class {
8049
8049
  // src/providers/codex-executable.ts
8050
8050
  import { execFile } from "child_process";
8051
8051
  import { constants as constants2 } from "fs";
8052
- import {
8053
- access,
8054
- chmod,
8055
- copyFile,
8056
- lstat as lstat2,
8057
- mkdir as mkdir2,
8058
- readFile as readFile3,
8059
- realpath as realpath3,
8060
- rename as rename2,
8061
- rm,
8062
- stat as stat3,
8063
- writeFile
8064
- } from "fs/promises";
8052
+ import { access, chmod, copyFile, link, realpath as realpath3, rename as rename2, rm, rmdir as rmdir2, stat as stat3 } from "fs/promises";
8065
8053
  import { randomUUID as randomUUID7 } from "crypto";
8066
- import { delimiter, isAbsolute as isAbsolute5, join as join12, sep as sep5 } from "path";
8054
+ import { basename as basename3, delimiter, dirname as dirname5, isAbsolute as isAbsolute5, join as join12, sep as sep5 } from "path";
8067
8055
  var CODEX_EXECUTABLE_PROBE_TIMEOUT_MS = 5e3;
8068
8056
  var OPENAI_CODE_SIGNING_TEAM_ID = "2DC432GLL2";
8069
- var STATE_SCHEMA_VERSION = 1;
8070
- var MAX_MANAGED_CODEX_BYTES = 1024 * 1024 * 1024;
8071
- var MANAGED_DIRECTORY = "provider-bin";
8072
- var MANAGED_EXECUTABLE = "codex-macos";
8073
- var STATE_FILE = "codex-macos-source.json";
8057
+ var MAX_CODEX_BYTES = 1024 * 1024 * 1024;
8074
8058
  var MAX_PROCESS_OUTPUT_BYTES = 16 * 1024;
8059
+ var LEGACY_MANAGED_DIRECTORY = "provider-bin";
8060
+ var LEGACY_MANAGED_EXECUTABLE = "codex-macos";
8061
+ var LEGACY_MANAGED_STATE = "codex-macos-source.json";
8075
8062
  function runBounded(executable, args, options) {
8076
8063
  return new Promise((resolve7) => {
8077
8064
  execFile(
@@ -8157,37 +8144,60 @@ function provenanceFor(executable) {
8157
8144
  if (/\/node_modules\/(?:@openai\/codex|@openai\/codex-[^/]+)\//i.test(normalized)) return "npm";
8158
8145
  return "unknown";
8159
8146
  }
8160
- function identityFor(sourcePath, sourceStat) {
8161
- return {
8162
- sourcePath,
8163
- device: String(sourceStat.dev),
8164
- inode: String(sourceStat.ino),
8165
- size: sourceStat.size,
8166
- modifiedAtMs: sourceStat.mtimeMs
8167
- };
8147
+ function validSource(sourceStat) {
8148
+ return sourceStat.isFile() && sourceStat.size > 0 && sourceStat.size <= MAX_CODEX_BYTES;
8168
8149
  }
8169
- function parseManagedState(raw) {
8170
- try {
8171
- const value = JSON.parse(raw);
8172
- if (value.schemaVersion !== STATE_SCHEMA_VERSION || typeof value.sourcePath !== "string" || typeof value.device !== "string" || typeof value.inode !== "string" || typeof value.size !== "number" || !Number.isSafeInteger(value.size) || typeof value.modifiedAtMs !== "number" || !Number.isFinite(value.modifiedAtMs)) {
8173
- return void 0;
8174
- }
8175
- return value;
8176
- } catch {
8177
- return void 0;
8178
- }
8150
+ function sameSource(left, right) {
8151
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs;
8179
8152
  }
8180
- function stateMatches(state, identity) {
8181
- return state.sourcePath === identity.sourcePath && state.device === identity.device && state.inode === identity.inode && state.size === identity.size && state.modifiedAtMs === identity.modifiedAtMs;
8153
+ async function removeLegacyManagedCopy(dataDir) {
8154
+ const directory = join12(dataDir, LEGACY_MANAGED_DIRECTORY);
8155
+ await Promise.all([
8156
+ rm(join12(directory, LEGACY_MANAGED_EXECUTABLE), { force: true }).catch(() => void 0),
8157
+ rm(join12(directory, LEGACY_MANAGED_STATE), { force: true }).catch(() => void 0)
8158
+ ]);
8159
+ await rmdir2(directory).catch(() => void 0);
8182
8160
  }
8183
- async function usableManagedExecutable(target, env, deps) {
8161
+ async function repairHomebrewExecutable(source, sourceStat, env, deps) {
8162
+ if (!await deps.verifyOfficialSignature(source)) return false;
8163
+ const nonce = randomUUID7();
8164
+ const directory = dirname5(source);
8165
+ const name = basename3(source);
8166
+ const temporary = join12(directory, `.${name}.roamcode-repair-${nonce}`);
8167
+ const backup = join12(directory, `.${name}.roamcode-backup-${nonce}`);
8168
+ let backupCreated = false;
8169
+ let sourceReplaced = false;
8170
+ let committed = false;
8171
+ let preserveBackup = false;
8184
8172
  try {
8185
- const targetStat = await lstat2(target);
8186
- if (!targetStat.isFile() || targetStat.isSymbolicLink()) return false;
8187
- if (!await deps.verifyOfficialSignature(target)) return false;
8188
- return (await deps.probe(target, env)).state === "ready";
8173
+ await copyFile(source, temporary, constants2.COPYFILE_EXCL);
8174
+ await chmod(temporary, sourceStat.mode & 511);
8175
+ if (!await deps.clearExtendedAttributes(temporary)) return false;
8176
+ if (!await deps.verifyOfficialSignature(temporary)) return false;
8177
+ if ((await deps.probe(temporary, env)).state !== "ready") return false;
8178
+ const currentStat = await stat3(source);
8179
+ if (!sameSource(sourceStat, currentStat)) return false;
8180
+ await link(source, backup);
8181
+ backupCreated = true;
8182
+ await rename2(temporary, source);
8183
+ sourceReplaced = true;
8184
+ if (!await deps.verifyOfficialSignature(source)) return false;
8185
+ if ((await deps.probe(source, env)).state !== "ready") return false;
8186
+ committed = true;
8187
+ return true;
8189
8188
  } catch {
8190
8189
  return false;
8190
+ } finally {
8191
+ if (sourceReplaced && !committed && backupCreated) {
8192
+ try {
8193
+ await rename2(backup, source);
8194
+ backupCreated = false;
8195
+ } catch {
8196
+ preserveBackup = true;
8197
+ }
8198
+ }
8199
+ await rm(temporary, { force: true }).catch(() => void 0);
8200
+ if (!preserveBackup) await rm(backup, { force: true }).catch(() => void 0);
8191
8201
  }
8192
8202
  }
8193
8203
  async function resolveCodexExecutable(options) {
@@ -8204,7 +8214,7 @@ async function resolveCodexExecutable(options) {
8204
8214
  if (!source) return unresolved;
8205
8215
  const provenance = provenanceFor(source);
8206
8216
  const ordinary = {
8207
- executable: source,
8217
+ executable: options.codexBin,
8208
8218
  sourceExecutable: source,
8209
8219
  provenance,
8210
8220
  recovered: false
@@ -8215,47 +8225,16 @@ async function resolveCodexExecutable(options) {
8215
8225
  } catch {
8216
8226
  return ordinary;
8217
8227
  }
8218
- if (!sourceStat.isFile() || sourceStat.size <= 0 || sourceStat.size > MAX_MANAGED_CODEX_BYTES) return ordinary;
8219
- const identity = identityFor(source, sourceStat);
8220
- const managedDir = join12(options.dataDir, MANAGED_DIRECTORY);
8221
- const target = join12(managedDir, MANAGED_EXECUTABLE);
8222
- const statePath = join12(managedDir, STATE_FILE);
8223
- try {
8224
- const state = parseManagedState(await readFile3(statePath, "utf8"));
8225
- if (state && stateMatches(state, identity) && await usableManagedExecutable(target, env, deps)) {
8226
- return { executable: target, sourceExecutable: source, provenance, recovered: true };
8227
- }
8228
- } catch {
8229
- }
8228
+ if (!validSource(sourceStat)) return ordinary;
8230
8229
  const sourceProbe = await deps.probe(source, env);
8231
- if (sourceProbe.state !== "timeout") return ordinary;
8232
- if (!await deps.verifyOfficialSignature(source)) return ordinary;
8233
- const nonce = randomUUID7();
8234
- const temporaryExecutable = join12(managedDir, `.codex-${nonce}.tmp`);
8235
- const temporaryState = join12(managedDir, `.codex-state-${nonce}.tmp`);
8236
- try {
8237
- await mkdir2(managedDir, { recursive: true, mode: 448 });
8238
- await chmod(managedDir, 448);
8239
- await copyFile(source, temporaryExecutable, constants2.COPYFILE_EXCL);
8240
- await chmod(temporaryExecutable, 448);
8241
- if (!await deps.clearExtendedAttributes(temporaryExecutable)) return ordinary;
8242
- if (!await deps.verifyOfficialSignature(temporaryExecutable)) return ordinary;
8243
- if ((await deps.probe(temporaryExecutable, env)).state !== "ready") return ordinary;
8244
- await rename2(temporaryExecutable, target);
8245
- if (!await usableManagedExecutable(target, env, deps)) return ordinary;
8246
- const state = { schemaVersion: STATE_SCHEMA_VERSION, ...identity };
8247
- await writeFile(temporaryState, `${JSON.stringify(state)}
8248
- `, { encoding: "utf8", mode: 384, flag: "wx" });
8249
- await rename2(temporaryState, statePath);
8250
- return { executable: target, sourceExecutable: source, provenance, recovered: true };
8251
- } catch {
8230
+ if (sourceProbe.state === "ready") {
8231
+ await removeLegacyManagedCopy(options.dataDir);
8252
8232
  return ordinary;
8253
- } finally {
8254
- await Promise.all([
8255
- rm(temporaryExecutable, { force: true }).catch(() => void 0),
8256
- rm(temporaryState, { force: true }).catch(() => void 0)
8257
- ]);
8258
8233
  }
8234
+ if (sourceProbe.state !== "timeout" || provenance !== "homebrew") return ordinary;
8235
+ if (!await repairHomebrewExecutable(source, sourceStat, env, deps)) return ordinary;
8236
+ await removeLegacyManagedCopy(options.dataDir);
8237
+ return { ...ordinary, recovered: true };
8259
8238
  }
8260
8239
 
8261
8240
  // src/start.ts
@@ -8318,7 +8297,7 @@ async function startServer(env = process.env) {
8318
8297
  const codexBin = codexExecutable.executable;
8319
8298
  if (codexExecutable.recovered) {
8320
8299
  console.warn(
8321
- "[roamcode] Recovered a macOS-blocked Codex CLI using a private verified copy of the official OpenAI-signed binary; the original installation was left unchanged."
8300
+ "[roamcode] Repaired a macOS-blocked Homebrew Codex CLI in place using a verified, byte-identical OpenAI-signed replacement; no private binary was retained."
8322
8301
  );
8323
8302
  }
8324
8303
  const store = openSessionStore({ dbPath: join13(config.dataDir, "sessions.db") });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roamcode.ai/server",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Host-native server for RoamCode",
5
5
  "homepage": "https://roamcode.ai",
6
6
  "bugs": "https://github.com/burakgon/roamcode/issues",
@@ -40,7 +40,7 @@
40
40
  "node-pty": "1.1.0",
41
41
  "web-push": "^3.6.7",
42
42
  "zod": "^4.4.3",
43
- "@roamcode.ai/web": "1.0.19"
43
+ "@roamcode.ai/web": "1.0.21"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/better-sqlite3": "^7.6.13",