@iam-brain/opencode-codex-auth 1.2.4 → 1.3.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/README.md +42 -83
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +41 -18
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts-tools.d.ts.map +1 -1
- package/dist/lib/accounts-tools.js +112 -29
- package/dist/lib/accounts-tools.js.map +1 -1
- package/dist/lib/cache-io.d.ts.map +1 -1
- package/dist/lib/cache-io.js +6 -1
- package/dist/lib/cache-io.js.map +1 -1
- package/dist/lib/codex-native/accounts.d.ts.map +1 -1
- package/dist/lib/codex-native/accounts.js +18 -12
- package/dist/lib/codex-native/accounts.js.map +1 -1
- package/dist/lib/codex-native/acquire-auth.d.ts +1 -1
- package/dist/lib/codex-native/acquire-auth.d.ts.map +1 -1
- package/dist/lib/codex-native/acquire-auth.js +318 -204
- package/dist/lib/codex-native/acquire-auth.js.map +1 -1
- package/dist/lib/codex-native/auth-menu-flow.d.ts.map +1 -1
- package/dist/lib/codex-native/auth-menu-flow.js +27 -12
- package/dist/lib/codex-native/auth-menu-flow.js.map +1 -1
- package/dist/lib/codex-native/auth-menu-quotas.d.ts.map +1 -1
- package/dist/lib/codex-native/auth-menu-quotas.js +11 -4
- package/dist/lib/codex-native/auth-menu-quotas.js.map +1 -1
- package/dist/lib/codex-native/catalog-auth.d.ts.map +1 -1
- package/dist/lib/codex-native/catalog-auth.js +4 -2
- package/dist/lib/codex-native/catalog-auth.js.map +1 -1
- package/dist/lib/codex-native/chat-hooks.d.ts.map +1 -1
- package/dist/lib/codex-native/chat-hooks.js +0 -8
- package/dist/lib/codex-native/chat-hooks.js.map +1 -1
- package/dist/lib/codex-native/client-identity.d.ts.map +1 -1
- package/dist/lib/codex-native/client-identity.js +11 -4
- package/dist/lib/codex-native/client-identity.js.map +1 -1
- package/dist/lib/codex-native/collaboration.d.ts +1 -1
- package/dist/lib/codex-native/collaboration.d.ts.map +1 -1
- package/dist/lib/codex-native/collaboration.js +9 -116
- package/dist/lib/codex-native/collaboration.js.map +1 -1
- package/dist/lib/codex-native/oauth-auth-methods.d.ts.map +1 -1
- package/dist/lib/codex-native/oauth-auth-methods.js +25 -6
- package/dist/lib/codex-native/oauth-auth-methods.js.map +1 -1
- package/dist/lib/codex-native/oauth-server-debug.d.ts +10 -0
- package/dist/lib/codex-native/oauth-server-debug.d.ts.map +1 -0
- package/dist/lib/codex-native/oauth-server-debug.js +92 -0
- package/dist/lib/codex-native/oauth-server-debug.js.map +1 -0
- package/dist/lib/codex-native/oauth-server-network.d.ts +5 -0
- package/dist/lib/codex-native/oauth-server-network.d.ts.map +1 -0
- package/dist/lib/codex-native/oauth-server-network.js +39 -0
- package/dist/lib/codex-native/oauth-server-network.js.map +1 -0
- package/dist/lib/codex-native/oauth-server-types.d.ts +24 -0
- package/dist/lib/codex-native/oauth-server-types.d.ts.map +1 -0
- package/dist/lib/codex-native/oauth-server-types.js +2 -0
- package/dist/lib/codex-native/oauth-server-types.js.map +1 -0
- package/dist/lib/codex-native/oauth-server.d.ts +2 -16
- package/dist/lib/codex-native/oauth-server.d.ts.map +1 -1
- package/dist/lib/codex-native/oauth-server.js +63 -118
- package/dist/lib/codex-native/oauth-server.js.map +1 -1
- package/dist/lib/codex-native/openai-loader-fetch-quota.d.ts +18 -0
- package/dist/lib/codex-native/openai-loader-fetch-quota.d.ts.map +1 -0
- package/dist/lib/codex-native/openai-loader-fetch-quota.js +71 -0
- package/dist/lib/codex-native/openai-loader-fetch-quota.js.map +1 -0
- package/dist/lib/codex-native/openai-loader-fetch-state.d.ts +27 -0
- package/dist/lib/codex-native/openai-loader-fetch-state.d.ts.map +1 -0
- package/dist/lib/codex-native/openai-loader-fetch-state.js +91 -0
- package/dist/lib/codex-native/openai-loader-fetch-state.js.map +1 -0
- package/dist/lib/codex-native/openai-loader-fetch.d.ts.map +1 -1
- package/dist/lib/codex-native/openai-loader-fetch.js +49 -131
- package/dist/lib/codex-native/openai-loader-fetch.js.map +1 -1
- package/dist/lib/codex-native/originator.d.ts.map +1 -1
- package/dist/lib/codex-native/originator.js +18 -1
- package/dist/lib/codex-native/originator.js.map +1 -1
- package/dist/lib/codex-native/request-transform-instructions.d.ts +16 -0
- package/dist/lib/codex-native/request-transform-instructions.d.ts.map +1 -0
- package/dist/lib/codex-native/request-transform-instructions.js +114 -0
- package/dist/lib/codex-native/request-transform-instructions.js.map +1 -0
- package/dist/lib/codex-native/request-transform-model.d.ts +39 -0
- package/dist/lib/codex-native/request-transform-model.d.ts.map +1 -0
- package/dist/lib/codex-native/request-transform-model.js +270 -0
- package/dist/lib/codex-native/request-transform-model.js.map +1 -0
- package/dist/lib/codex-native/request-transform-payload-helpers.d.ts +26 -0
- package/dist/lib/codex-native/request-transform-payload-helpers.d.ts.map +1 -0
- package/dist/lib/codex-native/request-transform-payload-helpers.js +232 -0
- package/dist/lib/codex-native/request-transform-payload-helpers.js.map +1 -0
- package/dist/lib/codex-native/request-transform-payload.d.ts +53 -0
- package/dist/lib/codex-native/request-transform-payload.d.ts.map +1 -0
- package/dist/lib/codex-native/request-transform-payload.js +214 -0
- package/dist/lib/codex-native/request-transform-payload.js.map +1 -0
- package/dist/lib/codex-native/request-transform-shared.d.ts +8 -0
- package/dist/lib/codex-native/request-transform-shared.d.ts.map +1 -0
- package/dist/lib/codex-native/request-transform-shared.js +49 -0
- package/dist/lib/codex-native/request-transform-shared.js.map +1 -0
- package/dist/lib/codex-native/request-transform.d.ts +3 -122
- package/dist/lib/codex-native/request-transform.d.ts.map +1 -1
- package/dist/lib/codex-native/request-transform.js +3 -831
- package/dist/lib/codex-native/request-transform.js.map +1 -1
- package/dist/lib/codex-native/session-affinity-state.d.ts +14 -1
- package/dist/lib/codex-native/session-affinity-state.d.ts.map +1 -1
- package/dist/lib/codex-native/session-affinity-state.js +21 -8
- package/dist/lib/codex-native/session-affinity-state.js.map +1 -1
- package/dist/lib/codex-native.js.map +1 -1
- package/dist/lib/codex-prompts-cache.d.ts.map +1 -1
- package/dist/lib/codex-prompts-cache.js.map +1 -1
- package/dist/lib/codex-quota-fetch.d.ts.map +1 -1
- package/dist/lib/codex-quota-fetch.js +13 -10
- package/dist/lib/codex-quota-fetch.js.map +1 -1
- package/dist/lib/codex-status-storage.d.ts.map +1 -1
- package/dist/lib/codex-status-storage.js.map +1 -1
- package/dist/lib/codex-status.d.ts.map +1 -1
- package/dist/lib/codex-status.js +28 -3
- package/dist/lib/codex-status.js.map +1 -1
- package/dist/lib/config/io.d.ts +16 -0
- package/dist/lib/config/io.d.ts.map +1 -0
- package/dist/lib/config/io.js +64 -0
- package/dist/lib/config/io.js.map +1 -0
- package/dist/lib/config/parse.d.ts +21 -0
- package/dist/lib/config/parse.d.ts.map +1 -0
- package/dist/lib/config/parse.js +347 -0
- package/dist/lib/config/parse.js.map +1 -0
- package/dist/lib/config/resolve.d.ts +27 -0
- package/dist/lib/config/resolve.d.ts.map +1 -0
- package/dist/lib/config/resolve.js +152 -0
- package/dist/lib/config/resolve.js.map +1 -0
- package/dist/lib/config/types.d.ts +72 -0
- package/dist/lib/config/types.d.ts.map +1 -0
- package/dist/lib/config/types.js +151 -0
- package/dist/lib/config/types.js.map +1 -0
- package/dist/lib/config/validation.d.ts +6 -0
- package/dist/lib/config/validation.d.ts.map +1 -0
- package/dist/lib/config/validation.js +160 -0
- package/dist/lib/config/validation.js.map +1 -0
- package/dist/lib/config.d.ts +5 -111
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +5 -835
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/fetch-orchestrator-helpers.d.ts +13 -0
- package/dist/lib/fetch-orchestrator-helpers.d.ts.map +1 -0
- package/dist/lib/fetch-orchestrator-helpers.js +63 -0
- package/dist/lib/fetch-orchestrator-helpers.js.map +1 -0
- package/dist/lib/fetch-orchestrator-types.d.ts +71 -0
- package/dist/lib/fetch-orchestrator-types.d.ts.map +1 -0
- package/dist/lib/fetch-orchestrator-types.js +11 -0
- package/dist/lib/fetch-orchestrator-types.js.map +1 -0
- package/dist/lib/fetch-orchestrator.d.ts +3 -69
- package/dist/lib/fetch-orchestrator.d.ts.map +1 -1
- package/dist/lib/fetch-orchestrator.js +78 -57
- package/dist/lib/fetch-orchestrator.js.map +1 -1
- package/dist/lib/identity.d.ts +6 -0
- package/dist/lib/identity.d.ts.map +1 -1
- package/dist/lib/identity.js +25 -4
- package/dist/lib/identity.js.map +1 -1
- package/dist/lib/model-catalog/cache-helpers.d.ts +23 -0
- package/dist/lib/model-catalog/cache-helpers.d.ts.map +1 -0
- package/dist/lib/model-catalog/cache-helpers.js +210 -0
- package/dist/lib/model-catalog/cache-helpers.js.map +1 -0
- package/dist/lib/model-catalog/catalog-fetch.d.ts +3 -0
- package/dist/lib/model-catalog/catalog-fetch.d.ts.map +1 -0
- package/dist/lib/model-catalog/catalog-fetch.js +159 -0
- package/dist/lib/model-catalog/catalog-fetch.js.map +1 -0
- package/dist/lib/model-catalog/provider.d.ts +6 -0
- package/dist/lib/model-catalog/provider.d.ts.map +1 -0
- package/dist/lib/model-catalog/provider.js +254 -0
- package/dist/lib/model-catalog/provider.js.map +1 -0
- package/dist/lib/model-catalog/shared.d.ts +95 -0
- package/dist/lib/model-catalog/shared.d.ts.map +1 -0
- package/dist/lib/model-catalog/shared.js +154 -0
- package/dist/lib/model-catalog/shared.js.map +1 -0
- package/dist/lib/model-catalog.d.ts +3 -68
- package/dist/lib/model-catalog.d.ts.map +1 -1
- package/dist/lib/model-catalog.js +3 -767
- package/dist/lib/model-catalog.js.map +1 -1
- package/dist/lib/opencode-install.d.ts.map +1 -1
- package/dist/lib/opencode-install.js +5 -6
- package/dist/lib/opencode-install.js.map +1 -1
- package/dist/lib/orchestrator-agent.d.ts.map +1 -1
- package/dist/lib/orchestrator-agent.js +2 -1
- package/dist/lib/orchestrator-agent.js.map +1 -1
- package/dist/lib/paths.d.ts.map +1 -1
- package/dist/lib/paths.js +8 -2
- package/dist/lib/paths.js.map +1 -1
- package/dist/lib/proactive-refresh.d.ts.map +1 -1
- package/dist/lib/proactive-refresh.js +48 -13
- package/dist/lib/proactive-refresh.js.map +1 -1
- package/dist/lib/quarantine.js.map +1 -1
- package/dist/lib/quota-threshold-alerts.d.ts.map +1 -1
- package/dist/lib/quota-threshold-alerts.js +3 -1
- package/dist/lib/quota-threshold-alerts.js.map +1 -1
- package/dist/lib/refresh-queue.d.ts.map +1 -1
- package/dist/lib/refresh-queue.js +1 -0
- package/dist/lib/refresh-queue.js.map +1 -1
- package/dist/lib/request-snapshots.d.ts.map +1 -1
- package/dist/lib/request-snapshots.js +46 -10
- package/dist/lib/request-snapshots.js.map +1 -1
- package/dist/lib/rotation.d.ts.map +1 -1
- package/dist/lib/rotation.js +3 -2
- package/dist/lib/rotation.js.map +1 -1
- package/dist/lib/session-affinity.d.ts.map +1 -1
- package/dist/lib/session-affinity.js +35 -20
- package/dist/lib/session-affinity.js.map +1 -1
- package/dist/lib/storage/domain-state.d.ts +23 -0
- package/dist/lib/storage/domain-state.d.ts.map +1 -0
- package/dist/lib/storage/domain-state.js +275 -0
- package/dist/lib/storage/domain-state.js.map +1 -0
- package/dist/lib/storage/migration.d.ts +13 -0
- package/dist/lib/storage/migration.d.ts.map +1 -0
- package/dist/lib/storage/migration.js +225 -0
- package/dist/lib/storage/migration.js.map +1 -0
- package/dist/lib/storage.d.ts +2 -9
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +44 -470
- package/dist/lib/storage.js.map +1 -1
- package/dist/lib/ui/auth-menu.d.ts +3 -2
- package/dist/lib/ui/auth-menu.d.ts.map +1 -1
- package/dist/lib/ui/auth-menu.js +1 -1
- package/dist/lib/ui/auth-menu.js.map +1 -1
- package/package.json +28 -15
|
@@ -1,768 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { resolveCustomPersonalityDescription } from "./personalities.js";
|
|
5
|
-
import { fetchRemoteText } from "./remote-cache-fetch.js";
|
|
6
|
-
import { isFsErrorCode, readJsonFileBestEffort, writeJsonFileBestEffort } from "./cache-io.js";
|
|
7
|
-
import { withLockedDirectory } from "./cache-lock.js";
|
|
8
|
-
import { buildCodexModelsMemoryCacheKey, codexAuthModelsCachePath, codexModelsCompatShardPath, codexModelsMetaPath, codexModelsSharedCachePath, isCodexModelsCacheFileName, resolveCodexCacheDir } from "./codex-cache-layout.js";
|
|
9
|
-
const CODEX_MODELS_ENDPOINT = "https://chatgpt.com/backend-api/codex/models";
|
|
10
|
-
const CODEX_GITHUB_MODELS_URL_PREFIX = "https://raw.githubusercontent.com/openai/codex";
|
|
11
|
-
const DEFAULT_CLIENT_VERSION = "0.97.0";
|
|
12
|
-
const CACHE_TTL_MS = 15 * 60 * 1000;
|
|
13
|
-
const FETCH_TIMEOUT_MS = 5000;
|
|
14
|
-
const EFFORT_SUFFIX_REGEX = /-(none|minimal|low|medium|high|xhigh)$/i;
|
|
15
|
-
const UNRESOLVED_TEMPLATE_MARKER_REGEX = /\{\{\s*[^}]+\s*\}\}/;
|
|
16
|
-
const STALE_BRIDGE_MARKERS = [
|
|
17
|
-
/multi_tool_use\.parallel/i,
|
|
18
|
-
/assistant\s+to=multi_tool_use\.parallel/i,
|
|
19
|
-
/functions\.(read|exec_command|write_stdin|apply_patch|edit|grep|glob|list)\b/i,
|
|
20
|
-
/recipient_name\s*[:=]/i
|
|
21
|
-
];
|
|
22
|
-
const REASONING_EFFORTS = new Set(["none", "minimal", "low", "medium", "high", "xhigh"]);
|
|
23
|
-
const TEXT_VERBOSITY = new Set(["low", "medium", "high"]);
|
|
24
|
-
const inMemoryCatalog = new Map();
|
|
25
|
-
const inFlightCatalogFetches = new Map();
|
|
26
|
-
function cacheKey(cacheDir, accountId) {
|
|
27
|
-
return buildCodexModelsMemoryCacheKey(cacheDir, accountId);
|
|
28
|
-
}
|
|
29
|
-
function cachePath(cacheDir, accountId) {
|
|
30
|
-
return codexAuthModelsCachePath(cacheDir, accountId);
|
|
31
|
-
}
|
|
32
|
-
function opencodeShardCachePath(cacheDir, accountId) {
|
|
33
|
-
return codexModelsCompatShardPath(cacheDir, accountId);
|
|
34
|
-
}
|
|
35
|
-
function opencodeSharedCachePath(cacheDir) {
|
|
36
|
-
return codexModelsSharedCachePath(cacheDir);
|
|
37
|
-
}
|
|
38
|
-
function parseSemver(value) {
|
|
39
|
-
if (typeof value !== "string")
|
|
40
|
-
return undefined;
|
|
41
|
-
const trimmed = value.trim();
|
|
42
|
-
const match = trimmed.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
43
|
-
if (!match)
|
|
44
|
-
return undefined;
|
|
45
|
-
return [Number.parseInt(match[1], 10), Number.parseInt(match[2], 10), Number.parseInt(match[3], 10)];
|
|
46
|
-
}
|
|
47
|
-
function compareSemver(a, b) {
|
|
48
|
-
if (a[0] !== b[0])
|
|
49
|
-
return a[0] - b[0];
|
|
50
|
-
if (a[1] !== b[1])
|
|
51
|
-
return a[1] - b[1];
|
|
52
|
-
return a[2] - b[2];
|
|
53
|
-
}
|
|
54
|
-
function normalizeSemver(value) {
|
|
55
|
-
const parsed = parseSemver(value);
|
|
56
|
-
if (!parsed)
|
|
57
|
-
return undefined;
|
|
58
|
-
return `${parsed[0]}.${parsed[1]}.${parsed[2]}`;
|
|
59
|
-
}
|
|
60
|
-
function githubModelsTag(version) {
|
|
61
|
-
return `rust-v${version}`;
|
|
62
|
-
}
|
|
63
|
-
function githubModelsUrl(version) {
|
|
64
|
-
return `${CODEX_GITHUB_MODELS_URL_PREFIX}/${githubModelsTag(version)}/codex-rs/core/models.json`;
|
|
65
|
-
}
|
|
66
|
-
function semverFromTag(tag) {
|
|
67
|
-
if (!tag)
|
|
68
|
-
return undefined;
|
|
69
|
-
const match = tag.match(/(\d+\.\d+\.\d+)/);
|
|
70
|
-
if (!match?.[1])
|
|
71
|
-
return undefined;
|
|
72
|
-
return normalizeSemver(match[1]);
|
|
73
|
-
}
|
|
74
|
-
async function readGitHubModelsCacheMeta(cacheDir) {
|
|
75
|
-
const file = codexModelsMetaPath(cacheDir);
|
|
76
|
-
const parsed = await readJsonFileBestEffort(file);
|
|
77
|
-
if (!isRecord(parsed))
|
|
78
|
-
return undefined;
|
|
79
|
-
const parsedVersion = normalizeSemver(typeof parsed.version === "string" ? parsed.version : undefined);
|
|
80
|
-
const parsedTag = typeof parsed.tag === "string" ? parsed.tag.trim() : "";
|
|
81
|
-
const tag = parsedTag || (parsedVersion ? githubModelsTag(parsedVersion) : "");
|
|
82
|
-
const url = typeof parsed.url === "string" ? parsed.url.trim() : "";
|
|
83
|
-
const lastChecked = typeof parsed.lastChecked === "number" && Number.isFinite(parsed.lastChecked) ? parsed.lastChecked : 0;
|
|
84
|
-
const etag = typeof parsed.etag === "string" ? parsed.etag.trim() : undefined;
|
|
85
|
-
if (!tag || !url)
|
|
86
|
-
return undefined;
|
|
87
|
-
return { etag, tag, url, lastChecked };
|
|
88
|
-
}
|
|
89
|
-
async function writeGitHubModelsCacheMeta(cacheDir, meta) {
|
|
90
|
-
const file = codexModelsMetaPath(cacheDir);
|
|
91
|
-
await writeJsonFileBestEffort(file, meta);
|
|
92
|
-
}
|
|
93
|
-
async function refreshSharedGitHubModelsCache(input) {
|
|
94
|
-
const targetVersion = normalizeSemver(input.targetClientVersion);
|
|
95
|
-
if (!targetVersion)
|
|
96
|
-
return;
|
|
97
|
-
const existingMeta = await readGitHubModelsCacheMeta(input.cacheDir);
|
|
98
|
-
const existingVersion = parseSemver(semverFromTag(existingMeta?.tag));
|
|
99
|
-
const target = parseSemver(targetVersion);
|
|
100
|
-
if (!target)
|
|
101
|
-
return;
|
|
102
|
-
if (existingVersion && compareSemver(existingVersion, target) >= 0)
|
|
103
|
-
return;
|
|
104
|
-
const tag = githubModelsTag(targetVersion);
|
|
105
|
-
const url = githubModelsUrl(targetVersion);
|
|
106
|
-
const result = await fetchRemoteText({
|
|
107
|
-
key: "models",
|
|
108
|
-
url,
|
|
109
|
-
etag: existingMeta?.url === url ? existingMeta.etag : undefined
|
|
110
|
-
}, {
|
|
111
|
-
fetchImpl: input.fetchImpl,
|
|
112
|
-
timeoutMs: FETCH_TIMEOUT_MS,
|
|
113
|
-
allowedHosts: ["raw.githubusercontent.com"]
|
|
114
|
-
});
|
|
115
|
-
if (result.status === "not_modified" && existingMeta) {
|
|
116
|
-
await writeGitHubModelsCacheMeta(input.cacheDir, {
|
|
117
|
-
etag: existingMeta.etag,
|
|
118
|
-
tag,
|
|
119
|
-
lastChecked: input.now,
|
|
120
|
-
url
|
|
121
|
-
});
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (result.status !== "ok") {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
try {
|
|
128
|
-
const payload = JSON.parse(result.text);
|
|
129
|
-
const models = parseCatalogResponse(payload);
|
|
130
|
-
if (models.length === 0)
|
|
131
|
-
return;
|
|
132
|
-
const etag = result.etag || (existingMeta?.url === url ? existingMeta.etag : undefined);
|
|
133
|
-
const file = opencodeSharedCachePath(input.cacheDir);
|
|
134
|
-
await writeJsonFileBestEffort(file, { fetchedAt: input.now, source: "github", models });
|
|
135
|
-
await writeGitHubModelsCacheMeta(input.cacheDir, {
|
|
136
|
-
etag,
|
|
137
|
-
tag,
|
|
138
|
-
lastChecked: input.now,
|
|
139
|
-
url
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
// Best effort refresh; continue without blocking catalog load.
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
function isRecord(value) {
|
|
147
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
148
|
-
}
|
|
149
|
-
function normalizeModelSlug(value) {
|
|
150
|
-
if (typeof value !== "string")
|
|
151
|
-
return undefined;
|
|
152
|
-
const normalized = value.trim().toLowerCase();
|
|
153
|
-
return normalized ? normalized : undefined;
|
|
154
|
-
}
|
|
155
|
-
function compareModelSlugs(a, b) {
|
|
156
|
-
return a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" });
|
|
157
|
-
}
|
|
158
|
-
function normalizeReasoningEffort(value) {
|
|
159
|
-
if (typeof value !== "string")
|
|
160
|
-
return undefined;
|
|
161
|
-
const normalized = value.trim().toLowerCase();
|
|
162
|
-
if (REASONING_EFFORTS.has(normalized)) {
|
|
163
|
-
return normalized;
|
|
164
|
-
}
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
function normalizeVerbosity(value) {
|
|
168
|
-
if (typeof value !== "string")
|
|
169
|
-
return undefined;
|
|
170
|
-
const normalized = value.trim().toLowerCase();
|
|
171
|
-
if (TEXT_VERBOSITY.has(normalized)) {
|
|
172
|
-
return normalized;
|
|
173
|
-
}
|
|
174
|
-
return undefined;
|
|
175
|
-
}
|
|
176
|
-
function parseReasoningLevels(value) {
|
|
177
|
-
if (!Array.isArray(value))
|
|
178
|
-
return null;
|
|
179
|
-
const out = [];
|
|
180
|
-
for (const item of value) {
|
|
181
|
-
if (!isRecord(item))
|
|
182
|
-
continue;
|
|
183
|
-
const effort = normalizeReasoningEffort(item.effort);
|
|
184
|
-
if (!effort)
|
|
185
|
-
continue;
|
|
186
|
-
out.push({ effort });
|
|
187
|
-
}
|
|
188
|
-
return out.length > 0 ? out : null;
|
|
189
|
-
}
|
|
190
|
-
function parseCatalogResponse(payload) {
|
|
191
|
-
if (!isRecord(payload))
|
|
192
|
-
return [];
|
|
193
|
-
const root = payload;
|
|
194
|
-
if (!Array.isArray(root.models))
|
|
195
|
-
return [];
|
|
196
|
-
const deduped = new Map();
|
|
197
|
-
for (const item of root.models) {
|
|
198
|
-
if (!isRecord(item))
|
|
199
|
-
continue;
|
|
200
|
-
const slug = normalizeModelSlug(item.slug);
|
|
201
|
-
if (!slug)
|
|
202
|
-
continue;
|
|
203
|
-
deduped.set(slug, {
|
|
204
|
-
slug,
|
|
205
|
-
model_messages: isRecord(item.model_messages)
|
|
206
|
-
? {
|
|
207
|
-
instructions_template: typeof item.model_messages.instructions_template === "string"
|
|
208
|
-
? item.model_messages.instructions_template
|
|
209
|
-
: null,
|
|
210
|
-
instructions_variables: isRecord(item.model_messages.instructions_variables)
|
|
211
|
-
? {
|
|
212
|
-
personality: typeof item.model_messages.instructions_variables.personality === "string"
|
|
213
|
-
? item.model_messages.instructions_variables.personality
|
|
214
|
-
: null,
|
|
215
|
-
personality_default: typeof item.model_messages.instructions_variables.personality_default === "string"
|
|
216
|
-
? item.model_messages.instructions_variables.personality_default
|
|
217
|
-
: null,
|
|
218
|
-
personality_friendly: typeof item.model_messages.instructions_variables.personality_friendly === "string"
|
|
219
|
-
? item.model_messages.instructions_variables.personality_friendly
|
|
220
|
-
: null,
|
|
221
|
-
personality_pragmatic: typeof item.model_messages.instructions_variables.personality_pragmatic === "string"
|
|
222
|
-
? item.model_messages.instructions_variables.personality_pragmatic
|
|
223
|
-
: null,
|
|
224
|
-
personalities: isRecord(item.model_messages.instructions_variables.personalities)
|
|
225
|
-
? item.model_messages.instructions_variables.personalities
|
|
226
|
-
: null
|
|
227
|
-
}
|
|
228
|
-
: null
|
|
229
|
-
}
|
|
230
|
-
: null,
|
|
231
|
-
base_instructions: typeof item.base_instructions === "string" ? item.base_instructions : null,
|
|
232
|
-
apply_patch_tool_type: typeof item.apply_patch_tool_type === "string" ? item.apply_patch_tool_type : null,
|
|
233
|
-
supported_reasoning_levels: parseReasoningLevels(item.supported_reasoning_levels),
|
|
234
|
-
default_reasoning_level: typeof item.default_reasoning_level === "string" ? item.default_reasoning_level : null,
|
|
235
|
-
supports_reasoning_summaries: typeof item.supports_reasoning_summaries === "boolean" ? item.supports_reasoning_summaries : null,
|
|
236
|
-
reasoning_summary_format: typeof item.reasoning_summary_format === "string" ? item.reasoning_summary_format : null,
|
|
237
|
-
support_verbosity: typeof item.support_verbosity === "boolean" ? item.support_verbosity : null,
|
|
238
|
-
default_verbosity: typeof item.default_verbosity === "string" ? item.default_verbosity : null
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
return Array.from(deduped.values()).sort((a, b) => compareModelSlugs(a.slug, b.slug));
|
|
242
|
-
}
|
|
243
|
-
function isFresh(cache, now) {
|
|
244
|
-
return now - cache.fetchedAt < CACHE_TTL_MS;
|
|
245
|
-
}
|
|
246
|
-
async function withCacheLock(cacheDir, fn) {
|
|
247
|
-
return withLockedDirectory(cacheDir, fn, { staleMs: 10_000 });
|
|
248
|
-
}
|
|
249
|
-
async function readCatalogFromDisk(cacheDir, accountId) {
|
|
250
|
-
return withCacheLock(cacheDir, async () => {
|
|
251
|
-
const file = cachePath(cacheDir, accountId);
|
|
252
|
-
const parsed = await readJsonFileBestEffort(file);
|
|
253
|
-
if (!isRecord(parsed))
|
|
254
|
-
return undefined;
|
|
255
|
-
if (typeof parsed.fetchedAt !== "number")
|
|
256
|
-
return undefined;
|
|
257
|
-
const models = parseCatalogResponse({ models: parsed.models });
|
|
258
|
-
return {
|
|
259
|
-
fetchedAt: parsed.fetchedAt,
|
|
260
|
-
models
|
|
261
|
-
};
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
async function readCatalogFromOpencodeCache(cacheDir) {
|
|
265
|
-
try {
|
|
266
|
-
const entries = await fs.readdir(cacheDir);
|
|
267
|
-
const candidates = entries.filter((name) => {
|
|
268
|
-
return isCodexModelsCacheFileName(name);
|
|
269
|
-
});
|
|
270
|
-
if (candidates.length === 0)
|
|
271
|
-
return undefined;
|
|
272
|
-
let best;
|
|
273
|
-
for (const fileName of candidates) {
|
|
274
|
-
const parsed = await readJsonFileBestEffort(path.join(cacheDir, fileName));
|
|
275
|
-
if (!isRecord(parsed))
|
|
276
|
-
continue;
|
|
277
|
-
const models = parseCatalogResponse({ models: parsed.models });
|
|
278
|
-
if (models.length === 0)
|
|
279
|
-
continue;
|
|
280
|
-
const fetchedAt = typeof parsed.fetchedAt === "number" ? parsed.fetchedAt : 0;
|
|
281
|
-
if (!best || fetchedAt > best.fetchedAt) {
|
|
282
|
-
best = {
|
|
283
|
-
fetchedAt,
|
|
284
|
-
models
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
return best;
|
|
289
|
-
}
|
|
290
|
-
catch (error) {
|
|
291
|
-
if (!isFsErrorCode(error, "ENOENT")) {
|
|
292
|
-
// Best effort fallback discovery.
|
|
293
|
-
}
|
|
294
|
-
return undefined;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
function parseFetchedAtFromUnknown(value) {
|
|
298
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
299
|
-
return value;
|
|
300
|
-
}
|
|
301
|
-
if (typeof value === "string") {
|
|
302
|
-
const parsed = Date.parse(value);
|
|
303
|
-
if (!Number.isNaN(parsed)) {
|
|
304
|
-
return parsed;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
return 0;
|
|
308
|
-
}
|
|
309
|
-
async function readCatalogFromCodexCliCache() {
|
|
310
|
-
const file = path.join(os.homedir(), ".codex", "models_cache.json");
|
|
311
|
-
const parsed = await readJsonFileBestEffort(file);
|
|
312
|
-
if (!isRecord(parsed))
|
|
313
|
-
return undefined;
|
|
314
|
-
const models = parseCatalogResponse({ models: parsed.models });
|
|
315
|
-
if (models.length === 0)
|
|
316
|
-
return undefined;
|
|
317
|
-
return {
|
|
318
|
-
fetchedAt: parseFetchedAtFromUnknown(parsed.fetched_at ?? parsed.fetchedAt),
|
|
319
|
-
models
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
async function writeCatalogToDisk(cacheDir, accountId, cache) {
|
|
323
|
-
await withCacheLock(cacheDir, async () => {
|
|
324
|
-
const primaryFile = cachePath(cacheDir, accountId);
|
|
325
|
-
const compatFile = opencodeShardCachePath(cacheDir, accountId);
|
|
326
|
-
const files = compatFile ? [primaryFile, compatFile] : [primaryFile];
|
|
327
|
-
for (const file of files) {
|
|
328
|
-
await writeJsonFileBestEffort(file, cache);
|
|
329
|
-
}
|
|
330
|
-
}).catch((error) => {
|
|
331
|
-
if (error instanceof Error) {
|
|
332
|
-
// Best effort cache persistence.
|
|
333
|
-
}
|
|
334
|
-
// Best effort cache persistence.
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
function emitEvent(input, event) {
|
|
338
|
-
try {
|
|
339
|
-
input.onEvent?.({
|
|
340
|
-
...event,
|
|
341
|
-
scope: input.accountId?.trim() ? "account" : "shared"
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
catch (error) {
|
|
345
|
-
if (error instanceof Error) {
|
|
346
|
-
// Keep catalog path side-effect free even if observer callback throws.
|
|
347
|
-
}
|
|
348
|
-
// Keep catalog path side-effect free.
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
function deriveReason(value) {
|
|
352
|
-
if (value instanceof Error) {
|
|
353
|
-
return value.name || "error";
|
|
354
|
-
}
|
|
355
|
-
return "error";
|
|
356
|
-
}
|
|
357
|
-
function emitStaleCacheFallback(input, fallback) {
|
|
358
|
-
if (fallback.disk) {
|
|
359
|
-
emitEvent(input, { type: "stale_cache_used", reason: "network_fetch_failed" });
|
|
360
|
-
return fallback.disk.models;
|
|
361
|
-
}
|
|
362
|
-
if (fallback.opencode) {
|
|
363
|
-
emitEvent(input, { type: "stale_cache_used", reason: "opencode_cache_fallback" });
|
|
364
|
-
return fallback.opencode.models;
|
|
365
|
-
}
|
|
366
|
-
if (fallback.codexCli) {
|
|
367
|
-
emitEvent(input, { type: "stale_cache_used", reason: "codex_cli_cache_fallback" });
|
|
368
|
-
return fallback.codexCli.models;
|
|
369
|
-
}
|
|
370
|
-
return undefined;
|
|
371
|
-
}
|
|
372
|
-
function normalizeClientVersion(value, fallback) {
|
|
373
|
-
const trimmed = value?.trim();
|
|
374
|
-
if (trimmed)
|
|
375
|
-
return trimmed;
|
|
376
|
-
const fallbackTrimmed = fallback?.trim();
|
|
377
|
-
return fallbackTrimmed || DEFAULT_CLIENT_VERSION;
|
|
378
|
-
}
|
|
379
|
-
function buildModelsEndpoint(clientVersion) {
|
|
380
|
-
const separator = CODEX_MODELS_ENDPOINT.includes("?") ? "&" : "?";
|
|
381
|
-
return `${CODEX_MODELS_ENDPOINT}${separator}client_version=${encodeURIComponent(clientVersion)}`;
|
|
382
|
-
}
|
|
383
|
-
export async function getCodexModelCatalog(input) {
|
|
384
|
-
const now = (input.now ?? Date.now)();
|
|
385
|
-
const cacheDir = resolveCodexCacheDir(input.cacheDir);
|
|
386
|
-
const key = cacheKey(cacheDir, input.accountId);
|
|
387
|
-
const fetchImpl = input.fetchImpl ?? fetch;
|
|
388
|
-
const accessToken = input.accessToken?.trim();
|
|
389
|
-
const targetClientVersion = normalizeSemver(input.clientVersion) ?? normalizeSemver(input.versionHeader);
|
|
390
|
-
const defaultGithubModelsRefresh = Boolean(accessToken) && input.fetchImpl === undefined && targetClientVersion !== undefined;
|
|
391
|
-
const shouldRefreshGithubModelsCache = input.refreshGithubModelsCache ?? defaultGithubModelsRefresh;
|
|
392
|
-
if (shouldRefreshGithubModelsCache) {
|
|
393
|
-
await refreshSharedGitHubModelsCache({
|
|
394
|
-
cacheDir,
|
|
395
|
-
targetClientVersion,
|
|
396
|
-
now,
|
|
397
|
-
fetchImpl
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
const memory = inMemoryCatalog.get(key);
|
|
401
|
-
const allowMemoryHit = !memory?.staleFallback || !accessToken;
|
|
402
|
-
if (memory && !input.forceRefresh && isFresh(memory, now) && allowMemoryHit) {
|
|
403
|
-
emitEvent(input, { type: "memory_cache_hit" });
|
|
404
|
-
return memory.models;
|
|
405
|
-
}
|
|
406
|
-
const disk = await readCatalogFromDisk(cacheDir, input.accountId);
|
|
407
|
-
const hasFreshDisk = !!disk && isFresh(disk, now);
|
|
408
|
-
if (hasFreshDisk && !input.forceRefresh) {
|
|
409
|
-
inMemoryCatalog.set(key, disk);
|
|
410
|
-
emitEvent(input, { type: "disk_cache_hit" });
|
|
411
|
-
return disk.models;
|
|
412
|
-
}
|
|
413
|
-
let opencodeCacheFallback;
|
|
414
|
-
let codexCliCacheFallback;
|
|
415
|
-
const ensureFallbackCaches = async () => {
|
|
416
|
-
if (opencodeCacheFallback !== undefined || codexCliCacheFallback !== undefined)
|
|
417
|
-
return;
|
|
418
|
-
opencodeCacheFallback = await readCatalogFromOpencodeCache(cacheDir);
|
|
419
|
-
codexCliCacheFallback = await readCatalogFromCodexCliCache();
|
|
420
|
-
};
|
|
421
|
-
if (!accessToken) {
|
|
422
|
-
await ensureFallbackCaches();
|
|
423
|
-
if (disk) {
|
|
424
|
-
emitEvent(input, { type: "stale_cache_used", reason: "missing_access_token" });
|
|
425
|
-
inMemoryCatalog.set(key, disk);
|
|
426
|
-
return disk.models;
|
|
427
|
-
}
|
|
428
|
-
const stale = emitStaleCacheFallback(input, {
|
|
429
|
-
opencode: opencodeCacheFallback,
|
|
430
|
-
codexCli: codexCliCacheFallback
|
|
431
|
-
});
|
|
432
|
-
if (stale) {
|
|
433
|
-
inMemoryCatalog.set(key, {
|
|
434
|
-
fetchedAt: now,
|
|
435
|
-
models: stale,
|
|
436
|
-
staleFallback: true
|
|
437
|
-
});
|
|
438
|
-
return stale;
|
|
439
|
-
}
|
|
440
|
-
emitEvent(input, { type: "catalog_unavailable", reason: "missing_access_token" });
|
|
441
|
-
return undefined;
|
|
442
|
-
}
|
|
443
|
-
const headers = {
|
|
444
|
-
authorization: `Bearer ${accessToken}`,
|
|
445
|
-
originator: input.originator?.trim() || "opencode"
|
|
446
|
-
};
|
|
447
|
-
const clientVersion = normalizeClientVersion(input.clientVersion, input.versionHeader);
|
|
448
|
-
const versionHeader = normalizeClientVersion(input.versionHeader, input.clientVersion);
|
|
449
|
-
if (versionHeader)
|
|
450
|
-
headers.version = versionHeader;
|
|
451
|
-
const userAgent = input.userAgent?.trim();
|
|
452
|
-
if (userAgent)
|
|
453
|
-
headers["user-agent"] = userAgent;
|
|
454
|
-
const betaValue = input.openaiBeta?.trim();
|
|
455
|
-
if (betaValue)
|
|
456
|
-
headers["openai-beta"] = betaValue;
|
|
457
|
-
const accountId = input.accountId?.trim() || undefined;
|
|
458
|
-
if (accountId) {
|
|
459
|
-
headers["chatgpt-account-id"] = accountId;
|
|
460
|
-
}
|
|
461
|
-
const existingInFlight = inFlightCatalogFetches.get(key);
|
|
462
|
-
if (existingInFlight) {
|
|
463
|
-
return existingInFlight;
|
|
464
|
-
}
|
|
465
|
-
const inFlight = (async () => {
|
|
466
|
-
try {
|
|
467
|
-
const controller = new AbortController();
|
|
468
|
-
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
469
|
-
let response;
|
|
470
|
-
try {
|
|
471
|
-
const endpoint = buildModelsEndpoint(clientVersion);
|
|
472
|
-
response = await fetchImpl(endpoint, {
|
|
473
|
-
method: "GET",
|
|
474
|
-
headers,
|
|
475
|
-
signal: controller.signal
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
finally {
|
|
479
|
-
clearTimeout(timeout);
|
|
480
|
-
}
|
|
481
|
-
if (!response.ok) {
|
|
482
|
-
throw new Error(`codex models request failed with status ${response.status}`);
|
|
483
|
-
}
|
|
484
|
-
const payload = (await response.json());
|
|
485
|
-
const models = parseCatalogResponse(payload);
|
|
486
|
-
if (models.length === 0) {
|
|
487
|
-
throw new Error("codex models response did not contain usable models");
|
|
488
|
-
}
|
|
489
|
-
const nextCache = {
|
|
490
|
-
fetchedAt: now,
|
|
491
|
-
models
|
|
492
|
-
};
|
|
493
|
-
inMemoryCatalog.set(key, nextCache);
|
|
494
|
-
await writeCatalogToDisk(cacheDir, accountId, nextCache);
|
|
495
|
-
emitEvent(input, { type: "network_fetch_success" });
|
|
496
|
-
return nextCache.models;
|
|
497
|
-
}
|
|
498
|
-
catch (error) {
|
|
499
|
-
emitEvent(input, { type: "network_fetch_failed", reason: deriveReason(error) });
|
|
500
|
-
await ensureFallbackCaches();
|
|
501
|
-
const stale = emitStaleCacheFallback(input, {
|
|
502
|
-
disk,
|
|
503
|
-
opencode: opencodeCacheFallback,
|
|
504
|
-
codexCli: codexCliCacheFallback
|
|
505
|
-
});
|
|
506
|
-
if (stale) {
|
|
507
|
-
inMemoryCatalog.set(key, {
|
|
508
|
-
fetchedAt: now,
|
|
509
|
-
models: stale,
|
|
510
|
-
staleFallback: true
|
|
511
|
-
});
|
|
512
|
-
return stale;
|
|
513
|
-
}
|
|
514
|
-
emitEvent(input, { type: "catalog_unavailable", reason: "network_fetch_failed" });
|
|
515
|
-
return undefined;
|
|
516
|
-
}
|
|
517
|
-
})();
|
|
518
|
-
inFlightCatalogFetches.set(key, inFlight);
|
|
519
|
-
return inFlight.finally(() => {
|
|
520
|
-
if (inFlightCatalogFetches.get(key) === inFlight) {
|
|
521
|
-
inFlightCatalogFetches.delete(key);
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
function cloneModelTemplate(template, slug) {
|
|
526
|
-
const cloned = { ...template };
|
|
527
|
-
setModelIdentityFields(cloned, slug);
|
|
528
|
-
return cloned;
|
|
529
|
-
}
|
|
530
|
-
function formatModelDisplayNameFromSlug(slug) {
|
|
531
|
-
const tokens = slug
|
|
532
|
-
.trim()
|
|
533
|
-
.split("-")
|
|
534
|
-
.map((part) => part.trim())
|
|
535
|
-
.filter((part) => part.length > 0);
|
|
536
|
-
if (tokens.length === 0)
|
|
537
|
-
return slug;
|
|
538
|
-
const words = tokens.map((token) => {
|
|
539
|
-
if (token.toLowerCase() === "gpt")
|
|
540
|
-
return "GPT";
|
|
541
|
-
if (token.length === 1)
|
|
542
|
-
return token.toUpperCase();
|
|
543
|
-
return `${token.charAt(0).toUpperCase()}${token.slice(1).toLowerCase()}`;
|
|
544
|
-
});
|
|
545
|
-
if (words[0] === "GPT" && words.length > 1) {
|
|
546
|
-
return [`${words[0]}-${words[1]}`, ...words.slice(2)].join(" ");
|
|
547
|
-
}
|
|
548
|
-
return words.join(" ");
|
|
549
|
-
}
|
|
550
|
-
function setModelIdentityFields(model, slug) {
|
|
551
|
-
const display = formatModelDisplayNameFromSlug(slug);
|
|
552
|
-
for (const key of ["id", "slug", "model"]) {
|
|
553
|
-
model[key] = slug;
|
|
554
|
-
}
|
|
555
|
-
for (const key of ["name", "displayName", "display_name"]) {
|
|
556
|
-
model[key] = display;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
function resolvePersonalityText(model, personality) {
|
|
560
|
-
const vars = model.model_messages?.instructions_variables;
|
|
561
|
-
const normalized = (personality ?? "none").trim().toLowerCase();
|
|
562
|
-
if (!normalized)
|
|
563
|
-
return undefined;
|
|
564
|
-
if (normalized !== "none") {
|
|
565
|
-
const fromFile = resolveCustomPersonalityDescription(normalized);
|
|
566
|
-
if (typeof fromFile === "string" && fromFile.trim()) {
|
|
567
|
-
return fromFile;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
if (!vars)
|
|
571
|
-
return undefined;
|
|
572
|
-
if (vars.personalities && typeof vars.personalities === "object") {
|
|
573
|
-
const fromMap = vars.personalities[normalized] ?? vars.personalities.default;
|
|
574
|
-
if (typeof fromMap === "string" && fromMap.trim()) {
|
|
575
|
-
return fromMap;
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
if (normalized === "friendly" && typeof vars.personality_friendly === "string" && vars.personality_friendly.trim()) {
|
|
579
|
-
return vars.personality_friendly;
|
|
580
|
-
}
|
|
581
|
-
if (normalized === "pragmatic" &&
|
|
582
|
-
typeof vars.personality_pragmatic === "string" &&
|
|
583
|
-
vars.personality_pragmatic.trim()) {
|
|
584
|
-
return vars.personality_pragmatic;
|
|
585
|
-
}
|
|
586
|
-
if (typeof vars.personality_default === "string" && vars.personality_default.trim()) {
|
|
587
|
-
return vars.personality_default;
|
|
588
|
-
}
|
|
589
|
-
if (typeof vars.personality === "string" && vars.personality.trim()) {
|
|
590
|
-
return vars.personality;
|
|
591
|
-
}
|
|
592
|
-
return undefined;
|
|
593
|
-
}
|
|
594
|
-
function isCompatibleInstructionsText(value) {
|
|
595
|
-
if (UNRESOLVED_TEMPLATE_MARKER_REGEX.test(value))
|
|
596
|
-
return false;
|
|
597
|
-
return !STALE_BRIDGE_MARKERS.some((pattern) => pattern.test(value));
|
|
598
|
-
}
|
|
599
|
-
function normalizeSafeInstructions(value) {
|
|
600
|
-
if (!value)
|
|
601
|
-
return undefined;
|
|
602
|
-
const trimmed = value.trim();
|
|
603
|
-
if (!trimmed)
|
|
604
|
-
return undefined;
|
|
605
|
-
return isCompatibleInstructionsText(trimmed) ? trimmed : undefined;
|
|
606
|
-
}
|
|
607
|
-
export function resolveInstructionsForModel(model, personality) {
|
|
608
|
-
const template = model.model_messages?.instructions_template?.trim();
|
|
609
|
-
const base = model.base_instructions?.trim();
|
|
610
|
-
const safeBase = normalizeSafeInstructions(base);
|
|
611
|
-
if (!template)
|
|
612
|
-
return safeBase;
|
|
613
|
-
if (!template.includes("{{") && !template.includes("}}")) {
|
|
614
|
-
return normalizeSafeInstructions(template) ?? safeBase;
|
|
615
|
-
}
|
|
616
|
-
const personalityText = resolvePersonalityText(model, personality) ?? "";
|
|
617
|
-
const rendered = template
|
|
618
|
-
.replace(/\{\{\s*personality\s*\}\}/gi, personalityText)
|
|
619
|
-
.replace(/\n{3,}/g, "\n\n")
|
|
620
|
-
.trim();
|
|
621
|
-
return normalizeSafeInstructions(rendered) ?? safeBase;
|
|
622
|
-
}
|
|
623
|
-
function resolveAllowedSlugs(catalogModels, fallback) {
|
|
624
|
-
const preferred = (catalogModels ?? []).map((model) => model.slug).filter((slug) => slug.length > 0);
|
|
625
|
-
if (preferred.length > 0) {
|
|
626
|
-
return Array.from(new Set(preferred)).sort(compareModelSlugs);
|
|
627
|
-
}
|
|
628
|
-
return Array.from(new Set(fallback.map((slug) => slug.trim().toLowerCase()).filter(Boolean))).sort(compareModelSlugs);
|
|
629
|
-
}
|
|
630
|
-
function resolveTemplateSource(providerModels) {
|
|
631
|
-
for (const candidate of ["gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex", "gpt-5.2"]) {
|
|
632
|
-
const found = providerModels[candidate];
|
|
633
|
-
if (found)
|
|
634
|
-
return found;
|
|
635
|
-
}
|
|
636
|
-
const first = Object.values(providerModels)[0];
|
|
637
|
-
return first;
|
|
638
|
-
}
|
|
639
|
-
function stripEffortSuffix(slug) {
|
|
640
|
-
return slug.replace(EFFORT_SUFFIX_REGEX, "");
|
|
641
|
-
}
|
|
642
|
-
function ensureModelOptions(model) {
|
|
643
|
-
const existing = model.options;
|
|
644
|
-
if (isRecord(existing))
|
|
645
|
-
return existing;
|
|
646
|
-
const options = {};
|
|
647
|
-
model.options = options;
|
|
648
|
-
return options;
|
|
649
|
-
}
|
|
650
|
-
function findModelBySlug(catalogModels, slug) {
|
|
651
|
-
if (!catalogModels || catalogModels.length === 0)
|
|
652
|
-
return undefined;
|
|
653
|
-
const normalized = slug.trim().toLowerCase();
|
|
654
|
-
const exact = catalogModels.find((item) => item.slug === normalized);
|
|
655
|
-
if (exact)
|
|
656
|
-
return exact;
|
|
657
|
-
const base = stripEffortSuffix(normalized);
|
|
658
|
-
return catalogModels.find((item) => item.slug === base);
|
|
659
|
-
}
|
|
660
|
-
export function getRuntimeDefaultsForModel(model) {
|
|
661
|
-
if (!model)
|
|
662
|
-
return undefined;
|
|
663
|
-
const out = {};
|
|
664
|
-
if (typeof model.apply_patch_tool_type === "string") {
|
|
665
|
-
const next = model.apply_patch_tool_type.trim();
|
|
666
|
-
if (next)
|
|
667
|
-
out.applyPatchToolType = next;
|
|
668
|
-
}
|
|
669
|
-
const defaultReasoningEffort = normalizeReasoningEffort(model.default_reasoning_level);
|
|
670
|
-
if (defaultReasoningEffort) {
|
|
671
|
-
out.defaultReasoningEffort = defaultReasoningEffort;
|
|
672
|
-
}
|
|
673
|
-
const supportedReasoningEfforts = Array.from(new Set((model.supported_reasoning_levels ?? [])
|
|
674
|
-
.map((level) => normalizeReasoningEffort(level.effort))
|
|
675
|
-
.filter((value) => value !== undefined)));
|
|
676
|
-
if (supportedReasoningEfforts.length > 0) {
|
|
677
|
-
out.supportedReasoningEfforts = supportedReasoningEfforts;
|
|
678
|
-
}
|
|
679
|
-
if (typeof model.supports_reasoning_summaries === "boolean") {
|
|
680
|
-
out.supportsReasoningSummaries = model.supports_reasoning_summaries;
|
|
681
|
-
}
|
|
682
|
-
if (typeof model.reasoning_summary_format === "string") {
|
|
683
|
-
const next = model.reasoning_summary_format.trim();
|
|
684
|
-
if (next)
|
|
685
|
-
out.reasoningSummaryFormat = next;
|
|
686
|
-
}
|
|
687
|
-
if (typeof model.support_verbosity === "boolean") {
|
|
688
|
-
out.supportsVerbosity = model.support_verbosity;
|
|
689
|
-
}
|
|
690
|
-
const defaultVerbosity = normalizeVerbosity(model.default_verbosity);
|
|
691
|
-
if (defaultVerbosity) {
|
|
692
|
-
out.defaultVerbosity = defaultVerbosity;
|
|
693
|
-
}
|
|
694
|
-
return Object.keys(out).length > 0 ? out : undefined;
|
|
695
|
-
}
|
|
696
|
-
export function getRuntimeDefaultsForSlug(slug, catalogModels) {
|
|
697
|
-
const model = findModelBySlug(catalogModels, slug);
|
|
698
|
-
return getRuntimeDefaultsForModel(model);
|
|
699
|
-
}
|
|
700
|
-
export function applyCodexCatalogToProviderModels(input) {
|
|
701
|
-
const allowedSlugs = resolveAllowedSlugs(input.catalogModels, input.fallbackModels);
|
|
702
|
-
const allowed = new Set(allowedSlugs);
|
|
703
|
-
const bySlug = new Map((input.catalogModels ?? []).map((model) => [model.slug, model]));
|
|
704
|
-
const templateSource = resolveTemplateSource(input.providerModels);
|
|
705
|
-
for (const slug of allowedSlugs) {
|
|
706
|
-
if (!input.providerModels[slug]) {
|
|
707
|
-
if (templateSource) {
|
|
708
|
-
input.providerModels[slug] = cloneModelTemplate(templateSource, slug);
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
input.providerModels[slug] = { id: slug, model: slug };
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
else {
|
|
715
|
-
setModelIdentityFields(input.providerModels[slug], slug);
|
|
716
|
-
}
|
|
717
|
-
const catalogModel = bySlug.get(slug);
|
|
718
|
-
const options = ensureModelOptions(input.providerModels[slug]);
|
|
719
|
-
if (catalogModel) {
|
|
720
|
-
const instructions = resolveInstructionsForModel(catalogModel, input.personality);
|
|
721
|
-
options.codexCatalogModel = catalogModel;
|
|
722
|
-
if (instructions) {
|
|
723
|
-
input.providerModels[slug].instructions = instructions;
|
|
724
|
-
options.codexInstructions = instructions;
|
|
725
|
-
}
|
|
726
|
-
else {
|
|
727
|
-
delete options.codexInstructions;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
delete options.codexCatalogModel;
|
|
732
|
-
}
|
|
733
|
-
const runtimeDefaults = getRuntimeDefaultsForSlug(slug, input.catalogModels);
|
|
734
|
-
if (runtimeDefaults) {
|
|
735
|
-
input.providerModels[slug].codexRuntimeDefaults = runtimeDefaults;
|
|
736
|
-
options.codexRuntimeDefaults = runtimeDefaults;
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
delete input.providerModels[slug].codexRuntimeDefaults;
|
|
740
|
-
delete options.codexRuntimeDefaults;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
for (const modelId of Object.keys(input.providerModels)) {
|
|
744
|
-
if (!allowed.has(modelId)) {
|
|
745
|
-
delete input.providerModels[modelId];
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
const orderedModelIds = Object.keys(input.providerModels).sort((a, b) => b.localeCompare(a));
|
|
749
|
-
if (orderedModelIds.length > 1) {
|
|
750
|
-
const orderedEntries = orderedModelIds.map((modelId) => [modelId, input.providerModels[modelId]]);
|
|
751
|
-
for (const modelId of Object.keys(input.providerModels)) {
|
|
752
|
-
delete input.providerModels[modelId];
|
|
753
|
-
}
|
|
754
|
-
for (const [modelId, model] of orderedEntries) {
|
|
755
|
-
if (model) {
|
|
756
|
-
input.providerModels[modelId] = model;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
for (const model of Object.values(input.providerModels)) {
|
|
761
|
-
model.cost = {
|
|
762
|
-
input: 0,
|
|
763
|
-
output: 0,
|
|
764
|
-
cache: { read: 0, write: 0 }
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
}
|
|
1
|
+
export { CACHE_TTL_MS, CODEX_GITHUB_MODELS_URL_PREFIX, CODEX_MODELS_ENDPOINT, compareModelSlugs, compareSemver, DEFAULT_CLIENT_VERSION, EFFORT_SUFFIX_REGEX, FETCH_TIMEOUT_MS, githubModelsTag, githubModelsUrl, isRecord, normalizeModelSlug, normalizeReasoningEffort, normalizeSemver, normalizeVerbosity, parseCatalogResponse, parseFetchedAtFromUnknown, parseReasoningLevels, parseSemver, semverFromTag } from "./model-catalog/shared.js";
|
|
2
|
+
export { getCodexModelCatalog } from "./model-catalog/catalog-fetch.js";
|
|
3
|
+
export { applyCodexCatalogToProviderModels, getRuntimeDefaultsForModel, getRuntimeDefaultsForSlug, resolveInstructionsForModel } from "./model-catalog/provider.js";
|
|
768
4
|
//# sourceMappingURL=model-catalog.js.map
|