@jacobbd/relay-ai 0.9.5 → 0.9.7

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.
@@ -11,7 +11,7 @@ import { join } from "path";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@jacobbd/relay-ai",
14
- version: "0.9.5",
14
+ version: "0.9.7",
15
15
  publishConfig: {
16
16
  access: "public"
17
17
  },
@@ -12888,6 +12888,7 @@ ${pc6.bold("Usage:")}
12888
12888
  relay-ai providers auth openai-oauth
12889
12889
  relay-ai providers auth github-copilot
12890
12890
  relay-ai providers auth cline-pass
12891
+ relay-ai providers auth antigravity
12891
12892
 
12892
12893
  ${pc6.bold("Device code (works on SSH/VPS):")}
12893
12894
  xai-oauth SuperGrok / X Premium (device code at x.ai/device)
@@ -12895,11 +12896,14 @@ ${pc6.bold("Device code (works on SSH/VPS):")}
12895
12896
  github-copilot GitHub Copilot Free or paid (device code at github.com/login/device)
12896
12897
  cline-pass ClinePass account (device code at app.cline.bot)
12897
12898
 
12899
+ ${pc6.bold("Browser sign-in:")}
12900
+ antigravity Google Cloud Code Assist OAuth (opens Google sign-in)
12901
+
12898
12902
  ${pc6.dim("OpenCode CLI configs: use")} relay-ai providers import${pc6.dim(" (optional one-time migration).")}`;
12899
12903
  }
12900
12904
 
12901
12905
  // src/codex/app-launch.ts
12902
- import { execSync as execSync2, spawn as spawn3 } from "child_process";
12906
+ import { execFileSync as execFileSync3, execSync as execSync2, spawn as spawn3 } from "child_process";
12903
12907
  import { copyFileSync as copyFileSync3, existsSync as existsSync11, mkdirSync as mkdirSync8, readdirSync as readdirSync2, realpathSync, statSync as statSync3 } from "fs";
12904
12908
  import { homedir as homedir7 } from "os";
12905
12909
  import { dirname as dirname5, join as join12, win32 as winPath } from "path";
@@ -13136,6 +13140,51 @@ function darwinIsRunning() {
13136
13140
  }
13137
13141
  });
13138
13142
  }
13143
+ function pgrepExact(names) {
13144
+ const pids = /* @__PURE__ */ new Set();
13145
+ for (const name of names) {
13146
+ try {
13147
+ for (const raw of run(`pgrep -x ${JSON.stringify(name)}`).split(/\s+/)) {
13148
+ const pid = Number.parseInt(raw, 10);
13149
+ if (Number.isFinite(pid) && pid > 0 && pid !== process.pid) pids.add(pid);
13150
+ }
13151
+ } catch {
13152
+ }
13153
+ }
13154
+ return [...pids];
13155
+ }
13156
+ function darwinMainExecutableCandidates(appPath) {
13157
+ return DARWIN_APP_NAMES.map((name) => join12(appPath, "Contents", "MacOS", name));
13158
+ }
13159
+ function darwinMainPidsFromProcessList(processList, commands, currentPid = process.pid) {
13160
+ const pids = /* @__PURE__ */ new Set();
13161
+ for (const line of processList.split("\n")) {
13162
+ const match = line.match(/^\s*(\d+)\s+(.+?)\s*$/);
13163
+ if (!match) continue;
13164
+ const pid = Number.parseInt(match[1], 10);
13165
+ const command = match[2];
13166
+ if (Number.isFinite(pid) && pid > 0 && pid !== currentPid && commands.some((candidate) => command === candidate || command.startsWith(`${candidate} `))) {
13167
+ pids.add(pid);
13168
+ }
13169
+ }
13170
+ return [...pids];
13171
+ }
13172
+ function darwinMatchingPids() {
13173
+ const appPath = findCodexApp("darwin");
13174
+ if (!appPath) return [];
13175
+ try {
13176
+ const processList = execFileSync3("ps", ["-axo", "pid=,command="], {
13177
+ encoding: "utf8",
13178
+ stdio: ["pipe", "pipe", "pipe"]
13179
+ });
13180
+ return darwinMainPidsFromProcessList(
13181
+ processList,
13182
+ darwinMainExecutableCandidates(appPath)
13183
+ );
13184
+ } catch {
13185
+ return [];
13186
+ }
13187
+ }
13139
13188
  function linuxIsRunning() {
13140
13189
  for (const name of ["ChatGPT", "chatgpt"]) {
13141
13190
  try {
@@ -13145,6 +13194,9 @@ function linuxIsRunning() {
13145
13194
  }
13146
13195
  return false;
13147
13196
  }
13197
+ function linuxMatchingPids() {
13198
+ return pgrepExact(["ChatGPT", "chatgpt"]);
13199
+ }
13148
13200
  function winMatchingPids() {
13149
13201
  try {
13150
13202
  const nameFilter = WIN_APP_NAMES.map((name) => `Name = '${name}.exe'`).join(" OR ");
@@ -13173,21 +13225,43 @@ function isCodexAppRunning() {
13173
13225
  if (process.platform === "linux") return linuxIsRunning();
13174
13226
  return false;
13175
13227
  }
13228
+ function codexAppMainPids(platform = process.platform) {
13229
+ if (platform === "darwin") return darwinMatchingPids();
13230
+ if (platform === "win32") return winMatchingPids();
13231
+ if (platform === "linux") return linuxMatchingPids();
13232
+ return [];
13233
+ }
13234
+ function pidIsAlive(pid) {
13235
+ try {
13236
+ process.kill(pid, 0);
13237
+ return true;
13238
+ } catch (err) {
13239
+ return err.code === "EPERM";
13240
+ }
13241
+ }
13176
13242
  function sleep(ms) {
13177
13243
  return new Promise((resolve) => setTimeout(resolve, ms));
13178
13244
  }
13179
- async function waitForQuit(timeoutMs) {
13245
+ async function waitForOriginalCodexPids(originalPids, timeoutMs, alive = pidIsAlive) {
13180
13246
  const deadline = Date.now() + timeoutMs;
13181
13247
  while (Date.now() < deadline) {
13182
- if (process.platform === "win32") {
13183
- if (winMatchingPids().length === 0) return true;
13184
- } else if (process.platform === "linux" ? !linuxIsRunning() : !darwinIsRunning()) {
13185
- return true;
13186
- }
13248
+ if (originalPids.every((pid) => !alive(pid))) return true;
13249
+ await sleep(200);
13250
+ }
13251
+ return originalPids.every((pid) => !alive(pid));
13252
+ }
13253
+ async function waitForCodexAppQuit(timeoutMs = 5e3) {
13254
+ const originalPids = codexAppMainPids();
13255
+ if (originalPids.length > 0) {
13256
+ const exited = await waitForOriginalCodexPids(originalPids, timeoutMs);
13257
+ return exited && !isCodexAppRunning();
13258
+ }
13259
+ const deadline = Date.now() + timeoutMs;
13260
+ while (Date.now() < deadline) {
13261
+ if (!isCodexAppRunning()) return true;
13187
13262
  await sleep(200);
13188
13263
  }
13189
- if (process.platform === "win32") return winMatchingPids().length === 0;
13190
- return process.platform === "linux" ? !linuxIsRunning() : !darwinIsRunning();
13264
+ return !isCodexAppRunning();
13191
13265
  }
13192
13266
  function openCodexAppAt(path) {
13193
13267
  if (process.platform === "darwin") {
@@ -13219,12 +13293,11 @@ function openCodexApp() {
13219
13293
  }
13220
13294
  openCodexAppAt(path);
13221
13295
  }
13296
+ function darwinQuitAppleScript() {
13297
+ return `tell application id "${CODEX_BUNDLE_ID}" to quit`;
13298
+ }
13222
13299
  function darwinQuit() {
13223
- try {
13224
- execSync2(`osascript -e 'tell application "Codex" to quit'`, { stdio: "pipe" });
13225
- } catch {
13226
- execSync2(`osascript -e 'tell application id "${CODEX_BUNDLE_ID}" to quit'`, { stdio: "pipe" });
13227
- }
13300
+ execFileSync3("osascript", ["-e", darwinQuitAppleScript()], { stdio: "pipe" });
13228
13301
  }
13229
13302
  function winQuitGraceful() {
13230
13303
  const nameFilter = WIN_APP_NAMES.map((name) => `'${name}'`).join(",");
@@ -13246,13 +13319,19 @@ function quitCodexAppGracefully() {
13246
13319
  else if (process.platform === "win32") winQuitGraceful();
13247
13320
  else if (process.platform === "linux") linuxQuitGraceful();
13248
13321
  }
13249
- function winForceQuit() {
13250
- const pids = winMatchingPids();
13322
+ function winForceQuit(pids = winMatchingPids()) {
13251
13323
  if (pids.length === 0) return;
13252
13324
  runPowerShell(`Stop-Process -Id ${pids.join(",")} -Force -ErrorAction SilentlyContinue`);
13253
13325
  }
13326
+ function restartTimeoutAction(platform) {
13327
+ return platform === "win32" ? "force-quit" : "fail-closed";
13328
+ }
13329
+ function gracefulQuitTimeoutMs(platform) {
13330
+ return platform === "darwin" ? 3e4 : 5e3;
13331
+ }
13254
13332
  async function launchOrRestartCodexApp(prompt = "Restart ChatGPT Desktop to apply relay-ai settings?", assumeYes = false) {
13255
13333
  const appPath = findCodexApp();
13334
+ const originalPids = codexAppMainPids();
13256
13335
  if (!isCodexAppRunning()) {
13257
13336
  if (!appPath) {
13258
13337
  throw new Error(
@@ -13262,6 +13341,9 @@ async function launchOrRestartCodexApp(prompt = "Restart ChatGPT Desktop to appl
13262
13341
  openCodexAppAt(appPath);
13263
13342
  return;
13264
13343
  }
13344
+ if (originalPids.length === 0) {
13345
+ throw new Error("ChatGPT Desktop is running but Relay could not identify its main process; refusing an unsafe restart.");
13346
+ }
13265
13347
  if (process.platform === "linux") {
13266
13348
  p6.log.info("Restarting ChatGPT Desktop to apply relay-ai settings...");
13267
13349
  linuxQuitGraceful();
@@ -13277,10 +13359,18 @@ async function launchOrRestartCodexApp(prompt = "Restart ChatGPT Desktop to appl
13277
13359
  if (process.platform === "darwin") darwinQuit();
13278
13360
  else if (process.platform === "win32") winQuitGraceful();
13279
13361
  }
13280
- if (!await waitForQuit(5e3)) {
13281
- if (process.platform === "win32") winForceQuit();
13282
- await waitForQuit(5e3);
13362
+ const gracefulTimeout = gracefulQuitTimeoutMs(process.platform);
13363
+ if (!await waitForOriginalCodexPids(originalPids, gracefulTimeout)) {
13364
+ if (restartTimeoutAction(process.platform) === "force-quit") {
13365
+ winForceQuit(originalPids);
13366
+ if (!await waitForOriginalCodexPids(originalPids, 5e3)) {
13367
+ throw new Error("ChatGPT Desktop did not exit after its force-quit timeout; refusing to launch a duplicate process.");
13368
+ }
13369
+ } else {
13370
+ throw new Error("ChatGPT Desktop did not exit after graceful shutdown; refusing to relaunch or force-quit it.");
13371
+ }
13283
13372
  }
13373
+ if (isCodexAppRunning()) return;
13284
13374
  if (appPath) openCodexAppAt(appPath);
13285
13375
  else openCodexApp();
13286
13376
  }
@@ -13356,7 +13446,7 @@ function linuxWhichClaude() {
13356
13446
  return null;
13357
13447
  }
13358
13448
  }
13359
- function linuxMatchingPids() {
13449
+ function linuxMatchingPids2() {
13360
13450
  try {
13361
13451
  const out = run2("pgrep -x claude-desktop");
13362
13452
  return out.split(/\s+/).map((s) => Number.parseInt(s, 10)).filter((n) => Number.isFinite(n) && n > 0);
@@ -13365,7 +13455,7 @@ function linuxMatchingPids() {
13365
13455
  }
13366
13456
  }
13367
13457
  function linuxMainPid() {
13368
- const pids = linuxMatchingPids();
13458
+ const pids = linuxMatchingPids2();
13369
13459
  for (const pid of pids) {
13370
13460
  try {
13371
13461
  const cmdline = readFileSync12(`/proc/${pid}/cmdline`, "utf8");
@@ -13384,7 +13474,7 @@ function linuxQuit() {
13384
13474
  }
13385
13475
  }
13386
13476
  function linuxForceQuit() {
13387
- for (const pid of linuxMatchingPids()) {
13477
+ for (const pid of linuxMatchingPids2()) {
13388
13478
  try {
13389
13479
  process.kill(pid, "SIGKILL");
13390
13480
  } catch {
@@ -13463,26 +13553,26 @@ function winHasWindow2() {
13463
13553
  function isClaudeAppRunning() {
13464
13554
  if (process.platform === "darwin") return darwinIsRunning2();
13465
13555
  if (process.platform === "win32") return winMatchingPids2().length > 0 || winHasWindow2();
13466
- if (process.platform === "linux") return linuxMatchingPids().length > 0;
13556
+ if (process.platform === "linux") return linuxMatchingPids2().length > 0;
13467
13557
  return false;
13468
13558
  }
13469
13559
  function sleep2(ms) {
13470
13560
  return new Promise((resolve) => setTimeout(resolve, ms));
13471
13561
  }
13472
- async function waitForQuit2(timeoutMs) {
13562
+ async function waitForQuit(timeoutMs) {
13473
13563
  const deadline = Date.now() + timeoutMs;
13474
13564
  while (Date.now() < deadline) {
13475
13565
  if (process.platform === "win32") {
13476
13566
  if (winMatchingPids2().length === 0) return true;
13477
13567
  } else if (process.platform === "linux") {
13478
- if (linuxMatchingPids().length === 0) return true;
13568
+ if (linuxMatchingPids2().length === 0) return true;
13479
13569
  } else if (!darwinIsRunning2()) {
13480
13570
  return true;
13481
13571
  }
13482
13572
  await sleep2(200);
13483
13573
  }
13484
13574
  if (process.platform === "win32") return winMatchingPids2().length === 0;
13485
- if (process.platform === "linux") return linuxMatchingPids().length === 0;
13575
+ if (process.platform === "linux") return linuxMatchingPids2().length === 0;
13486
13576
  return !darwinIsRunning2();
13487
13577
  }
13488
13578
  function openClaudeAppAt(path) {
@@ -13558,10 +13648,10 @@ async function launchOrRestartClaudeApp(prompt = "Restart Claude Desktop to appl
13558
13648
  if (process.platform === "darwin") darwinQuit2();
13559
13649
  else if (process.platform === "win32") winQuitGraceful2();
13560
13650
  }
13561
- if (!await waitForQuit2(5e3)) {
13651
+ if (!await waitForQuit(5e3)) {
13562
13652
  if (process.platform === "win32") winForceQuit2();
13563
13653
  else if (process.platform === "linux") linuxForceQuit();
13564
- await waitForQuit2(5e3);
13654
+ await waitForQuit(5e3);
13565
13655
  }
13566
13656
  if (appPath) openClaudeAppAt(appPath);
13567
13657
  else openClaudeApp();
@@ -13631,6 +13721,7 @@ export {
13631
13721
  getAppHome,
13632
13722
  getConfigPath,
13633
13723
  getProvidersPath,
13724
+ getLogsPath,
13634
13725
  loadPreferences,
13635
13726
  savePreferences,
13636
13727
  getAppPathOverride,
@@ -13806,6 +13897,7 @@ export {
13806
13897
  findCodexApp,
13807
13898
  findEmbeddedCodexBinary,
13808
13899
  isCodexAppRunning,
13900
+ waitForCodexAppQuit,
13809
13901
  quitCodexAppGracefully,
13810
13902
  launchOrRestartCodexApp,
13811
13903
  codexAppInstallHint,
@@ -13818,4 +13910,4 @@ export {
13818
13910
  supportsClaudeTransparentMode,
13819
13911
  buildHttpProxyRoutes
13820
13912
  };
13821
- //# sourceMappingURL=chunk-SCW2TYSG.js.map
13913
+ //# sourceMappingURL=chunk-BJ4AE3PS.js.map