@blue-soda/dsh-remote 0.4.28 → 0.4.29

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.js CHANGED
@@ -18354,8 +18354,63 @@ var AsyncValueQueue2 = class {
18354
18354
  import { platform } from "node:os";
18355
18355
 
18356
18356
  // src/server-credentials.ts
18357
- import { chmod, mkdir, readFile as readFile2, rename, rm, stat, writeFile } from "node:fs/promises";
18358
- import { dirname as dirname2, join as join2 } from "node:path";
18357
+ import { chmod, mkdir, readFile as readFile2, rm as rm2, stat as stat2, writeFile } from "node:fs/promises";
18358
+ import { dirname as dirname2, join as join3 } from "node:path";
18359
+
18360
+ // src/atomic-file.ts
18361
+ import { readdir, rename, rm, stat } from "node:fs/promises";
18362
+ import { join as join2 } from "node:path";
18363
+ var TRANSIENT_REPLACE_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
18364
+ var REPLACE_RETRY_DELAYS_MS = [20, 40, 80, 160, 320];
18365
+ var TEMPORARY_NAME = /\.\d+\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp$/iu;
18366
+ var STALE_TEMPORARY_AGE_MS = 10 * 60 * 1e3;
18367
+ async function replaceFile(temporary, target2, options = {}) {
18368
+ const delays = options.delaysMs ?? REPLACE_RETRY_DELAYS_MS;
18369
+ for (let attempt = 0; ; attempt += 1) {
18370
+ try {
18371
+ await rename(temporary, target2);
18372
+ return;
18373
+ } catch (error) {
18374
+ const delay = delays[attempt];
18375
+ if (delay === void 0 || !TRANSIENT_REPLACE_CODES.has(errorCode2(error) ?? "")) throw error;
18376
+ await sleep2(delay);
18377
+ }
18378
+ }
18379
+ }
18380
+ async function sweepStaleTemporaries(directory, options = {}) {
18381
+ const maxAgeMs = options.maxAgeMs ?? STALE_TEMPORARY_AGE_MS;
18382
+ const removed = [];
18383
+ let entries;
18384
+ try {
18385
+ entries = await readdir(directory);
18386
+ } catch {
18387
+ return removed;
18388
+ }
18389
+ for (const entry of entries) {
18390
+ if (!TEMPORARY_NAME.test(entry)) continue;
18391
+ const path = join2(directory, entry);
18392
+ try {
18393
+ const info = await stat(path);
18394
+ if (!info.isFile() || Date.now() - info.mtimeMs < maxAgeMs) continue;
18395
+ await rm(path, { force: true });
18396
+ removed.push(path);
18397
+ } catch {
18398
+ }
18399
+ }
18400
+ return removed;
18401
+ }
18402
+ function errorCode2(error) {
18403
+ if (typeof error !== "object" || error === null || !("code" in error)) return void 0;
18404
+ const code = error.code;
18405
+ return typeof code === "string" ? code : void 0;
18406
+ }
18407
+ function sleep2(ms) {
18408
+ return new Promise((resolve4) => {
18409
+ setTimeout(resolve4, ms);
18410
+ });
18411
+ }
18412
+
18413
+ // src/server-credentials.ts
18359
18414
  var credentialSchema = external_exports.object({
18360
18415
  schemaVersion: external_exports.literal(1),
18361
18416
  serverUrl: external_exports.string().url(),
@@ -18370,7 +18425,7 @@ var credentialSchema = external_exports.object({
18370
18425
  var ServerCredentialStore = class {
18371
18426
  path;
18372
18427
  constructor(directory) {
18373
- this.path = join2(directory, "server-credentials.json");
18428
+ this.path = join3(directory, "server-credentials.json");
18374
18429
  }
18375
18430
  /** Serialize the complete read/refresh/write transaction across processes.
18376
18431
  * Never steal an old lock: a suspended owner may still consume a one-use token.
@@ -18393,7 +18448,7 @@ var ServerCredentialStore = class {
18393
18448
  try {
18394
18449
  return await operation();
18395
18450
  } finally {
18396
- await rm(lock, { recursive: true });
18451
+ await rm2(lock, { recursive: true });
18397
18452
  }
18398
18453
  }
18399
18454
  async load(serverUrl, deviceId) {
@@ -18414,7 +18469,7 @@ var ServerCredentialStore = class {
18414
18469
  return record7;
18415
18470
  }
18416
18471
  async clear() {
18417
- await rm(this.path, { force: true });
18472
+ await rm2(this.path, { force: true });
18418
18473
  }
18419
18474
  };
18420
18475
  var ServerCredentialsInvalidError = class extends Error {
@@ -18427,21 +18482,23 @@ var ServerCredentialsBusyError = class extends Error {
18427
18482
  }
18428
18483
  };
18429
18484
  async function atomicWrite(path, contents) {
18430
- await mkdir(dirname2(path), { recursive: true, mode: 448 });
18485
+ const directory = dirname2(path);
18486
+ await mkdir(directory, { recursive: true, mode: 448 });
18487
+ await sweepStaleTemporaries(directory);
18431
18488
  const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
18432
18489
  await writeFile(temporary, contents, { encoding: "utf8", mode: 384, flag: "wx" });
18433
18490
  await chmod(temporary, 384);
18434
- await rename(temporary, path);
18491
+ await replaceFile(temporary, path);
18435
18492
  await chmod(path, 384);
18436
18493
  }
18437
18494
  async function assertPrivateMode(path) {
18438
18495
  if (process.platform === "win32") return;
18439
- const mode = (await stat(path)).mode & 511;
18496
+ const mode = (await stat2(path)).mode & 511;
18440
18497
  if ((mode & 63) !== 0) throw new ServerCredentialsInvalidError("server credentials permissions must be 0600");
18441
18498
  }
18442
18499
  async function exists(path) {
18443
18500
  try {
18444
- await stat(path);
18501
+ await stat2(path);
18445
18502
  return true;
18446
18503
  } catch (error) {
18447
18504
  if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
@@ -18572,7 +18629,7 @@ function normalizeServerUrl(value) {
18572
18629
  }
18573
18630
 
18574
18631
  // src/version.ts
18575
- var PLUGIN_VERSION = "0.4.28";
18632
+ var PLUGIN_VERSION = "0.4.29";
18576
18633
 
18577
18634
  // src/server-api.ts
18578
18635
  var ENABLED_QR_PROVIDERS = ["github"];
@@ -19317,7 +19374,7 @@ import { networkInterfaces } from "node:os";
19317
19374
  // src/native-rtc-helper.ts
19318
19375
  import { spawn, spawnSync } from "node:child_process";
19319
19376
  import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
19320
- import { delimiter, dirname as dirname3, join as join3 } from "node:path";
19377
+ import { delimiter, dirname as dirname3, join as join4 } from "node:path";
19321
19378
  import { fileURLToPath } from "node:url";
19322
19379
  var cachedExternalFactory;
19323
19380
  var cachedExternalFactoryResolved = false;
@@ -19368,21 +19425,21 @@ function nodeBinaryCandidates() {
19368
19425
  add3(process.env.DSH_REMOTE_NODE);
19369
19426
  add3(process.env.NODE);
19370
19427
  if (process.versions.electron === void 0) add3(process.execPath);
19371
- for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join3(part, process.platform === "win32" ? "node.exe" : "node"));
19428
+ for (const part of (process.env.PATH ?? "").split(delimiter)) add3(join4(part, process.platform === "win32" ? "node.exe" : "node"));
19372
19429
  add3("/opt/homebrew/bin/node");
19373
19430
  add3("/usr/local/bin/node");
19374
19431
  add3("/usr/bin/node");
19375
- add3(join3(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
19376
- add3(join3(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
19377
- add3(join3(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
19432
+ add3(join4(process.env.HOME ?? "", ".volta", "bin", process.platform === "win32" ? "node.exe" : "node"));
19433
+ add3(join4(process.env.HOME ?? "", ".asdf", "shims", process.platform === "win32" ? "node.exe" : "node"));
19434
+ add3(join4(process.env.HOME ?? "", ".local", "bin", process.platform === "win32" ? "node.exe" : "node"));
19378
19435
  for (const nvmNode of nvmNodeCandidates()) add3(nvmNode);
19379
19436
  return candidates;
19380
19437
  }
19381
19438
  function nvmNodeCandidates() {
19382
- const root = join3(process.env.HOME ?? "", ".nvm", "versions", "node");
19439
+ const root = join4(process.env.HOME ?? "", ".nvm", "versions", "node");
19383
19440
  if (root === "" || !existsSync(root)) return [];
19384
19441
  try {
19385
- return readdirSync(root).map((version) => join3(root, version, "bin", process.platform === "win32" ? "node.exe" : "node")).sort((left, right) => right.localeCompare(left, "en", { numeric: true }));
19442
+ return readdirSync(root).map((version) => join4(root, version, "bin", process.platform === "win32" ? "node.exe" : "node")).sort((left, right) => right.localeCompare(left, "en", { numeric: true }));
19386
19443
  } catch {
19387
19444
  return [];
19388
19445
  }
@@ -19413,10 +19470,10 @@ function isUsableExternalNode(candidate, requireFrom) {
19413
19470
  }
19414
19471
  function isExecutableFile(candidate) {
19415
19472
  try {
19416
- const stat7 = statSync(candidate);
19417
- if (!stat7.isFile()) return false;
19473
+ const stat8 = statSync(candidate);
19474
+ if (!stat8.isFile()) return false;
19418
19475
  if (process.platform === "win32") return true;
19419
- return (stat7.mode & 73) !== 0;
19476
+ return (stat8.mode & 73) !== 0;
19420
19477
  } catch {
19421
19478
  return false;
19422
19479
  }
@@ -21613,9 +21670,9 @@ import { execFileSync } from "node:child_process";
21613
21670
 
21614
21671
  // src/identity-store.ts
21615
21672
  import { createHash } from "node:crypto";
21616
- import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, rename as rename2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
21673
+ import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, rm as rm3, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
21617
21674
  import { homedir } from "node:os";
21618
- import { dirname as dirname4, join as join4 } from "node:path";
21675
+ import { dirname as dirname4, join as join5 } from "node:path";
21619
21676
  var identitySchema = external_exports.object({
21620
21677
  schemaVersion: external_exports.literal(1),
21621
21678
  deviceId: external_exports.string().uuid(),
@@ -21644,14 +21701,14 @@ var IdentityStore = class {
21644
21701
  peers = /* @__PURE__ */ new Map();
21645
21702
  constructor(options = {}) {
21646
21703
  const env = options.env ?? process.env;
21647
- const dshHome = env.DSH_HOME || join4(options.homeDirectory ?? homedir(), ".dsh");
21648
- this.directory = options.directory ?? join4(dshHome, "remote");
21704
+ const dshHome = env.DSH_HOME || join5(options.homeDirectory ?? homedir(), ".dsh");
21705
+ this.directory = options.directory ?? join5(dshHome, "remote");
21649
21706
  }
21650
21707
  async loadOrCreate(deviceName) {
21651
21708
  await mkdir2(this.directory, { recursive: true, mode: 448 });
21652
21709
  await chmod2(this.directory, 448);
21653
- const devicePath = join4(this.directory, "device.json");
21654
- const keyPath = join4(this.directory, "device.key");
21710
+ const devicePath = join5(this.directory, "device.json");
21711
+ const keyPath = join5(this.directory, "device.key");
21655
21712
  const [hasDevice, hasKey] = await Promise.all([exists2(devicePath), exists2(keyPath)]);
21656
21713
  if (hasDevice !== hasKey) {
21657
21714
  throw new IdentityInvalidError("device identity is incomplete; repair it explicitly before reconnecting");
@@ -21688,7 +21745,7 @@ var IdentityStore = class {
21688
21745
  return this.identity;
21689
21746
  }
21690
21747
  async reset(deviceName) {
21691
- await rm2(this.directory, { recursive: true, force: true });
21748
+ await rm3(this.directory, { recursive: true, force: true });
21692
21749
  this.identity = void 0;
21693
21750
  this.peers.clear();
21694
21751
  return this.loadOrCreate(deviceName);
@@ -21720,7 +21777,7 @@ var IdentityStore = class {
21720
21777
  return removed;
21721
21778
  }
21722
21779
  async loadPeers() {
21723
- const path = join4(this.directory, "trusted-peers.json");
21780
+ const path = join5(this.directory, "trusted-peers.json");
21724
21781
  if (!await exists2(path)) {
21725
21782
  await atomicJsonWrite(path, { schemaVersion: 1, peers: [] }, 384);
21726
21783
  }
@@ -21736,7 +21793,7 @@ var IdentityStore = class {
21736
21793
  this.peers = peers;
21737
21794
  }
21738
21795
  async savePeers() {
21739
- await atomicJsonWrite(join4(this.directory, "trusted-peers.json"), {
21796
+ await atomicJsonWrite(join5(this.directory, "trusted-peers.json"), {
21740
21797
  schemaVersion: 1,
21741
21798
  peers: [...this.peers.values()]
21742
21799
  }, 384);
@@ -21745,7 +21802,7 @@ var IdentityStore = class {
21745
21802
  function serverStorageDirectory(root, serverUrl, role) {
21746
21803
  const origin = new URL(serverUrl).origin;
21747
21804
  const scope = createHash("sha256").update(origin).digest("hex").slice(0, 24);
21748
- return join4(root, "servers", scope, role);
21805
+ return join5(root, "servers", scope, role);
21749
21806
  }
21750
21807
  function fingerprint(publicKey) {
21751
21808
  const compact = createHash("sha256").update(fromBase64Url2(publicKey)).digest("hex").slice(0, 12).toUpperCase();
@@ -21753,7 +21810,7 @@ function fingerprint(publicKey) {
21753
21810
  }
21754
21811
  async function assertPrivateMode2(path) {
21755
21812
  if (process.platform === "win32") return;
21756
- const mode = (await stat2(path)).mode & 511;
21813
+ const mode = (await stat3(path)).mode & 511;
21757
21814
  if ((mode & 63) !== 0) {
21758
21815
  throw new IdentityInvalidError(`private key permissions must be 0600, got ${mode.toString(8).padStart(3, "0")}`);
21759
21816
  }
@@ -21763,16 +21820,18 @@ async function atomicJsonWrite(path, value, mode) {
21763
21820
  `, mode);
21764
21821
  }
21765
21822
  async function atomicTextWrite(path, value, mode) {
21766
- await mkdir2(dirname4(path), { recursive: true, mode: 448 });
21823
+ const directory = dirname4(path);
21824
+ await mkdir2(directory, { recursive: true, mode: 448 });
21825
+ await sweepStaleTemporaries(directory);
21767
21826
  const temporary = `${path}.${process.pid}.${uuidV7()}.tmp`;
21768
21827
  await writeFile2(temporary, value, { encoding: "utf8", mode, flag: "wx" });
21769
21828
  await chmod2(temporary, mode);
21770
- await rename2(temporary, path);
21829
+ await replaceFile(temporary, path);
21771
21830
  await chmod2(path, mode);
21772
21831
  }
21773
21832
  async function exists2(path) {
21774
21833
  try {
21775
- await stat2(path);
21834
+ await stat3(path);
21776
21835
  return true;
21777
21836
  } catch (error) {
21778
21837
  if (isNodeError(error) && error.code === "ENOENT") return false;
@@ -21932,9 +21991,9 @@ var ControlStatusStream = class {
21932
21991
  // src/codex/domain.ts
21933
21992
  import { randomUUID as randomUUID2 } from "node:crypto";
21934
21993
  import { accessSync, constants, existsSync as existsSync2, readFileSync, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
21935
- import { readdir, realpath, stat as stat3 } from "node:fs/promises";
21994
+ import { readdir as readdir2, realpath, stat as stat4 } from "node:fs/promises";
21936
21995
  import { homedir as homedir2 } from "node:os";
21937
- import { basename as basename2, isAbsolute as isAbsolute2, join as join5, relative, resolve } from "node:path";
21996
+ import { basename as basename2, isAbsolute as isAbsolute2, join as join6, relative, resolve } from "node:path";
21938
21997
 
21939
21998
  // src/codex/app-server.ts
21940
21999
  import { spawn as spawn2 } from "node:child_process";
@@ -22489,7 +22548,7 @@ var CodexRemoteDomain = class {
22489
22548
  } catch (error) {
22490
22549
  this.available = false;
22491
22550
  this.state = "unavailable";
22492
- this.unavailableCode = errorCode2(error);
22551
+ this.unavailableCode = errorCode3(error);
22493
22552
  await this.disposeAppServer(this.appServer);
22494
22553
  this.logger.warn("Codex Remote domain unavailable", { code: this.unavailableCode });
22495
22554
  }
@@ -22734,7 +22793,7 @@ var CodexRemoteDomain = class {
22734
22793
  this.appServer = appServer;
22735
22794
  this.unsubscribeInbound = appServer.onInbound((message) => {
22736
22795
  void this.handleInbound(message).catch((error) => {
22737
- this.logger.warn("Codex inbound handling failed", { code: errorCode2(error) });
22796
+ this.logger.warn("Codex inbound handling failed", { code: errorCode3(error) });
22738
22797
  });
22739
22798
  });
22740
22799
  this.unsubscribeUnavailable = appServer.onUnavailable((code) => {
@@ -22800,7 +22859,7 @@ var CodexRemoteDomain = class {
22800
22859
  } catch (error) {
22801
22860
  this.available = false;
22802
22861
  this.state = "restarting";
22803
- this.unavailableCode = errorCode2(error);
22862
+ this.unavailableCode = errorCode3(error);
22804
22863
  this.logger.warn("Codex App Server restart failed", {
22805
22864
  attempt: this.restartAttempt,
22806
22865
  code: this.unavailableCode
@@ -23013,7 +23072,7 @@ var CodexRemoteDomain = class {
23013
23072
  this.logger.warn("Codex history read fallback", {
23014
23073
  connectionId: maskId(connectionId),
23015
23074
  stage,
23016
- code: errorCode2(error)
23075
+ code: errorCode3(error)
23017
23076
  });
23018
23077
  }
23019
23078
  async assertResultThreadAllowed(result) {
@@ -23084,7 +23143,7 @@ var CodexRemoteDomain = class {
23084
23143
  this.approvalExpiryTimer = setTimeout(() => {
23085
23144
  this.approvalExpiryTimer = void 0;
23086
23145
  void this.expireApprovals().catch((error) => {
23087
- this.logger.warn("Codex approval expiry failed", { code: errorCode2(error) });
23146
+ this.logger.warn("Codex approval expiry failed", { code: errorCode3(error) });
23088
23147
  });
23089
23148
  }, Math.max(0, nextExpiry - Date.now()));
23090
23149
  this.approvalExpiryTimer.unref?.();
@@ -23174,7 +23233,7 @@ var CodexRemoteDomain = class {
23174
23233
  let candidate;
23175
23234
  try {
23176
23235
  candidate = await realpath(path);
23177
- if (!(await stat3(candidate)).isDirectory()) throw new Error("not a directory");
23236
+ if (!(await stat4(candidate)).isDirectory()) throw new Error("not a directory");
23178
23237
  } catch {
23179
23238
  throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23180
23239
  }
@@ -23194,7 +23253,7 @@ var CodexRemoteDomain = class {
23194
23253
  }
23195
23254
  try {
23196
23255
  const canonical = await realpath(resolve(path));
23197
- if (!(await stat3(canonical)).isDirectory()) throw new Error("not a directory");
23256
+ if (!(await stat4(canonical)).isDirectory()) throw new Error("not a directory");
23198
23257
  return canonical;
23199
23258
  } catch {
23200
23259
  throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX project directory must be an existing absolute directory.");
@@ -23202,7 +23261,7 @@ var CodexRemoteDomain = class {
23202
23261
  }
23203
23262
  async listCodexDirectory(path) {
23204
23263
  const target2 = await this.resolveCodexDirectory(path);
23205
- const rows = await readdir(target2.path, { withFileTypes: true }).catch(() => void 0);
23264
+ const rows = await readdir2(target2.path, { withFileTypes: true }).catch(() => void 0);
23206
23265
  if (rows === void 0) {
23207
23266
  throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23208
23267
  }
@@ -23211,12 +23270,12 @@ var CodexRemoteDomain = class {
23211
23270
  const child = resolve(target2.path, row.name);
23212
23271
  let directory = row.isDirectory();
23213
23272
  if (!directory && row.isSymbolicLink()) {
23214
- directory = await stat3(child).then((value) => value.isDirectory()).catch(() => false);
23273
+ directory = await stat4(child).then((value) => value.isDirectory()).catch(() => false);
23215
23274
  }
23216
23275
  if (!directory) continue;
23217
23276
  try {
23218
23277
  const canonicalChild = await realpath(child);
23219
- if (!(await stat3(canonicalChild)).isDirectory()) continue;
23278
+ if (!(await stat4(canonicalChild)).isDirectory()) continue;
23220
23279
  if (!containsCodexPath(target2.canonicalRoot, canonicalChild)) continue;
23221
23280
  } catch {
23222
23281
  continue;
@@ -23245,7 +23304,7 @@ var CodexRemoteDomain = class {
23245
23304
  let canonicalCandidate;
23246
23305
  try {
23247
23306
  canonicalCandidate = await realpath(path);
23248
- if (!(await stat3(canonicalCandidate)).isDirectory()) throw new Error("not a directory");
23307
+ if (!(await stat4(canonicalCandidate)).isDirectory()) throw new Error("not a directory");
23249
23308
  } catch {
23250
23309
  throw new RpcError("CODEX_PATH_NOT_ALLOWED", "The CodeX working directory is not available as a Workspace.");
23251
23310
  }
@@ -23275,15 +23334,15 @@ function codexBinaryCandidates(configured, hostPlatform = process.platform, user
23275
23334
  if (hostPlatform !== "darwin") return [configured];
23276
23335
  const bundledCandidates = [
23277
23336
  "/Applications/ChatGPT.app",
23278
- join5(userHome, "Applications", "ChatGPT.app")
23337
+ join6(userHome, "Applications", "ChatGPT.app")
23279
23338
  ].flatMap((chatGptApp) => {
23280
- const codexCli = join5(chatGptApp, "Contents", "Resources", "codex-cli");
23339
+ const codexCli = join6(chatGptApp, "Contents", "Resources", "codex-cli");
23281
23340
  try {
23282
- const manifest = JSON.parse(readFileSync(join5(codexCli, "codex-package.json"), "utf8"));
23341
+ const manifest = JSON.parse(readFileSync(join6(codexCli, "codex-package.json"), "utf8"));
23283
23342
  if (!isRecord11(manifest) || typeof manifest.entrypoint !== "string" || manifest.entrypoint.length === 0) {
23284
23343
  return [];
23285
23344
  }
23286
- const candidate = join5(codexCli, manifest.entrypoint);
23345
+ const candidate = join6(codexCli, manifest.entrypoint);
23287
23346
  if (!existsSync2(candidate)) return [];
23288
23347
  accessSync(candidate, constants.X_OK);
23289
23348
  return [candidate];
@@ -23294,14 +23353,14 @@ function codexBinaryCandidates(configured, hostPlatform = process.platform, user
23294
23353
  return [.../* @__PURE__ */ new Set([
23295
23354
  ...bundledCandidates,
23296
23355
  "/Applications/ChatGPT.app/Contents/Resources/codex",
23297
- join5(userHome, "Applications", "ChatGPT.app", "Contents", "Resources", "codex"),
23356
+ join6(userHome, "Applications", "ChatGPT.app", "Contents", "Resources", "codex"),
23298
23357
  configured
23299
23358
  ])];
23300
23359
  }
23301
23360
  function bundledWindowsCodex(userHome) {
23302
- const bin = join5(userHome, "AppData", "Local", "OpenAI", "Codex", "bin");
23361
+ const bin = join6(userHome, "AppData", "Local", "OpenAI", "Codex", "bin");
23303
23362
  try {
23304
- const newest = readdirSync2(bin, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join5(bin, entry.name, "codex.exe")).filter((candidate2) => existsSync2(candidate2)).map((candidate2) => ({ candidate: candidate2, modified: statSync2(candidate2).mtimeMs })).sort((left, right) => right.modified - left.modified);
23363
+ const newest = readdirSync2(bin, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join6(bin, entry.name, "codex.exe")).filter((candidate2) => existsSync2(candidate2)).map((candidate2) => ({ candidate: candidate2, modified: statSync2(candidate2).mtimeMs })).sort((left, right) => right.modified - left.modified);
23305
23364
  const candidate = newest[0]?.candidate;
23306
23365
  return candidate === void 0 ? [] : [candidate];
23307
23366
  } catch {
@@ -23511,7 +23570,7 @@ function mapAppServerError(error) {
23511
23570
  }
23512
23571
  return new RpcError("CODEX_UPSTREAM_ERROR", "Codex App Server could not complete the request.");
23513
23572
  }
23514
- function errorCode2(error) {
23573
+ function errorCode3(error) {
23515
23574
  if (error instanceof RpcError || error instanceof CodexAppServerError) return error.code;
23516
23575
  return "CODEX_START_FAILED";
23517
23576
  }
@@ -24714,7 +24773,7 @@ var HostServerConnection = class {
24714
24773
  await this.connectOnce();
24715
24774
  delayMs = this.config.reconnect.initialDelayMs;
24716
24775
  } catch (error) {
24717
- const code = errorCode3(error);
24776
+ const code = errorCode4(error);
24718
24777
  if (code === "CREDENTIALS_REFRESHED") continue;
24719
24778
  this.terminalError = code;
24720
24779
  this.logger.warn("server control connection failed", {
@@ -24729,7 +24788,7 @@ var HostServerConnection = class {
24729
24788
  try {
24730
24789
  await this.api.clearAuthorization();
24731
24790
  } catch (clearError) {
24732
- this.logger.error("failed to clear revoked Host authorization", { code: errorCode3(clearError) });
24791
+ this.logger.error("failed to clear revoked Host authorization", { code: errorCode4(clearError) });
24733
24792
  }
24734
24793
  }
24735
24794
  if (TERMINAL_AUTH_ERRORS.has(code) || !this.config.reconnect.enabled) return;
@@ -24809,7 +24868,7 @@ var HostServerConnection = class {
24809
24868
  if (!acknowledged) throw new ControlConnectionError("INVALID_MESSAGE", "Server sent a frame before hello.ack.");
24810
24869
  await this.handleFrame(frame);
24811
24870
  }).catch((error) => {
24812
- const code = errorCode3(error);
24871
+ const code = errorCode4(error);
24813
24872
  this.terminalError = code;
24814
24873
  this.logger.error("server control frame failed", {
24815
24874
  code,
@@ -24921,7 +24980,7 @@ var HostServerConnection = class {
24921
24980
  this.sendControl("connect.rejected", { connectionId: payload.connectionId });
24922
24981
  this.logger.warn("connection rejected by account authorization", {
24923
24982
  clientDeviceId: shortId3(payload.clientDeviceId),
24924
- code: errorCode3(error)
24983
+ code: errorCode4(error)
24925
24984
  });
24926
24985
  return;
24927
24986
  }
@@ -25070,7 +25129,7 @@ var HostServerConnection = class {
25070
25129
  } catch (error) {
25071
25130
  this.logger.warn("TURN credentials unavailable; trying direct candidates", {
25072
25131
  connectionId: shortId3(tunnel.connectionId),
25073
- code: errorCode3(error)
25132
+ code: errorCode4(error)
25074
25133
  });
25075
25134
  }
25076
25135
  if (!tunnel.preferredTransports.includes("turn")) iceServers = stunOnlyIceServers(iceServers);
@@ -25507,11 +25566,11 @@ function rtcDiagnostics(rtc) {
25507
25566
  return void 0;
25508
25567
  }
25509
25568
  }
25510
- function errorCode3(error) {
25569
+ function errorCode4(error) {
25511
25570
  return error instanceof ServerApiError || error instanceof ControlConnectionError ? error.code : "CONNECTION_FAILED";
25512
25571
  }
25513
25572
  function isRetryable(error) {
25514
- return !TERMINAL_AUTH_ERRORS.has(errorCode3(error)) && (!(error instanceof ServerApiError) || error.retryable);
25573
+ return !TERMINAL_AUTH_ERRORS.has(errorCode4(error)) && (!(error instanceof ServerApiError) || error.retryable);
25515
25574
  }
25516
25575
  function closeCode(code) {
25517
25576
  if (code === 4002) return "AUTH_INVALID";
@@ -25523,7 +25582,7 @@ function closeCode(code) {
25523
25582
  }
25524
25583
 
25525
25584
  // src/remote-directory-browser.ts
25526
- import { readdir as readdir2, stat as stat4 } from "node:fs/promises";
25585
+ import { readdir as readdir3, stat as stat5 } from "node:fs/promises";
25527
25586
  import { homedir as homedir3, platform as platform2 } from "node:os";
25528
25587
  import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute3, parse, resolve as resolve2 } from "node:path";
25529
25588
  var MAX_ENTRIES = 500;
@@ -25532,13 +25591,13 @@ async function listRemoteDirectory(path, signal) {
25532
25591
  const home = resolve2(homedir3());
25533
25592
  const target2 = path === void 0 || path.trim() === "" ? home : resolve2(path);
25534
25593
  if (!isAbsolute3(target2)) throw new Error("The remote directory path must be absolute.");
25535
- const rows = await readdir2(target2, { withFileTypes: true });
25594
+ const rows = await readdir3(target2, { withFileTypes: true });
25536
25595
  const directories = [];
25537
25596
  for (const row of rows) {
25538
25597
  signal?.throwIfAborted();
25539
25598
  const child = resolve2(target2, row.name);
25540
25599
  let directory = row.isDirectory();
25541
- if (!directory && row.isSymbolicLink()) directory = await stat4(child).then((value) => value.isDirectory()).catch(() => false);
25600
+ if (!directory && row.isSymbolicLink()) directory = await stat5(child).then((value) => value.isDirectory()).catch(() => false);
25542
25601
  if (!directory) continue;
25543
25602
  directories.push({ name: row.name, path: child, hidden: platform2() !== "win32" && row.name.startsWith(".") });
25544
25603
  }
@@ -26963,8 +27022,8 @@ import { execFileSync as execFileSync2 } from "node:child_process";
26963
27022
 
26964
27023
  // src/codex-workspace-bridge.ts
26965
27024
  import { spawn as spawn4 } from "node:child_process";
26966
- import { realpath as realpath2, lstat, readdir as readdir3, readFile as readFile4, stat as stat5, watch } from "node:fs/promises";
26967
- import { isAbsolute as isAbsolute4, join as join6, relative as relative2, resolve as resolve3 } from "node:path";
27025
+ import { realpath as realpath2, lstat, readdir as readdir4, readFile as readFile4, stat as stat6, watch } from "node:fs/promises";
27026
+ import { isAbsolute as isAbsolute4, join as join7, relative as relative2, resolve as resolve3 } from "node:path";
26968
27027
  var MAX_READ_BYTES = 4 * 1024 * 1024;
26969
27028
  var MAX_INPUT_BYTES = 64 * 1024;
26970
27029
  var MAX_COLS = 240;
@@ -27051,17 +27110,17 @@ var CodexWorkspaceBridge = class {
27051
27110
  const target2 = await this.safePath(root, path, endpoint === "workspaceFiles/list");
27052
27111
  try {
27053
27112
  if (endpoint === "workspaceFiles/list") {
27054
- const entries = await readdir3(target2, { withFileTypes: true });
27113
+ const entries = await readdir4(target2, { withFileTypes: true });
27055
27114
  const result = [];
27056
27115
  for (const entry of entries.slice(0, 500)) {
27057
- const item = join6(target2, entry.name);
27116
+ const item = join7(target2, entry.name);
27058
27117
  const info2 = await lstat(item);
27059
27118
  if (info2.isSymbolicLink()) continue;
27060
27119
  result.push({ name: entry.name, type: info2.isDirectory() ? "directory" : info2.isFile() ? "file" : "other", ...info2.isFile() ? { size: info2.size } : {} });
27061
27120
  }
27062
27121
  return { ok: true, value: { path, entries: result, truncated: entries.length > 500 } };
27063
27122
  }
27064
- const info = await stat5(target2);
27123
+ const info = await stat6(target2);
27065
27124
  const absolutePath = target2;
27066
27125
  const version = `${info.mtimeMs}:${info.size}`;
27067
27126
  if (endpoint === "workspaceFiles/stat") {
@@ -27268,7 +27327,7 @@ var CodexWorkspaceBridge = class {
27268
27327
  if (!cwd) throw new RpcError("CODEX_WORKSPACE_UNAVAILABLE", "The CodeX thread has no available workspace.");
27269
27328
  try {
27270
27329
  const root = await realpath2(cwd);
27271
- const info = await stat5(root);
27330
+ const info = await stat6(root);
27272
27331
  if (!info.isDirectory()) throw new Error();
27273
27332
  return root;
27274
27333
  } catch {
@@ -27757,12 +27816,12 @@ var HostPluginRuntime = class {
27757
27816
  }
27758
27817
  async readHarnessVersion() {
27759
27818
  let reportedVersion;
27760
- let errorCode5;
27819
+ let errorCode6;
27761
27820
  try {
27762
27821
  const response = await this.apiProxy?.host.describe({ rpcId: randomUUID3(), payload: {} });
27763
27822
  if (response === void 0) throw new Error("ApiProxy is unavailable");
27764
27823
  if (!response.result.ok) {
27765
- errorCode5 = response.result.error.code;
27824
+ errorCode6 = response.result.error.code;
27766
27825
  } else {
27767
27826
  reportedVersion = normalizeHarnessVersion(response.result.value.version);
27768
27827
  }
@@ -27771,7 +27830,7 @@ var HostPluginRuntime = class {
27771
27830
  const distributionVersion = reportedVersion === void 0 || reportedVersion === "0.0.1" ? await readHarnessDistributionVersion() : void 0;
27772
27831
  const version = selectHarnessVersion(reportedVersion, distributionVersion);
27773
27832
  if (version !== void 0) return version;
27774
- this.logger.warn("Harness version is unavailable", errorCode5 === void 0 ? void 0 : { code: errorCode5 });
27833
+ this.logger.warn("Harness version is unavailable", errorCode6 === void 0 ? void 0 : { code: errorCode6 });
27775
27834
  return void 0;
27776
27835
  }
27777
27836
  hostCapabilities() {
@@ -27827,9 +27886,9 @@ function isPlainRecord(value) {
27827
27886
  }
27828
27887
 
27829
27888
  // src/cli.ts
27830
- import { stat as stat6 } from "node:fs/promises";
27889
+ import { stat as stat7 } from "node:fs/promises";
27831
27890
  import { hostname as hostname3 } from "node:os";
27832
- import { join as join7 } from "node:path";
27891
+ import { join as join8 } from "node:path";
27833
27892
  var QR_POLL_INTERVAL_MS = 2e3;
27834
27893
  var TERMINAL_QR_MARGIN = 4;
27835
27894
  async function runCli(args = process.argv.slice(2), dependencies = {}) {
@@ -27917,7 +27976,7 @@ async function status(args, runtime) {
27917
27976
  `Server: ${serverUrl}`,
27918
27977
  "Host control: enabled (dsh-TUI default)"
27919
27978
  ];
27920
- if (!await exists3(join7(directory, "device.json"))) {
27979
+ if (!await exists3(join8(directory, "device.json"))) {
27921
27980
  lines.push("Device: not initialized", "Authorization: logged out", "Credential: unavailable");
27922
27981
  write(runtime.stdout, `${lines.join("\n")}
27923
27982
 
@@ -27961,7 +28020,7 @@ async function logout(args, runtime) {
27961
28020
  const serverUrl = selectedServer();
27962
28021
  const root = new IdentityStore({ env: runtime.env }).directory;
27963
28022
  const directory = serverStorageDirectory(root, serverUrl, "host");
27964
- if (!await exists3(join7(directory, "device.json"))) {
28023
+ if (!await exists3(join8(directory, "device.json"))) {
27965
28024
  await new ServerCredentialStore(directory).clear();
27966
28025
  write(runtime.stdout, "This Host is already logged out.\n");
27967
28026
  return 0;
@@ -28081,7 +28140,7 @@ function terminalLink(url, target2) {
28081
28140
  }
28082
28141
  async function exists3(path) {
28083
28142
  try {
28084
- await stat6(path);
28143
+ await stat7(path);
28085
28144
  return true;
28086
28145
  } catch (error) {
28087
28146
  if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
@@ -28148,7 +28207,7 @@ function installTuiRemoteCommand(ctx, resolveTarget) {
28148
28207
  } catch (error) {
28149
28208
  return {
28150
28209
  kind: "error",
28151
- text: `Local Remote Host credentials were cleared, but Server revocation failed (${errorCode4(error)}).`
28210
+ text: `Local Remote Host credentials were cleared, but Server revocation failed (${errorCode5(error)}).`
28152
28211
  };
28153
28212
  }
28154
28213
  }
@@ -28204,7 +28263,7 @@ function registerOptional(ctx, feature, register2) {
28204
28263
  return register2() ?? (() => {
28205
28264
  });
28206
28265
  } catch (error) {
28207
- ctx.logger.warn(`dsh-TUI Remote ${feature} is unavailable`, { code: errorCode4(error) });
28266
+ ctx.logger.warn(`dsh-TUI Remote ${feature} is unavailable`, { code: errorCode5(error) });
28208
28267
  return () => {
28209
28268
  };
28210
28269
  }
@@ -28317,7 +28376,7 @@ var RemoteLoginController = class {
28317
28376
  await this.poll(attempt, provider, session, target2.runtime);
28318
28377
  } catch (error) {
28319
28378
  if (attempt !== this.attempt) return;
28320
- this.update({ phase: "error", provider, error: errorCode4(error) });
28379
+ this.update({ phase: "error", provider, error: errorCode5(error) });
28321
28380
  }
28322
28381
  }
28323
28382
  async poll(attempt, provider, session, runtime) {
@@ -28435,7 +28494,7 @@ function terminalLink2(url) {
28435
28494
  function printableWidth(value) {
28436
28495
  return value.replace(/\u001B\[[0-9;]*m/gu, "").length;
28437
28496
  }
28438
- function errorCode4(error) {
28497
+ function errorCode5(error) {
28439
28498
  if (error instanceof ServerApiError) return error.code;
28440
28499
  if (error instanceof Error && "code" in error && typeof error.code === "string") return error.code;
28441
28500
  return "CONNECTION_FAILED";