@agent-proxy/gateway 0.20.3 → 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.
- package/dist/index.js +162 -42
- 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", "
|
|
224
|
-
webUiLaunchAgent: runtimeHomeOverride ? path2.join(runtimeHome, "Library", "LaunchAgents", "
|
|
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 = ".
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
384
|
-
const
|
|
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
|
|
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,
|
|
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
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
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 = "
|
|
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
|
|
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
|
-
|
|
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) =>
|
|
758
|
-
|
|
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 = "
|
|
783
|
-
var WEBUI_LAUNCHD_LABEL = "
|
|
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("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
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("<", "<").replaceAll(">", ">").replaceAll(""", '"').replaceAll("'", "'").replaceAll("&", "&");
|
|
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}/${
|
|
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}/${
|
|
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
|
-
|
|
1065
|
+
const actual = loadedPlistLabel(plistPath, label);
|
|
1066
|
+
if (!launchAgentStatus(actual))
|
|
969
1067
|
launchctl(["bootstrap", domain, plistPath]);
|
|
970
|
-
launchctl(["kickstart", `${domain}/${
|
|
1068
|
+
launchctl(["kickstart", `${domain}/${actual}`]);
|
|
971
1069
|
}
|
|
972
1070
|
function stopLaunchAgent(plistPath) {
|
|
973
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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,
|
|
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,
|
|
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.
|
|
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.
|
|
25
|
-
"@agent-proxy/codebuddy": "0.20.
|
|
26
|
-
"@agent-proxy/opencode-zen": "0.20.
|
|
27
|
-
"@agent-proxy/plugin-api": "0.20.
|
|
28
|
-
"@agent-proxy/qoder": "0.20.
|
|
29
|
-
"@agent-proxy/zcode": "0.20.
|
|
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.
|
|
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": "
|
|
52
|
+
"gitHead": "b7dc893fa999f8af0d53411a52adb06d20f83dfa"
|
|
53
53
|
}
|