sneakoscope 8.1.2 → 8.2.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 +96 -58
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +5 -51
- package/dist/cli/command-manifest-lite.js +2 -1
- package/dist/cli/command-registry.js +2 -1
- package/dist/cli/global-mode-router.js +1 -1
- package/dist/cli/install-helpers-codex-lb-config.js +112 -914
- package/dist/cli/install-helpers-codex-lb-shared.js +3 -44
- package/dist/cli/install-helpers-install-support.js +4 -20
- package/dist/cli/install-helpers.js +36 -2716
- package/dist/cli/router.js +9 -9
- package/dist/commands/bridge-command-output.js +111 -0
- package/dist/commands/bridge.js +320 -0
- package/dist/commands/codex-app.js +39 -123
- package/dist/commands/doctor-helpers.js +380 -0
- package/dist/commands/doctor-profile.js +3 -0
- package/dist/commands/doctor.js +114 -873
- package/dist/commands/mad-sks.js +0 -4
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/agents/agent-effort-policy.js +11 -21
- package/dist/core/agents/agent-recursion-guard.js +26 -0
- package/dist/core/agents/agent-runner-codex-exec.js +7 -9
- package/dist/core/architecture-hardening/contracts/contracts.js +225 -161
- package/dist/core/architecture-hardening/state/state-service.js +49 -13
- package/dist/core/auto-review.js +15 -9
- package/dist/core/codex/codex-config-guard.js +8 -0
- package/dist/core/codex-adapter.js +8 -10
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -29
- package/dist/core/codex-app/codex-app-launcher.js +10 -12
- package/dist/core/codex-app/codex-app-restart.js +10 -12
- package/dist/core/codex-app/menubar/constants.js +2 -5
- package/dist/core/codex-app/menubar/global-install.js +3 -2
- package/dist/core/codex-app/menubar/installer/result.js +23 -0
- package/dist/core/codex-app/menubar/installer/runtime.js +28 -0
- package/dist/core/codex-app/menubar/installer.js +5 -96
- package/dist/core/codex-app/menubar/migration.js +1 -42
- package/dist/core/codex-app/openrouter-provider.js +0 -78
- package/dist/core/codex-app.js +97 -360
- package/dist/core/codex-control/codex-app-server-runtime-env.js +8 -28
- package/dist/core/codex-control/codex-current-image-path-real-probe.js +2 -3
- package/dist/core/codex-control/codex-current-web-search-probe.js +2 -3
- package/dist/core/codex-control/codex-sdk-adapter.js +5 -48
- package/dist/core/codex-control/codex-task-runner.js +16 -60
- package/dist/core/codex-control/desktop-bridge-launch-guard.js +227 -0
- package/dist/core/codex-exec-output-schema.js +11 -13
- package/dist/core/codex-lb/bridge-contracts.js +2 -0
- package/dist/core/codex-lb/bridge-runtime-validation/capability.js +104 -0
- package/dist/core/codex-lb/bridge-runtime-validation/catalog.js +52 -0
- package/dist/core/codex-lb/bridge-runtime-validation/shared.js +109 -0
- package/dist/core/codex-lb/bridge-runtime-validation/status.js +250 -0
- package/dist/core/codex-lb/bridge-runtime-validation.js +106 -0
- package/dist/core/codex-lb/capability-runner.js +505 -201
- package/dist/core/codex-lb/codex-lb-env.js +179 -12
- package/dist/core/codex-lb/combined-catalog/builder.js +86 -0
- package/dist/core/codex-lb/combined-catalog/contracts.js +5 -0
- package/dist/core/codex-lb/combined-catalog/normalize.js +170 -0
- package/dist/core/codex-lb/combined-catalog/persistence.js +207 -0
- package/dist/core/codex-lb/combined-catalog/shared.js +12 -0
- package/dist/core/codex-lb/combined-catalog/storage.js +171 -0
- package/dist/core/codex-lb/combined-catalog.js +12 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +48 -63
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +115 -83
- package/dist/core/codex-lb/desktop-bridge/launchd.js +20 -13
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/security.js +381 -267
- package/dist/core/codex-lb/desktop-bridge/server.js +148 -19
- package/dist/core/codex-lb/desktop-bridge/state.js +56 -70
- package/dist/core/codex-lb/desktop-bridge/types.js +6 -1
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +281 -106
- package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +245 -0
- package/dist/core/codex-lb/desktop-bridge-migration/historical-toml.js +56 -0
- package/dist/core/codex-lb/desktop-bridge-migration/metadata.js +73 -0
- package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +52 -0
- package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +106 -0
- package/dist/core/codex-lb/desktop-bridge-migration/types.js +1 -0
- package/dist/core/codex-lb/desktop-bridge-migration.js +238 -0
- package/dist/core/codex-lb/desktop-controller-v3/catalog.js +257 -0
- package/dist/core/codex-lb/desktop-controller-v3/diagnostics.js +62 -0
- package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +203 -0
- package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +293 -0
- package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +144 -0
- package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +182 -0
- package/dist/core/codex-lb/desktop-controller-v3/shared.js +279 -0
- package/dist/core/codex-lb/desktop-controller-v3/status.js +269 -0
- package/dist/core/codex-lb/desktop-controller-v3/types.js +1 -0
- package/dist/core/codex-lb/desktop-controller-v3/verification.js +89 -0
- package/dist/core/codex-lb/desktop-controller-v3.js +67 -0
- package/dist/core/codex-lb/desktop-controller.js +2 -1730
- package/dist/core/codex-lb/desktop-service.js +462 -627
- package/dist/core/codex-lb/migration-receipt.js +560 -118
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +37 -53
- package/dist/core/codex-lb/probes/bridge-probe.js +125 -64
- package/dist/core/codex-lb/probes/catalog-probe.js +36 -136
- package/dist/core/codex-lb/probes/computer-use-probe.js +63 -39
- package/dist/core/codex-lb/probes/image-generation-probe.js +98 -41
- package/dist/core/codex-lb/probes/probe-evidence.js +32 -33
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +69 -43
- package/dist/core/codex-lb/provider-credentials/configuration.js +127 -0
- package/dist/core/codex-lb/provider-credentials/locks.js +11 -0
- package/dist/core/codex-lb/provider-credentials/private-snapshot.js +28 -0
- package/dist/core/codex-lb/provider-credentials/removal.js +33 -0
- package/dist/core/codex-lb/provider-credentials/resolution.js +59 -0
- package/dist/core/codex-lb/provider-credentials/runtime.js +55 -0
- package/dist/core/codex-lb/provider-credentials/types.js +1 -0
- package/dist/core/codex-lb/provider-credentials/validation-store.js +137 -0
- package/dist/core/codex-lb/provider-credentials.js +5 -0
- package/dist/core/codex-lb/provider-registry.js +323 -0
- package/dist/core/codex-lb/provider-route-policy.js +118 -0
- package/dist/core/codex-lb/request-route-resolver.js +172 -0
- package/dist/core/codex-lb/route-index.js +148 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +158 -286
- package/dist/core/commands/basic-cli.js +1 -1
- package/dist/core/commands/check-command.js +3 -10
- package/dist/core/commands/gate-result-contract.js +10 -14
- package/dist/core/commands/intent-normalization/normalizer.js +1 -1
- package/dist/core/commands/mad-sks-command.js +2 -6
- package/dist/core/commands/release-command.js +11 -1
- package/dist/core/commands/telegram-command.js +46 -6
- package/dist/core/doctor/doctor-readiness-matrix.js +9 -7
- package/dist/core/doctor/skill-legacy-surface.js +5 -5
- package/dist/core/evidence/v2/evidence-key.js +30 -3
- package/dist/core/feature-fixtures.js +8 -1
- package/dist/core/feature-registry.js +3 -2
- package/dist/core/hooks-runtime/stop-finalization.js +11 -1
- package/dist/core/hooks-runtime/subagent-context.js +15 -1
- package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +73 -87
- package/dist/core/image-ux-review/imagegen-adapter.js +108 -94
- package/dist/core/image-ux-review/real-callout-extractor.js +144 -14
- package/dist/core/imagegen/desktop-bridge-imagegen-target.js +77 -0
- package/dist/core/imagegen/imagegen-capability.js +43 -151
- package/dist/core/init/skills/filesystem.js +89 -0
- package/dist/core/init/skills/inventory.js +97 -0
- package/dist/core/init/skills/manifest.js +151 -0
- package/dist/core/init/skills.js +5 -325
- package/dist/core/init.js +0 -63
- package/dist/core/install/installed-package-smoke.js +1 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +0 -1
- package/dist/core/policy/latest-version-guidance.js +15 -2
- package/dist/core/preflight/parallel-preflight-engine.js +27 -51
- package/dist/core/provider/provider-badge.js +9 -4
- package/dist/core/provider/provider-context.js +57 -107
- package/dist/core/providers/openrouter/openrouter-account.js +3 -1
- package/dist/core/providers/openrouter/openrouter-secret-store.js +91 -22
- package/dist/core/release/desktop-bridge-release-evidence.js +457 -0
- package/dist/core/release/gate-manifest.js +0 -1
- package/dist/core/release/main-push-guard.js +13 -0
- package/dist/core/release/main-push-receipt.js +11 -0
- package/dist/core/release/physical-release-gates.js +664 -61
- package/dist/core/release/publish-preflight.js +3 -3
- package/dist/core/release/release-gate-batch-runner.js +19 -6
- package/dist/core/release/release-gate-dag.js +33 -6
- package/dist/core/release/release-gate-node.js +1 -3
- package/dist/core/release/release-gate-output-contract.js +55 -0
- package/dist/core/release/release-pack-contract.js +1 -1
- package/dist/core/release/release-pack-receipt.js +209 -8
- package/dist/core/release/release-real-contract.js +1 -1
- package/dist/core/release/stage-publish.js +106 -28
- package/dist/core/retention/temp-lease.js +204 -0
- package/dist/core/retention.js +18 -195
- package/dist/core/routes.js +1 -2
- package/dist/core/safety/intent-contract/intent-contract.js +3 -3
- package/dist/core/subagents/naruto-host-credentials.js +6 -1
- package/dist/core/subagents/official-subagent-config.js +27 -9
- package/dist/core/subagents/official-subagent-runner.js +14 -31
- package/dist/core/subagents/wave-lifecycle.js +36 -8
- package/dist/core/subagents/wave-parent-guidance.js +2 -1
- package/dist/core/telegram/doctor.js +2 -2
- package/dist/core/telegram/liveness.js +10 -4
- package/dist/core/triwiki/context-graph/compiler/index.js +18 -18
- package/dist/core/update/update-migration-state/fast-mode-config.js +172 -0
- package/dist/core/update/update-migration-state/session-state-split.js +247 -0
- package/dist/core/update/update-migration-state/simple-stages.js +46 -0
- package/dist/core/update/update-migration-state.js +4 -481
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +28 -4
- package/dist/native/sks-menubar/Sources/OperationModels.swift +112 -2
- package/dist/native/sks-menubar/Sources/ProviderRouteExplanation.swift +21 -0
- package/dist/native/sks-menubar/Sources/ProvidersBridgeCatalog.swift +140 -0
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +107 -470
- package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +27 -197
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +335 -269
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +233 -437
- package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +16 -7
- package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +39 -9
- package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
- package/dist/scripts/canonical-test-runner.js +7 -1
- package/dist/scripts/check-runtime-schemas.js +7 -2
- package/dist/scripts/codex-sdk-real-smoke-check.js +3 -1
- package/dist/scripts/desktop-bridge-capabilities-check.js +123 -0
- package/dist/scripts/{codex-lb-desktop-bridge-latency-check.js → desktop-bridge-latency-check.js} +61 -16
- package/dist/scripts/desktop-bridge-real-evidence-check.js +64 -0
- package/dist/scripts/{codex-lb-desktop-bridge-check.js → desktop-bridge-transport-check.js} +92 -155
- package/dist/scripts/desktop-bridge-transport-client.js +116 -0
- package/dist/scripts/desktop-bridge-unification-check.js +276 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -8
- package/dist/scripts/gate-policy-audit-check.js +52 -2
- package/dist/scripts/provider-badge-context-check.js +55 -12
- package/dist/scripts/publish-preflight.js +1 -2
- package/dist/scripts/release-affected-selector-check.js +1 -2
- package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
- package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
- package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -0
- package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
- package/dist/scripts/release-gate-batch-runner-check.js +9 -0
- package/dist/scripts/release-gate-dag-runner-check.js +6 -0
- package/dist/scripts/release-gate-dag-runner.js +4 -2
- package/dist/scripts/release-metadata-check.js +16 -3
- package/dist/scripts/release-pack-receipt.js +29 -6
- package/dist/scripts/release-physical-gates-check.js +39 -0
- package/dist/scripts/release-real-check.js +7 -5
- package/package.json +6 -8
- package/release-gates.v2.json +156 -157
- package/runtime-required-scripts.json +11 -11
- package/schemas/codex/desktop-bridge-status-v3.schema.json +286 -0
- package/schemas/codex/desktop-capabilities-v3.schema.json +198 -0
- package/schemas/release/release-gate-node.schema.json +2 -3
- package/dist/cli/codex-lb-setup-warning-output.js +0 -17
- package/dist/cli/install-helpers-codex-lb-chain.js +0 -529
- package/dist/commands/codex-lb.js +0 -1232
- package/dist/core/architecture-hardening/migration/migration.js +0 -111
- package/dist/core/codex-app/auxiliary-oauth-policy.js +0 -36
- package/dist/core/codex-app/catalog-compat/catalog-service.js +0 -104
- package/dist/core/codex-app/child-policy/child-policy.js +0 -35
- package/dist/core/codex-app/desktop-routing-snapshot.js +0 -233
- package/dist/core/codex-app/glm-model-profile.js +0 -1
- package/dist/core/codex-app/glm-profile-installer.js +0 -191
- package/dist/core/codex-app/glm-profile-schema.js +0 -42
- package/dist/core/codex-app/multi-provider-router.js +0 -391
- package/dist/core/codex-app/openrouter-activate.js +0 -405
- package/dist/core/codex-app/openrouter-model-catalog.js +0 -217
- package/dist/core/codex-app/provider-child-policy.js +0 -64
- package/dist/core/codex-app/provider-mode.js +0 -91
- package/dist/core/codex-app/provider-session-policy.js +0 -85
- package/dist/core/codex-app/session-policy/session-pinning.js +0 -68
- package/dist/core/codex-control/codex-lb-launch-recovery.js +0 -319
- package/dist/core/codex-lb/codex-lb-setup.js +0 -293
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +0 -456
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +0 -290
- package/dist/core/codex-lb/desktop-center-credentials.js +0 -1083
- package/dist/core/codex-lb/desktop-mode.js +0 -32
- package/dist/core/codex-lb/desktop-real-evidence.js +0 -168
- package/dist/core/codex-lb/legacy-migration.js +0 -576
- package/dist/core/codex-lb/native-openai-transport/transport.js +0 -68
- package/dist/core/codex-lb/provider-routing/provider-router.js +0 -58
- package/dist/core/codex-lb/routing-truth.js +0 -327
- package/dist/core/imagegen/codex-lb-imagegen-target.js +0 -92
- package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +0 -118
- package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +0 -17
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +0 -331
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +0 -260
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +0 -120
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +0 -169
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +0 -287
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +0 -123
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +0 -114
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +0 -152
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +0 -231
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { codexLbConfigPath, ensureGlobalCodexAppGlmProfile } from '../../cli/install-helpers.js';
|
|
3
|
-
import { readText, writeJsonAtomic, nowIso, rmrf } from '../fsx.js';
|
|
4
|
-
import { OPENROUTER_DEFAULT_PROFILE_ID, OPENROUTER_AUTH_COMMAND, OPENROUTER_AUTH_REFRESH_INTERVAL_MS, OPENROUTER_AUTH_TIMEOUT_MS, openRouterAuthCommandArgs, OPENROUTER_PROVIDER_ID, RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS, buildGlmCodexAppModelProfile } from './openrouter-provider.js';
|
|
5
|
-
import { openRouterSecretPaths, resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
|
|
6
|
-
import { escapeRegExp } from '../text/regex.js';
|
|
7
|
-
export async function installCodexAppGlmProfile(input) {
|
|
8
|
-
const root = path.resolve(input.root);
|
|
9
|
-
const profile = buildGlmCodexAppModelProfile();
|
|
10
|
-
const profilePath = path.join(root, '.sneakoscope', 'codex-app', 'glm-model-profile.json');
|
|
11
|
-
const reportPath = path.join(root, '.sneakoscope', 'reports', 'codex-app-glm-profile.json');
|
|
12
|
-
const key = await resolveOpenRouterApiKey({ env: input.env || process.env });
|
|
13
|
-
const home = input.home || input.env?.HOME;
|
|
14
|
-
const configWrite = input.apply === false
|
|
15
|
-
? null
|
|
16
|
-
: await ensureGlobalCodexAppGlmProfile({ home, configPath: input.configPath, env: input.env });
|
|
17
|
-
if (input.apply !== false) {
|
|
18
|
-
await rmrf(profilePath).catch(() => undefined);
|
|
19
|
-
await rmrf(reportPath).catch(() => undefined);
|
|
20
|
-
}
|
|
21
|
-
const configStatus = input.apply === false
|
|
22
|
-
? await previewCodexAppGlmConfigStatus({ home, configPath: input.configPath, ...(input.env ? { env: input.env } : {}) })
|
|
23
|
-
: await inspectCodexAppOpenRouterConfig({ home, configPath: input.configPath, ...(input.env ? { env: input.env } : {}) });
|
|
24
|
-
const configWriteBlockers = configWrite?.ok === false
|
|
25
|
-
? [`glm_codex_app_config_${configWrite.status || 'failed'}`]
|
|
26
|
-
: [];
|
|
27
|
-
const blockers = input.apply === false ? [] : [...configWriteBlockers, ...configStatus.blockers];
|
|
28
|
-
const warnings = [
|
|
29
|
-
...key.warnings,
|
|
30
|
-
...(key.key ? [] : ['openrouter_key_missing_until_sks_codex_app_set_openrouter_key']),
|
|
31
|
-
...(input.apply === false ? ['codex_desktop_openrouter_config_not_written_apply_false'] : []),
|
|
32
|
-
'glm_desktop_profiles_retired_use_openrouter'
|
|
33
|
-
];
|
|
34
|
-
const result = {
|
|
35
|
-
schema: 'sks.codex-app-glm-profile-result.v1',
|
|
36
|
-
generated_at: nowIso(),
|
|
37
|
-
ok: blockers.length === 0,
|
|
38
|
-
status: blockers.length === 0
|
|
39
|
-
? input.apply === false
|
|
40
|
-
? 'valid'
|
|
41
|
-
: 'removed'
|
|
42
|
-
: 'blocked',
|
|
43
|
-
profile,
|
|
44
|
-
profile_path: '.sneakoscope/codex-app/glm-model-profile.json',
|
|
45
|
-
report_path: '.sneakoscope/reports/codex-app-glm-profile.json',
|
|
46
|
-
config_path: configStatus.config_path,
|
|
47
|
-
codex_config_profile: OPENROUTER_DEFAULT_PROFILE_ID,
|
|
48
|
-
codex_reasoning_profiles: [],
|
|
49
|
-
config_write: configWrite,
|
|
50
|
-
config_status: configStatus,
|
|
51
|
-
openrouter_key_source: key.source,
|
|
52
|
-
blockers,
|
|
53
|
-
warnings
|
|
54
|
-
};
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
export async function doctorCodexAppGlmProfile(input) {
|
|
58
|
-
const root = path.resolve(input.root);
|
|
59
|
-
const profilePath = path.join(root, '.sneakoscope', 'codex-app', 'glm-model-profile.json');
|
|
60
|
-
const reportPath = path.join(root, '.sneakoscope', 'reports', 'codex-app-glm-profile.json');
|
|
61
|
-
const key = await resolveOpenRouterApiKey({ env: input.env || process.env });
|
|
62
|
-
const profile = buildGlmCodexAppModelProfile();
|
|
63
|
-
const home = input.home || input.env?.HOME;
|
|
64
|
-
const configStatus = await inspectCodexAppOpenRouterConfig({ home, configPath: input.configPath, ...(input.env ? { env: input.env } : {}) });
|
|
65
|
-
const leftoverMeta = await readText(profilePath, '').catch(() => '');
|
|
66
|
-
const blockers = [
|
|
67
|
-
...configStatus.blockers,
|
|
68
|
-
...(leftoverMeta.trim() ? ['retired_glm_model_profile_metadata_present'] : [])
|
|
69
|
-
];
|
|
70
|
-
const warnings = [
|
|
71
|
-
...key.warnings,
|
|
72
|
-
...(key.key ? [] : ['openrouter_key_missing_until_sks_codex_app_set_openrouter_key']),
|
|
73
|
-
'glm_desktop_profiles_retired_use_openrouter'
|
|
74
|
-
];
|
|
75
|
-
const result = {
|
|
76
|
-
schema: 'sks.codex-app-glm-profile-result.v1',
|
|
77
|
-
generated_at: nowIso(),
|
|
78
|
-
ok: blockers.length === 0,
|
|
79
|
-
status: blockers.length === 0 ? 'valid' : 'blocked',
|
|
80
|
-
profile,
|
|
81
|
-
profile_path: '.sneakoscope/codex-app/glm-model-profile.json',
|
|
82
|
-
report_path: '.sneakoscope/reports/codex-app-glm-profile.json',
|
|
83
|
-
config_path: configStatus.config_path,
|
|
84
|
-
codex_config_profile: OPENROUTER_DEFAULT_PROFILE_ID,
|
|
85
|
-
codex_reasoning_profiles: [],
|
|
86
|
-
config_write: null,
|
|
87
|
-
config_status: configStatus,
|
|
88
|
-
openrouter_key_source: key.source,
|
|
89
|
-
blockers,
|
|
90
|
-
warnings
|
|
91
|
-
};
|
|
92
|
-
await writeJsonAtomic(reportPath, result).catch(() => undefined);
|
|
93
|
-
return result;
|
|
94
|
-
}
|
|
95
|
-
async function previewCodexAppGlmConfigStatus(input) {
|
|
96
|
-
const configPath = input.configPath || codexLbConfigPath(input.home);
|
|
97
|
-
return {
|
|
98
|
-
schema: 'sks.codex-app-glm-config-status.v1',
|
|
99
|
-
ok: true,
|
|
100
|
-
config_path: configPath,
|
|
101
|
-
provider_present: false,
|
|
102
|
-
provider_auth_present: false,
|
|
103
|
-
profiles_present: [],
|
|
104
|
-
profiles_missing: [],
|
|
105
|
-
retired_profiles_remaining: [],
|
|
106
|
-
blockers: []
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
async function inspectCodexAppOpenRouterConfig(input) {
|
|
110
|
-
const configPath = input.configPath || codexLbConfigPath(input.home);
|
|
111
|
-
const text = await readText(configPath, '').catch(() => '');
|
|
112
|
-
const providerBody = tomlTableBody(text, `model_providers.${OPENROUTER_PROVIDER_ID}`);
|
|
113
|
-
const providerPresent = Boolean(providerBody);
|
|
114
|
-
const authBody = tomlTableBody(text, `model_providers.${OPENROUTER_PROVIDER_ID}.auth`);
|
|
115
|
-
const providerAuthPresent = Boolean(authBody);
|
|
116
|
-
const providerEnvKeyPresent = hasTomlKey(providerBody, 'env_key');
|
|
117
|
-
const authEnv = { ...(input.env || process.env), ...(input.home ? { HOME: input.home } : {}) };
|
|
118
|
-
const authArgs = openRouterAuthCommandArgs(openRouterSecretPaths(authEnv).keyPath);
|
|
119
|
-
const blockers = [];
|
|
120
|
-
if (!providerPresent) {
|
|
121
|
-
blockers.push('glm_codex_app_config_missing_openrouter_provider');
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
if (!hasTomlString(providerBody, 'base_url', 'https://openrouter.ai/api/v1'))
|
|
125
|
-
blockers.push('glm_codex_app_config_invalid_openrouter_base_url');
|
|
126
|
-
if (!hasTomlString(providerBody, 'wire_api', 'responses'))
|
|
127
|
-
blockers.push('glm_codex_app_config_invalid_openrouter_wire_api');
|
|
128
|
-
if (providerEnvKeyPresent)
|
|
129
|
-
blockers.push('glm_codex_app_config_openrouter_auth_env_key_conflict');
|
|
130
|
-
}
|
|
131
|
-
if (!providerAuthPresent) {
|
|
132
|
-
blockers.push('glm_codex_app_config_missing_openrouter_auth');
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
if (!hasTomlString(authBody, 'command', OPENROUTER_AUTH_COMMAND))
|
|
136
|
-
blockers.push('glm_codex_app_config_invalid_openrouter_auth_command');
|
|
137
|
-
if (!hasTomlStringArray(authBody, 'args', authArgs))
|
|
138
|
-
blockers.push('glm_codex_app_config_invalid_openrouter_auth_args');
|
|
139
|
-
if (!hasTomlInteger(authBody, 'timeout_ms', OPENROUTER_AUTH_TIMEOUT_MS))
|
|
140
|
-
blockers.push('glm_codex_app_config_invalid_openrouter_auth_timeout');
|
|
141
|
-
if (!hasTomlInteger(authBody, 'refresh_interval_ms', OPENROUTER_AUTH_REFRESH_INTERVAL_MS))
|
|
142
|
-
blockers.push('glm_codex_app_config_invalid_openrouter_auth_refresh');
|
|
143
|
-
}
|
|
144
|
-
const retiredRemaining = [];
|
|
145
|
-
for (const profileId of RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS) {
|
|
146
|
-
if (tomlTableBody(text, `profiles.${profileId}`)) {
|
|
147
|
-
retiredRemaining.push(profileId);
|
|
148
|
-
blockers.push(`retired_glm_desktop_profile_present:${profileId}`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return {
|
|
152
|
-
schema: 'sks.codex-app-glm-config-status.v1',
|
|
153
|
-
ok: blockers.length === 0,
|
|
154
|
-
config_path: configPath,
|
|
155
|
-
provider_present: providerPresent,
|
|
156
|
-
provider_auth_present: providerAuthPresent,
|
|
157
|
-
profiles_present: [],
|
|
158
|
-
profiles_missing: [],
|
|
159
|
-
retired_profiles_remaining: retiredRemaining,
|
|
160
|
-
blockers
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
function tomlTableBody(text, table) {
|
|
164
|
-
const header = `[${table}]`;
|
|
165
|
-
const lines = String(text || '').split('\n');
|
|
166
|
-
const start = lines.findIndex((line) => line.trim() === header);
|
|
167
|
-
if (start === -1)
|
|
168
|
-
return '';
|
|
169
|
-
const body = [];
|
|
170
|
-
for (let index = start + 1; index < lines.length; index += 1) {
|
|
171
|
-
const line = lines[index];
|
|
172
|
-
if (/^\s*\[[^\]]+\]\s*$/.test(line || ''))
|
|
173
|
-
break;
|
|
174
|
-
body.push(line || '');
|
|
175
|
-
}
|
|
176
|
-
return body.join('\n');
|
|
177
|
-
}
|
|
178
|
-
function hasTomlString(text, key, value) {
|
|
179
|
-
const pattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"${escapeRegExp(value)}"\\s*(?:#.*)?$`, 'm');
|
|
180
|
-
return pattern.test(text);
|
|
181
|
-
}
|
|
182
|
-
function hasTomlKey(text, key) {
|
|
183
|
-
return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`, 'm').test(text);
|
|
184
|
-
}
|
|
185
|
-
function hasTomlStringArray(text, key, values) {
|
|
186
|
-
const expected = values.map((value) => JSON.stringify(value)).join(', ');
|
|
187
|
-
return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*\\[${escapeRegExp(expected)}\\]\\s*(?:#.*)?$`, 'm').test(text);
|
|
188
|
-
}
|
|
189
|
-
function hasTomlInteger(text, key, value) {
|
|
190
|
-
return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*${value}\\s*(?:#.*)?$`, 'm').test(text);
|
|
191
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { OPENROUTER_DEFAULT_PROFILE_ID, OPENROUTER_PROVIDER_ID, GLM_CODEX_SELECTABLE_REASONING_EFFORTS, GLM_CODEX_CONFIG_REASONING_PROFILES, GLM_52_OPENROUTER_MODEL, buildGlmCodexAppModelProfile } from './openrouter-provider.js';
|
|
2
|
-
export function validateGlmCodexAppModelProfile(value) {
|
|
3
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
4
|
-
return { ok: false, blockers: ['glm_codex_app_profile_missing'], profile: null };
|
|
5
|
-
}
|
|
6
|
-
const profile = value;
|
|
7
|
-
const expected = buildGlmCodexAppModelProfile();
|
|
8
|
-
const blockers = [
|
|
9
|
-
profile.schema === expected.schema ? null : 'glm_codex_app_profile_invalid_schema',
|
|
10
|
-
profile.id === OPENROUTER_DEFAULT_PROFILE_ID ? null : 'glm_codex_app_profile_invalid_id',
|
|
11
|
-
profile.provider === 'openrouter' ? null : 'glm_codex_app_profile_invalid_provider',
|
|
12
|
-
profile.model === GLM_52_OPENROUTER_MODEL ? null : 'glm_codex_app_profile_invalid_model',
|
|
13
|
-
profile.codexConfigProvider === OPENROUTER_PROVIDER_ID ? null : 'glm_codex_app_profile_invalid_codex_config_provider',
|
|
14
|
-
profile.codexConfigProfile === OPENROUTER_DEFAULT_PROFILE_ID ? null : 'glm_codex_app_profile_invalid_codex_config_profile',
|
|
15
|
-
hasExpectedReasoningEfforts(profile.supportedReasoningEfforts) ? null : 'glm_codex_app_profile_invalid_reasoning_efforts',
|
|
16
|
-
hasExpectedReasoningProfiles(profile.reasoningProfiles) ? null : 'glm_codex_app_profile_invalid_reasoning_profiles',
|
|
17
|
-
profile.mode === 'openrouter-desktop' ? null : 'glm_codex_app_profile_invalid_mode',
|
|
18
|
-
profile.strictModelLock === false ? null : 'glm_codex_app_profile_unexpected_strict_lock',
|
|
19
|
-
profile.gptFallbackAllowed === false ? null : 'glm_codex_app_profile_allows_gpt_fallback',
|
|
20
|
-
profile.defaultProfile === 'speed' ? null : 'glm_codex_app_profile_default_not_speed',
|
|
21
|
-
profile.defaultSettings?.tool_choice === 'none' ? null : 'glm_codex_app_profile_default_tools_not_omitted',
|
|
22
|
-
profile.defaultSettings?.provider_require_parameters === false ? null : 'glm_codex_app_profile_default_requires_parameters',
|
|
23
|
-
profile.defaultSettings?.provider_allow_fallbacks === false ? null : 'glm_codex_app_profile_allows_provider_fallback'
|
|
24
|
-
].filter((item) => Boolean(item));
|
|
25
|
-
return {
|
|
26
|
-
ok: blockers.length === 0,
|
|
27
|
-
blockers,
|
|
28
|
-
profile: blockers.length === 0 ? profile : null
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function hasExpectedReasoningEfforts(value) {
|
|
32
|
-
if (!Array.isArray(value)) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
return GLM_CODEX_SELECTABLE_REASONING_EFFORTS.every((effort) => value.includes(effort));
|
|
36
|
-
}
|
|
37
|
-
function hasExpectedReasoningProfiles(value) {
|
|
38
|
-
if (!Array.isArray(value)) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
return value.length === 0 && GLM_CODEX_CONFIG_REASONING_PROFILES.length === 0;
|
|
42
|
-
}
|
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import { exists, nowIso, readText } from '../fsx.js';
|
|
3
|
-
import { ensureTrailingNewline, normalizeCodexFastModeUiConfig, safeWriteCodexConfigToml, upsertTopLevelTomlString, upsertTomlTable } from '../codex-runtime/codex-desktop-config-policy.js';
|
|
4
|
-
import { unselectCodexLbProvider } from '../../cli/install-helpers.js';
|
|
5
|
-
import { restartCodexApp } from './codex-app-restart.js';
|
|
6
|
-
import { codexUserConfigPath, defaultOpenCodexCatalogPath, normalizeCodexModelId, readCodexModelCatalogFile, readTopLevelTomlString } from './codex-model-catalog.js';
|
|
7
|
-
import { hasTomlKey, hasTomlTablePrefix, hasUnexpectedTomlKeys, isLoopbackHostname, isSksManagedCatalogPath, probeRouterModels, resolveCatalogPath, routerBlocked, tomlBoolean, tomlString, tomlTableBody, uniqueStrings } from './multi-provider-router-support.js';
|
|
8
|
-
import { classifyCodexDesktopRouting, OPENCODEX_DESIGN_B_FORCE_HINT, opencodexDesignBBlocksRouterActivation } from './codex-desktop-routing-ownership.js';
|
|
9
|
-
import { desktopPickerStatusFromCache, invalidateCodexModelsCache } from './codex-models-cache.js';
|
|
10
|
-
export const MULTI_PROVIDER_ROUTER_ID = 'sks-router';
|
|
11
|
-
export const MULTI_PROVIDER_ROUTER_NAME = 'SKS Multi-Provider Router';
|
|
12
|
-
export const MULTI_PROVIDER_ROUTER_DEFAULT_BASE_URL = 'http://127.0.0.1:10100/v1';
|
|
13
|
-
const MULTI_PROVIDER_ROUTER_TIMEOUT_MS = 5_000;
|
|
14
|
-
export function normalizeMultiProviderRouterBaseUrl(value) {
|
|
15
|
-
const raw = String(value || '').trim() || MULTI_PROVIDER_ROUTER_DEFAULT_BASE_URL;
|
|
16
|
-
let parsed;
|
|
17
|
-
try {
|
|
18
|
-
parsed = new URL(raw);
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return { ok: false, value: null, blocker: 'multi_provider_router_base_url_invalid' };
|
|
22
|
-
}
|
|
23
|
-
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
24
|
-
return { ok: false, value: null, blocker: 'multi_provider_router_base_url_protocol_unsupported' };
|
|
25
|
-
}
|
|
26
|
-
if (parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
27
|
-
return { ok: false, value: null, blocker: 'multi_provider_router_base_url_contains_credentials_or_query' };
|
|
28
|
-
}
|
|
29
|
-
if (!isLoopbackHostname(parsed.hostname)) {
|
|
30
|
-
return { ok: false, value: null, blocker: 'multi_provider_router_requires_loopback' };
|
|
31
|
-
}
|
|
32
|
-
const normalizedPath = parsed.pathname.replace(/\/+$/, '');
|
|
33
|
-
if (!normalizedPath || normalizedPath === '')
|
|
34
|
-
parsed.pathname = '/v1';
|
|
35
|
-
else if (normalizedPath === '/')
|
|
36
|
-
parsed.pathname = '/v1';
|
|
37
|
-
else if (normalizedPath !== '/v1') {
|
|
38
|
-
return { ok: false, value: null, blocker: 'multi_provider_router_base_url_must_end_in_v1' };
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
parsed.pathname = '/v1';
|
|
42
|
-
}
|
|
43
|
-
return { ok: true, value: parsed.toString().replace(/\/$/, ''), blocker: null };
|
|
44
|
-
}
|
|
45
|
-
export async function multiProviderRouterStatus(input = {}) {
|
|
46
|
-
const env = input.env || process.env;
|
|
47
|
-
const home = input.home || env.HOME || os.homedir();
|
|
48
|
-
const configPath = input.configPath || codexUserConfigPath({ home, env });
|
|
49
|
-
const config = await readText(configPath, '');
|
|
50
|
-
const providerBody = tomlTableBody(config, `model_providers.${MULTI_PROVIDER_ROUTER_ID}`);
|
|
51
|
-
const providerPresent = Boolean(providerBody);
|
|
52
|
-
const providerName = tomlString(providerBody, 'name');
|
|
53
|
-
const configuredBaseUrl = tomlString(providerBody, 'base_url');
|
|
54
|
-
const baseUrlResult = normalizeMultiProviderRouterBaseUrl(input.baseUrl || configuredBaseUrl || MULTI_PROVIDER_ROUTER_DEFAULT_BASE_URL);
|
|
55
|
-
const baseUrl = baseUrlResult.value || String(input.baseUrl || configuredBaseUrl || MULTI_PROVIDER_ROUTER_DEFAULT_BASE_URL);
|
|
56
|
-
const configuredCatalogPath = readTopLevelTomlString(config, 'model_catalog_json');
|
|
57
|
-
const catalogPath = resolveCatalogPath(input.catalogPath || configuredCatalogPath || defaultOpenCodexCatalogPath({ home, env }), { home, env, configPath });
|
|
58
|
-
const catalog = await readCodexModelCatalogFile({
|
|
59
|
-
filePath: catalogPath,
|
|
60
|
-
configured: Boolean(configuredCatalogPath)
|
|
61
|
-
});
|
|
62
|
-
const selectedProvider = readTopLevelTomlString(config, 'model_provider');
|
|
63
|
-
const selected = selectedProvider === MULTI_PROVIDER_ROUTER_ID;
|
|
64
|
-
const activeModel = selected ? readTopLevelTomlString(config, 'model') : null;
|
|
65
|
-
const activeModelPresent = Boolean(activeModel && catalog.models.some((entry) => entry.model === activeModel));
|
|
66
|
-
const providerContractOk = providerPresent
|
|
67
|
-
&& providerName === MULTI_PROVIDER_ROUTER_NAME
|
|
68
|
-
&& baseUrlResult.ok
|
|
69
|
-
&& configuredBaseUrl === baseUrl
|
|
70
|
-
&& tomlString(providerBody, 'wire_api') === 'responses'
|
|
71
|
-
&& tomlBoolean(providerBody, 'requires_openai_auth') === false
|
|
72
|
-
&& !hasTomlKey(providerBody, 'env_key')
|
|
73
|
-
&& !hasTomlKey(providerBody, 'experimental_bearer_token')
|
|
74
|
-
&& !hasTomlKey(providerBody, 'http_headers')
|
|
75
|
-
&& !hasTomlKey(providerBody, 'env_http_headers')
|
|
76
|
-
&& !hasUnexpectedTomlKeys(providerBody, ['name', 'base_url', 'wire_api', 'requires_openai_auth'])
|
|
77
|
-
&& !hasTomlTablePrefix(config, `model_providers.${MULTI_PROVIDER_ROUTER_ID}.`);
|
|
78
|
-
const blockers = uniqueStrings([
|
|
79
|
-
...(providerPresent ? [] : ['multi_provider_router_provider_missing']),
|
|
80
|
-
...(providerPresent && !providerContractOk ? ['multi_provider_router_provider_contract_drift'] : []),
|
|
81
|
-
...(baseUrlResult.blocker ? [baseUrlResult.blocker] : []),
|
|
82
|
-
...catalog.blockers,
|
|
83
|
-
...(selected && !activeModel ? ['multi_provider_router_active_model_missing'] : []),
|
|
84
|
-
...(selected && activeModel && !activeModelPresent ? ['multi_provider_router_active_model_not_in_catalog'] : [])
|
|
85
|
-
]);
|
|
86
|
-
const ready = providerContractOk && catalog.ok && (!selected || activeModelPresent);
|
|
87
|
-
const ownership = classifyCodexDesktopRouting(config, { home, env, configPath });
|
|
88
|
-
return {
|
|
89
|
-
schema: 'sks.codex-app-multi-provider-router-status.v1',
|
|
90
|
-
ok: ready,
|
|
91
|
-
status: !providerPresent
|
|
92
|
-
? 'not_configured'
|
|
93
|
-
: ready
|
|
94
|
-
? 'configured'
|
|
95
|
-
: 'blocked',
|
|
96
|
-
provider: MULTI_PROVIDER_ROUTER_ID,
|
|
97
|
-
provider_name: MULTI_PROVIDER_ROUTER_NAME,
|
|
98
|
-
selected_provider: selectedProvider,
|
|
99
|
-
selected,
|
|
100
|
-
provider_present: providerPresent,
|
|
101
|
-
provider_contract_ok: providerContractOk,
|
|
102
|
-
base_url: baseUrl,
|
|
103
|
-
base_url_loopback: baseUrlResult.ok,
|
|
104
|
-
catalog_path: catalogPath,
|
|
105
|
-
catalog_configured: Boolean(configuredCatalogPath),
|
|
106
|
-
catalog,
|
|
107
|
-
models: catalog.models.map((entry) => entry.model),
|
|
108
|
-
model_count: catalog.model_count,
|
|
109
|
-
models_truncated: catalog.truncated,
|
|
110
|
-
active_model: activeModel,
|
|
111
|
-
active_model_present: activeModelPresent,
|
|
112
|
-
runtime_verified: false,
|
|
113
|
-
config_path: configPath,
|
|
114
|
-
routing_ownership: ownership,
|
|
115
|
-
desktop_picker: desktopPickerStatusFromCache({
|
|
116
|
-
catalogOk: catalog.ok,
|
|
117
|
-
cache: null,
|
|
118
|
-
restartAppRequested: false
|
|
119
|
-
}),
|
|
120
|
-
blockers,
|
|
121
|
-
warnings: uniqueStrings([
|
|
122
|
-
...catalog.warnings,
|
|
123
|
-
...ownership.warnings,
|
|
124
|
-
...(selected
|
|
125
|
-
&& activeModel
|
|
126
|
-
&& activeModelPresent
|
|
127
|
-
&& catalog.models.find((entry) => entry.model === activeModel)?.multi_agent_version !== 'v2'
|
|
128
|
-
? ['multi_provider_router_active_model_multi_agent_v2_missing']
|
|
129
|
-
: [])
|
|
130
|
-
])
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
export async function testMultiProviderRouter(input) {
|
|
134
|
-
const env = input.env || process.env;
|
|
135
|
-
const home = input.home || env.HOME || os.homedir();
|
|
136
|
-
const baseUrlResult = normalizeMultiProviderRouterBaseUrl(input.baseUrl);
|
|
137
|
-
if (!baseUrlResult.ok || !baseUrlResult.value) {
|
|
138
|
-
return routerBlocked('sks.codex-app-multi-provider-router-test.v1', baseUrlResult.blocker || 'multi_provider_router_base_url_invalid');
|
|
139
|
-
}
|
|
140
|
-
const catalogPath = resolveCatalogPath(input.catalogPath || defaultOpenCodexCatalogPath({ home, env }), { home, env });
|
|
141
|
-
const catalog = await readCodexModelCatalogFile({ filePath: catalogPath, configured: true });
|
|
142
|
-
if (!catalog.ok) {
|
|
143
|
-
return {
|
|
144
|
-
...routerBlocked('sks.codex-app-multi-provider-router-test.v1', ...catalog.blockers),
|
|
145
|
-
base_url: baseUrlResult.value,
|
|
146
|
-
catalog_path: catalogPath,
|
|
147
|
-
catalog
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
const model = input.model ? normalizeCodexModelId(input.model) : null;
|
|
151
|
-
if (input.model && !model) {
|
|
152
|
-
return routerBlocked('sks.codex-app-multi-provider-router-test.v1', 'multi_provider_router_model_invalid');
|
|
153
|
-
}
|
|
154
|
-
if (model && !catalog.models.some((entry) => entry.model === model)) {
|
|
155
|
-
return {
|
|
156
|
-
...routerBlocked('sks.codex-app-multi-provider-router-test.v1', 'multi_provider_router_model_not_in_catalog'),
|
|
157
|
-
base_url: baseUrlResult.value,
|
|
158
|
-
catalog_path: catalogPath,
|
|
159
|
-
model
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
const probe = await probeRouterModels({
|
|
163
|
-
baseUrl: baseUrlResult.value,
|
|
164
|
-
fetchImpl: input.fetchImpl || fetch,
|
|
165
|
-
timeoutMs: input.timeoutMs || MULTI_PROVIDER_ROUTER_TIMEOUT_MS
|
|
166
|
-
});
|
|
167
|
-
const liveModelPresent = !model || probe.models.includes(model);
|
|
168
|
-
const blockers = uniqueStrings([
|
|
169
|
-
...probe.blockers,
|
|
170
|
-
...(liveModelPresent ? [] : ['multi_provider_router_model_not_live'])
|
|
171
|
-
]);
|
|
172
|
-
return {
|
|
173
|
-
schema: 'sks.codex-app-multi-provider-router-test.v1',
|
|
174
|
-
generated_at: nowIso(),
|
|
175
|
-
ok: blockers.length === 0,
|
|
176
|
-
status: blockers.length === 0 ? 'connected' : 'blocked',
|
|
177
|
-
base_url: baseUrlResult.value,
|
|
178
|
-
catalog_path: catalogPath,
|
|
179
|
-
model,
|
|
180
|
-
catalog_model_count: catalog.model_count,
|
|
181
|
-
live_model_count: probe.models.length,
|
|
182
|
-
live_model_present: liveModelPresent,
|
|
183
|
-
probe,
|
|
184
|
-
blockers,
|
|
185
|
-
warnings: catalog.warnings
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
export async function useMultiProviderRouter(input) {
|
|
189
|
-
const env = input.env || process.env;
|
|
190
|
-
const home = input.home || env.HOME || os.homedir();
|
|
191
|
-
const configPath = input.configPath || codexUserConfigPath({ home, env });
|
|
192
|
-
const model = normalizeCodexModelId(input.model);
|
|
193
|
-
if (!model)
|
|
194
|
-
return routerBlocked('sks.codex-app-use-multi-provider-router.v1', 'multi_provider_router_model_invalid');
|
|
195
|
-
const baseUrlResult = normalizeMultiProviderRouterBaseUrl(input.baseUrl);
|
|
196
|
-
if (!baseUrlResult.ok || !baseUrlResult.value) {
|
|
197
|
-
return routerBlocked('sks.codex-app-use-multi-provider-router.v1', baseUrlResult.blocker || 'multi_provider_router_base_url_invalid');
|
|
198
|
-
}
|
|
199
|
-
const catalogPath = resolveCatalogPath(input.catalogPath || defaultOpenCodexCatalogPath({ home, env }), { home, env, configPath });
|
|
200
|
-
const catalog = await readCodexModelCatalogFile({ filePath: catalogPath, configured: true });
|
|
201
|
-
if (!catalog.ok) {
|
|
202
|
-
return {
|
|
203
|
-
...routerBlocked('sks.codex-app-use-multi-provider-router.v1', ...catalog.blockers),
|
|
204
|
-
model,
|
|
205
|
-
base_url: baseUrlResult.value,
|
|
206
|
-
catalog_path: catalogPath,
|
|
207
|
-
catalog
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
if (!catalog.models.some((entry) => entry.model === model)) {
|
|
211
|
-
return {
|
|
212
|
-
...routerBlocked('sks.codex-app-use-multi-provider-router.v1', 'multi_provider_router_model_not_in_catalog'),
|
|
213
|
-
model,
|
|
214
|
-
base_url: baseUrlResult.value,
|
|
215
|
-
catalog_path: catalogPath
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
const probe = await testMultiProviderRouter({
|
|
219
|
-
baseUrl: baseUrlResult.value,
|
|
220
|
-
catalogPath,
|
|
221
|
-
model,
|
|
222
|
-
home,
|
|
223
|
-
env,
|
|
224
|
-
...(input.fetchImpl ? { fetchImpl: input.fetchImpl } : {}),
|
|
225
|
-
...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs })
|
|
226
|
-
});
|
|
227
|
-
if (!probe.ok) {
|
|
228
|
-
return {
|
|
229
|
-
schema: 'sks.codex-app-use-multi-provider-router.v1',
|
|
230
|
-
generated_at: nowIso(),
|
|
231
|
-
ok: false,
|
|
232
|
-
status: 'probe_blocked',
|
|
233
|
-
model,
|
|
234
|
-
base_url: baseUrlResult.value,
|
|
235
|
-
catalog_path: catalogPath,
|
|
236
|
-
probe,
|
|
237
|
-
blockers: probe.blockers,
|
|
238
|
-
warnings: probe.warnings || []
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
const configExistedBefore = await exists(configPath);
|
|
242
|
-
const current = await readText(configPath, '');
|
|
243
|
-
const ownership = classifyCodexDesktopRouting(current, { home, env, configPath });
|
|
244
|
-
const designBBlocker = opencodexDesignBBlocksRouterActivation(ownership, {
|
|
245
|
-
forceRoutingOverride: input.forceRoutingOverride === true
|
|
246
|
-
});
|
|
247
|
-
if (designBBlocker) {
|
|
248
|
-
return {
|
|
249
|
-
...routerBlocked('sks.codex-app-use-multi-provider-router.v1', designBBlocker),
|
|
250
|
-
model,
|
|
251
|
-
base_url: baseUrlResult.value,
|
|
252
|
-
catalog_path: catalogPath,
|
|
253
|
-
routing_ownership: ownership,
|
|
254
|
-
hint: OPENCODEX_DESIGN_B_FORCE_HINT
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
const providerBody = tomlTableBody(current, `model_providers.${MULTI_PROVIDER_ROUTER_ID}`);
|
|
258
|
-
const existingProviderName = tomlString(providerBody, 'name');
|
|
259
|
-
if (providerBody && existingProviderName !== MULTI_PROVIDER_ROUTER_NAME) {
|
|
260
|
-
return routerBlocked('sks.codex-app-use-multi-provider-router.v1', 'multi_provider_router_provider_id_conflict');
|
|
261
|
-
}
|
|
262
|
-
const providerContractConflict = (providerBody || hasTomlTablePrefix(current, `model_providers.${MULTI_PROVIDER_ROUTER_ID}.`)) && (hasUnexpectedTomlKeys(providerBody, ['name', 'base_url', 'wire_api', 'requires_openai_auth'])
|
|
263
|
-
|| hasTomlTablePrefix(current, `model_providers.${MULTI_PROVIDER_ROUTER_ID}.`));
|
|
264
|
-
if (providerContractConflict) {
|
|
265
|
-
return routerBlocked('sks.codex-app-use-multi-provider-router.v1', 'multi_provider_router_existing_provider_contract_conflict');
|
|
266
|
-
}
|
|
267
|
-
const configuredCatalogPath = readTopLevelTomlString(current, 'model_catalog_json');
|
|
268
|
-
const configuredCatalogResolved = configuredCatalogPath
|
|
269
|
-
? resolveCatalogPath(configuredCatalogPath, { home, env, configPath })
|
|
270
|
-
: null;
|
|
271
|
-
const catalogConflict = configuredCatalogPath
|
|
272
|
-
&& configuredCatalogResolved !== catalogPath
|
|
273
|
-
&& !isSksManagedCatalogPath(configuredCatalogResolved, { home, env })
|
|
274
|
-
&& input.replaceCatalog !== true;
|
|
275
|
-
if (catalogConflict) {
|
|
276
|
-
return {
|
|
277
|
-
...routerBlocked('sks.codex-app-use-multi-provider-router.v1', 'multi_provider_router_user_catalog_conflict'),
|
|
278
|
-
configured_catalog_path: configuredCatalogPath,
|
|
279
|
-
requested_catalog_path: catalogPath,
|
|
280
|
-
hint: 'Re-run with --replace-catalog only if replacing the current user model catalog is intentional.'
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
const unselect = await unselectCodexLbProvider({
|
|
284
|
-
home,
|
|
285
|
-
configPath,
|
|
286
|
-
processEnv: env
|
|
287
|
-
}).catch((err) => ({ ok: false, status: 'failed', provider_error: err?.message || String(err) }));
|
|
288
|
-
if (unselect?.ok !== true) {
|
|
289
|
-
return {
|
|
290
|
-
...routerBlocked('sks.codex-app-use-multi-provider-router.v1', unselect?.reason === 'shared_codex_lb_auth_active'
|
|
291
|
-
? 'legacy_codex_lb_desktop_config_requires_migration'
|
|
292
|
-
: String(unselect?.status || 'codex_lb_unselect_failed')),
|
|
293
|
-
model,
|
|
294
|
-
base_url: baseUrlResult.value,
|
|
295
|
-
catalog_path: catalogPath,
|
|
296
|
-
unselect,
|
|
297
|
-
guidance: unselect?.reason === 'shared_codex_lb_auth_active'
|
|
298
|
-
? ['Run: sks codex-lb migrate-legacy-desktop --restart-app']
|
|
299
|
-
: []
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
const currentAfterUnselect = await readText(configPath, '');
|
|
303
|
-
const providerBlock = [
|
|
304
|
-
`[model_providers.${MULTI_PROVIDER_ROUTER_ID}]`,
|
|
305
|
-
`name = ${JSON.stringify(MULTI_PROVIDER_ROUTER_NAME)}`,
|
|
306
|
-
`base_url = ${JSON.stringify(baseUrlResult.value)}`,
|
|
307
|
-
'wire_api = "responses"',
|
|
308
|
-
'requires_openai_auth = false'
|
|
309
|
-
].join('\n');
|
|
310
|
-
let next = upsertTomlTable(currentAfterUnselect, `model_providers.${MULTI_PROVIDER_ROUTER_ID}`, providerBlock);
|
|
311
|
-
next = upsertTopLevelTomlString(next, 'model_catalog_json', catalogPath);
|
|
312
|
-
next = upsertTopLevelTomlString(next, 'model_provider', MULTI_PROVIDER_ROUTER_ID);
|
|
313
|
-
next = upsertTopLevelTomlString(next, 'model', model);
|
|
314
|
-
next = normalizeCodexFastModeUiConfig(next);
|
|
315
|
-
next = ensureTrailingNewline(next);
|
|
316
|
-
const write = await safeWriteCodexConfigToml(configPath, currentAfterUnselect, next, 'multi-provider-router-use', {
|
|
317
|
-
verifyUnchangedBeforeWrite: true,
|
|
318
|
-
expectedBeforeExists: configExistedBefore
|
|
319
|
-
});
|
|
320
|
-
if (!write.ok) {
|
|
321
|
-
return {
|
|
322
|
-
...routerBlocked('sks.codex-app-use-multi-provider-router.v1', String(write.status || 'multi_provider_router_config_write_blocked')),
|
|
323
|
-
model,
|
|
324
|
-
base_url: baseUrlResult.value,
|
|
325
|
-
catalog_path: catalogPath,
|
|
326
|
-
write
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
const restart = await (input.restartImpl || restartCodexApp)({ enabled: Boolean(input.restartApp) });
|
|
330
|
-
const cache = await invalidateCodexModelsCache({ home, env, catalogPath });
|
|
331
|
-
const status = await multiProviderRouterStatus({ home, env, configPath });
|
|
332
|
-
const configApplied = status.selected
|
|
333
|
-
&& status.provider_contract_ok
|
|
334
|
-
&& status.catalog.ok
|
|
335
|
-
&& status.active_model === model
|
|
336
|
-
&& status.active_model_present;
|
|
337
|
-
const restartRequired = Boolean(input.restartApp);
|
|
338
|
-
const restartCompleted = !restartRequired || (restart.ok && restart.skipped !== true);
|
|
339
|
-
const operationOk = configApplied && restartCompleted;
|
|
340
|
-
const desktopPicker = desktopPickerStatusFromCache({
|
|
341
|
-
catalogOk: catalog.ok && configApplied,
|
|
342
|
-
cache,
|
|
343
|
-
restartAppRequested: Boolean(input.restartApp)
|
|
344
|
-
});
|
|
345
|
-
return {
|
|
346
|
-
schema: 'sks.codex-app-use-multi-provider-router.v1',
|
|
347
|
-
generated_at: nowIso(),
|
|
348
|
-
ok: operationOk,
|
|
349
|
-
status: !configApplied
|
|
350
|
-
? 'configuration_incomplete'
|
|
351
|
-
: restartCompleted
|
|
352
|
-
? restartRequired ? 'configured_restarted' : 'configured'
|
|
353
|
-
: 'configured_restart_blocked',
|
|
354
|
-
provider: MULTI_PROVIDER_ROUTER_ID,
|
|
355
|
-
model,
|
|
356
|
-
base_url: baseUrlResult.value,
|
|
357
|
-
catalog_path: catalogPath,
|
|
358
|
-
config_path: configPath,
|
|
359
|
-
probe,
|
|
360
|
-
write,
|
|
361
|
-
unselect,
|
|
362
|
-
restart_app: restart,
|
|
363
|
-
restart_ok: restart.ok && restart.skipped !== true,
|
|
364
|
-
restart_completed: restartCompleted,
|
|
365
|
-
restart_skipped: restart.skipped === true,
|
|
366
|
-
config_applied: configApplied,
|
|
367
|
-
runtime_verified: false,
|
|
368
|
-
models_cache: cache,
|
|
369
|
-
desktop_picker: desktopPicker,
|
|
370
|
-
routing_ownership: ownership,
|
|
371
|
-
router: {
|
|
372
|
-
...status,
|
|
373
|
-
desktop_picker: desktopPicker
|
|
374
|
-
},
|
|
375
|
-
blockers: uniqueStrings([
|
|
376
|
-
...(configApplied ? [] : ['multi_provider_router_configuration_incomplete']),
|
|
377
|
-
...(restartCompleted ? [] : (restart.blockers || ['multi_provider_router_restart_blocked']))
|
|
378
|
-
]),
|
|
379
|
-
warnings: uniqueStrings([
|
|
380
|
-
...catalog.warnings,
|
|
381
|
-
...ownership.warnings,
|
|
382
|
-
...cache.warnings,
|
|
383
|
-
...(catalog.models.find((entry) => entry.model === model)?.multi_agent_version === 'v2'
|
|
384
|
-
? []
|
|
385
|
-
: ['multi_provider_router_model_multi_agent_v2_missing']),
|
|
386
|
-
...(desktopPicker.models_cache_invalidated ? [] : ['multi_provider_router_models_cache_not_invalidated']),
|
|
387
|
-
'multi_provider_router_runtime_not_verified',
|
|
388
|
-
'desktop_picker_restart_recommended_after_catalog_change'
|
|
389
|
-
])
|
|
390
|
-
};
|
|
391
|
-
}
|