@agent-proxy/gateway 0.20.2 → 0.20.4

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.
Files changed (2) hide show
  1. package/dist/index.js +162 -42
  2. package/package.json +9 -9
package/dist/index.js CHANGED
@@ -220,8 +220,14 @@ function resolvePaths(env, runtimeHomeOverride) {
220
220
  logDir: path2.join(runtimeHome, "logs"),
221
221
  uiTokenFile: path2.join(runtimeHome, "ui-token"),
222
222
  credentialsFile: path2.join(runtimeHome, "credentials.json"),
223
- launchAgent: runtimeHomeOverride ? path2.join(runtimeHome, "Library", "LaunchAgents", "codex-cliproxy-gateway-temp.plist") : path2.join(home, "Library", "LaunchAgents", `codex-cliproxy-gateway${instanceSuffix}.plist`),
224
- webUiLaunchAgent: runtimeHomeOverride ? path2.join(runtimeHome, "Library", "LaunchAgents", "codex-cliproxy-webui-temp.plist") : path2.join(home, "Library", "LaunchAgents", `codex-cliproxy-webui${instanceSuffix}.plist`)
223
+ launchAgent: runtimeHomeOverride ? path2.join(runtimeHome, "Library", "LaunchAgents", "agent-proxy-gateway-temp.plist") : path2.join(home, "Library", "LaunchAgents", `agent-proxy-gateway${instanceSuffix}.plist`),
224
+ webUiLaunchAgent: runtimeHomeOverride ? path2.join(runtimeHome, "Library", "LaunchAgents", "agent-proxy-webui-temp.plist") : path2.join(home, "Library", "LaunchAgents", `agent-proxy-webui${instanceSuffix}.plist`)
225
+ };
226
+ }
227
+ function legacyLaunchAgentPaths(paths) {
228
+ return {
229
+ legacyGatewayPlist: path2.join(paths.home, "Library", "LaunchAgents", `codex-cliproxy-gateway${paths.instanceSuffix}.plist`),
230
+ legacyWebUiPlist: path2.join(paths.home, "Library", "LaunchAgents", `codex-cliproxy-webui${paths.instanceSuffix}.plist`)
225
231
  };
226
232
  }
227
233
  function catalogFileFor(paths, upstreamType) {
@@ -264,7 +270,8 @@ import path6 from "path";
264
270
  import fs3 from "fs";
265
271
  import path3 from "path";
266
272
  import { randomUUID } from "crypto";
267
- var OWNER_FILE = ".codex-cliproxy-owner.json";
273
+ var OWNER_FILE = ".agent-proxy-owner.json";
274
+ var LEGACY_OWNER_FILE = ".codex-cliproxy-owner.json";
268
275
  function readState(file) {
269
276
  if (!fs3.existsSync(file))
270
277
  return;
@@ -324,6 +331,36 @@ function resolveInstalledCodexPaths(paths, env = process.env) {
324
331
  function ownerPath(paths) {
325
332
  return path3.join(paths.codexHome, OWNER_FILE);
326
333
  }
334
+ function legacyOwnerPath(paths) {
335
+ return path3.join(paths.codexHome, LEGACY_OWNER_FILE);
336
+ }
337
+ function readOwnerAny(paths) {
338
+ const file = ownerPath(paths);
339
+ if (!fs3.existsSync(file))
340
+ return readOwner(legacyOwnerPath(paths));
341
+ const owner = readOwner(file);
342
+ const legacy = legacyOwnerPath(paths);
343
+ if (fs3.existsSync(legacy) && readOwner(legacy) !== owner) {
344
+ throw new Error(`Codex ownership conflict between ${file} and ${legacy}; preserve both files and repair their ownership before retrying install`);
345
+ }
346
+ return owner;
347
+ }
348
+ function adoptOwnerFile(paths, acceptableLegacyOwner) {
349
+ const file = ownerPath(paths);
350
+ const legacy = legacyOwnerPath(paths);
351
+ if (!fs3.existsSync(legacy))
352
+ return;
353
+ if (!fs3.existsSync(file)) {
354
+ fs3.renameSync(legacy, file);
355
+ return;
356
+ }
357
+ const current = readOwner(file);
358
+ const previous = readOwner(legacy);
359
+ if (previous !== current && previous !== acceptableLegacyOwner) {
360
+ throw new Error(`Codex ownership conflict between ${file} and ${legacy}; preserve both files and repair their ownership before retrying install`);
361
+ }
362
+ fs3.rmSync(legacy);
363
+ }
327
364
  function readOwner(file) {
328
365
  if (!fs3.existsSync(file))
329
366
  return;
@@ -338,10 +375,7 @@ function readOwner(file) {
338
375
  }
339
376
  }
340
377
  function codexOwnerRuntimeHome(paths) {
341
- const file = ownerPath(paths);
342
- if (!fs3.existsSync(file))
343
- return;
344
- return readOwner(file);
378
+ return readOwnerAny(paths);
345
379
  }
346
380
  function assertOwner(paths, owner) {
347
381
  if (owner && owner !== realPathOrResolve(paths.runtimeHome)) {
@@ -356,9 +390,10 @@ function xmlValue(plist, key) {
356
390
  return match?.[1] === undefined ? undefined : decodeXml(match[1]);
357
391
  }
358
392
  function resolveLegacyServiceCodexPaths(paths, env) {
359
- if (!fs3.existsSync(paths.launchAgent))
393
+ const plistPath = fs3.existsSync(paths.launchAgent) ? paths.launchAgent : legacyLaunchAgentPaths(paths).legacyGatewayPlist;
394
+ if (!fs3.existsSync(plistPath))
360
395
  return resolveInstalledCodexPaths(paths, env);
361
- const plist = fs3.readFileSync(paths.launchAgent, "utf8");
396
+ const plist = fs3.readFileSync(plistPath, "utf8");
362
397
  const args = plist.match(/<key>\s*ProgramArguments\s*<\/key>\s*<array>([\s\S]*?)<\/array>/)?.[1];
363
398
  const configArg = args?.match(/<string>--config<\/string>\s*<string>([^<]*)<\/string>/)?.[1];
364
399
  if (!configArg || decodeXml(configArg) !== paths.gatewayConfig)
@@ -380,8 +415,9 @@ function assertLegacyOwners(paths) {
380
415
  const clientForState = (state) => typeof state.codexHome === "string" ? normalizeCodexHome(state.codexHome, paths.home) : backupCodexHome(state, paths.home) ?? realPathOrResolve(path3.join(paths.home, ".codex"));
381
416
  const defaultTransferred = nextDefaultState?.defaultRuntimeMigratedFrom === realPathOrResolve(defaultRuntime) && legacyDefaultState !== undefined && clientForState(nextDefaultState) === clientForState(legacyDefaultState) && clientForState(nextDefaultState) === realPathOrResolve(paths.codexHome);
382
417
  const migratedFrom = readState(paths.stateFile)?.defaultRuntimeMigratedFrom;
383
- const owner = readOwner(ownerPath(paths));
384
- const ownerMigration = owner ? JSON.parse(fs3.readFileSync(ownerPath(paths), "utf8")).defaultRuntimeMigratedFrom : undefined;
418
+ const owner = readOwnerAny(paths);
419
+ const ownerFile = fs3.existsSync(ownerPath(paths)) ? ownerPath(paths) : legacyOwnerPath(paths);
420
+ const ownerMigration = owner ? JSON.parse(fs3.readFileSync(ownerFile, "utf8")).defaultRuntimeMigratedFrom : undefined;
385
421
  const migrated = defaultTransferred || (migratedFrom === realPathOrResolve(defaultRuntime) || ownerMigration === realPathOrResolve(defaultRuntime)) && owner === realPathOrResolve(paths.runtimeHome);
386
422
  for (const runtimeHome of [nextDefaultRuntime, defaultRuntime]) {
387
423
  if (migrated && runtimeHome === defaultRuntime)
@@ -397,7 +433,7 @@ function assertLegacyOwners(paths) {
397
433
  if (!fs3.existsSync(agentsDir))
398
434
  return;
399
435
  for (const entry of fs3.readdirSync(agentsDir)) {
400
- if (!/^codex-cliproxy-gateway(?:-[0-9a-f]{8})?\.plist$/.test(entry))
436
+ if (!/^(?:codex-cliproxy|agent-proxy)-gateway(?:-[0-9a-f]{8})?\.plist$/.test(entry))
401
437
  continue;
402
438
  const plist = fs3.readFileSync(path3.join(agentsDir, entry), "utf8");
403
439
  const args = plist.match(/<key>\s*ProgramArguments\s*<\/key>\s*<array>([\s\S]*?)<\/array>/)?.[1];
@@ -415,9 +451,10 @@ function assertLegacyOwners(paths) {
415
451
  }
416
452
  }
417
453
  function hasLegacyCodexOwnership(paths, legacyRuntimeHome) {
418
- return readOwner(ownerPath(paths)) === realPathOrResolve(legacyRuntimeHome);
454
+ return readOwnerAny(paths) === realPathOrResolve(legacyRuntimeHome);
419
455
  }
420
456
  function migrateCodexOwnership(paths, legacyRuntimeHome) {
457
+ adoptOwnerFile(paths, realPathOrResolve(legacyRuntimeHome));
421
458
  const file = ownerPath(paths);
422
459
  const owner = readOwner(file);
423
460
  const current = realPathOrResolve(paths.runtimeHome);
@@ -450,11 +487,12 @@ function assertCodexHomeAvailable(paths, manual) {
450
487
  if (paths.instanceSuffix && realPathOrResolve(paths.codexHome) === realPathOrResolve(path3.join(paths.home, ".codex"))) {
451
488
  throw new Error("Custom gateway instances must use a distinct CODEX_HOME; the default ~/.codex directory cannot be managed. Use --manual-codex-config instead");
452
489
  }
453
- assertOwner(paths, readOwner(ownerPath(paths)));
490
+ assertOwner(paths, readOwnerAny(paths));
454
491
  assertLegacyOwners(paths);
455
492
  }
456
493
  function claimCodexHome(paths) {
457
494
  assertCodexHomeAvailable(paths, false);
495
+ adoptOwnerFile(paths, realPathOrResolve(paths.runtimeHome));
458
496
  const file = ownerPath(paths);
459
497
  const existingOwner = readOwner(file);
460
498
  assertOwner(paths, existingOwner);
@@ -487,10 +525,11 @@ function claimCodexHome(paths) {
487
525
  };
488
526
  }
489
527
  function releaseCodexHome(paths) {
490
- const file = ownerPath(paths);
491
- const owner = readOwner(file);
492
- if (owner === realPathOrResolve(paths.runtimeHome))
493
- fs3.rmSync(file);
528
+ const current = realPathOrResolve(paths.runtimeHome);
529
+ for (const file of [ownerPath(paths), legacyOwnerPath(paths)]) {
530
+ if (readOwner(file) === current)
531
+ fs3.rmSync(file);
532
+ }
494
533
  }
495
534
 
496
535
  // src/keychain.ts
@@ -696,7 +735,8 @@ function deleteUpstreamApiKey(file) {
696
735
  }
697
736
 
698
737
  // src/keychain.ts
699
- var KEYCHAIN_SERVICE = "codex-cliproxy-gateway";
738
+ var KEYCHAIN_SERVICE = "agent-proxy-gateway";
739
+ var LEGACY_KEYCHAIN_SERVICE = "codex-cliproxy-gateway";
700
740
  function keychainService() {
701
741
  return KEYCHAIN_SERVICE + resolvePaths().instanceSuffix;
702
742
  }
@@ -721,7 +761,7 @@ function saveApiKeyInKeychain(apiKey, account, service, execute) {
721
761
  apiKey
722
762
  ]);
723
763
  }
724
- function readApiKeyFromKeychain(optional, account, service, execute) {
764
+ function findKeychainPassword(account, service, execute) {
725
765
  try {
726
766
  return execute([
727
767
  "find-generic-password",
@@ -732,9 +772,7 @@ function readApiKeyFromKeychain(optional, account, service, execute) {
732
772
  "-w"
733
773
  ]);
734
774
  } catch {
735
- if (optional)
736
- return "";
737
- throw new Error(`Upstream API key was not found in macOS Keychain (service: ${service})`);
775
+ return;
738
776
  }
739
777
  }
740
778
  function deleteApiKeyFromKeychain(account, service, execute) {
@@ -751,11 +789,28 @@ function deleteApiKeyFromKeychain(account, service, execute) {
751
789
  function createApiKeyStore(platform = process.platform, credentialsFile = resolvePaths().credentialsFile, executeKeychain = security) {
752
790
  if (platform === "darwin") {
753
791
  const account = keychainAccount();
792
+ const instanceSuffix = resolvePaths().instanceSuffix;
754
793
  const service = keychainService();
755
794
  return {
756
795
  save: (apiKey) => saveApiKeyInKeychain(apiKey, account, service, executeKeychain),
757
- read: (optional = false) => readApiKeyFromKeychain(optional, account, service, executeKeychain),
758
- delete: () => deleteApiKeyFromKeychain(account, service, executeKeychain)
796
+ read: (optional = false) => {
797
+ const current = findKeychainPassword(account, service, executeKeychain);
798
+ if (current !== undefined)
799
+ return current;
800
+ if (instanceSuffix === "") {
801
+ const legacy = findKeychainPassword(account, LEGACY_KEYCHAIN_SERVICE, executeKeychain);
802
+ if (legacy !== undefined)
803
+ return legacy;
804
+ }
805
+ if (optional)
806
+ return "";
807
+ throw new Error(`Upstream API key was not found in macOS Keychain (service: ${service})`);
808
+ },
809
+ delete: () => {
810
+ deleteApiKeyFromKeychain(account, service, executeKeychain);
811
+ if (instanceSuffix === "")
812
+ deleteApiKeyFromKeychain(account, LEGACY_KEYCHAIN_SERVICE, executeKeychain);
813
+ }
759
814
  };
760
815
  }
761
816
  return {
@@ -779,14 +834,22 @@ import fs6 from "fs";
779
834
  import path5 from "path";
780
835
  import { randomUUID as randomUUID2 } from "crypto";
781
836
  import { execFileSync as execFileSync2 } from "child_process";
782
- var LAUNCHD_LABEL = "codex-cliproxy-gateway";
783
- var WEBUI_LAUNCHD_LABEL = "codex-cliproxy-webui";
837
+ var LAUNCHD_LABEL = "agent-proxy-gateway";
838
+ var WEBUI_LAUNCHD_LABEL = "agent-proxy-webui";
839
+ var LEGACY_LAUNCHD_LABEL = "codex-cliproxy-gateway";
840
+ var LEGACY_WEBUI_LAUNCHD_LABEL = "codex-cliproxy-webui";
784
841
  function gatewayServiceLabel(instanceSuffix) {
785
842
  return `${LAUNCHD_LABEL}${instanceSuffix}`;
786
843
  }
787
844
  function webUiServiceLabel(instanceSuffix) {
788
845
  return `${WEBUI_LAUNCHD_LABEL}${instanceSuffix}`;
789
846
  }
847
+ function legacyGatewayServiceLabel(instanceSuffix) {
848
+ return `${LEGACY_LAUNCHD_LABEL}${instanceSuffix}`;
849
+ }
850
+ function legacyWebUiServiceLabel(instanceSuffix) {
851
+ return `${LEGACY_WEBUI_LAUNCHD_LABEL}${instanceSuffix}`;
852
+ }
790
853
  function xmlEscape(value) {
791
854
  return String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
792
855
  }
@@ -868,6 +931,7 @@ function startWebUiLaunchAgent(plistPath, options) {
868
931
  fs6.mkdirSync(path5.dirname(plistPath), { recursive: true });
869
932
  atomicWrite(plistPath, renderWebUiAgent(options), 420);
870
933
  const domain = launchDomain();
934
+ launchctl(["bootout", `${domain}/${legacyLabelForLabel(options.label, "webui")}`], true);
871
935
  launchctl(["bootout", domain, plistPath], true);
872
936
  launchctl(["bootstrap", domain, plistPath]);
873
937
  launchctl(["kickstart", `${domain}/${options.label}`]);
@@ -875,6 +939,25 @@ function startWebUiLaunchAgent(plistPath, options) {
875
939
  function launchDomain() {
876
940
  return `gui/${process.getuid?.() ?? process.geteuid?.() ?? 0}`;
877
941
  }
942
+ function adoptLegacyLaunchAgents(options) {
943
+ const renamed = [];
944
+ for (const [legacy, next] of [[options.legacyGatewayPlist, options.gatewayPlist], [options.legacyWebUiPlist, options.webUiPlist]]) {
945
+ if (!fs6.existsSync(legacy) || fs6.existsSync(next))
946
+ continue;
947
+ fs6.renameSync(legacy, next);
948
+ renamed.push(next);
949
+ }
950
+ return renamed;
951
+ }
952
+ function removeLegacyLaunchAgents(options, execute = launchctl) {
953
+ const domain = launchDomain();
954
+ execute(["bootout", `${domain}/${legacyGatewayServiceLabel(options.instanceSuffix)}`], true);
955
+ execute(["bootout", `${domain}/${legacyWebUiServiceLabel(options.instanceSuffix)}`], true);
956
+ for (const plistPath of [options.legacyGatewayPlist, options.legacyWebUiPlist]) {
957
+ execute(["bootout", domain, plistPath], true);
958
+ fs6.rmSync(plistPath, { force: true });
959
+ }
960
+ }
878
961
  function decodePlistString(value) {
879
962
  return value.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&quot;", '"').replaceAll("&apos;", "'").replaceAll("&amp;", "&");
880
963
  }
@@ -882,6 +965,9 @@ function plistString(source, key) {
882
965
  const value = source.match(new RegExp(`<key>\\s*${key}\\s*</key>\\s*<string>([^<]*)</string>`))?.[1];
883
966
  return value === undefined ? undefined : decodePlistString(value);
884
967
  }
968
+ function plistServiceLabel(source, fallback) {
969
+ return plistString(source, "Label") ?? fallback;
970
+ }
885
971
  function programArguments(source) {
886
972
  const array = source.match(/<key>\s*ProgramArguments\s*<\/key>\s*<array>([\s\S]*?)<\/array>/)?.[1];
887
973
  return array ? [...array.matchAll(/<string>([^<]*)<\/string>/g)].map((value) => decodePlistString(value[1])) : [];
@@ -895,7 +981,7 @@ function prepareManagedWebUiMigration(options, execute = launchctl) {
895
981
  const gatewayArgs = programArguments(gateway);
896
982
  if (plistString(before, "CODEX_CLIPROXY_HOME") !== undefined)
897
983
  return;
898
- if (plistString(before, "Label") !== WEBUI_LAUNCHD_LABEL || plistString(before, "CODEX_CLIPROXY_UI_SERVICE") !== "1" || args.length !== 3 || args[2] !== "web" || gatewayArgs[2] !== "serve" || gatewayArgs[3] !== "--config" || gatewayArgs[4] !== path5.join(options.legacyRuntimeHome, "config.json") || args[0] !== gatewayArgs[0] || args[1] !== gatewayArgs[1] || !/(?:^|\/)(?:src|dist)\/index\.(?:js|ts)$/.test(args[1]))
984
+ if (plistString(before, "Label") !== WEBUI_LAUNCHD_LABEL && plistString(before, "Label") !== LEGACY_WEBUI_LAUNCHD_LABEL || plistString(before, "CODEX_CLIPROXY_UI_SERVICE") !== "1" || args.length !== 3 || args[2] !== "web" || gatewayArgs[2] !== "serve" || gatewayArgs[3] !== "--config" || gatewayArgs[4] !== path5.join(options.legacyRuntimeHome, "config.json") || args[0] !== gatewayArgs[0] || args[1] !== gatewayArgs[1] || !/(?:^|\/)(?:src|dist)\/index\.(?:js|ts)$/.test(args[1]))
899
985
  return;
900
986
  let after = before.replace(/(<key>\s*ProgramArguments\s*<\/key>\s*)<array>[\s\S]*?<\/array>/, (_, prefix) => `${prefix}${plistArray([options.bunPath, options.cliPath, "web"])}`);
901
987
  for (const key of ["StandardOutPath", "StandardErrorPath"]) {
@@ -906,11 +992,12 @@ function prepareManagedWebUiMigration(options, execute = launchctl) {
906
992
  if (after === before)
907
993
  return;
908
994
  const mode = fs6.statSync(options.plistPath).mode & 511;
995
+ const serviceLabel = plistServiceLabel(before, WEBUI_LAUNCHD_LABEL);
909
996
  return () => {
910
997
  const domain = launchDomain();
911
998
  let status;
912
999
  try {
913
- status = execute(["print", `${domain}/${WEBUI_LAUNCHD_LABEL}`]);
1000
+ status = execute(["print", `${domain}/${serviceLabel}`]);
914
1001
  } catch {}
915
1002
  const running = status !== undefined && /\bstate\s*=\s*running\b/.test(status);
916
1003
  const backup = `${options.plistPath}.bak-agent-proxy-migration-${randomUUID2()}`;
@@ -919,10 +1006,11 @@ function prepareManagedWebUiMigration(options, execute = launchctl) {
919
1006
  let touched = false;
920
1007
  const reload = () => {
921
1008
  execute(["bootout", domain, options.plistPath], true);
1009
+ execute(["bootout", `${domain}/${serviceLabel}`], true);
922
1010
  if (status !== undefined) {
923
1011
  execute(["bootstrap", domain, options.plistPath]);
924
1012
  if (running)
925
- execute(["kickstart", `${domain}/${WEBUI_LAUNCHD_LABEL}`]);
1013
+ execute(["kickstart", `${domain}/${serviceLabel}`]);
926
1014
  }
927
1015
  };
928
1016
  const rollback = () => {
@@ -948,11 +1036,17 @@ function prepareManagedWebUiMigration(options, execute = launchctl) {
948
1036
  }
949
1037
  };
950
1038
  }
1039
+ function legacyLabelForLabel(label, kind = "gateway") {
1040
+ const current = kind === "gateway" ? LAUNCHD_LABEL : WEBUI_LAUNCHD_LABEL;
1041
+ const legacy = kind === "gateway" ? LEGACY_LAUNCHD_LABEL : LEGACY_WEBUI_LAUNCHD_LABEL;
1042
+ return label.startsWith(current) ? legacy + label.slice(current.length) : label;
1043
+ }
951
1044
  function installLaunchAgent(options) {
952
1045
  fs6.mkdirSync(path5.dirname(options.plistPath), { recursive: true });
953
1046
  atomicWrite(options.plistPath, renderLaunchAgent(options), 420);
954
1047
  const domain = launchDomain();
955
1048
  launchctl(["bootout", domain, options.plistPath], true);
1049
+ launchctl(["bootout", `${domain}/${legacyLabelForLabel(options.label)}`], true);
956
1050
  launchctl(["bootstrap", domain, options.plistPath]);
957
1051
  launchctl(["kickstart", "-k", `${domain}/${options.label}`]);
958
1052
  }
@@ -961,16 +1055,26 @@ function uninstallLaunchAgent(plistPath) {
961
1055
  launchctl(["bootout", domain, plistPath], true);
962
1056
  fs6.rmSync(plistPath, { force: true });
963
1057
  }
1058
+ function loadedPlistLabel(plistPath, fallback) {
1059
+ return plistServiceLabel(fs6.existsSync(plistPath) ? fs6.readFileSync(plistPath, "utf8") : "", fallback);
1060
+ }
964
1061
  function startLaunchAgent(plistPath, label) {
965
1062
  if (!fs6.existsSync(plistPath))
966
1063
  throw new Error("Gateway LaunchAgent is not installed");
967
1064
  const domain = launchDomain();
968
- if (!launchAgentStatus(label))
1065
+ const actual = loadedPlistLabel(plistPath, label);
1066
+ if (!launchAgentStatus(actual))
969
1067
  launchctl(["bootstrap", domain, plistPath]);
970
- launchctl(["kickstart", `${domain}/${label}`]);
1068
+ launchctl(["kickstart", `${domain}/${actual}`]);
971
1069
  }
972
1070
  function stopLaunchAgent(plistPath) {
973
- launchctl(["bootout", launchDomain(), plistPath], true);
1071
+ const domain = launchDomain();
1072
+ launchctl(["bootout", domain, plistPath], true);
1073
+ if (fs6.existsSync(plistPath)) {
1074
+ const actual = plistServiceLabel(fs6.readFileSync(plistPath, "utf8"), "");
1075
+ if (actual)
1076
+ launchctl(["bootout", `${domain}/${actual}`], true);
1077
+ }
974
1078
  }
975
1079
  function restartLaunchAgent(plistPath, label) {
976
1080
  const domain = launchDomain();
@@ -982,8 +1086,10 @@ function restartLaunchAgent(plistPath, label) {
982
1086
  function reloadLaunchAgent(plistPath, label) {
983
1087
  const domain = launchDomain();
984
1088
  launchctl(["bootout", domain, plistPath], true);
1089
+ launchctl(["bootout", `${domain}/${label}`], true);
985
1090
  launchctl(["bootstrap", domain, plistPath]);
986
- launchctl(["kickstart", `${domain}/${label}`]);
1091
+ const actual = loadedPlistLabel(plistPath, label);
1092
+ launchctl(["kickstart", `${domain}/${actual}`]);
987
1093
  }
988
1094
  function launchAgentStatus(label) {
989
1095
  const domain = launchDomain();
@@ -8407,7 +8513,7 @@ function officialRealtimeNotImplementedResponse() {
8407
8513
  }
8408
8514
  }, {
8409
8515
  status: 426,
8410
- headers: { "x-codex-cliproxy-gateway": "official-realtime-not-implemented" }
8516
+ headers: { "x-agent-proxy": "official-realtime-not-implemented" }
8411
8517
  });
8412
8518
  }
8413
8519
  function websocketNotSupportedResponse(marker = "websocket-not-supported") {
@@ -8415,7 +8521,7 @@ function websocketNotSupportedResponse(marker = "websocket-not-supported") {
8415
8521
  status: 426,
8416
8522
  headers: {
8417
8523
  connection: "close",
8418
- "x-codex-cliproxy-gateway": marker
8524
+ "x-agent-proxy": marker
8419
8525
  }
8420
8526
  });
8421
8527
  }
@@ -8604,7 +8710,7 @@ function excludedModelResponse(model) {
8604
8710
  message: `Model "${model}" is excluded by the gateway's excludedModels configuration`,
8605
8711
  hint: "Remove or narrow the exclusion (agent-proxy models --exclude, or the Web UI) to use this model"
8606
8712
  }
8607
- }, { status: 404, headers: { "x-codex-cliproxy-gateway": "model-excluded" } });
8713
+ }, { status: 404, headers: { "x-agent-proxy": "model-excluded" } });
8608
8714
  }
8609
8715
  async function availablePluginCatalog(registry, processLog, mountPath, operation) {
8610
8716
  const catalogs = await Promise.all(registry.plugins.map(async (plugin) => {
@@ -9115,7 +9221,7 @@ function startPluginGateway(config, registry, realtimeProviderMode = "invalid",
9115
9221
  });
9116
9222
  return new Response("Realtime upstream WebSocket failed", {
9117
9223
  status: 502,
9118
- headers: { "x-codex-cliproxy-gateway": "realtime-upstream-unavailable" }
9224
+ headers: { "x-agent-proxy": "realtime-upstream-unavailable" }
9119
9225
  });
9120
9226
  });
9121
9227
  }
@@ -11753,6 +11859,12 @@ async function install(options) {
11753
11859
  return;
11754
11860
  }
11755
11861
  }
11862
+ adoptLegacyLaunchAgents({
11863
+ gatewayPlist: migration.paths.launchAgent,
11864
+ webUiPlist: migration.paths.webUiLaunchAgent,
11865
+ ...legacyLaunchAgentPaths(migration.paths),
11866
+ instanceSuffix: migration.paths.instanceSuffix
11867
+ });
11756
11868
  const rollback = migration.apply();
11757
11869
  try {
11758
11870
  await runWithInstancePaths(migration.paths, () => installCurrent({ ...options, yes: true }, () => migration.complete()));
@@ -11817,6 +11929,12 @@ async function installCurrent(options, completeMigration) {
11817
11929
  return;
11818
11930
  }
11819
11931
  }
11932
+ adoptLegacyLaunchAgents({
11933
+ gatewayPlist: paths.launchAgent,
11934
+ webUiPlist: paths.webUiLaunchAgent,
11935
+ ...legacyLaunchAgentPaths(paths),
11936
+ instanceSuffix: paths.instanceSuffix
11937
+ });
11820
11938
  const { config } = mergedGatewayConfig(paths, currentGatewayConfig, overrides);
11821
11939
  config.configVersion = GATEWAY_CONFIG_VERSION;
11822
11940
  config.$schema = GATEWAY_CONFIG_SCHEMA_URL;
@@ -11862,7 +11980,8 @@ async function installCurrent(options, completeMigration) {
11862
11980
  let installationTouched = false;
11863
11981
  let launchInstalled = false;
11864
11982
  const previousPlist = fs24.existsSync(paths.launchAgent) ? fs24.readFileSync(paths.launchAgent, "utf8") : undefined;
11865
- const previousServiceLoaded = previousPlist !== undefined && launchAgentStatus(gatewayServiceLabel(paths.instanceSuffix)) !== null;
11983
+ const previousServiceLabel = previousPlist !== undefined ? plistServiceLabel(previousPlist, gatewayServiceLabel(paths.instanceSuffix)) : gatewayServiceLabel(paths.instanceSuffix);
11984
+ const previousServiceLoaded = previousPlist !== undefined && launchAgentStatus(previousServiceLabel) !== null;
11866
11985
  let launchTouched = false;
11867
11986
  let tomlUnchanged = false;
11868
11987
  try {
@@ -11976,7 +12095,7 @@ async function installCurrent(options, completeMigration) {
11976
12095
  if (previousPlist !== undefined) {
11977
12096
  atomicWrite(paths.launchAgent, previousPlist, 420);
11978
12097
  if (previousServiceLoaded)
11979
- reloadLaunchAgent(paths.launchAgent, gatewayServiceLabel(paths.instanceSuffix));
12098
+ reloadLaunchAgent(paths.launchAgent, previousServiceLabel);
11980
12099
  else
11981
12100
  stopLaunchAgent(paths.launchAgent);
11982
12101
  } else {
@@ -11996,7 +12115,7 @@ async function installCurrent(options, completeMigration) {
11996
12115
  if (previousPlist !== undefined) {
11997
12116
  atomicWrite(paths.launchAgent, previousPlist, 420);
11998
12117
  if (previousServiceLoaded)
11999
- reloadLaunchAgent(paths.launchAgent, gatewayServiceLabel(paths.instanceSuffix));
12118
+ reloadLaunchAgent(paths.launchAgent, previousServiceLabel);
12000
12119
  else
12001
12120
  stopLaunchAgent(paths.launchAgent);
12002
12121
  } else {
@@ -12066,6 +12185,7 @@ async function uninstall(options) {
12066
12185
  assertCodexHomeAvailable(paths, false);
12067
12186
  uninstallLaunchAgent(paths.launchAgent);
12068
12187
  uninstallLaunchAgent(paths.webUiLaunchAgent);
12188
+ removeLegacyLaunchAgents({ ...legacyLaunchAgentPaths(paths), instanceSuffix: paths.instanceSuffix });
12069
12189
  const legacyCatalogFile = path17.join(paths.codexHome, "cliproxy-catalog.json");
12070
12190
  if (restoresClient) {
12071
12191
  const currentToml = fs24.existsSync(paths.configToml) ? fs24.readFileSync(paths.configToml, "utf8") : "";
@@ -12360,7 +12480,7 @@ async function status() {
12360
12480
  const paths = resolvePaths();
12361
12481
  const installed = fs24.existsSync(paths.stateFile);
12362
12482
  const config = fs24.existsSync(paths.gatewayConfig) ? loadGatewayConfig(paths.gatewayConfig) : undefined;
12363
- const service = process.platform === "darwin" ? launchAgentStatus(gatewayServiceLabel(paths.instanceSuffix)) : null;
12483
+ const service = process.platform === "darwin" ? launchAgentStatus(gatewayServiceLabel(paths.instanceSuffix)) ?? (fs24.existsSync(paths.launchAgent) ? launchAgentStatus(plistServiceLabel(fs24.readFileSync(paths.launchAgent, "utf8"), gatewayServiceLabel(paths.instanceSuffix))) : null) : null;
12364
12484
  const source = fs24.existsSync(paths.configToml) ? fs24.readFileSync(paths.configToml, "utf8") : "";
12365
12485
  const configuredBaseUrl = readRootTomlString(source, "openai_base_url");
12366
12486
  const configuredCatalog = readRootTomlString(source, "model_catalog_json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-proxy/gateway",
3
- "version": "0.20.2",
3
+ "version": "0.20.4",
4
4
  "description": "Agent Proxy 的 Bun 网关与 CLI 业务应用。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,12 +21,12 @@
21
21
  "prepack": "bun ../../scripts/verify-package.ts ."
22
22
  },
23
23
  "dependencies": {
24
- "@agent-proxy/agy": "0.20.2",
25
- "@agent-proxy/codebuddy": "0.20.2",
26
- "@agent-proxy/opencode-zen": "0.20.2",
27
- "@agent-proxy/plugin-api": "0.20.2",
28
- "@agent-proxy/qoder": "0.20.2",
29
- "@agent-proxy/zcode": "0.20.2",
24
+ "@agent-proxy/agy": "0.20.4",
25
+ "@agent-proxy/codebuddy": "0.20.4",
26
+ "@agent-proxy/opencode-zen": "0.20.4",
27
+ "@agent-proxy/plugin-api": "0.20.4",
28
+ "@agent-proxy/qoder": "0.20.4",
29
+ "@agent-proxy/zcode": "0.20.4",
30
30
  "bytes": "^3.1.2",
31
31
  "i18next": "^26.4.2",
32
32
  "react": "^19.2.0",
@@ -35,7 +35,7 @@
35
35
  "yaml": "^2.9.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@agent-proxy/models": "0.20.2"
38
+ "@agent-proxy/models": "0.20.4"
39
39
  },
40
40
  "engines": {
41
41
  "bun": ">=1.2.0"
@@ -49,5 +49,5 @@
49
49
  "type": "git",
50
50
  "url": "git+https://github.com/aceHubert/codex-cliproxy-gateway.git"
51
51
  },
52
- "gitHead": "da33f92fc5cb05404b8f7ef7a91eb6321382cfc1"
52
+ "gitHead": "b7dc893fa999f8af0d53411a52adb06d20f83dfa"
53
53
  }