Files
OmniRoute/src/lib/cli-helper/codexProfileAutoSync.ts
backryun 67a0b99240 feat(providers): add GPT-5.6 model family (#6862)
* feat(providers): add GPT-5.6 model family

* fix(chatgpt-web): resume temporary chat handoffs

* fix(codex): auto-merge discovery, filter denylist, revalidate on lifecycle

Restore live/GitHub auto-merge for Codex catalogs, drop models via
explicit denylist (GPT-5.4 family), and run scrub+live re-sync once on
first-start, app upgrade, or setup completion. Success log:
kill deprecated models complete.

* fix(codex): preserve live catalog reconciliation

Expose remote-only Codex models without dropping user custom entries, and complete lifecycle revalidation only after a successful internal sync. Keep credentialed self-fetches pinned to the active dashboard listener.

---------

Co-authored-by: backryun <backryun@daonlab.local>
2026-07-12 03:30:47 -03:00

85 lines
2.8 KiB
TypeScript

import path from "node:path";
import { ensureCliConfigWriteAllowed, getCliConfigPaths } from "../../shared/services/cliRuntime";
import {
fetchModelSyncInternal,
getModelSyncInternalBaseUrl,
} from "../../shared/services/modelSyncScheduler";
import { isFeatureFlagEnabled } from "../../shared/utils/featureFlags";
type SyncResult =
| {
ok: true;
written: number;
skipped: number;
reason: string;
}
| {
ok: false;
skipped: true;
reason: string;
};
function isAutoSyncEnabled() {
// Opt-in, default OFF. Backed by the OMNIROUTE_AUTO_SYNC_CODEX_PROFILES feature flag
// (resolver precedence: DB/dashboard-toggle override > env > default "false"), so a
// provider model sync never silently writes ~/.codex/*.config.toml unless the operator
// turned it on — via the providers-dashboard toggle or the env var.
return isFeatureFlagEnabled("OMNIROUTE_AUTO_SYNC_CODEX_PROFILES");
}
function forwardAuthHeaders(request: Request): Record<string, string> {
const headers: Record<string, string> = {};
const cookie = request.headers.get("cookie");
const authorization = request.headers.get("authorization");
if (cookie) headers.cookie = cookie;
if (authorization) headers.authorization = authorization;
return headers;
}
export async function autoSyncCodexProfilesFromLiveCatalog(
request: Request,
reason: string
): Promise<SyncResult> {
if (!isAutoSyncEnabled()) {
return { ok: false, skipped: true, reason: "disabled" };
}
const writeGuard = ensureCliConfigWriteAllowed();
if (writeGuard) {
return { ok: false, skipped: true, reason: writeGuard };
}
const baseUrl = getModelSyncInternalBaseUrl().replace(/\/$/, "");
const res = await fetchModelSyncInternal(`${baseUrl}/v1/models`, {
headers: forwardAuthHeaders(request),
redirect: "error",
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) {
return { ok: false, skipped: true, reason: `catalog_http_${res.status}` };
}
const body = await res.json();
const candidateModels = Array.isArray(body) ? body : body.data || body.models || [];
const models = Array.isArray(candidateModels) ? candidateModels : [];
const codexPaths = getCliConfigPaths("codex");
if (!codexPaths?.config) {
return { ok: false, skipped: true, reason: "codex_config_path_unavailable" };
}
const codexHome = path.dirname(codexPaths.config);
// Reuse the CLI generator so automatic sync and `omniroute setup-codex`
// stay behaviorally identical.
// @ts-ignore - bin CLI modules are shipped as ESM JavaScript, without TS declarations.
const { syncCodexProfilesFromModels } = await import("../../../bin/cli/commands/setup-codex.mjs");
const result = await syncCodexProfilesFromModels(models, { codexHome });
return {
ok: true,
written: result.written,
skipped: result.skipped,
reason,
};
}