codex-multi-auth 2.16.0 → 2.17.0
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/.codex-plugin/plugin.json +1 -1
- package/README.md +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/lib/account-policy.d.ts +4 -0
- package/dist/lib/account-policy.d.ts.map +1 -1
- package/dist/lib/account-policy.js +8 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
- package/dist/lib/accounts/rate-limits.js +2 -0
- package/dist/lib/accounts.d.ts +21 -3
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +229 -17
- package/dist/lib/api-route-store.d.ts +24 -0
- package/dist/lib/api-route-store.d.ts.map +1 -0
- package/dist/lib/api-route-store.js +145 -0
- package/dist/lib/auth/account-access.d.ts +115 -0
- package/dist/lib/auth/account-access.d.ts.map +1 -0
- package/dist/lib/auth/account-access.js +214 -0
- package/dist/lib/auth/token-utils.d.ts +54 -1
- package/dist/lib/auth/token-utils.d.ts.map +1 -1
- package/dist/lib/auth/token-utils.js +92 -7
- package/dist/lib/cli.d.ts +1 -1
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/cli.js +4 -0
- package/dist/lib/codex-cli/state.d.ts +7 -0
- package/dist/lib/codex-cli/state.d.ts.map +1 -1
- package/dist/lib/codex-cli/state.js +18 -0
- package/dist/lib/codex-cli/writer.d.ts.map +1 -1
- package/dist/lib/codex-cli/writer.js +60 -8
- package/dist/lib/codex-manager/account-manager-commands.d.ts.map +1 -1
- package/dist/lib/codex-manager/account-manager-commands.js +1 -0
- package/dist/lib/codex-manager/account-pool-write.d.ts +3 -1
- package/dist/lib/codex-manager/account-pool-write.d.ts.map +1 -1
- package/dist/lib/codex-manager/account-pool-write.js +4 -0
- package/dist/lib/codex-manager/api-login-menu.d.ts +14 -0
- package/dist/lib/codex-manager/api-login-menu.d.ts.map +1 -0
- package/dist/lib/codex-manager/api-login-menu.js +224 -0
- package/dist/lib/codex-manager/commands/account.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/account.js +27 -0
- package/dist/lib/codex-manager/commands/best.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/best.js +6 -2
- package/dist/lib/codex-manager/commands/check.d.ts +7 -1
- package/dist/lib/codex-manager/commands/check.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/check.js +24 -3
- package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/forecast.js +5 -1
- package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/report.js +5 -1
- package/dist/lib/codex-manager/commands/resets.d.ts +2 -0
- package/dist/lib/codex-manager/commands/resets.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/resets.js +67 -0
- package/dist/lib/codex-manager/commands/rotation.d.ts +2 -2
- package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/rotation.js +63 -12
- package/dist/lib/codex-manager/commands/status.d.ts +12 -1
- package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
- package/dist/lib/codex-manager/commands/status.js +166 -22
- package/dist/lib/codex-manager/commands/uninstall.js +1 -1
- package/dist/lib/codex-manager/formatters/quota-formatters.d.ts.map +1 -1
- package/dist/lib/codex-manager/formatters/quota-formatters.js +3 -2
- package/dist/lib/codex-manager/health-check.d.ts +3 -0
- package/dist/lib/codex-manager/health-check.d.ts.map +1 -1
- package/dist/lib/codex-manager/health-check.js +56 -6
- package/dist/lib/codex-manager/help.d.ts.map +1 -1
- package/dist/lib/codex-manager/help.js +4 -2
- package/dist/lib/codex-manager/login-flow.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-flow.js +69 -7
- package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-menu-data.js +14 -5
- package/dist/lib/codex-manager/login-oauth.d.ts +3 -0
- package/dist/lib/codex-manager/login-oauth.d.ts.map +1 -1
- package/dist/lib/codex-manager/login-oauth.js +6 -1
- package/dist/lib/codex-manager/login-workspace-choice.d.ts +11 -0
- package/dist/lib/codex-manager/login-workspace-choice.d.ts.map +1 -0
- package/dist/lib/codex-manager/login-workspace-choice.js +37 -0
- package/dist/lib/codex-manager/persist-selected-account.d.ts.map +1 -1
- package/dist/lib/codex-manager/persist-selected-account.js +2 -1
- package/dist/lib/codex-manager/repair-commands.d.ts +13 -0
- package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
- package/dist/lib/codex-manager/repair-commands.js +143 -8
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +25 -6
- package/dist/lib/concurrency.d.ts +3 -0
- package/dist/lib/concurrency.d.ts.map +1 -0
- package/dist/lib/concurrency.js +31 -0
- package/dist/lib/model-route-policy.d.ts +52 -0
- package/dist/lib/model-route-policy.d.ts.map +1 -0
- package/dist/lib/model-route-policy.js +159 -0
- package/dist/lib/policy/runtime-policy.d.ts +1 -0
- package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
- package/dist/lib/policy/runtime-policy.js +3 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts +2 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -1
- package/dist/lib/preemptive-quota-scheduler.js +4 -0
- package/dist/lib/quota-probe.d.ts +5 -0
- package/dist/lib/quota-probe.d.ts.map +1 -1
- package/dist/lib/quota-probe.js +26 -2
- package/dist/lib/request/client-cancellation.d.ts +5 -0
- package/dist/lib/request/client-cancellation.d.ts.map +1 -0
- package/dist/lib/request/client-cancellation.js +7 -0
- package/dist/lib/request/response-outcome.d.ts +25 -0
- package/dist/lib/request/response-outcome.d.ts.map +1 -0
- package/dist/lib/request/response-outcome.js +49 -0
- package/dist/lib/request/stream-failover-runtime.d.ts +2 -1
- package/dist/lib/request/stream-failover-runtime.d.ts.map +1 -1
- package/dist/lib/request/stream-failover-runtime.js +29 -10
- package/dist/lib/runtime/account-check.d.ts.map +1 -1
- package/dist/lib/runtime/account-check.js +2 -8
- package/dist/lib/runtime/account-model-catalog.d.ts +64 -0
- package/dist/lib/runtime/account-model-catalog.d.ts.map +1 -0
- package/dist/lib/runtime/account-model-catalog.js +154 -0
- package/dist/lib/runtime/account-pool.d.ts +3 -1
- package/dist/lib/runtime/account-pool.d.ts.map +1 -1
- package/dist/lib/runtime/account-pool.js +4 -0
- package/dist/lib/runtime/account-reset-credits.d.ts +42 -0
- package/dist/lib/runtime/account-reset-credits.d.ts.map +1 -0
- package/dist/lib/runtime/account-reset-credits.js +82 -0
- package/dist/lib/runtime/api-model-capabilities.d.ts +30 -0
- package/dist/lib/runtime/api-model-capabilities.d.ts.map +1 -0
- package/dist/lib/runtime/api-model-capabilities.js +434 -0
- package/dist/lib/runtime/api-model-runtime.d.ts +42 -0
- package/dist/lib/runtime/api-model-runtime.d.ts.map +1 -0
- package/dist/lib/runtime/api-model-runtime.js +358 -0
- package/dist/lib/runtime/app-bind.d.ts +19 -1
- package/dist/lib/runtime/app-bind.d.ts.map +1 -1
- package/dist/lib/runtime/app-bind.js +79 -9
- package/dist/lib/runtime/automatic-account-checks.d.ts +31 -0
- package/dist/lib/runtime/automatic-account-checks.d.ts.map +1 -0
- package/dist/lib/runtime/automatic-account-checks.js +113 -0
- package/dist/lib/runtime/automatic-subscription-checks.d.ts +7 -0
- package/dist/lib/runtime/automatic-subscription-checks.d.ts.map +1 -0
- package/dist/lib/runtime/automatic-subscription-checks.js +57 -0
- package/dist/lib/runtime/catalog-capabilities.d.ts +8 -0
- package/dist/lib/runtime/catalog-capabilities.d.ts.map +1 -0
- package/dist/lib/runtime/catalog-capabilities.js +61 -0
- package/dist/lib/runtime/inference-activity.d.ts +15 -0
- package/dist/lib/runtime/inference-activity.d.ts.map +1 -0
- package/dist/lib/runtime/inference-activity.js +95 -0
- package/dist/lib/runtime/model-discovery-status.d.ts +100 -0
- package/dist/lib/runtime/model-discovery-status.d.ts.map +1 -0
- package/dist/lib/runtime/model-discovery-status.js +387 -0
- package/dist/lib/runtime/native-account-storage.d.ts +11 -0
- package/dist/lib/runtime/native-account-storage.d.ts.map +1 -0
- package/dist/lib/runtime/native-account-storage.js +50 -0
- package/dist/lib/runtime/native-account-sync.d.ts +19 -0
- package/dist/lib/runtime/native-account-sync.d.ts.map +1 -0
- package/dist/lib/runtime/native-account-sync.js +62 -0
- package/dist/lib/runtime/native-binding-lock.d.ts +3 -0
- package/dist/lib/runtime/native-binding-lock.d.ts.map +1 -0
- package/dist/lib/runtime/native-binding-lock.js +5 -0
- package/dist/lib/runtime/native-client-auth.d.ts +3 -0
- package/dist/lib/runtime/native-client-auth.d.ts.map +1 -0
- package/dist/lib/runtime/native-client-auth.js +42 -0
- package/dist/lib/runtime/native-provider-config.d.ts +4 -0
- package/dist/lib/runtime/native-provider-config.d.ts.map +1 -0
- package/dist/lib/runtime/native-provider-config.js +52 -0
- package/dist/lib/runtime/native-rate-limits.d.ts +18 -0
- package/dist/lib/runtime/native-rate-limits.d.ts.map +1 -0
- package/dist/lib/runtime/native-rate-limits.js +135 -0
- package/dist/lib/runtime/reset-credit-routing.d.ts +41 -0
- package/dist/lib/runtime/reset-credit-routing.d.ts.map +1 -0
- package/dist/lib/runtime/reset-credit-routing.js +66 -0
- package/dist/lib/runtime/reset-credits.d.ts +101 -0
- package/dist/lib/runtime/reset-credits.d.ts.map +1 -0
- package/dist/lib/runtime/reset-credits.js +162 -0
- package/dist/lib/runtime/response-output-history.d.ts +17 -0
- package/dist/lib/runtime/response-output-history.d.ts.map +1 -0
- package/dist/lib/runtime/response-output-history.js +85 -0
- package/dist/lib/runtime/responses-websocket.d.ts +31 -0
- package/dist/lib/runtime/responses-websocket.d.ts.map +1 -0
- package/dist/lib/runtime/responses-websocket.js +636 -0
- package/dist/lib/runtime/resume-picker.d.ts +41 -0
- package/dist/lib/runtime/resume-picker.d.ts.map +1 -0
- package/dist/lib/runtime/resume-picker.js +169 -0
- package/dist/lib/runtime/rotation-account-selection.d.ts +6 -0
- package/dist/lib/runtime/rotation-account-selection.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-account-selection.js +63 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts +33 -0
- package/dist/lib/runtime/rotation-proxy-state.d.ts.map +1 -1
- package/dist/lib/runtime/rotation-server-types.d.ts +18 -0
- package/dist/lib/runtime/rotation-server-types.d.ts.map +1 -1
- package/dist/lib/runtime/runtime-capability-failures.d.ts +14 -0
- package/dist/lib/runtime/runtime-capability-failures.d.ts.map +1 -0
- package/dist/lib/runtime/runtime-capability-failures.js +76 -0
- package/dist/lib/runtime/runtime-observability.d.ts +7 -0
- package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
- package/dist/lib/runtime/runtime-observability.js +42 -2
- package/dist/lib/runtime/subscription-first-use.d.ts +10 -0
- package/dist/lib/runtime/subscription-first-use.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-first-use.js +64 -0
- package/dist/lib/runtime/subscription-quota-event.d.ts +6 -0
- package/dist/lib/runtime/subscription-quota-event.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-quota-event.js +31 -0
- package/dist/lib/runtime/subscription-quota-order.d.ts +17 -0
- package/dist/lib/runtime/subscription-quota-order.d.ts.map +1 -0
- package/dist/lib/runtime/subscription-quota-order.js +31 -0
- package/dist/lib/runtime/workspace-model-scopes.d.ts +15 -0
- package/dist/lib/runtime/workspace-model-scopes.d.ts.map +1 -0
- package/dist/lib/runtime/workspace-model-scopes.js +34 -0
- package/dist/lib/runtime-constants.d.ts +3 -0
- package/dist/lib/runtime-constants.d.ts.map +1 -1
- package/dist/lib/runtime-constants.js +5 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -1
- package/dist/lib/runtime-paths.js +4 -1
- package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
- package/dist/lib/runtime-rotation-proxy.js +852 -42
- package/dist/lib/schemas.d.ts +20 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +3 -0
- package/dist/lib/storage/credential-sidecars.d.ts +13 -0
- package/dist/lib/storage/credential-sidecars.d.ts.map +1 -0
- package/dist/lib/storage/credential-sidecars.js +52 -0
- package/dist/lib/storage/file-lock.d.ts +9 -0
- package/dist/lib/storage/file-lock.d.ts.map +1 -0
- package/dist/lib/storage/file-lock.js +186 -0
- package/dist/lib/storage/flagged-storage.d.ts.map +1 -1
- package/dist/lib/storage/flagged-storage.js +12 -0
- package/dist/lib/storage/path-state.d.ts +2 -0
- package/dist/lib/storage/path-state.d.ts.map +1 -1
- package/dist/lib/storage/path-state.js +9 -0
- package/dist/lib/storage/paths.js +4 -4
- package/dist/lib/storage/pending-auth.d.ts +17 -0
- package/dist/lib/storage/pending-auth.d.ts.map +1 -0
- package/dist/lib/storage/pending-auth.js +153 -0
- package/dist/lib/storage/public-types.d.ts +13 -0
- package/dist/lib/storage/public-types.d.ts.map +1 -1
- package/dist/lib/storage/snapshot-merge.d.ts +16 -0
- package/dist/lib/storage/snapshot-merge.d.ts.map +1 -0
- package/dist/lib/storage/snapshot-merge.js +206 -0
- package/dist/lib/storage/transactions.d.ts.map +1 -1
- package/dist/lib/storage/transactions.js +5 -4
- package/dist/lib/storage.d.ts +6 -0
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +77 -7
- package/dist/lib/ui/auth-menu-builder.d.ts +2 -0
- package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu-builder.js +1 -0
- package/dist/lib/ui/auth-menu.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu.js +1 -0
- package/dist/lib/ui/check-progress.d.ts +8 -0
- package/dist/lib/ui/check-progress.d.ts.map +1 -0
- package/dist/lib/ui/check-progress.js +30 -0
- package/dist/lib/ui/format.d.ts +3 -1
- package/dist/lib/ui/format.d.ts.map +1 -1
- package/dist/lib/ui/format.js +8 -0
- package/dist/lib/ui/ui-copy.d.ts +2 -2
- package/dist/lib/ui/ui-copy.js +2 -2
- package/dist/lib/usage/pricing.js +2 -2
- package/dist/lib/usage/usage-extraction.d.ts +3 -1
- package/dist/lib/usage/usage-extraction.d.ts.map +1 -1
- package/dist/lib/usage/usage-extraction.js +46 -9
- package/package.json +7 -4
- package/scripts/codex-app-router.js +7 -0
- package/scripts/codex-bin-resolver.js +40 -8
- package/scripts/codex-routing.js +1 -0
- package/scripts/codex.js +100 -22
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ApiRouteCredential } from "../api-route-store.js";
|
|
2
|
+
import { type RouteCatalog, type RouteModel, modelEntitlements } from "../model-route-policy.js";
|
|
3
|
+
/** Endpoint-family exclusions, not a fixed allowlist of text model releases.
|
|
4
|
+
* Model ID discovery alone does not prove Responses/tool compatibility. */
|
|
5
|
+
export declare function isNonResponsesModel(id: string): boolean;
|
|
6
|
+
/** Conservative native-picker metadata: discovery proves an ID, not optional tool capabilities. */
|
|
7
|
+
export declare function apiPickerModel(id: string): RouteModel;
|
|
8
|
+
export declare function boundedJson(response: Response, max?: number): Promise<unknown>;
|
|
9
|
+
export interface ApiCatalogStatus {
|
|
10
|
+
id: string;
|
|
11
|
+
kind: "api" | "zdr";
|
|
12
|
+
label: string;
|
|
13
|
+
priority: number;
|
|
14
|
+
checkedAt: number;
|
|
15
|
+
error: boolean;
|
|
16
|
+
availableModels: string[];
|
|
17
|
+
visibleModels: string[];
|
|
18
|
+
entitlements: ReturnType<typeof modelEntitlements>[];
|
|
19
|
+
}
|
|
20
|
+
export declare class ApiModelRuntime {
|
|
21
|
+
private readonly fetchImpl;
|
|
22
|
+
private readonly now;
|
|
23
|
+
private readonly updateDiscovery?;
|
|
24
|
+
private readonly enrichModels?;
|
|
25
|
+
private readonly onCatalogUpdated?;
|
|
26
|
+
private readonly discoveryUpdates;
|
|
27
|
+
lastPersistenceError: string | null;
|
|
28
|
+
private readonly failures;
|
|
29
|
+
private readonly visibility;
|
|
30
|
+
private readonly snapshots;
|
|
31
|
+
private activeKeys;
|
|
32
|
+
cachedCatalogs(): RouteCatalog[];
|
|
33
|
+
private readonly cache;
|
|
34
|
+
constructor(fetchImpl?: typeof fetch, now?: () => number, updateDiscovery?: ((route: ApiRouteCredential, models: string[]) => Promise<ApiRouteCredential>) | undefined, enrichModels?: ((models: RouteModel[], refresh: boolean, route: ApiRouteCredential, forceProbes: boolean) => Promise<RouteModel[]>) | undefined, onCatalogUpdated?: (() => void) | undefined);
|
|
35
|
+
private key;
|
|
36
|
+
catalogs(routes: ApiRouteCredential[], refresh?: boolean, forceProbes?: boolean, waitForCapabilities?: boolean, waitForPersistence?: boolean, requestKind?: "oauth" | "api" | "zdr"): Promise<RouteCatalog[]>;
|
|
37
|
+
statuses(routes: ApiRouteCredential[]): ApiCatalogStatus[];
|
|
38
|
+
request(alias: string, body: Record<string, unknown>, routes: ApiRouteCredential[], signal?: AbortSignal, onAttempt?: (credentialIndex: number) => void): Promise<Response>;
|
|
39
|
+
recordStreamFailure(credential: ApiRouteCredential, alias: string, body: Record<string, unknown>, error: unknown): void;
|
|
40
|
+
private failure;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=api-model-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-model-runtime.d.ts","sourceRoot":"","sources":["../../../lib/runtime/api-model-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { ClientCancellationError } from "../request/client-cancellation.js";
|
|
2
|
+
import { mapWithConcurrency } from "../concurrency.js";
|
|
3
|
+
import { RuntimeCapabilityFailures, classifyCapabilityFailure, } from "./runtime-capability-failures.js";
|
|
4
|
+
import { readErrorBody } from "../request/stream-failover-runtime.js";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { resolveModelRoute, parseModelRoute, modelEntitlements, } from "../model-route-policy.js";
|
|
7
|
+
import { isRecord } from "../utils.js";
|
|
8
|
+
/** Endpoint-family exclusions, not a fixed allowlist of text model releases.
|
|
9
|
+
* Model ID discovery alone does not prove Responses/tool compatibility. */
|
|
10
|
+
export function isNonResponsesModel(id) {
|
|
11
|
+
return (/(?:^|[-_])(?:tts|whisper|embedding|moderation|transcribe|transcription|realtime|audio|image|sora)(?:[-_]|$)/i.test(id) ||
|
|
12
|
+
/^(?:dall-e|babbage|davinci)(?:-|$)/i.test(id) ||
|
|
13
|
+
/(?:^|-)live(?:-|$)/i.test(id));
|
|
14
|
+
}
|
|
15
|
+
/** Conservative native-picker metadata: discovery proves an ID, not optional tool capabilities. */
|
|
16
|
+
export function apiPickerModel(id) {
|
|
17
|
+
return {
|
|
18
|
+
slug: id,
|
|
19
|
+
display_name: id,
|
|
20
|
+
description: "API model; optional capabilities are not advertised by model discovery.",
|
|
21
|
+
supported_reasoning_levels: [],
|
|
22
|
+
default_reasoning_level: null,
|
|
23
|
+
shell_type: "shell_command",
|
|
24
|
+
visibility: "list",
|
|
25
|
+
supported_in_api: true,
|
|
26
|
+
priority: 0,
|
|
27
|
+
availability_nux: null,
|
|
28
|
+
upgrade: null,
|
|
29
|
+
model_messages: {
|
|
30
|
+
instructions_template: "You are a coding assistant. Follow the user instructions and supplied tool definitions.",
|
|
31
|
+
},
|
|
32
|
+
support_verbosity: false,
|
|
33
|
+
default_verbosity: null,
|
|
34
|
+
apply_patch_tool_type: null,
|
|
35
|
+
truncation_policy: { mode: "bytes", limit: 10000 },
|
|
36
|
+
experimental_supported_tools: [],
|
|
37
|
+
input_modalities: ["text"],
|
|
38
|
+
supports_reasoning_summary_parameter: false,
|
|
39
|
+
use_responses_lite: false,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Apply current credential policy, never metadata inherited from another credential or old config. */
|
|
43
|
+
function withAccessPrograms(model, route) {
|
|
44
|
+
const result = { ...model };
|
|
45
|
+
delete result.available_access_programs;
|
|
46
|
+
const override = route.modelAccessPrograms && Object.hasOwn(route.modelAccessPrograms, model.slug)
|
|
47
|
+
? route.modelAccessPrograms[model.slug] : undefined;
|
|
48
|
+
const programs = override ?? route.accessPrograms;
|
|
49
|
+
if (programs)
|
|
50
|
+
result.available_access_programs = structuredClone(programs);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
export async function boundedJson(response, max = 8 * 1024 * 1024) {
|
|
54
|
+
const reader = response.body?.getReader();
|
|
55
|
+
if (!reader)
|
|
56
|
+
throw Error("Empty model catalog");
|
|
57
|
+
const chunks = [];
|
|
58
|
+
let size = 0;
|
|
59
|
+
try {
|
|
60
|
+
for (;;) {
|
|
61
|
+
const { done, value } = await reader.read();
|
|
62
|
+
if (done)
|
|
63
|
+
break;
|
|
64
|
+
size += value.byteLength;
|
|
65
|
+
if (size > max)
|
|
66
|
+
throw Error("Model catalog too large");
|
|
67
|
+
chunks.push(value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
await reader.cancel();
|
|
72
|
+
}
|
|
73
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
74
|
+
}
|
|
75
|
+
export class ApiModelRuntime {
|
|
76
|
+
fetchImpl;
|
|
77
|
+
now;
|
|
78
|
+
updateDiscovery;
|
|
79
|
+
enrichModels;
|
|
80
|
+
onCatalogUpdated;
|
|
81
|
+
discoveryUpdates = new Map();
|
|
82
|
+
lastPersistenceError = null;
|
|
83
|
+
failures = new RuntimeCapabilityFailures(() => this.now());
|
|
84
|
+
visibility = new Map();
|
|
85
|
+
snapshots = new Map();
|
|
86
|
+
activeKeys = new Set();
|
|
87
|
+
cachedCatalogs() {
|
|
88
|
+
return [...this.snapshots]
|
|
89
|
+
.filter(([key]) => this.activeKeys.has(key))
|
|
90
|
+
.map(([, catalog]) => catalog);
|
|
91
|
+
}
|
|
92
|
+
cache = new Map();
|
|
93
|
+
constructor(fetchImpl = fetch, now = Date.now, updateDiscovery, enrichModels, onCatalogUpdated) {
|
|
94
|
+
this.fetchImpl = fetchImpl;
|
|
95
|
+
this.now = now;
|
|
96
|
+
this.updateDiscovery = updateDiscovery;
|
|
97
|
+
this.enrichModels = enrichModels;
|
|
98
|
+
this.onCatalogUpdated = onCatalogUpdated;
|
|
99
|
+
}
|
|
100
|
+
key(route) {
|
|
101
|
+
return createHash("sha256")
|
|
102
|
+
.update(route.id)
|
|
103
|
+
.update("\0")
|
|
104
|
+
.update(route.apiKey)
|
|
105
|
+
.digest("hex");
|
|
106
|
+
}
|
|
107
|
+
async catalogs(routes, refresh = false, forceProbes = false, waitForCapabilities = true, waitForPersistence = true, requestKind) {
|
|
108
|
+
this.activeKeys = new Set(routes.map((route) => this.key(route)));
|
|
109
|
+
return mapWithConcurrency(requestKind ? routes.filter(route => route.kind === requestKind) : routes, 3, async (route) => {
|
|
110
|
+
const key = this.key(route), cached = this.cache.get(key);
|
|
111
|
+
if (route.enabled &&
|
|
112
|
+
(refresh ||
|
|
113
|
+
!cached ||
|
|
114
|
+
cached.at + (cached.error ? 5000 : 5 * 60_000) <= this.now())) {
|
|
115
|
+
let models = [];
|
|
116
|
+
let error = false;
|
|
117
|
+
try {
|
|
118
|
+
const response = await this.fetchImpl("https://api.openai.com/v1/models", {
|
|
119
|
+
method: "GET",
|
|
120
|
+
headers: { authorization: `Bearer ${route.apiKey}` },
|
|
121
|
+
redirect: "error",
|
|
122
|
+
signal: AbortSignal.timeout(15000),
|
|
123
|
+
});
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
await response.body?.cancel();
|
|
126
|
+
throw Error("API catalog unavailable");
|
|
127
|
+
}
|
|
128
|
+
const value = await boundedJson(response);
|
|
129
|
+
if (!isRecord(value) ||
|
|
130
|
+
!Array.isArray(value.data) ||
|
|
131
|
+
value.data.length > 10000)
|
|
132
|
+
throw Error("Invalid API catalog");
|
|
133
|
+
models = value.data.flatMap((m) => isRecord(m) &&
|
|
134
|
+
typeof m.id === "string" &&
|
|
135
|
+
/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/.test(m.id) &&
|
|
136
|
+
!isNonResponsesModel(m.id)
|
|
137
|
+
? [
|
|
138
|
+
{
|
|
139
|
+
...apiPickerModel(m.id),
|
|
140
|
+
...cached?.models.find((old) => old.slug === m.id),
|
|
141
|
+
},
|
|
142
|
+
]
|
|
143
|
+
: []);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
error = true;
|
|
147
|
+
}
|
|
148
|
+
if (this.cache.size >= 100)
|
|
149
|
+
this.cache.delete(this.cache.keys().next().value ?? "");
|
|
150
|
+
this.cache.set(key, { at: this.now(), models, error });
|
|
151
|
+
}
|
|
152
|
+
const entry = this.cache.get(key);
|
|
153
|
+
if (route.enabled && entry && !entry.error && this.updateDiscovery) {
|
|
154
|
+
let pending = this.discoveryUpdates.get(key);
|
|
155
|
+
if (!pending) {
|
|
156
|
+
pending = this.updateDiscovery(route, entry.models.map(m => m.slug));
|
|
157
|
+
this.discoveryUpdates.set(key, pending);
|
|
158
|
+
void pending.then(() => { this.lastPersistenceError = null; }, () => { this.lastPersistenceError = "api_catalog_persistence_failed"; }).finally(() => { this.discoveryUpdates.delete(key); });
|
|
159
|
+
}
|
|
160
|
+
if (waitForPersistence) {
|
|
161
|
+
try {
|
|
162
|
+
route = await pending;
|
|
163
|
+
}
|
|
164
|
+
catch { /* Keep the explicit policy read for this request; discovery cannot expand it. */ }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
this.visibility.set(key, route.visibleModels);
|
|
168
|
+
const snapshot = {
|
|
169
|
+
id: route.id,
|
|
170
|
+
kind: route.kind,
|
|
171
|
+
enabled: route.enabled,
|
|
172
|
+
priority: route.priority,
|
|
173
|
+
models: (entry?.models ?? []).map(model => withAccessPrograms(model, route)),
|
|
174
|
+
visibleModels: route.visibleModels,
|
|
175
|
+
};
|
|
176
|
+
this.snapshots.set(key, snapshot);
|
|
177
|
+
if (route.enabled && entry && !entry.error && this.enrichModels && (!requestKind || !cached || cached.error)) {
|
|
178
|
+
const pending = this.enrichModels(entry.models.filter((m) => route.visibleModels.includes(m.slug)), refresh, requestKind ? { ...route, probeCapabilities: false } : route, forceProbes).then((enriched) => {
|
|
179
|
+
const byId = new Map(enriched.map((m) => [m.slug, m]));
|
|
180
|
+
snapshot.models = entry.models.map((m) => withAccessPrograms(byId.get(m.slug) ?? m, route));
|
|
181
|
+
// The caller receives its own completed result; only the newest refresh may replace the shared cache.
|
|
182
|
+
if (this.cache.get(key) !== entry ||
|
|
183
|
+
this.snapshots.get(key) !== snapshot ||
|
|
184
|
+
!this.activeKeys.has(key))
|
|
185
|
+
return;
|
|
186
|
+
entry.models = snapshot.models;
|
|
187
|
+
this.onCatalogUpdated?.();
|
|
188
|
+
});
|
|
189
|
+
if (waitForCapabilities)
|
|
190
|
+
await pending;
|
|
191
|
+
else
|
|
192
|
+
void pending.catch(() => {
|
|
193
|
+
entry.error = true;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return snapshot;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
statuses(routes) {
|
|
200
|
+
return routes.map((r) => {
|
|
201
|
+
const c = this.cache.get(this.key(r));
|
|
202
|
+
return {
|
|
203
|
+
id: r.id,
|
|
204
|
+
kind: r.kind,
|
|
205
|
+
label: r.label,
|
|
206
|
+
priority: r.priority,
|
|
207
|
+
checkedAt: c?.at ?? 0,
|
|
208
|
+
error: c?.error ?? true,
|
|
209
|
+
availableModels: c?.models.map((m) => m.slug) ?? [],
|
|
210
|
+
entitlements: (c?.models ?? [])
|
|
211
|
+
.filter((m) => (this.visibility.get(this.key(r)) ?? r.visibleModels).includes(m.slug))
|
|
212
|
+
.map(model => modelEntitlements(withAccessPrograms(model, r))),
|
|
213
|
+
visibleModels: (this.visibility.get(this.key(r)) ?? r.visibleModels).filter((id) => c?.models.some((m) => m.slug === id)),
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async request(alias, body, routes, signal, onAttempt) {
|
|
218
|
+
let route;
|
|
219
|
+
try {
|
|
220
|
+
if (isNonResponsesModel(parseModelRoute(alias).upstreamModel))
|
|
221
|
+
return this.failure(400, "model_requires_other_endpoint", "This model uses a media, audio, embedding, or legacy endpoint and cannot serve a Codex Responses turn. Select a text/coding model.");
|
|
222
|
+
route = resolveModelRoute(alias, await this.catalogs(routes, false, false, true, false, parseModelRoute(alias).kind), {
|
|
223
|
+
serviceTier: typeof body.service_tier === "string" ? body.service_tier : undefined,
|
|
224
|
+
reasoningEffort: isRecord(body.reasoning) && typeof body.reasoning.effort === "string"
|
|
225
|
+
? body.reasoning.effort
|
|
226
|
+
: undefined,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return this.failure(400, "invalid_model_route");
|
|
231
|
+
}
|
|
232
|
+
if (route.kind === "oauth")
|
|
233
|
+
return this.failure(400, "explicit_api_model_required");
|
|
234
|
+
// Until connection-affinity support exists, never guess the owner of server-side response state.
|
|
235
|
+
if (body.background === true || body.conversation)
|
|
236
|
+
return this.failure(400, "stateful_api_route_unsupported");
|
|
237
|
+
if (body.previous_response_id)
|
|
238
|
+
return this.failure(400, "previous_response_not_found");
|
|
239
|
+
const payload = {
|
|
240
|
+
...body,
|
|
241
|
+
model: route.upstreamModel,
|
|
242
|
+
store: false,
|
|
243
|
+
};
|
|
244
|
+
if (route.serviceTier)
|
|
245
|
+
payload.service_tier = route.serviceTier;
|
|
246
|
+
delete payload.client_metadata;
|
|
247
|
+
let lastStatus = 503;
|
|
248
|
+
let lastRejection;
|
|
249
|
+
for (const candidate of route.candidates) {
|
|
250
|
+
const credential = routes.find((r) => r.id === candidate.id);
|
|
251
|
+
if (!credential)
|
|
252
|
+
continue;
|
|
253
|
+
const effort = isRecord(body.reasoning) && typeof body.reasoning.effort === "string"
|
|
254
|
+
? body.reasoning.effort
|
|
255
|
+
: undefined;
|
|
256
|
+
if (!this.failures.supports(this.key(credential), route.upstreamModel, effort, route.serviceTier))
|
|
257
|
+
continue;
|
|
258
|
+
if (signal?.aborted)
|
|
259
|
+
throw new ClientCancellationError();
|
|
260
|
+
const controller = new AbortController();
|
|
261
|
+
const abort = () => controller.abort();
|
|
262
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
263
|
+
const timer = setTimeout(abort, 60000);
|
|
264
|
+
try {
|
|
265
|
+
onAttempt?.(routes.indexOf(credential));
|
|
266
|
+
const response = await this.fetchImpl("https://api.openai.com/v1/responses", {
|
|
267
|
+
method: "POST",
|
|
268
|
+
headers: {
|
|
269
|
+
authorization: `Bearer ${credential.apiKey}`,
|
|
270
|
+
"content-type": "application/json",
|
|
271
|
+
},
|
|
272
|
+
body: JSON.stringify(payload),
|
|
273
|
+
redirect: "error",
|
|
274
|
+
signal: controller.signal,
|
|
275
|
+
});
|
|
276
|
+
lastStatus = response.status;
|
|
277
|
+
lastRejection = undefined;
|
|
278
|
+
if (response.ok) {
|
|
279
|
+
const headers = new Headers();
|
|
280
|
+
for (const name of [
|
|
281
|
+
"content-type",
|
|
282
|
+
"cache-control",
|
|
283
|
+
"x-request-id",
|
|
284
|
+
"retry-after",
|
|
285
|
+
]) {
|
|
286
|
+
const value = response.headers.get(name);
|
|
287
|
+
if (value)
|
|
288
|
+
headers.set(name, value);
|
|
289
|
+
}
|
|
290
|
+
return new Response(response.body, {
|
|
291
|
+
status: response.status,
|
|
292
|
+
headers,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const errorBody = await readErrorBody(response, 5000, 65536);
|
|
296
|
+
let data;
|
|
297
|
+
try {
|
|
298
|
+
data = JSON.parse(errorBody);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
data = null;
|
|
302
|
+
}
|
|
303
|
+
const rejection = classifyCapabilityFailure(response.status, data);
|
|
304
|
+
const source = isRecord(data) && isRecord(data.error) ? data.error : {};
|
|
305
|
+
const sanitized = { message: "Upstream rejected the API request.", code: "api_request_rejected" };
|
|
306
|
+
for (const field of ["code", "type", "param"]) {
|
|
307
|
+
const value = source[field];
|
|
308
|
+
if (typeof value === "string" && /^[A-Za-z0-9_.]{1,80}$/.test(value))
|
|
309
|
+
sanitized[field] = value;
|
|
310
|
+
}
|
|
311
|
+
if (rejection) {
|
|
312
|
+
lastRejection = Response.json({ error: sanitized }, { status: response.status });
|
|
313
|
+
this.failures.record(this.key(credential), route.upstreamModel, rejection, effort, route.serviceTier);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (![401, 403, 429, 500, 502, 503, 504].includes(response.status)) {
|
|
317
|
+
return Response.json({ error: sanitized }, { status: response.status });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
if (error instanceof ClientCancellationError)
|
|
322
|
+
throw error;
|
|
323
|
+
if (signal?.aborted)
|
|
324
|
+
throw new ClientCancellationError();
|
|
325
|
+
lastStatus = 502;
|
|
326
|
+
lastRejection = undefined;
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
clearTimeout(timer);
|
|
330
|
+
signal?.removeEventListener("abort", abort);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (lastRejection)
|
|
334
|
+
return lastRejection;
|
|
335
|
+
// A 401/403 here belongs to the API key, not the desktop login; the native
|
|
336
|
+
// client must not read an exhausted credential pool as a bad login.
|
|
337
|
+
return this.failure(lastStatus === 401 || lastStatus === 403 ? 503 : lastStatus, "model_route_pool_unavailable");
|
|
338
|
+
}
|
|
339
|
+
recordStreamFailure(credential, alias, body, error) {
|
|
340
|
+
const route = parseModelRoute(alias);
|
|
341
|
+
if (route.kind !== credential.kind)
|
|
342
|
+
return;
|
|
343
|
+
const rejection = classifyCapabilityFailure(400, error);
|
|
344
|
+
if (!rejection)
|
|
345
|
+
return;
|
|
346
|
+
const effort = isRecord(body.reasoning) && typeof body.reasoning.effort === "string" ? body.reasoning.effort : undefined;
|
|
347
|
+
const tier = route.serviceTier ?? (typeof body.service_tier === "string" ? body.service_tier : undefined);
|
|
348
|
+
this.failures.record(this.key(credential), route.upstreamModel, rejection, effort, tier);
|
|
349
|
+
}
|
|
350
|
+
failure(status, code, message = "No eligible credential could serve the selected API model route. No other pool was used.") {
|
|
351
|
+
return Response.json({
|
|
352
|
+
error: {
|
|
353
|
+
code,
|
|
354
|
+
message,
|
|
355
|
+
},
|
|
356
|
+
}, { status });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
@@ -11,6 +11,11 @@ export interface AppBindPaths {
|
|
|
11
11
|
launchAgentPath: string | null;
|
|
12
12
|
}
|
|
13
13
|
export interface AppBindState {
|
|
14
|
+
nativeOpenai?: boolean;
|
|
15
|
+
catalogAccount?: {
|
|
16
|
+
email: string;
|
|
17
|
+
accountId: string;
|
|
18
|
+
};
|
|
14
19
|
version: 1;
|
|
15
20
|
platform: NodeJS.Platform;
|
|
16
21
|
host: string;
|
|
@@ -70,6 +75,11 @@ export interface AppBindResult {
|
|
|
70
75
|
}
|
|
71
76
|
export type ProcessIdentityVerifier = (pid: number, startedAt: number, platform: NodeJS.Platform, identityToken?: string) => boolean | Promise<boolean>;
|
|
72
77
|
export interface AppBindOptions {
|
|
78
|
+
nativeOpenai?: boolean;
|
|
79
|
+
catalogAccount?: {
|
|
80
|
+
email: string;
|
|
81
|
+
accountId: string;
|
|
82
|
+
};
|
|
73
83
|
env?: NodeJS.ProcessEnv;
|
|
74
84
|
platform?: NodeJS.Platform;
|
|
75
85
|
home?: string;
|
|
@@ -93,6 +103,8 @@ export interface DetachedProcessStopOptions {
|
|
|
93
103
|
/** Expected per-process nonce when verifying a persisted PID. */
|
|
94
104
|
identityToken?: string;
|
|
95
105
|
verifyProcessIdentity?: ProcessIdentityVerifier;
|
|
106
|
+
/** Called once the recorded PID is verified as this router, before it is stopped. */
|
|
107
|
+
onOwnershipVerified?: () => void;
|
|
96
108
|
}
|
|
97
109
|
export interface RuntimeRotationAppHelperStatus {
|
|
98
110
|
state: string | null;
|
|
@@ -104,9 +116,15 @@ export interface RuntimeRotationAppHelperStatus {
|
|
|
104
116
|
/** Per-process nonce echoed by the helper command line. */
|
|
105
117
|
identityToken?: string | null;
|
|
106
118
|
}
|
|
107
|
-
export declare function rewriteConfigTomlForAppBind(rawConfig: string, baseUrl: string, clientApiKey?: string): string;
|
|
119
|
+
export declare function rewriteConfigTomlForAppBind(rawConfig: string, baseUrl: string, clientApiKey?: string, nativeOpenai?: boolean): string;
|
|
108
120
|
export declare function restoreConfigTomlFromAppBind(currentConfig: string, originalConfig: string): string;
|
|
109
121
|
export declare function resolveAppBindPaths(options?: AppBindOptions): AppBindPaths;
|
|
122
|
+
/**
|
|
123
|
+
* Whether anything still accepts connections at a recorded router address.
|
|
124
|
+
* Only "refused" is proof that no router serves it; a timeout or an unusable
|
|
125
|
+
* address is "unknown".
|
|
126
|
+
*/
|
|
127
|
+
export declare function probeRouterAddress(baseUrl: string | null | undefined, platform?: NodeJS.Platform, timeoutMs?: number): Promise<"refused" | "listening" | "unknown">;
|
|
110
128
|
export declare function stopRuntimeRotationRouterProcess(router: Pick<AppBindRouterStatus, "state" | "pid" | "startedAt" | "updatedAt" | "statusPath" | "routerScriptPath" | "identityToken"> | null, platform: NodeJS.Platform, routerScriptPath: string, options?: DetachedProcessStopOptions): Promise<boolean>;
|
|
111
129
|
/** Parse the documented local-time format emitted by `ps -o lstart=`. */
|
|
112
130
|
export declare function parsePosixProcessStartTime(value: string): number | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-bind.d.ts","sourceRoot":"","sources":["../../../lib/runtime/app-bind.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-bind.d.ts","sourceRoot":"","sources":["../../../lib/runtime/app-bind.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { withNativeBindingLock } from "./native-binding-lock.js";
|
|
2
|
+
import { isRecord } from "../utils.js";
|
|
3
|
+
import { hasNativeProviderConfig, rewriteNativeProviderConfig, restoreNativeProviderConfig } from "./native-provider-config.js";
|
|
1
4
|
import { spawn } from "node:child_process";
|
|
2
5
|
import { createHash, randomBytes } from "node:crypto";
|
|
3
6
|
import { closeSync, existsSync, mkdirSync, openSync } from "node:fs";
|
|
4
7
|
import { mkdir, open, readFile, readdir, rename, rm, unlink } from "node:fs/promises";
|
|
8
|
+
import { connect } from "node:net";
|
|
5
9
|
import { homedir } from "node:os";
|
|
6
10
|
import { basename, dirname, join } from "node:path";
|
|
7
11
|
import process from "node:process";
|
|
@@ -51,11 +55,13 @@ async function withAppBindLock(key, operation) {
|
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
|
-
export function rewriteConfigTomlForAppBind(rawConfig, baseUrl, clientApiKey = "") {
|
|
58
|
+
export function rewriteConfigTomlForAppBind(rawConfig, baseUrl, clientApiKey = "", nativeOpenai = false) {
|
|
59
|
+
if (nativeOpenai)
|
|
60
|
+
return rewriteNativeProviderConfig(rawConfig, baseUrl);
|
|
55
61
|
return rewriteConfigTomlForRuntimeRotationProvider(rawConfig, baseUrl, clientApiKey);
|
|
56
62
|
}
|
|
57
63
|
export function restoreConfigTomlFromAppBind(currentConfig, originalConfig) {
|
|
58
|
-
return restoreConfigTomlFromRuntimeRotationProvider(currentConfig, originalConfig);
|
|
64
|
+
return restoreConfigTomlFromRuntimeRotationProvider(restoreNativeProviderConfig(currentConfig, originalConfig), originalConfig);
|
|
59
65
|
}
|
|
60
66
|
function sha256(value) {
|
|
61
67
|
return createHash("sha256").update(value).digest("hex");
|
|
@@ -183,6 +189,8 @@ function readAppBindStateRecord(record) {
|
|
|
183
189
|
nodePath,
|
|
184
190
|
routerScriptPath,
|
|
185
191
|
clientApiKey,
|
|
192
|
+
nativeOpenai: record.nativeOpenai === true,
|
|
193
|
+
catalogAccount: isRecord(record.catalogAccount) && typeof record.catalogAccount.email === "string" && typeof record.catalogAccount.accountId === "string" ? { email: record.catalogAccount.email, accountId: record.catalogAccount.accountId } : undefined,
|
|
186
194
|
identityToken: identityToken ?? undefined,
|
|
187
195
|
startupPath: readString(record, "startupPath"),
|
|
188
196
|
launchAgentPath: readString(record, "launchAgentPath"),
|
|
@@ -421,6 +429,30 @@ function spawnRouter(state) {
|
|
|
421
429
|
closeSync(logFd);
|
|
422
430
|
}
|
|
423
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* Whether anything still accepts connections at a recorded router address.
|
|
434
|
+
* Only "refused" is proof that no router serves it; a timeout or an unusable
|
|
435
|
+
* address is "unknown".
|
|
436
|
+
*/
|
|
437
|
+
export async function probeRouterAddress(baseUrl, platform = process.platform, timeoutMs = platform === "win32" ? WINDOWS_PROCESS_IDENTITY_PROBE_TIMEOUT_MS : 1000) {
|
|
438
|
+
let url;
|
|
439
|
+
try {
|
|
440
|
+
url = new URL(baseUrl ?? "");
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
return "unknown";
|
|
444
|
+
}
|
|
445
|
+
const port = Number(url.port);
|
|
446
|
+
if (!Number.isInteger(port) || port <= 0)
|
|
447
|
+
return "unknown";
|
|
448
|
+
return new Promise((resolve) => {
|
|
449
|
+
const socket = connect({ host: url.hostname.replace(/^\[|\]$/g, ""), port });
|
|
450
|
+
const done = (result) => { clearTimeout(timer); socket.destroy(); resolve(result); };
|
|
451
|
+
const timer = setTimeout(() => done("unknown"), timeoutMs);
|
|
452
|
+
socket.once("connect", () => done("listening"));
|
|
453
|
+
socket.once("error", (error) => done(error.code === "ECONNREFUSED" ? "refused" : "unknown"));
|
|
454
|
+
});
|
|
455
|
+
}
|
|
424
456
|
async function maybeStartRouter(state, options) {
|
|
425
457
|
if (options.spawnDetached === false)
|
|
426
458
|
return false;
|
|
@@ -497,6 +529,7 @@ export async function stopRuntimeRotationRouterProcess(router, platform, routerS
|
|
|
497
529
|
if (!verified) {
|
|
498
530
|
return false;
|
|
499
531
|
}
|
|
532
|
+
options.onOwnershipVerified?.();
|
|
500
533
|
return stopDetachedProcess(router.pid, platform, options);
|
|
501
534
|
}
|
|
502
535
|
async function stopRouter(router, platform, routerScriptPath, options = {}) {
|
|
@@ -960,7 +993,7 @@ export async function getAppBindStatus(options = {}) {
|
|
|
960
993
|
if (state === null) {
|
|
961
994
|
const current = await readConfigIfExists(paths.configPath);
|
|
962
995
|
unmanagedBind =
|
|
963
|
-
current.existed && configHasRuntimeRotationProvider(current.content);
|
|
996
|
+
current.existed && (configHasRuntimeRotationProvider(current.content) || hasNativeProviderConfig(current.content));
|
|
964
997
|
}
|
|
965
998
|
return {
|
|
966
999
|
bound: state !== null || unmanagedBind,
|
|
@@ -973,12 +1006,42 @@ export async function getAppBindStatus(options = {}) {
|
|
|
973
1006
|
}
|
|
974
1007
|
export async function bindCodexAppRuntimeRotation(options = {}) {
|
|
975
1008
|
const paths = resolveAppBindPaths(options);
|
|
976
|
-
return withAppBindLock(paths.bindDir, () => bindCodexAppRuntimeRotationLocked(options, paths));
|
|
1009
|
+
return withAppBindLock(paths.bindDir, () => withNativeBindingLock(paths.configPath, () => bindCodexAppRuntimeRotationLocked(options, paths)));
|
|
977
1010
|
}
|
|
978
1011
|
async function bindCodexAppRuntimeRotationLocked(options, paths) {
|
|
979
1012
|
const platform = options.platform ?? process.platform;
|
|
980
1013
|
const now = options.now?.() ?? Date.now();
|
|
981
1014
|
const existingState = await readAppBindState(paths.statePath);
|
|
1015
|
+
const nativeOpenai = options.nativeOpenai ?? existingState?.nativeOpenai ??
|
|
1016
|
+
((options.env ?? process.env).CODEX_MULTI_AUTH_NATIVE_OPENAI === "1");
|
|
1017
|
+
const catalogAccount = options.nativeOpenai === false ? undefined : options.catalogAccount ?? existingState?.catalogAccount;
|
|
1018
|
+
if (existingState && (!!existingState.nativeOpenai !== nativeOpenai || JSON.stringify(catalogAccount) !== JSON.stringify(existingState.catalogAccount))) {
|
|
1019
|
+
const router = await readRouterStatus(paths.statusPath);
|
|
1020
|
+
let ownRouter = false;
|
|
1021
|
+
await stopRouter(router, platform, existingState.routerScriptPath, {
|
|
1022
|
+
log: options.log, identityToken: existingState.identityToken, verifyProcessIdentity: options.verifyProcessIdentity,
|
|
1023
|
+
onOwnershipVerified: () => { ownRouter = true; },
|
|
1024
|
+
});
|
|
1025
|
+
// Refuse only when the recorded PID is verifiably our router and it survived
|
|
1026
|
+
// the stop. A recycled PID now owned by another process must not block
|
|
1027
|
+
// mode changes forever (unbind treats the same case as a warning).
|
|
1028
|
+
if (router?.pid && isProcessAlive(router.pid)) {
|
|
1029
|
+
if (ownRouter)
|
|
1030
|
+
throw new Error("Stop the existing app router before changing provider mode");
|
|
1031
|
+
// A failed identity check is not proof: a slow or failing process probe
|
|
1032
|
+
// (common on Windows) also returns false for a live router. Treat the pid
|
|
1033
|
+
// as recycled only when nothing answers at the recorded router address.
|
|
1034
|
+
const address = await probeRouterAddress(router.baseUrl ?? existingState.baseUrl, options.platform ?? process.platform);
|
|
1035
|
+
if (address !== "refused") {
|
|
1036
|
+
throw new Error(`Could not confirm that the app router recorded as pid ${router.pid} has stopped (its address ${address === "listening" ? "still accepts connections" : "could not be checked"}). Stop it before changing provider mode.`);
|
|
1037
|
+
}
|
|
1038
|
+
options.log?.(`Warning: recorded router pid ${router.pid} is not the app router; continuing`);
|
|
1039
|
+
// The record describes a router that no longer exists. Drop it so
|
|
1040
|
+
// maybeStartRouter starts a replacement instead of trusting the live
|
|
1041
|
+
// (recycled) pid and leaving the new mode pointed at a dead address.
|
|
1042
|
+
await unlinkIfExists(paths.statusPath);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
982
1045
|
const host = existingState?.host ?? "127.0.0.1";
|
|
983
1046
|
let port = existingState && existingState.port > 0 ? existingState.port : 0;
|
|
984
1047
|
let baseUrl = existingState?.baseUrl ?? formatBaseUrl(host, port);
|
|
@@ -993,8 +1056,13 @@ async function bindCodexAppRuntimeRotationLocked(options, paths) {
|
|
|
993
1056
|
content,
|
|
994
1057
|
createdAt: now,
|
|
995
1058
|
};
|
|
996
|
-
|
|
1059
|
+
const bindingContent = nativeOpenai
|
|
1060
|
+
? restoreConfigTomlFromAppBind(content, backup.content)
|
|
1061
|
+
: restoreNativeProviderConfig(content, backup.content);
|
|
1062
|
+
let boundConfig = rewriteConfigTomlForAppBind(bindingContent, baseUrl, clientApiKey, nativeOpenai);
|
|
997
1063
|
let state = {
|
|
1064
|
+
nativeOpenai,
|
|
1065
|
+
catalogAccount,
|
|
998
1066
|
version: 1,
|
|
999
1067
|
platform,
|
|
1000
1068
|
host,
|
|
@@ -1056,7 +1124,7 @@ async function bindCodexAppRuntimeRotationLocked(options, paths) {
|
|
|
1056
1124
|
}
|
|
1057
1125
|
throw new Error("Codex app bind could not resolve a runtime router port; refusing to write config.toml with port=0.");
|
|
1058
1126
|
}
|
|
1059
|
-
boundConfig = rewriteConfigTomlForAppBind(
|
|
1127
|
+
boundConfig = rewriteConfigTomlForAppBind(bindingContent, baseUrl, clientApiKey, nativeOpenai);
|
|
1060
1128
|
state = {
|
|
1061
1129
|
...state,
|
|
1062
1130
|
port,
|
|
@@ -1078,7 +1146,7 @@ async function bindCodexAppRuntimeRotationLocked(options, paths) {
|
|
|
1078
1146
|
}
|
|
1079
1147
|
export async function unbindCodexAppRuntimeRotation(options = {}) {
|
|
1080
1148
|
const paths = resolveAppBindPaths(options);
|
|
1081
|
-
return withAppBindLock(paths.bindDir, () => unbindCodexAppRuntimeRotationLocked(options, paths));
|
|
1149
|
+
return withAppBindLock(paths.bindDir, () => withNativeBindingLock(paths.configPath, () => unbindCodexAppRuntimeRotationLocked(options, paths)));
|
|
1082
1150
|
}
|
|
1083
1151
|
async function unbindCodexAppRuntimeRotationLocked(options, paths) {
|
|
1084
1152
|
const state = await readAppBindState(paths.statePath);
|
|
@@ -1262,8 +1330,8 @@ async function unbindCodexAppRuntimeRotationLocked(options, paths) {
|
|
|
1262
1330
|
// above can't see this, so consult the config directly and self-heal it
|
|
1263
1331
|
// back to a working provider when it is bound.
|
|
1264
1332
|
const current = await readConfigIfExists(paths.configPath);
|
|
1265
|
-
if (current.existed && configHasRuntimeRotationProvider(current.content)) {
|
|
1266
|
-
await atomicWriteFile(paths.configPath, restoreConfigTomlFromRuntimeRotationProviderWithoutBackup(current.content));
|
|
1333
|
+
if (current.existed && (configHasRuntimeRotationProvider(current.content) || hasNativeProviderConfig(current.content))) {
|
|
1334
|
+
await atomicWriteFile(paths.configPath, restoreConfigTomlFromRuntimeRotationProviderWithoutBackup(restoreNativeProviderConfig(current.content, "")));
|
|
1267
1335
|
selfHealed = true;
|
|
1268
1336
|
}
|
|
1269
1337
|
}
|
|
@@ -1328,6 +1396,8 @@ export function formatAppBindStatus(status) {
|
|
|
1328
1396
|
else if (status.router?.lastAccountIndex !== null && status.router?.lastAccountIndex !== undefined) {
|
|
1329
1397
|
parts.push(`lastAccount=Account ${status.router.lastAccountIndex + 1}`);
|
|
1330
1398
|
}
|
|
1399
|
+
if (status.state.nativeOpenai)
|
|
1400
|
+
return `Codex app bind: ${parts.join(", ")}, native OpenAI, live account catalogs`;
|
|
1331
1401
|
return [
|
|
1332
1402
|
`Codex app bind: ${parts.join(", ")}`,
|
|
1333
1403
|
[
|