sneakoscope 7.3.0 → 7.6.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 +129 -10
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/codex-app-command.js +1 -1
- package/dist/cli/command-manifest-lite.js +4 -1
- package/dist/cli/command-registry.js +2 -0
- package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
- package/dist/cli/install-helpers-codex-lb-config.js +114 -9
- package/dist/cli/install-helpers-install-support.js +6 -16
- package/dist/cli/install-helpers.js +788 -600
- package/dist/cli/router.js +30 -2
- package/dist/commands/codex-app.js +37 -1
- package/dist/commands/codex-lb.js +491 -300
- package/dist/config/skills-manifest.json +57 -71
- package/dist/core/align/align-route.js +127 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
- package/dist/core/codex-app/codex-app-restart.js +83 -0
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
- package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
- package/dist/core/codex-app/menubar/constants.js +5 -0
- package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
- package/dist/core/codex-app/menubar/installer.js +18 -0
- package/dist/core/codex-app/multi-provider-router.js +22 -10
- package/dist/core/codex-app/openrouter-activate.js +26 -1
- package/dist/core/codex-app.js +118 -26
- package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
- package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
- package/dist/core/codex-control/codex-task-runner.js +2 -2
- package/dist/core/codex-lb/capability-runner.js +204 -0
- package/dist/core/codex-lb/capability-types.js +1 -0
- package/dist/core/codex-lb/codex-lb-env.js +9 -13
- package/dist/core/codex-lb/codex-lb-setup.js +45 -8
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
- package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
- package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
- package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
- package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
- package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
- package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
- package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
- package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
- package/dist/core/codex-lb/desktop-controller.js +1410 -0
- package/dist/core/codex-lb/desktop-mode.js +32 -0
- package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
- package/dist/core/codex-lb/desktop-service.js +613 -0
- package/dist/core/codex-lb/legacy-migration.js +575 -0
- package/dist/core/codex-lb/migration-receipt.js +246 -0
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
- package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
- package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
- package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
- package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
- package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
- package/dist/core/codex-native/core-skill-manifest.js +22 -6
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
- package/dist/core/commands/align-command.js +220 -0
- package/dist/core/commands/image-ux-review-command.js +280 -68
- package/dist/core/commands/naruto-command.js +257 -8
- package/dist/core/commands/ppt-command.js +2 -2
- package/dist/core/commands/telegram-command.js +402 -34
- package/dist/core/commands/triwiki-command.js +11 -1
- package/dist/core/commands/triwiki-graph-command.js +160 -0
- package/dist/core/commands/wiki-command.js +100 -20
- package/dist/core/doctor/skill-legacy-surface.js +1 -3
- package/dist/core/errors/message.js +3 -0
- package/dist/core/evidence/evidence-freshness.js +14 -0
- package/dist/core/feature-fixtures.js +9 -1
- package/dist/core/feature-registry.js +5 -3
- package/dist/core/fsx.js +7 -3
- package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
- package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
- package/dist/core/hooks-runtime/hook-context.js +15 -2
- package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
- package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +33 -89
- package/dist/core/image-ux-review/callout-extraction.js +6 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
- package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
- package/dist/core/image-ux-review.js +230 -12
- package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
- package/dist/core/imagegen/imagegen-capability.js +10 -4
- package/dist/core/imagegen/require-imagegen.js +29 -5
- package/dist/core/init/skills.js +25 -13
- package/dist/core/init.js +1 -1
- package/dist/core/install/installed-package-smoke.js +157 -4
- package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
- package/dist/core/managed-path-safety.js +8 -10
- package/dist/core/mcp-config/backup.js +2 -4
- package/dist/core/mcp-config/config-reader.js +2 -4
- package/dist/core/mcp-config/scope.js +3 -5
- package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
- package/dist/core/naruto/context-graph-advisor.js +0 -0
- package/dist/core/perf/release-latency-slo.js +12 -6
- package/dist/core/pipeline-internals/runtime-core.js +78 -0
- package/dist/core/pipeline-internals/runtime-gates.js +5 -2
- package/dist/core/policy/latest-version-guidance.js +113 -0
- package/dist/core/ppt.js +8 -8
- package/dist/core/preflight/parallel-preflight-engine.js +1 -1
- package/dist/core/proof/auto-finalize.js +98 -6
- package/dist/core/proof/route-proof-gate.js +12 -1
- package/dist/core/proof/route-proof-policy.js +8 -8
- package/dist/core/release/gate-manifest.js +32 -1
- package/dist/core/release/npm-stage-contract.js +21 -0
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-gate-contract.js +5 -0
- package/dist/core/release/release-proof-io.js +1 -3
- package/dist/core/release/release-real-contract.js +5 -4
- package/dist/core/release/release-stamp-proof.js +1 -3
- package/dist/core/release/stage-publish.js +27 -0
- package/dist/core/remote/audit-idempotency.js +50 -18
- package/dist/core/remote/codex-public-activity.js +283 -0
- package/dist/core/remote/index.js +1 -0
- package/dist/core/remote/ssh-worker-client.js +4 -1
- package/dist/core/remote/worker.js +206 -27
- package/dist/core/research/research-adversarial-review.js +1 -3
- package/dist/core/research/research-claim-synthesizer.js +1 -3
- package/dist/core/research/research-falsification-runner.js +1 -3
- package/dist/core/research/research-source-evidence.js +1 -3
- package/dist/core/responses-stream.js +65 -19
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/design-policy.js +7 -2
- package/dist/core/routes/dollar-manifest-lite.js +2 -4
- package/dist/core/routes/evidence.js +1 -1
- package/dist/core/routes.js +30 -10
- package/dist/core/safety/command-contract/contracts.js +2 -0
- package/dist/core/safety/command-contract/types.js +1 -1
- package/dist/core/search/context-graph-seeds.js +205 -0
- package/dist/core/search/context.js +0 -0
- package/dist/core/search/provider.js +2 -2
- package/dist/core/secret-redaction.js +6 -0
- package/dist/core/subagents/naruto-help-contract.js +2 -1
- package/dist/core/subagents/naruto-host-credentials.js +161 -0
- package/dist/core/subagents/official-subagent-preparation.js +4 -1
- package/dist/core/subagents/official-subagent-prompt.js +44 -2
- package/dist/core/subagents/official-subagent-runner.js +26 -8
- package/dist/core/subagents/triwiki-attention.js +135 -123
- package/dist/core/telegram/bot-api.js +85 -10
- package/dist/core/telegram/center-snapshot.js +718 -0
- package/dist/core/telegram/config.js +158 -31
- package/dist/core/telegram/hub.js +288 -33
- package/dist/core/telegram/index.js +2 -0
- package/dist/core/telegram/ingress-spool.js +419 -0
- package/dist/core/telegram/ledgers.js +188 -29
- package/dist/core/telegram/messages.js +26 -6
- package/dist/core/telegram/owner-lock.js +10 -7
- package/dist/core/telegram/runtime-projection.js +38 -3
- package/dist/core/telegram/runtime.js +298 -63
- package/dist/core/telegram/service.js +137 -8
- package/dist/core/telegram/setup.js +291 -73
- package/dist/core/text/strings.js +3 -0
- package/dist/core/triwiki/code-pack.js +12 -120
- package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
- package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
- package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
- package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
- package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
- package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
- package/dist/core/triwiki/context-graph/contracts.js +236 -0
- package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
- package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
- package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
- package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
- package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
- package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
- package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
- package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
- package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
- package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
- package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
- package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
- package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
- package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
- package/dist/core/triwiki/context-graph/graph-index.js +143 -0
- package/dist/core/triwiki/context-graph/ids.js +0 -0
- package/dist/core/triwiki/context-graph/lint/index.js +0 -0
- package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
- package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
- package/dist/core/triwiki/context-graph/paths.js +116 -0
- package/dist/core/triwiki/context-graph/profiles.js +78 -0
- package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
- package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
- package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
- package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
- package/dist/core/triwiki/context-graph/projections/index.js +7 -0
- package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
- package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
- package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
- package/dist/core/triwiki/context-graph/query/explain.js +0 -0
- package/dist/core/triwiki/context-graph/query/index.js +178 -0
- package/dist/core/triwiki/context-graph/query/load.js +116 -0
- package/dist/core/triwiki/context-graph/query/pack.js +117 -0
- package/dist/core/triwiki/context-graph/query/rank.js +142 -0
- package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
- package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
- package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
- package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
- package/dist/core/triwiki/context-graph/query-types.js +29 -0
- package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
- package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
- package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
- package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
- package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
- package/dist/core/verification/context-graph-affected.js +271 -0
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
- package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
- package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
- package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
- package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
- package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
- package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
- package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
- package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
- package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
- package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
- package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
- package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
- package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
- package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
- package/dist/scripts/context-graph-check.js +191 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
- package/dist/scripts/gate-policy-audit-check.js +1 -1
- package/dist/scripts/helper-dedup-check.js +0 -2
- package/dist/scripts/installed-package-smoke-check.js +4 -1
- package/dist/scripts/latest-version-guidance-check.js +74 -0
- package/dist/scripts/release-metadata-check.js +21 -4
- package/dist/scripts/release-real-check.js +7 -0
- package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
- package/package.json +8 -3
- package/release-gates.v2.json +176 -1
- package/runtime-required-scripts.json +30 -0
- package/schemas/triwiki/context-graph.schema.json +119 -0
- package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
- package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
- package/dist/core/triwiki/code-index-scanner.js +0 -309
|
@@ -6,7 +6,6 @@ import { stdin as input, stdout as output } from 'node:process';
|
|
|
6
6
|
import { ensureDir, exists, globalSksRoot, packageRoot, PACKAGE_VERSION, readText, runProcess, tmpdir, which, writeTextAtomic } from '../core/fsx.js';
|
|
7
7
|
import { createRequestedScopeContract } from '../core/safety/requested-scope-contract.js';
|
|
8
8
|
import { guardedPackageInstall, guardContextForRoute } from '../core/safety/mutation-guard.js';
|
|
9
|
-
import { EMPTY_CODEX_INFO, getCodexInfo } from '../core/codex-adapter.js';
|
|
10
9
|
import { formatHarnessConflictReport, llmHarnessCleanupPrompt, scanHarnessConflicts } from '../core/harness-conflicts.js';
|
|
11
10
|
import { initProject, installGlobalSkills } from '../core/init.js';
|
|
12
11
|
import { context7ConfigToml, DOLLAR_SKILL_NAMES, GETDESIGN_REFERENCE, hasContext7ConfigText, RECOMMENDED_SKILLS } from '../core/routes.js';
|
|
@@ -16,18 +15,21 @@ import { restartCodexApp } from '../core/codex-app/codex-app-restart.js';
|
|
|
16
15
|
import { cleanupMacLaunchSecretEnvironment } from '../core/codex-app/menubar/index.js';
|
|
17
16
|
import { recordCodexLbHealthEvent } from '../core/codex-lb-circuit.js';
|
|
18
17
|
import { loadCodexLbEnv, writeCodexLbKeychain, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
|
|
19
|
-
import { codexLbToolCatalogPath
|
|
18
|
+
import { codexLbToolCatalogPath } from '../core/codex-lb/codex-lb-tool-catalog.js';
|
|
20
19
|
import { codexLbToolOutputRecoveryNotChecked, codexLbToolOutputRecoveryNotSelected, codexLbToolOutputRecoveryOverrideAcknowledged, probeCodexLbToolOutputRecovery } from '../core/codex-lb/codex-lb-tool-output-recovery.js';
|
|
21
20
|
import { GLM_CODEX_CONFIG_PROFILE_ID, GLM_CODEX_CONFIG_PROVIDER_ID, GLM_CODEX_CONFIG_REASONING_PROFILES, GLM_52_OPENROUTER_MODEL } from '../core/codex-app/openrouter-provider.js';
|
|
22
21
|
import { buildCodexLbSetupPlan, codexLbPersistenceSummary, installCodexLbShellProfileSnippet, selectedCodexLbPersistenceModes } from '../core/codex-lb/codex-lb-setup.js';
|
|
22
|
+
import { DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT, modeRequiresChatGptOAuth, parseCodexLbDesktopMode, parseCodexLbGatewayAuthTransport } from '../core/codex-lb/desktop-mode.js';
|
|
23
|
+
import { assertDesktopAuthUnchangedBySks, captureCodexAuthSnapshot } from '../core/codex-lb/desktop-auth-invariant.js';
|
|
23
24
|
import { extractTomlTable, writeCodexConfigGuarded } from '../core/codex/codex-config-guard.js';
|
|
24
|
-
import { ensureGlobalCodexFastModeDuringInstall, ensureTrailingNewline, normalizeCodexFastModeUiConfig, removeTopLevelTomlKeyIfValue, safeWriteCodexConfigToml,
|
|
25
|
+
import { ensureGlobalCodexFastModeDuringInstall, ensureTrailingNewline, normalizeCodexFastModeUiConfig, removeTopLevelTomlKeyIfValue, safeWriteCodexConfigToml, upsertTomlTable } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
25
26
|
import { runPostinstallGlobalDoctorAndMarkPending } from '../core/update/update-migration-state.js';
|
|
26
27
|
import { repairCodexImagegen } from '../core/doctor/imagegen-repair.js';
|
|
27
|
-
import { canAskYesNo, compareVersions, hasCodexUnstableFeatureWarningSuppression, hasDeprecatedCodexHooksFeatureFlag,
|
|
28
|
+
import { canAskYesNo, compareVersions, hasCodexUnstableFeatureWarningSuppression, hasDeprecatedCodexHooksFeatureFlag, isProjectSetupCandidate } from './install-tool-helpers.js';
|
|
28
29
|
import { checkCodexLbResponseChain } from './install-helpers-codex-lb-chain.js';
|
|
29
|
-
import { CODEX_LB_CANONICAL_FAST_SERVICE_TIER, CODEX_LB_PROVIDER_ENV_KEY,
|
|
30
|
-
import { appliedCodexLbPersistenceModes, captureCodexLbSetupWriteState, codexLbSharedOpenAiRoutingState, detectCodexLbSetupDrift, ensureGlobalCodexAppGlmProfile, ensureStoredOpenRouterProviderDuringInstall, parseCodexLbEnvBaseUrl,
|
|
30
|
+
import { CODEX_LB_CANONICAL_FAST_SERVICE_TIER, CODEX_LB_PROVIDER_ENV_KEY, askPostinstallQuestion, codexAuthChatgptBackupPath, codexAuthPath, codexLbConfigPath, codexLbEnvPath, hasTopLevelCodexLbSelected, normalizeCodexLbBaseUrl, parseCodexLbEnvKey, redactSecretText } from './install-helpers-codex-lb-shared.js';
|
|
31
|
+
import { CODEX_LB_DESKTOP_BRIDGE_MARKER, CODEX_LB_DESKTOP_COMPAT_MARKER, appliedCodexLbPersistenceModes, captureCodexLbSetupWriteState, codexLbSharedOpenAiRoutingState, detectCodexLbSetupDrift, ensureGlobalCodexAppGlmProfile, ensureStoredOpenRouterProviderDuringInstall, parseCodexLbEnvBaseUrl, removeCodexLbSharedOpenAiRouting, removeCodexLbManagedDesktopConfig, sha256Text, shellSingleQuote, upsertCodexAppGlmConfig, upsertCodexLbConfig, upsertCodexLbCliProviderConfig, upsertCodexLbCompatDesktopConfig, upsertCodexLbNativeDesktopConfig, upsertCodexLbSharedOpenAiRouting } from './install-helpers-codex-lb-config.js';
|
|
32
|
+
import { detectLegacyCodexLbDesktopState } from '../core/codex-lb/legacy-migration.js';
|
|
31
33
|
import { ensureCodexImagegenDuringInstall, ensureGlobalCodexSkillsDuringInstall, ensureGlobalContext7DuringInstall, ensureGlobalGetdesignSkillDuringInstall, ensureSksCommandDuringInstall } from './install-helpers-install-support.js';
|
|
32
34
|
import { escapeRegExp } from '../core/text/regex.js';
|
|
33
35
|
function packagedSksEntrypoint() {
|
|
@@ -35,24 +37,31 @@ function packagedSksEntrypoint() {
|
|
|
35
37
|
}
|
|
36
38
|
export async function postinstall({ bootstrap, args = [] }) {
|
|
37
39
|
const installRoot = path.resolve(process.env.INIT_CWD || process.cwd());
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
console.log('\nSKS installed.');
|
|
41
|
+
await restoreInstalledPackageBuildStamp();
|
|
42
|
+
if (!postinstallExternalMutationsAllowed(process.env)) {
|
|
43
|
+
console.log('Automatic bootstrap was not run; npm install leaves project, HOME, Codex, and global SKS state unchanged by default.');
|
|
44
|
+
console.log('Next: run `sks bootstrap` when you are ready to initialize SKS.');
|
|
45
|
+
console.log('Dependency diagnostics remain explicit: sks deps check');
|
|
46
|
+
console.log('Explicit lifecycle opt-in: SKS_POSTINSTALL_BOOTSTRAP=1 npm i -g sneakoscope');
|
|
47
|
+
console.log('Optional Homebrew/npm-global tool repair remains off unless SKS_POSTINSTALL_AUTO_INSTALL_CLI_TOOLS=1 is also set.');
|
|
48
|
+
const reason = process.env.SKS_POSTINSTALL_NO_BOOTSTRAP === '1'
|
|
49
|
+
? 'SKS_POSTINSTALL_NO_BOOTSTRAP=1'
|
|
50
|
+
: 'explicit opt-in required (SKS_POSTINSTALL_BOOTSTRAP=1)';
|
|
51
|
+
console.log(`Reason: ${reason}.`);
|
|
41
52
|
return;
|
|
42
53
|
}
|
|
43
|
-
|
|
54
|
+
let codexLbConfigSnapshot = null;
|
|
55
|
+
let reconcileCodexLb = false;
|
|
44
56
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
await
|
|
49
|
-
|
|
50
|
-
console.log('SKS build stamp: restored for update self-verification (npm packages ship without it).');
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
console.log(`SKS build stamp: could not restore (${err?.message || err}); \`sks update\` self-verification may report dist_stamp missing.`);
|
|
57
|
+
const bootstrapDecision = await postinstallBootstrapDecision(installRoot);
|
|
58
|
+
const conflictScan = await scanHarnessConflicts(installRoot);
|
|
59
|
+
if (conflictScan.hard_block) {
|
|
60
|
+
await postinstallHarnessConflictNotice(conflictScan);
|
|
61
|
+
return;
|
|
55
62
|
}
|
|
63
|
+
codexLbConfigSnapshot = await capturePostinstallCodexLbConfigSnapshot();
|
|
64
|
+
reconcileCodexLb = true;
|
|
56
65
|
const shim = await ensureSksCommandDuringInstall();
|
|
57
66
|
if (shim.status === 'present')
|
|
58
67
|
console.log(`SKS command: available (${shim.command ?? 'unknown'}).`);
|
|
@@ -77,19 +86,7 @@ export async function postinstall({ bootstrap, args = [] }) {
|
|
|
77
86
|
console.log(`Context7 MCP: skipped (${context7Install.reason}).`);
|
|
78
87
|
else if (context7Install.status === 'failed')
|
|
79
88
|
console.log(`Context7 MCP: auto setup failed. Run \`sks context7 setup --scope global\` or \`sks setup\`. ${context7Install.error || ''}`.trim());
|
|
80
|
-
|
|
81
|
-
if (fastModeRepair.status === 'updated')
|
|
82
|
-
console.log(`Codex App Fast mode: updated ${fastModeRepair.config_path}${fastModeRepair.backup_path ? ` (backup ${fastModeRepair.backup_path})` : ''}.`);
|
|
83
|
-
else if (fastModeRepair.status === 'present')
|
|
84
|
-
console.log('Codex App Fast mode: config already compatible.');
|
|
85
|
-
else if (fastModeRepair.status === 'unparseable_config_preserved')
|
|
86
|
-
console.log(`Codex App Fast mode: existing ${fastModeRepair.config_path} is not valid TOML — left untouched, backed up to ${fastModeRepair.backup_path}. Run \`sks doctor --fix\` to recover it.`);
|
|
87
|
-
else if (fastModeRepair.status === 'skipped_unsafe_rewrite')
|
|
88
|
-
console.log(`Codex App Fast mode: skipped (managed rewrite would not parse; ${fastModeRepair.config_path} left untouched).`);
|
|
89
|
-
else if (fastModeRepair.status === 'skipped')
|
|
90
|
-
console.log(`Codex App Fast mode: skipped (${fastModeRepair.reason}).`);
|
|
91
|
-
else if (fastModeRepair.status === 'failed')
|
|
92
|
-
console.log(`Codex App Fast mode: auto repair failed. Run \`sks setup\`. ${fastModeRepair.error || ''}`.trim());
|
|
89
|
+
console.log('Codex App Fast mode: left unchanged during install; use the explicit Fast-mode command to change it.');
|
|
93
90
|
const openRouterProviderRepair = await ensureStoredOpenRouterProviderDuringInstall();
|
|
94
91
|
if (openRouterProviderRepair.status === 'updated')
|
|
95
92
|
console.log('OpenRouter provider: repaired for the stored key (credentials and active model were preserved).');
|
|
@@ -108,7 +105,12 @@ export async function postinstall({ bootstrap, args = [] }) {
|
|
|
108
105
|
console.log(`Codex App Image Gen: blocked; run \`sks doctor --fix\`. ${(imagegenRepair.blockers || []).join(', ')}`.trim());
|
|
109
106
|
else if (imagegenRepair.status === 'skipped')
|
|
110
107
|
console.log(`Codex App Image Gen: skipped (${imagegenRepair.reason}).`);
|
|
111
|
-
const postinstallDoctor = await runPostinstallGlobalDoctorAndMarkPending(
|
|
108
|
+
const postinstallDoctor = await runPostinstallGlobalDoctorAndMarkPending({
|
|
109
|
+
env: {
|
|
110
|
+
...process.env,
|
|
111
|
+
SKS_POSTINSTALL_GLOBAL_DOCTOR: '0'
|
|
112
|
+
}
|
|
113
|
+
}).catch((err) => ({
|
|
112
114
|
ok: false,
|
|
113
115
|
doctor: null,
|
|
114
116
|
pending: null,
|
|
@@ -116,7 +118,7 @@ export async function postinstall({ bootstrap, args = [] }) {
|
|
|
116
118
|
warnings: []
|
|
117
119
|
}));
|
|
118
120
|
if (postinstallDoctor.ok)
|
|
119
|
-
console.log('SKS update migration:
|
|
121
|
+
console.log('SKS update migration: pending receipt recorded; no global Doctor repair ran during install.');
|
|
120
122
|
else
|
|
121
123
|
console.log(`SKS update migration: global Doctor did not complete; first normal command will retry. ${(postinstallDoctor.blockers || []).join(', ')}`.trim());
|
|
122
124
|
const postinstallRetention = await runPostinstallProjectRetentionCleanup(installRoot);
|
|
@@ -150,35 +152,43 @@ export async function postinstall({ bootstrap, args = [] }) {
|
|
|
150
152
|
else if (globalSkills.status === 'failed')
|
|
151
153
|
console.log(`Codex App global $ skills: auto setup failed. Run \`sks doctor --fix\`. ${globalSkills.error || ''}`.trim());
|
|
152
154
|
const getdesignSkill = await ensureGlobalGetdesignSkillDuringInstall();
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
else if (getdesignSkill.status === 'skills_cli_missing')
|
|
158
|
-
console.log(`getdesign Codex skill: skills CLI missing; generated getdesign-reference skill is installed. Later run \`${getdesignSkill.install}\` if the skills CLI is available.`);
|
|
159
|
-
else if (getdesignSkill.status === 'skipped')
|
|
160
|
-
console.log(`getdesign Codex skill: skipped (${getdesignSkill.reason}).`);
|
|
161
|
-
else if (getdesignSkill.status === 'failed')
|
|
162
|
-
console.log(`getdesign Codex skill: auto setup failed; generated getdesign-reference skill remains available. ${getdesignSkill.error || ''}`.trim());
|
|
163
|
-
const bootstrapDecision = await postinstallBootstrapDecision(installRoot);
|
|
164
|
-
if (bootstrapDecision.run) {
|
|
165
|
-
console.log(`SKS bootstrap: ${bootstrapDecision.reason}.`);
|
|
166
|
-
await runPostinstallBootstrap(installRoot, bootstrap);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
console.log('\nNext:');
|
|
170
|
-
console.log(' sks bootstrap');
|
|
171
|
-
console.log(`\nSKS bootstrap was not run automatically: ${bootstrapDecision.reason}.`);
|
|
172
|
-
console.log('This initializes the current project, installs SKS Codex App skills, verifies Codex App/Context7 readiness, and checks Zellij runtime dependencies.');
|
|
173
|
-
console.log('Dependency repair: sks bootstrap --yes, sks deps check --yes, or sks --mad --yes. Postinstall reports missing CLI tools but does not mutate Homebrew/npm globals unless SKS_POSTINSTALL_AUTO_INSTALL_CLI_TOOLS=1 is set.');
|
|
174
|
-
console.log('Open runtime after readiness is green: sks\n');
|
|
155
|
+
console.log(`getdesign Codex skill: not installed automatically; generated getdesign-reference skill is available. To install the upstream skill manually, review commit ${getdesignSkill.reviewed_ref} and run \`${getdesignSkill.install}\`.`);
|
|
156
|
+
console.log(`SKS bootstrap: ${bootstrapDecision.reason}.`);
|
|
157
|
+
await runPostinstallBootstrap(installRoot, bootstrap, bootstrapDecision);
|
|
158
|
+
return;
|
|
175
159
|
}
|
|
176
160
|
catch (err) {
|
|
177
161
|
console.log(`\nSKS postinstall: a setup step did not complete; installation continues. Run \`sks doctor --fix\` afterward. (${err?.message || err})`);
|
|
178
162
|
}
|
|
179
163
|
finally {
|
|
180
|
-
|
|
181
|
-
|
|
164
|
+
if (reconcileCodexLb) {
|
|
165
|
+
await restorePostinstallCodexLbConfigSnapshot(codexLbConfigSnapshot).catch(() => { });
|
|
166
|
+
await reportPostinstallCodexLbAuth(codexLbConfigSnapshot).catch(() => { });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function postinstallExternalMutationsAllowed(env) {
|
|
171
|
+
return env.SKS_POSTINSTALL_BOOTSTRAP === '1' && env.SKS_POSTINSTALL_NO_BOOTSTRAP !== '1';
|
|
172
|
+
}
|
|
173
|
+
async function restoreInstalledPackageBuildStamp() {
|
|
174
|
+
try {
|
|
175
|
+
const stampLib = await import('../scripts/lib/ensure-dist-fresh.js');
|
|
176
|
+
const root = path.resolve(packageRoot());
|
|
177
|
+
const rawStamp = String(stampLib.distStampPath || '').trim();
|
|
178
|
+
if (!rawStamp)
|
|
179
|
+
throw new Error('dist_stamp_path_missing');
|
|
180
|
+
const stamp = path.resolve(rawStamp);
|
|
181
|
+
const relative = path.relative(root, stamp);
|
|
182
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
183
|
+
throw new Error('dist_stamp_outside_package_root');
|
|
184
|
+
}
|
|
185
|
+
await fsp.access(stamp).catch(async () => {
|
|
186
|
+
await fsp.writeFile(stamp, `${JSON.stringify(stampLib.buildStampPayload(), null, 2)}\n`);
|
|
187
|
+
console.log('SKS build stamp: restored inside the installed package for update self-verification.');
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
console.log(`SKS build stamp: could not restore (${err?.message || err}); \`sks update\` self-verification may report dist_stamp missing.`);
|
|
182
192
|
}
|
|
183
193
|
}
|
|
184
194
|
async function runPostinstallProjectRetentionCleanup(root) {
|
|
@@ -212,9 +222,7 @@ async function runPostinstallProjectRetentionCleanup(root) {
|
|
|
212
222
|
}
|
|
213
223
|
}
|
|
214
224
|
async function reportPostinstallCodexLbAuth(snapshot = null) {
|
|
215
|
-
const preserveActiveCodexLb = snapshot?.selected === true && snapshot?.codex_lb_key_in_shared_auth === true;
|
|
216
225
|
const codexLbAuth = await ensureCodexLbAuthDuringInstall({
|
|
217
|
-
forceCodexLbApiKeyAuth: preserveActiveCodexLb,
|
|
218
226
|
...(snapshot?.secret_api_key ? { apiKey: snapshot.secret_api_key } : {}),
|
|
219
227
|
...(snapshot?.base_url ? { baseUrl: snapshot.base_url } : {})
|
|
220
228
|
});
|
|
@@ -230,25 +238,18 @@ async function reportPostinstallCodexLbAuth(snapshot = null) {
|
|
|
230
238
|
console.log('codex-lb auth: stored key missing. Run `sks codex-lb setup --host <domain> --api-key-stdin` to repair.');
|
|
231
239
|
else if (codexLbAuth.status === 'missing_base_url')
|
|
232
240
|
console.log('codex-lb auth: stored key has no recoverable base URL. Run `sks codex-lb reconfigure --host <domain> --api-key-stdin` once.');
|
|
241
|
+
else if (codexLbAuth.status === 'legacy_migration_required')
|
|
242
|
+
console.log('codex-lb legacy Desktop auth routing was left unchanged. Migrate explicitly with `sks codex-lb migrate-legacy-desktop --restart-app`.');
|
|
233
243
|
else if (codexLbAuth.status === 'not_configured')
|
|
234
|
-
console.log('codex-lb (optional multi-account load balancer): not configured —
|
|
244
|
+
console.log('codex-lb (optional multi-account load balancer): not configured — use `sks codex-lb setup`, then choose `use-desktop-full`, `use-cli`, or `disable` explicitly.');
|
|
235
245
|
else if (codexLbAuth.status && codexLbAuth.status !== 'not_configured')
|
|
236
246
|
console.log(`codex-lb auth: repair skipped (${codexLbAuth.status}${codexLbAuth.error ? `: ${codexLbAuth.error}` : ''}).`);
|
|
237
247
|
const reconcile = codexLbAuth.auth_reconcile;
|
|
238
|
-
if (reconcile?.status === '
|
|
239
|
-
console.log(
|
|
240
|
-
}
|
|
241
|
-
else if (reconcile?.status === 'oauth_restored') {
|
|
242
|
-
console.log(`codex-lb auth: restored ChatGPT OAuth from ${reconcile.backup_path ?? 'unknown'} and cleared codex-lb provider selection.`);
|
|
248
|
+
if (reconcile?.status === 'oauth_untouched') {
|
|
249
|
+
console.log('codex-lb auth: ChatGPT OAuth/shared Codex auth was left byte-for-byte unchanged.');
|
|
243
250
|
}
|
|
244
|
-
else if (reconcile?.status === '
|
|
245
|
-
console.log(
|
|
246
|
-
}
|
|
247
|
-
else if (reconcile?.status === 'backup_only') {
|
|
248
|
-
console.log(`codex-lb auth: detected ChatGPT OAuth tokens in auth.json. OAuth backup written to ${reconcile.backup_path ?? 'unknown'}; auth.json left untouched because SKS_CODEX_LB_NO_AUTH_RECONCILE=1.`);
|
|
249
|
-
}
|
|
250
|
-
else if (reconcile?.status === 'failed') {
|
|
251
|
-
console.log(`codex-lb auth: ChatGPT OAuth reconciliation could not complete (${reconcile.reason || 'unknown'}${reconcile.error ? `: ${reconcile.error}` : ''}). Run \`sks codex-lb repair\` to retry.`);
|
|
251
|
+
else if (reconcile?.status === 'legacy_migration_required') {
|
|
252
|
+
console.log('codex-lb auth: legacy shared-auth routing requires `sks codex-lb migrate-legacy-desktop --restart-app`; background install will not migrate it.');
|
|
252
253
|
}
|
|
253
254
|
if (codexLbAuth.base_url && codexLbAuth.codex_lb?.env_key_configured && canAskYesNo() && process.env.SKS_SKIP_CODEX_LB_KEY_PROMPT !== '1') {
|
|
254
255
|
const changeKey = (await askPostinstallQuestion('codex-lb key changed? Update now? [y/N] ')).trim();
|
|
@@ -294,23 +295,17 @@ function shouldAskPostinstallQuestion() {
|
|
|
294
295
|
export async function postinstallBootstrapDecision(root) {
|
|
295
296
|
if (process.env.SKS_POSTINSTALL_NO_BOOTSTRAP === '1')
|
|
296
297
|
return { run: false, reason: 'SKS_POSTINSTALL_NO_BOOTSTRAP=1' };
|
|
297
|
-
if (process.env.SKS_POSTINSTALL_BOOTSTRAP
|
|
298
|
-
return { run: false, reason: 'SKS_POSTINSTALL_BOOTSTRAP=
|
|
298
|
+
if (process.env.SKS_POSTINSTALL_BOOTSTRAP !== '1') {
|
|
299
|
+
return { run: false, reason: 'explicit opt-in required (SKS_POSTINSTALL_BOOTSTRAP=1)' };
|
|
300
|
+
}
|
|
299
301
|
const installRoot = path.resolve(root || process.cwd());
|
|
300
302
|
const candidate = await isProjectSetupCandidate(installRoot);
|
|
301
303
|
const target = candidate ? installRoot : globalSksRoot();
|
|
302
|
-
|
|
303
|
-
return { run: true, target, reason: 'forced by SKS_POSTINSTALL_BOOTSTRAP=1' };
|
|
304
|
-
if (process.env.npm_config_global === 'true' && candidate) {
|
|
305
|
-
return { run: true, target: globalSksRoot(), reason: 'global install: bootstrapping global SKS runtime only (run `sks setup` inside a project to initialize it)' };
|
|
306
|
-
}
|
|
307
|
-
if (candidate)
|
|
308
|
-
return { run: true, target, reason: 'auto-running sks setup --bootstrap --install-scope global --force' };
|
|
309
|
-
return { run: true, target, reason: 'no project marker found; auto-running global SKS runtime bootstrap' };
|
|
304
|
+
return { run: true, target, reason: 'forced by SKS_POSTINSTALL_BOOTSTRAP=1' };
|
|
310
305
|
}
|
|
311
|
-
async function runPostinstallBootstrap(root, bootstrap) {
|
|
306
|
+
async function runPostinstallBootstrap(root, bootstrap, selectedDecision) {
|
|
312
307
|
const previousCwd = process.cwd();
|
|
313
|
-
const decision = await postinstallBootstrapDecision(root);
|
|
308
|
+
const decision = selectedDecision || await postinstallBootstrapDecision(root);
|
|
314
309
|
const target = path.resolve(decision.target || root || previousCwd);
|
|
315
310
|
await ensureDir(target);
|
|
316
311
|
process.chdir(target);
|
|
@@ -321,147 +316,324 @@ async function runPostinstallBootstrap(root, bootstrap) {
|
|
|
321
316
|
process.chdir(previousCwd);
|
|
322
317
|
}
|
|
323
318
|
}
|
|
319
|
+
export async function configureCodexLbDesktopRouting(opts) {
|
|
320
|
+
const home = opts.home || process.env.HOME || os.homedir();
|
|
321
|
+
const configPath = opts.configPath || codexLbConfigPath(home);
|
|
322
|
+
const authPath = opts.authPath || codexAuthPath(home);
|
|
323
|
+
const mode = parseCodexLbDesktopMode(opts.mode);
|
|
324
|
+
const gatewayAuthTransport = parseCodexLbGatewayAuthTransport(opts.gatewayAuthTransport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT);
|
|
325
|
+
const authBefore = await captureCodexAuthSnapshot({ home, authPath });
|
|
326
|
+
const identityPlane = authBefore.mode === 'chatgpt_oauth' || authBefore.mode === 'mixed'
|
|
327
|
+
? 'chatgpt_oauth'
|
|
328
|
+
: 'unavailable';
|
|
329
|
+
const base = {
|
|
330
|
+
schema: 'sks.codex-lb-desktop-routing.v1',
|
|
331
|
+
mode,
|
|
332
|
+
gateway_auth_transport: gatewayAuthTransport,
|
|
333
|
+
identity_plane: identityPlane,
|
|
334
|
+
auth_mutated: false,
|
|
335
|
+
config_path: configPath,
|
|
336
|
+
auth_path: authPath,
|
|
337
|
+
auth_before: authBefore
|
|
338
|
+
};
|
|
339
|
+
if (mode === 'cli-provider') {
|
|
340
|
+
return {
|
|
341
|
+
...base,
|
|
342
|
+
ok: false,
|
|
343
|
+
status: 'invalid_desktop_routing_input',
|
|
344
|
+
routing_plane: 'unchanged',
|
|
345
|
+
oauth_preserved: true,
|
|
346
|
+
auth_after: authBefore,
|
|
347
|
+
blockers: ['cli_provider_is_not_a_desktop_routing_mode']
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
if (modeRequiresChatGptOAuth(mode) && identityPlane !== 'chatgpt_oauth') {
|
|
351
|
+
return {
|
|
352
|
+
...base,
|
|
353
|
+
ok: false,
|
|
354
|
+
status: 'desktop_oauth_required',
|
|
355
|
+
routing_plane: 'unchanged',
|
|
356
|
+
oauth_preserved: false,
|
|
357
|
+
auth_after: authBefore,
|
|
358
|
+
blockers: ['chatgpt_oauth_required_for_desktop']
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
if (mode === 'desktop-dual-auth-compat' && gatewayAuthTransport !== 'x-codex-lb-api-key') {
|
|
362
|
+
return {
|
|
363
|
+
...base,
|
|
364
|
+
ok: false,
|
|
365
|
+
status: 'desktop_gateway_auth_transport_unsupported',
|
|
366
|
+
routing_plane: 'unchanged',
|
|
367
|
+
oauth_preserved: true,
|
|
368
|
+
auth_after: authBefore,
|
|
369
|
+
blockers: ['desktop_compat_cannot_use_authorization_bearer_without_replacing_oauth']
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
if (mode === 'desktop-native-bridge' && (!opts.bridgeBaseUrl || !opts.remoteBaseUrl)) {
|
|
373
|
+
return {
|
|
374
|
+
...base,
|
|
375
|
+
ok: false,
|
|
376
|
+
status: 'invalid_desktop_routing_input',
|
|
377
|
+
routing_plane: 'unchanged',
|
|
378
|
+
oauth_preserved: true,
|
|
379
|
+
auth_after: authBefore,
|
|
380
|
+
blockers: [
|
|
381
|
+
...(!opts.bridgeBaseUrl ? ['missing_bridge_base_url'] : []),
|
|
382
|
+
...(!opts.remoteBaseUrl ? ['missing_remote_base_url'] : [])
|
|
383
|
+
]
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
if (mode === 'desktop-dual-auth-compat' && !opts.remoteBaseUrl) {
|
|
387
|
+
return {
|
|
388
|
+
...base,
|
|
389
|
+
ok: false,
|
|
390
|
+
status: 'invalid_desktop_routing_input',
|
|
391
|
+
routing_plane: 'unchanged',
|
|
392
|
+
oauth_preserved: true,
|
|
393
|
+
auth_after: authBefore,
|
|
394
|
+
blockers: ['missing_remote_base_url']
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const current = await readText(configPath, '');
|
|
398
|
+
let next;
|
|
399
|
+
try {
|
|
400
|
+
next = mode === 'desktop-native-bridge'
|
|
401
|
+
? upsertCodexLbNativeDesktopConfig(current, {
|
|
402
|
+
bridgeBaseUrl: String(opts.bridgeBaseUrl),
|
|
403
|
+
remoteBaseUrl: String(opts.remoteBaseUrl)
|
|
404
|
+
})
|
|
405
|
+
: mode === 'desktop-dual-auth-compat'
|
|
406
|
+
? upsertCodexLbCompatDesktopConfig(current, { remoteBaseUrl: String(opts.remoteBaseUrl) })
|
|
407
|
+
: removeCodexLbManagedDesktopConfig(current);
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
return {
|
|
411
|
+
...base,
|
|
412
|
+
ok: false,
|
|
413
|
+
status: 'failed',
|
|
414
|
+
routing_plane: 'unchanged',
|
|
415
|
+
oauth_preserved: true,
|
|
416
|
+
auth_after: authBefore,
|
|
417
|
+
blockers: ['desktop_config_conflict'],
|
|
418
|
+
error: error instanceof Error ? error.message : String(error)
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
const write = await safeWriteCodexConfigToml(configPath, current, next, 'codex-lb-desktop-routing', { verifyUnchangedBeforeWrite: true });
|
|
422
|
+
if (!write.ok) {
|
|
423
|
+
return {
|
|
424
|
+
...base,
|
|
425
|
+
ok: false,
|
|
426
|
+
status: 'failed',
|
|
427
|
+
routing_plane: 'unchanged',
|
|
428
|
+
oauth_preserved: true,
|
|
429
|
+
auth_after: authBefore,
|
|
430
|
+
backup_path: write.backup_path,
|
|
431
|
+
blockers: ['desktop_config_write_failed'],
|
|
432
|
+
error: write.status
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
const authAfter = await captureCodexAuthSnapshot({ home, authPath });
|
|
436
|
+
try {
|
|
437
|
+
await assertDesktopAuthUnchangedBySks(authBefore, authAfter);
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
const writtenText = await readText(configPath, '');
|
|
441
|
+
const rollbackWrite = await safeWriteCodexConfigToml(configPath, writtenText, current, 'codex-lb-desktop-routing-auth-invariant-rollback', { verifyUnchangedBeforeWrite: true });
|
|
442
|
+
return {
|
|
443
|
+
...base,
|
|
444
|
+
ok: false,
|
|
445
|
+
status: 'failed',
|
|
446
|
+
routing_plane: 'unchanged',
|
|
447
|
+
oauth_preserved: false,
|
|
448
|
+
auth_after: authAfter,
|
|
449
|
+
backup_path: write.backup_path,
|
|
450
|
+
rollback: {
|
|
451
|
+
ok: rollbackWrite.ok,
|
|
452
|
+
status: rollbackWrite.status,
|
|
453
|
+
config_restored: rollbackWrite.ok
|
|
454
|
+
},
|
|
455
|
+
blockers: ['desktop_auth_byte_invariant_failed', ...(rollbackWrite.ok ? [] : ['desktop_config_rollback_failed'])],
|
|
456
|
+
error: error instanceof Error ? error.message : String(error)
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
const routingPlane = mode === 'desktop-native-bridge'
|
|
460
|
+
? 'desktop_native_bridge'
|
|
461
|
+
: mode === 'desktop-dual-auth-compat'
|
|
462
|
+
? 'desktop_compat_provider'
|
|
463
|
+
: 'disabled';
|
|
464
|
+
return {
|
|
465
|
+
...base,
|
|
466
|
+
ok: true,
|
|
467
|
+
status: mode === 'disabled'
|
|
468
|
+
? 'desktop_routing_disabled'
|
|
469
|
+
: write.changed === true
|
|
470
|
+
? 'desktop_routing_configured'
|
|
471
|
+
: 'desktop_routing_present',
|
|
472
|
+
routing_plane: routingPlane,
|
|
473
|
+
oauth_preserved: true,
|
|
474
|
+
auth_after: authAfter,
|
|
475
|
+
backup_path: write.backup_path,
|
|
476
|
+
blockers: []
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
export async function configureCodexLbCliProvider(opts) {
|
|
480
|
+
const home = opts.home || process.env.HOME || os.homedir();
|
|
481
|
+
const configPath = opts.configPath || codexLbConfigPath(home);
|
|
482
|
+
const authPath = opts.authPath || codexAuthPath(home);
|
|
483
|
+
const authBefore = await captureCodexAuthSnapshot({ home, authPath });
|
|
484
|
+
const current = await readText(configPath, '');
|
|
485
|
+
const next = upsertCodexLbCliProviderConfig(current, {
|
|
486
|
+
remoteBaseUrl: opts.remoteBaseUrl,
|
|
487
|
+
...(opts.selectGlobally === undefined ? {} : { selectGlobally: opts.selectGlobally })
|
|
488
|
+
});
|
|
489
|
+
const write = await safeWriteCodexConfigToml(configPath, current, next, 'codex-lb-cli-provider', { verifyUnchangedBeforeWrite: true });
|
|
490
|
+
const authAfter = await captureCodexAuthSnapshot({ home, authPath });
|
|
491
|
+
try {
|
|
492
|
+
await assertDesktopAuthUnchangedBySks(authBefore, authAfter);
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
return {
|
|
496
|
+
schema: 'sks.codex-lb-cli-provider.v1',
|
|
497
|
+
ok: false,
|
|
498
|
+
status: 'auth_changed_during_cli_provider_write',
|
|
499
|
+
mode: 'cli-provider',
|
|
500
|
+
identity_plane: 'unchanged',
|
|
501
|
+
routing_plane: 'cli_provider',
|
|
502
|
+
oauth_preserved: false,
|
|
503
|
+
auth_mutated: false,
|
|
504
|
+
config_path: configPath,
|
|
505
|
+
auth_path: authPath,
|
|
506
|
+
auth_before: authBefore,
|
|
507
|
+
auth_after: authAfter,
|
|
508
|
+
blockers: ['shared_auth_byte_invariant_failed'],
|
|
509
|
+
backup_path: write.backup_path
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
schema: 'sks.codex-lb-cli-provider.v1',
|
|
514
|
+
ok: write.ok,
|
|
515
|
+
status: write.ok ? (write.changed ? 'configured' : 'present') : write.status,
|
|
516
|
+
mode: 'cli-provider',
|
|
517
|
+
identity_plane: 'unchanged',
|
|
518
|
+
routing_plane: 'cli_provider',
|
|
519
|
+
oauth_preserved: true,
|
|
520
|
+
auth_mutated: false,
|
|
521
|
+
config_path: configPath,
|
|
522
|
+
auth_path: authPath,
|
|
523
|
+
auth_before: authBefore,
|
|
524
|
+
auth_after: authAfter,
|
|
525
|
+
blockers: write.ok ? [] : ['cli_provider_config_write_failed'],
|
|
526
|
+
backup_path: write.backup_path
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
function hasTopLevelMarker(text, marker) {
|
|
530
|
+
const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
|
|
531
|
+
return topLevel.split(/\r?\n/).some((line) => line.trim() === marker);
|
|
532
|
+
}
|
|
533
|
+
function managedCodexLbDesktopMode(text) {
|
|
534
|
+
if (hasTopLevelMarker(text, CODEX_LB_DESKTOP_BRIDGE_MARKER))
|
|
535
|
+
return 'desktop-native-bridge';
|
|
536
|
+
if (hasTopLevelMarker(text, CODEX_LB_DESKTOP_COMPAT_MARKER))
|
|
537
|
+
return 'desktop-dual-auth-compat';
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
function codexLbRoutingPlane(mode) {
|
|
541
|
+
if (mode === 'desktop-native-bridge')
|
|
542
|
+
return 'desktop_native_bridge';
|
|
543
|
+
if (mode === 'desktop-dual-auth-compat')
|
|
544
|
+
return 'desktop_compat_provider';
|
|
545
|
+
return 'cli_provider';
|
|
546
|
+
}
|
|
324
547
|
async function capturePostinstallCodexLbConfigSnapshot(home = process.env.HOME || os.homedir()) {
|
|
325
548
|
const configPath = codexLbConfigPath(home);
|
|
326
549
|
const envPath = codexLbEnvPath(home);
|
|
327
550
|
const authPath = codexAuthPath(home);
|
|
328
551
|
const config = await readText(configPath, '');
|
|
329
552
|
const envText = await readText(envPath, '');
|
|
330
|
-
const
|
|
331
|
-
const authText = authExisted ? await readText(authPath, '') : '';
|
|
553
|
+
const auth = await captureCodexAuthSnapshot({ home, authPath });
|
|
332
554
|
const envLoad = await loadCodexLbEnv({ home, envPath });
|
|
333
555
|
const envKey = envLoad.secret_api_key || '';
|
|
334
556
|
const providerConfigured = /\[model_providers\.codex-lb\]/.test(config);
|
|
335
557
|
const baseUrl = envLoad.base_url || codexLbProviderBaseUrl(config) || parseCodexLbEnvBaseUrl(envText);
|
|
336
|
-
|
|
558
|
+
const desktopMode = managedCodexLbDesktopMode(config);
|
|
559
|
+
const bridgeBaseUrl = desktopMode === 'desktop-native-bridge'
|
|
560
|
+
? topLevelTomlString(config, 'openai_base_url')
|
|
561
|
+
: '';
|
|
562
|
+
if (!envKey && !providerConfigured && !baseUrl && !desktopMode)
|
|
337
563
|
return null;
|
|
338
564
|
return {
|
|
339
565
|
config_path: configPath,
|
|
340
566
|
env_path: envPath,
|
|
341
567
|
auth_path: authPath,
|
|
342
568
|
base_url: baseUrl ? normalizeCodexLbBaseUrl(baseUrl) : null,
|
|
569
|
+
bridge_base_url: bridgeBaseUrl || null,
|
|
570
|
+
desktop_mode: desktopMode,
|
|
343
571
|
selected: hasTopLevelCodexLbSelected(config),
|
|
344
|
-
auth_mode:
|
|
345
|
-
|
|
572
|
+
auth_mode: auth.mode,
|
|
573
|
+
auth_sha256: auth.sha256,
|
|
346
574
|
credential_source: envLoad.source,
|
|
347
575
|
credential_fingerprint: envLoad.api_key.fingerprint,
|
|
348
576
|
credential_binding: envLoad.credential_binding,
|
|
349
|
-
secret_api_key: envKey || null
|
|
350
|
-
auth_existed: authExisted,
|
|
351
|
-
auth_text: authText
|
|
577
|
+
secret_api_key: envKey || null
|
|
352
578
|
};
|
|
353
579
|
}
|
|
354
580
|
async function restorePostinstallCodexLbConfigSnapshot(snapshot) {
|
|
355
581
|
if (!snapshot)
|
|
356
582
|
return { status: 'skipped', reason: 'no_snapshot' };
|
|
357
583
|
let configRestored = false;
|
|
584
|
+
let configStatus = 'present';
|
|
358
585
|
if (snapshot.base_url) {
|
|
359
586
|
const current = await readText(snapshot.config_path, '');
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
587
|
+
let next = current;
|
|
588
|
+
try {
|
|
589
|
+
if (snapshot.desktop_mode === 'desktop-native-bridge' && snapshot.bridge_base_url) {
|
|
590
|
+
next = upsertCodexLbNativeDesktopConfig(current, {
|
|
591
|
+
bridgeBaseUrl: snapshot.bridge_base_url,
|
|
592
|
+
remoteBaseUrl: snapshot.base_url
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
else if (snapshot.desktop_mode === 'desktop-dual-auth-compat') {
|
|
596
|
+
next = upsertCodexLbCompatDesktopConfig(current, { remoteBaseUrl: snapshot.base_url });
|
|
597
|
+
}
|
|
598
|
+
else if (snapshot.selected !== true) {
|
|
599
|
+
next = upsertCodexLbCliProviderConfig(current, {
|
|
600
|
+
remoteBaseUrl: snapshot.base_url,
|
|
601
|
+
selectGlobally: false
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
configStatus = 'legacy_migration_required';
|
|
606
|
+
}
|
|
365
607
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
await
|
|
379
|
-
|
|
608
|
+
catch (error) {
|
|
609
|
+
return {
|
|
610
|
+
status: 'failed',
|
|
611
|
+
reason: 'managed_routing_restore_conflict',
|
|
612
|
+
config_path: snapshot.config_path,
|
|
613
|
+
auth_path: snapshot.auth_path,
|
|
614
|
+
config_restored: false,
|
|
615
|
+
auth_restored: false,
|
|
616
|
+
error: error instanceof Error ? error.message : String(error)
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
if (configStatus !== 'legacy_migration_required' && ensureTrailingNewline(next) !== ensureTrailingNewline(current)) {
|
|
620
|
+
const safeWrite = await safeWriteCodexConfigToml(snapshot.config_path, current, next, 'codex-lb-safe-routing-restore', { verifyUnchangedBeforeWrite: true });
|
|
621
|
+
configRestored = safeWrite.ok && safeWrite.changed === true;
|
|
622
|
+
configStatus = safeWrite.ok ? (configRestored ? 'restored' : 'present') : safeWrite.status;
|
|
380
623
|
}
|
|
381
624
|
}
|
|
625
|
+
const authAfter = await captureCodexAuthSnapshot({
|
|
626
|
+
home: path.dirname(path.dirname(snapshot.auth_path)),
|
|
627
|
+
authPath: snapshot.auth_path
|
|
628
|
+
});
|
|
629
|
+
const authUnchanged = snapshot.auth_sha256 === authAfter.sha256;
|
|
382
630
|
return {
|
|
383
|
-
status:
|
|
631
|
+
status: configStatus,
|
|
384
632
|
config_path: snapshot.config_path,
|
|
385
633
|
auth_path: snapshot.auth_path,
|
|
386
634
|
config_restored: configRestored,
|
|
387
|
-
auth_restored:
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
async function ensureCodexLbToolCatalogSelection(input, opts = {}) {
|
|
391
|
-
const codexHome = opts.codexHome || path.join(input.home, '.codex');
|
|
392
|
-
const catalogPath = codexLbToolCatalogPath(codexHome);
|
|
393
|
-
const current = await readText(input.configPath, '');
|
|
394
|
-
const selected = hasTopLevelCodexLbSelected(current);
|
|
395
|
-
const canBindCatalog = selected || opts.allowUnselectedCatalogWrite === true;
|
|
396
|
-
const existingCatalogPath = topLevelTomlString(current, 'model_catalog_json');
|
|
397
|
-
const { isSksManagedCatalogPath, resolveCatalogPath } = await import('../core/codex-app/multi-provider-router-support.js');
|
|
398
|
-
const existingCatalogResolved = existingCatalogPath
|
|
399
|
-
? resolveCatalogPath(existingCatalogPath, { home: input.home, configPath: input.configPath })
|
|
400
|
-
: null;
|
|
401
|
-
const existingCatalogReplaceable = !existingCatalogResolved
|
|
402
|
-
|| (isSksManagedCatalogPath(existingCatalogResolved, { home: input.home })
|
|
403
|
-
&& (selected || opts.willSelectProvider === true));
|
|
404
|
-
if (canBindCatalog && existingCatalogResolved && existingCatalogResolved !== path.resolve(catalogPath) && !existingCatalogReplaceable) {
|
|
405
|
-
return {
|
|
406
|
-
schema: 'sks.codex-lb-tool-catalog-selection.v1',
|
|
407
|
-
ok: false,
|
|
408
|
-
required: true,
|
|
409
|
-
status: 'user_catalog_conflict',
|
|
410
|
-
path: catalogPath,
|
|
411
|
-
configured_path: existingCatalogPath,
|
|
412
|
-
config_changed: false,
|
|
413
|
-
selected,
|
|
414
|
-
blockers: ['codex_lb_user_model_catalog_conflict']
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
let hostname = '';
|
|
418
|
-
try {
|
|
419
|
-
hostname = new URL(input.baseUrl).hostname.toLowerCase();
|
|
420
|
-
}
|
|
421
|
-
catch { }
|
|
422
|
-
const reservedFixtureHost = /(?:^|\.)(?:test|invalid|example)$/.test(hostname);
|
|
423
|
-
if (reservedFixtureHost && typeof opts.toolCatalogFetch !== 'function') {
|
|
424
|
-
return {
|
|
425
|
-
schema: 'sks.codex-lb-tool-catalog-selection.v1',
|
|
426
|
-
ok: true,
|
|
427
|
-
required: false,
|
|
428
|
-
status: 'skipped_reserved_host',
|
|
429
|
-
path: catalogPath,
|
|
430
|
-
config_changed: false,
|
|
431
|
-
selected,
|
|
432
|
-
blockers: []
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
const catalog = await ensureCodexLbToolCatalog({
|
|
436
|
-
codexHome,
|
|
437
|
-
baseUrl: input.baseUrl,
|
|
438
|
-
apiKey: input.apiKey,
|
|
439
|
-
...(typeof opts.toolCatalogFetch === 'function' ? { fetchImpl: opts.toolCatalogFetch } : {}),
|
|
440
|
-
timeoutMs: Number(opts.toolCatalogTimeoutMs || 5000),
|
|
441
|
-
force: opts.forceToolCatalog === true
|
|
442
|
-
});
|
|
443
|
-
if (!catalog.ok || !canBindCatalog) {
|
|
444
|
-
return {
|
|
445
|
-
...catalog,
|
|
446
|
-
schema: 'sks.codex-lb-tool-catalog-selection.v1',
|
|
447
|
-
config_changed: false,
|
|
448
|
-
selected
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
const next = ensureTrailingNewline(upsertTopLevelTomlString(current, 'model_catalog_json', catalog.path));
|
|
452
|
-
if (next === ensureTrailingNewline(current)) {
|
|
453
|
-
return { ...catalog, schema: 'sks.codex-lb-tool-catalog-selection.v1', config_changed: false, selected };
|
|
454
|
-
}
|
|
455
|
-
const safeWrite = await safeWriteCodexConfigToml(input.configPath, current, next, 'codex-lb-tool-catalog');
|
|
456
|
-
return {
|
|
457
|
-
...catalog,
|
|
458
|
-
schema: 'sks.codex-lb-tool-catalog-selection.v1',
|
|
459
|
-
ok: catalog.ok && safeWrite.ok,
|
|
460
|
-
status: safeWrite.ok ? catalog.status : safeWrite.status,
|
|
461
|
-
config_changed: safeWrite.ok && safeWrite.changed === true,
|
|
462
|
-
backup_path: safeWrite.backup_path,
|
|
463
|
-
selected,
|
|
464
|
-
blockers: safeWrite.ok ? catalog.blockers : [...new Set([...(catalog.blockers || []), 'codex_lb_tool_catalog_config_write_failed'])]
|
|
635
|
+
auth_restored: false,
|
|
636
|
+
auth_unchanged: authUnchanged
|
|
465
637
|
};
|
|
466
638
|
}
|
|
467
639
|
export async function configureCodexLb(opts = {}) {
|
|
@@ -471,7 +643,12 @@ export async function configureCodexLb(opts = {}) {
|
|
|
471
643
|
const rawHost = String(opts.host || opts.baseUrl || '');
|
|
472
644
|
const baseUrl = normalizeCodexLbBaseUrl(rawHost);
|
|
473
645
|
const apiKey = String(opts.apiKey || '').trim();
|
|
474
|
-
const
|
|
646
|
+
const initialConfig = await readText(configPath, '');
|
|
647
|
+
const preservedDesktopMode = managedCodexLbDesktopMode(initialConfig);
|
|
648
|
+
const preservedLegacySelection = hasTopLevelCodexLbSelected(initialConfig) && !preservedDesktopMode;
|
|
649
|
+
const requestedDesktopMode = parseCodexLbDesktopMode(opts.desktopMode || 'cli-provider');
|
|
650
|
+
const desktopMode = preservedDesktopMode || 'cli-provider';
|
|
651
|
+
const useDefaultProvider = desktopMode === 'desktop-dual-auth-compat' || preservedLegacySelection;
|
|
475
652
|
const writeEnvFile = opts.writeEnvFile !== false;
|
|
476
653
|
const storeKeychain = opts.storeKeychain === true || opts.keychain === true;
|
|
477
654
|
const syncLaunchctl = opts.syncLaunchctl === true || opts.syncLaunchEnv === true;
|
|
@@ -479,7 +656,8 @@ export async function configureCodexLb(opts = {}) {
|
|
|
479
656
|
const setupAnswers = {
|
|
480
657
|
host_or_base_url: rawHost,
|
|
481
658
|
api_key_source: opts.apiKeySource || 'stdin',
|
|
482
|
-
|
|
659
|
+
desktop_mode: 'cli-provider',
|
|
660
|
+
gateway_auth_transport: (opts.gatewayAuthTransport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT),
|
|
483
661
|
write_env_file: writeEnvFile,
|
|
484
662
|
store_keychain: storeKeychain,
|
|
485
663
|
sync_launchctl: syncLaunchctl,
|
|
@@ -526,16 +704,63 @@ export async function configureCodexLb(opts = {}) {
|
|
|
526
704
|
? ['codex-lb base URL uses http outside localhost; prefer https or pass an explicit allow flag in the calling surface.']
|
|
527
705
|
: [];
|
|
528
706
|
const beforeState = await captureCodexLbSetupWriteState({ home, configPath, envPath, shellProfile });
|
|
707
|
+
const authBefore = await captureCodexAuthSnapshot({ home, authPath: opts.authPath || codexAuthPath(home) });
|
|
529
708
|
const appliedActions = [];
|
|
530
709
|
await ensureDir(path.dirname(configPath));
|
|
531
710
|
const current = await readText(configPath, '');
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
711
|
+
let providerOnly = current;
|
|
712
|
+
try {
|
|
713
|
+
if (desktopMode === 'desktop-native-bridge') {
|
|
714
|
+
const bridgeBaseUrl = topLevelTomlString(current, 'openai_base_url');
|
|
715
|
+
if (!bridgeBaseUrl) {
|
|
716
|
+
return {
|
|
717
|
+
ok: false,
|
|
718
|
+
status: 'managed_desktop_bridge_missing_base_url',
|
|
719
|
+
mode: desktopMode,
|
|
720
|
+
config_path: configPath,
|
|
721
|
+
env_path: envPath
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
providerOnly = upsertCodexLbNativeDesktopConfig(current, {
|
|
725
|
+
bridgeBaseUrl,
|
|
726
|
+
remoteBaseUrl: baseUrl
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
else if (desktopMode === 'desktop-dual-auth-compat') {
|
|
730
|
+
providerOnly = upsertCodexLbCompatDesktopConfig(current, { remoteBaseUrl: baseUrl });
|
|
731
|
+
}
|
|
732
|
+
else if (!preservedLegacySelection) {
|
|
733
|
+
providerOnly = upsertCodexLbCliProviderConfig(current, {
|
|
734
|
+
remoteBaseUrl: baseUrl,
|
|
735
|
+
selectGlobally: false
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
catch (error) {
|
|
740
|
+
return {
|
|
741
|
+
ok: false,
|
|
742
|
+
status: 'managed_desktop_routing_conflict',
|
|
743
|
+
mode: desktopMode,
|
|
744
|
+
config_path: configPath,
|
|
745
|
+
env_path: envPath,
|
|
746
|
+
error: error instanceof Error ? error.message : String(error)
|
|
747
|
+
};
|
|
748
|
+
}
|
|
535
749
|
const safeWrite = await safeWriteCodexConfigToml(configPath, current, providerOnly, 'codex-lb');
|
|
536
750
|
if (!safeWrite.ok)
|
|
537
751
|
return { ok: false, status: safeWrite.status, config_path: configPath, env_path: envPath, backup_path: safeWrite.backup_path };
|
|
538
|
-
appliedActions.push({
|
|
752
|
+
appliedActions.push({
|
|
753
|
+
type: preservedLegacySelection
|
|
754
|
+
? 'preserve_legacy_desktop_routing'
|
|
755
|
+
: desktopMode === 'desktop-native-bridge'
|
|
756
|
+
? 'preserve_desktop_native_bridge'
|
|
757
|
+
: desktopMode === 'desktop-dual-auth-compat'
|
|
758
|
+
? 'preserve_desktop_compat_provider'
|
|
759
|
+
: 'write_cli_provider',
|
|
760
|
+
target: configPath,
|
|
761
|
+
ok: true,
|
|
762
|
+
backup_path: safeWrite.backup_path
|
|
763
|
+
});
|
|
539
764
|
if (writeEnvFile) {
|
|
540
765
|
await writeTextAtomic(envPath, `export CODEX_LB_BASE_URL=${shellSingleQuote(baseUrl)}\nexport CODEX_LB_API_KEY=${shellSingleQuote(apiKey)}\n`, { mode: 0o600 });
|
|
541
766
|
await fsp.chmod(envPath, 0o600).catch(() => { });
|
|
@@ -543,47 +768,20 @@ export async function configureCodexLb(opts = {}) {
|
|
|
543
768
|
}
|
|
544
769
|
process.env.CODEX_LB_BASE_URL = baseUrl;
|
|
545
770
|
process.env.CODEX_LB_API_KEY = apiKey;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
return {
|
|
561
|
-
ok: false,
|
|
562
|
-
status: selectWrite.status || 'provider_selection_failed',
|
|
563
|
-
config_path: configPath,
|
|
564
|
-
env_path: envPath,
|
|
565
|
-
backup_path: selectWrite.backup_path,
|
|
566
|
-
tool_catalog: toolCatalog,
|
|
567
|
-
tool_output_recovery: toolOutputRecovery,
|
|
568
|
-
error: 'codex_lb_provider_selection_write_failed'
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
providerSelected = true;
|
|
572
|
-
appliedActions.push({ type: 'select_default_provider', target: configPath, ok: true, backup_path: selectWrite.backup_path });
|
|
573
|
-
toolCatalog = await ensureCodexLbToolCatalogSelection({ home, configPath, baseUrl, apiKey }, opts);
|
|
574
|
-
if (toolCatalog.config_changed) {
|
|
575
|
-
appliedActions.push({ type: 'write_model_tool_catalog', target: toolCatalog.path, ok: toolCatalog.ok === true, status: toolCatalog.status });
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
else if (useDefaultProvider) {
|
|
579
|
-
appliedActions.push({
|
|
580
|
-
type: 'select_default_provider',
|
|
581
|
-
target: configPath,
|
|
582
|
-
ok: false,
|
|
583
|
-
status: 'deferred_until_tool_catalog_ready',
|
|
584
|
-
blockers: toolCatalog.blockers || ['codex_lb_gpt56_tool_catalog_not_ready']
|
|
585
|
-
});
|
|
586
|
-
}
|
|
771
|
+
const toolCatalog = {
|
|
772
|
+
schema: 'sks.codex-lb-tool-catalog-selection.v1',
|
|
773
|
+
ok: true,
|
|
774
|
+
required: false,
|
|
775
|
+
status: desktopMode === 'desktop-native-bridge'
|
|
776
|
+
? 'not_bound_for_desktop_native'
|
|
777
|
+
: desktopMode === 'desktop-dual-auth-compat'
|
|
778
|
+
? 'not_bound_for_desktop_compat'
|
|
779
|
+
: 'not_bound_for_cli_provider',
|
|
780
|
+
path: codexLbToolCatalogPath(path.join(home, '.codex')),
|
|
781
|
+
config_changed: false,
|
|
782
|
+
selected: desktopMode === 'desktop-dual-auth-compat',
|
|
783
|
+
blockers: []
|
|
784
|
+
};
|
|
587
785
|
const keyFingerprint = await sha256Text(apiKey);
|
|
588
786
|
const metadataPath = opts.metadataPath || codexLbMetadataPath(home);
|
|
589
787
|
await writeTextAtomic(metadataPath, `${JSON.stringify({
|
|
@@ -591,6 +789,9 @@ export async function configureCodexLb(opts = {}) {
|
|
|
591
789
|
base_url: baseUrl,
|
|
592
790
|
updated_at: new Date().toISOString(),
|
|
593
791
|
source: opts.source || 'setup',
|
|
792
|
+
desktop_mode: desktopMode,
|
|
793
|
+
requested_desktop_mode: requestedDesktopMode,
|
|
794
|
+
gateway_auth_transport: opts.gatewayAuthTransport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT,
|
|
594
795
|
api_key: { redacted: true, sha256: keyFingerprint }
|
|
595
796
|
}, null, 2)}\n`);
|
|
596
797
|
await fsp.chmod(metadataPath, 0o600).catch(() => { });
|
|
@@ -598,25 +799,54 @@ export async function configureCodexLb(opts = {}) {
|
|
|
598
799
|
const keychain = storeKeychain ? await writeCodexLbKeychain(apiKey, opts).catch((err) => ({ ok: false, status: 'keychain_store_failed', error: err.message })) : { ok: false, status: 'skipped' };
|
|
599
800
|
if (storeKeychain)
|
|
600
801
|
appliedActions.push({ type: 'store_keychain', target: 'macOS Keychain service sks-codex-lb', ok: keychain.ok === true, status: keychain.status });
|
|
601
|
-
const
|
|
602
|
-
|
|
802
|
+
const { syncDesktopCenterLaunchCredentials } = await import('../core/codex-lb/desktop-center-credentials.js');
|
|
803
|
+
const centerCredentials = await syncDesktopCenterLaunchCredentials({
|
|
804
|
+
mode: desktopMode,
|
|
805
|
+
home,
|
|
806
|
+
loadedEnv: await loadCodexLbEnv({ home, processEnv: {}, envPath, metadataPath })
|
|
807
|
+
});
|
|
808
|
+
appliedActions.push({
|
|
809
|
+
type: 'sync_center_desktop_credentials',
|
|
810
|
+
target: 'launchctl from official sks-codex-lb store',
|
|
811
|
+
ok: centerCredentials.ok === true,
|
|
812
|
+
status: centerCredentials.status
|
|
813
|
+
});
|
|
814
|
+
const codexEnvironment = await syncCodexLbProviderEnvironment({ env_path: envPath, base_url: baseUrl }, {
|
|
815
|
+
...opts,
|
|
816
|
+
home,
|
|
817
|
+
apiKey,
|
|
818
|
+
baseUrl,
|
|
819
|
+
syncLaunchEnv: syncLaunchctl && desktopMode !== 'desktop-dual-auth-compat'
|
|
820
|
+
});
|
|
821
|
+
if (syncLaunchctl && desktopMode !== 'desktop-dual-auth-compat') {
|
|
603
822
|
appliedActions.push({ type: 'sync_launchctl', target: 'macOS launchctl user environment (base URL only; API-key env removed)', ok: codexEnvironment.ok === true, status: codexEnvironment.status });
|
|
823
|
+
}
|
|
604
824
|
const shellProfileResult = await installCodexLbShellProfileSnippet({ home, envPath, shellProfile }).catch((err) => ({ ok: false, status: 'failed', files: [], error: err.message }));
|
|
605
825
|
if (shellProfile !== 'skip')
|
|
606
826
|
appliedActions.push({ type: 'install_shell_profile_snippet', target: shellProfileResult.files?.join(', ') || shellProfile, ok: shellProfileResult.ok === true, status: shellProfileResult.status });
|
|
607
827
|
const codexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
828
|
+
const authAfter = await captureCodexAuthSnapshot({ home, authPath: opts.authPath || codexAuthPath(home) });
|
|
829
|
+
let authInvariantError = null;
|
|
830
|
+
try {
|
|
831
|
+
await assertDesktopAuthUnchangedBySks(authBefore, authAfter);
|
|
832
|
+
}
|
|
833
|
+
catch (error) {
|
|
834
|
+
authInvariantError = error instanceof Error ? error.message : String(error);
|
|
835
|
+
}
|
|
836
|
+
const authReconcile = {
|
|
837
|
+
status: authInvariantError ? 'failed' : 'oauth_untouched',
|
|
838
|
+
reason: authInvariantError || 'credential_setup_does_not_mutate_desktop_auth',
|
|
839
|
+
auth_path: authBefore.path,
|
|
840
|
+
...(authInvariantError ? { error: authInvariantError } : {})
|
|
841
|
+
};
|
|
842
|
+
const codexLogin = {
|
|
843
|
+
ok: !authInvariantError,
|
|
844
|
+
status: authInvariantError ? 'desktop_auth_byte_invariant_failed' : 'not_required',
|
|
845
|
+
reason: authInvariantError || 'cli_gateway_key_is_separate_from_chatgpt_oauth',
|
|
846
|
+
error: authInvariantError
|
|
847
|
+
};
|
|
618
848
|
const finalCodexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
619
|
-
const ok = Boolean(codexEnvironment.ok && codexLogin.ok);
|
|
849
|
+
const ok = Boolean(codexEnvironment.ok && codexLogin.ok && centerCredentials.ok !== false);
|
|
620
850
|
const afterState = await captureCodexLbSetupWriteState({ home, configPath, envPath, shellProfile });
|
|
621
851
|
const drift = [
|
|
622
852
|
...detectCodexLbSetupDrift({
|
|
@@ -633,6 +863,7 @@ export async function configureCodexLb(opts = {}) {
|
|
|
633
863
|
beforeState,
|
|
634
864
|
afterState
|
|
635
865
|
}),
|
|
866
|
+
...(authInvariantError ? ['desktop_auth_byte_invariant_failed'] : []),
|
|
636
867
|
...(toolCatalog.required !== false && toolCatalog.ok !== true ? ['codex_lb_gpt56_tool_catalog_not_ready'] : [])
|
|
637
868
|
];
|
|
638
869
|
const appliedPersistenceModes = appliedCodexLbPersistenceModes({
|
|
@@ -654,20 +885,30 @@ export async function configureCodexLb(opts = {}) {
|
|
|
654
885
|
const warnings = [
|
|
655
886
|
...insecureLocalWarning,
|
|
656
887
|
...persistence.warnings,
|
|
888
|
+
...(preservedLegacySelection ? ['legacy_desktop_auth_routing_requires_explicit_migrate_legacy_desktop'] : []),
|
|
657
889
|
...(toolCatalog.required !== false && toolCatalog.ok !== true ? ['codex_lb_gpt56_tool_catalog_not_ready'] : [])
|
|
658
890
|
];
|
|
659
|
-
const failureStatus =
|
|
660
|
-
? (
|
|
661
|
-
:
|
|
662
|
-
? (
|
|
663
|
-
:
|
|
891
|
+
const failureStatus = centerCredentials.ok !== true
|
|
892
|
+
? (centerCredentials.status || 'center_desktop_credentials_failed')
|
|
893
|
+
: codexEnvironment.ok !== true
|
|
894
|
+
? (codexEnvironment.status || 'environment_failed')
|
|
895
|
+
: codexLogin.ok !== true
|
|
896
|
+
? (codexLogin.status || 'login_failed')
|
|
897
|
+
: null;
|
|
664
898
|
return {
|
|
665
899
|
ok: ok && drift.length === 0,
|
|
666
900
|
status: ok && drift.length === 0 ? 'configured' : failureStatus || (drift.length ? 'setup_choice_drift' : 'configuration_failed'),
|
|
901
|
+
mode: desktopMode,
|
|
902
|
+
identity_plane: 'unchanged',
|
|
903
|
+
routing_plane: codexLbRoutingPlane(desktopMode),
|
|
904
|
+
gateway_auth_transport: opts.gatewayAuthTransport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT,
|
|
905
|
+
oauth_preserved: !authInvariantError,
|
|
906
|
+
auth_mutated: false,
|
|
667
907
|
plan: plan,
|
|
668
908
|
applied_actions: appliedActions,
|
|
669
909
|
drift,
|
|
670
910
|
persistence,
|
|
911
|
+
center_credentials: centerCredentials,
|
|
671
912
|
config_path: configPath,
|
|
672
913
|
env_path: envPath,
|
|
673
914
|
metadata_path: metadataPath,
|
|
@@ -709,19 +950,44 @@ export async function codexLbStatus(opts = {}) {
|
|
|
709
950
|
const providerRequiresOpenAiAuth = codexLbProviderRequiresOpenAiAuth(config);
|
|
710
951
|
const providerOpenAiAuthDisabled = codexLbProviderOpenAiAuthDisabled(config);
|
|
711
952
|
const providerEnvKey = codexLbProviderEnvKey(config);
|
|
953
|
+
const providerHasSeparateGatewayAuth = codexLbProviderHasSeparateGatewayAuth(config);
|
|
954
|
+
const desktopMode = managedCodexLbDesktopMode(config);
|
|
955
|
+
const bridgeBaseUrl = desktopMode === 'desktop-native-bridge'
|
|
956
|
+
? topLevelTomlString(config, 'openai_base_url')
|
|
957
|
+
: '';
|
|
712
958
|
const sharedOpenAiRouting = codexLbSharedOpenAiRoutingState(config, baseUrl);
|
|
713
959
|
const codexLbKeyInSharedAuth = Boolean(authApiKey && envLoad.secret_api_key && authApiKey === envLoad.secret_api_key);
|
|
714
960
|
const sharedOpenAiRoutingSafe = !codexLbKeyInSharedAuth || sharedOpenAiRouting.status === 'matched';
|
|
715
|
-
const
|
|
716
|
-
const providerContractOk = providerConfigured
|
|
961
|
+
const cliProviderContractOk = providerConfigured
|
|
717
962
|
&& providerBaseUrlMatchesCredential
|
|
718
|
-
&& providerName ===
|
|
963
|
+
&& providerName === 'codex-lb'
|
|
719
964
|
&& providerWireApi === 'responses'
|
|
720
965
|
&& providerEnvKey === CODEX_LB_PROVIDER_ENV_KEY
|
|
721
966
|
&& providerSupportsWebsockets === true
|
|
722
|
-
&&
|
|
967
|
+
&& providerOpenAiAuthDisabled;
|
|
968
|
+
const compatProviderContractOk = providerConfigured
|
|
969
|
+
&& providerBaseUrlMatchesCredential
|
|
970
|
+
&& providerName === 'OpenAI'
|
|
971
|
+
&& providerWireApi === 'responses'
|
|
972
|
+
&& providerSupportsWebsockets === true
|
|
973
|
+
&& providerRequiresOpenAiAuth === true
|
|
974
|
+
&& !providerEnvKey
|
|
975
|
+
&& providerHasSeparateGatewayAuth;
|
|
976
|
+
const providerContractOk = desktopMode === 'desktop-dual-auth-compat'
|
|
977
|
+
? compatProviderContractOk
|
|
978
|
+
: cliProviderContractOk;
|
|
723
979
|
const providerUsesCodexLbEnvAuth = providerConfigured && providerEnvKey === CODEX_LB_PROVIDER_ENV_KEY && providerOpenAiAuthDisabled;
|
|
724
|
-
const
|
|
980
|
+
const oauthAvailable = authMode.mode === 'chatgpt_oauth' || authMode.mode === 'browser_marker';
|
|
981
|
+
const authRoutingCoherent = desktopMode === 'desktop-native-bridge'
|
|
982
|
+
? !selected && oauthAvailable && !codexLbKeyInSharedAuth
|
|
983
|
+
: desktopMode === 'desktop-dual-auth-compat'
|
|
984
|
+
? selected && oauthAvailable && !codexLbKeyInSharedAuth
|
|
985
|
+
: !codexLbKeyInSharedAuth && (!selected || providerUsesCodexLbEnvAuth);
|
|
986
|
+
const codexAppUsableWithCodexLb = desktopMode === 'desktop-native-bridge'
|
|
987
|
+
? Boolean(bridgeBaseUrl && oauthAvailable)
|
|
988
|
+
: desktopMode === 'desktop-dual-auth-compat'
|
|
989
|
+
? compatProviderContractOk && oauthAvailable
|
|
990
|
+
: authMode.codex_app_usable;
|
|
725
991
|
const fastMode = codexLbFastModeConfigStatus(config);
|
|
726
992
|
const launchEnvironment = await inspectCodexLbMacLaunchEnvironment(baseUrl, opts).catch((err) => ({
|
|
727
993
|
checked: true,
|
|
@@ -729,7 +995,11 @@ export async function codexLbStatus(opts = {}) {
|
|
|
729
995
|
status: 'inspect_failed',
|
|
730
996
|
error: err.message
|
|
731
997
|
}));
|
|
732
|
-
const providerReady = providerContractOk
|
|
998
|
+
const providerReady = providerContractOk
|
|
999
|
+
&& envLoad.configured
|
|
1000
|
+
&& Boolean(baseUrl)
|
|
1001
|
+
&& authRoutingCoherent
|
|
1002
|
+
&& (desktopMode === 'desktop-native-bridge' ? Boolean(bridgeBaseUrl) : true);
|
|
733
1003
|
const probeToolOutputRecovery = opts.probeToolOutputRecovery === true;
|
|
734
1004
|
const toolOutputRecovery = !selected
|
|
735
1005
|
? codexLbToolOutputRecoveryNotSelected()
|
|
@@ -758,7 +1028,14 @@ export async function codexLbStatus(opts = {}) {
|
|
|
758
1028
|
provider_requires_openai_auth: providerRequiresOpenAiAuth,
|
|
759
1029
|
provider_openai_auth_disabled: providerOpenAiAuthDisabled,
|
|
760
1030
|
provider_env_key: providerEnvKey || null,
|
|
1031
|
+
provider_has_separate_gateway_auth: providerHasSeparateGatewayAuth,
|
|
761
1032
|
provider_uses_codex_lb_env_auth: providerUsesCodexLbEnvAuth,
|
|
1033
|
+
desktop_mode: desktopMode || (selected ? 'legacy-or-manual-selected-provider' : 'cli-provider'),
|
|
1034
|
+
bridge_base_url: bridgeBaseUrl || null,
|
|
1035
|
+
legacy_migration_required: Boolean(!desktopMode
|
|
1036
|
+
&& selected
|
|
1037
|
+
&& authMode.mode === 'apikey'
|
|
1038
|
+
&& sharedOpenAiRouting.managed),
|
|
762
1039
|
selected,
|
|
763
1040
|
env_file: envExists,
|
|
764
1041
|
env_key_configured: envKeyConfigured,
|
|
@@ -784,7 +1061,7 @@ export async function codexLbStatus(opts = {}) {
|
|
|
784
1061
|
managed: sharedOpenAiRouting.managed,
|
|
785
1062
|
configured_base_url: sharedOpenAiRouting.configured_base_url
|
|
786
1063
|
},
|
|
787
|
-
auth_usable_for_codex_app:
|
|
1064
|
+
auth_usable_for_codex_app: codexAppUsableWithCodexLb && authRoutingCoherent,
|
|
788
1065
|
auth_summary: !sharedOpenAiRoutingSafe
|
|
789
1066
|
? 'codex-lb key is active in shared auth without a verified built-in OpenAI routing guard'
|
|
790
1067
|
: !authRoutingCoherent
|
|
@@ -829,38 +1106,36 @@ export function formatCodexLbStatusText(status = {}, opts = {}) {
|
|
|
829
1106
|
if (status.base_url)
|
|
830
1107
|
lines.push(`Base URL: ${status.base_url}`);
|
|
831
1108
|
lines.push(`ChatGPT backup: ${backupPresent ? `yes (${backupPath})` : 'no'}`);
|
|
832
|
-
if (status.shared_openai_routing?.safe === false)
|
|
833
|
-
lines.push('', 'Run: sks codex-lb
|
|
1109
|
+
if (status.legacy_migration_required || status.shared_openai_routing?.safe === false)
|
|
1110
|
+
lines.push('', 'Run: sks codex-lb migrate-legacy-desktop --restart-app. Ordinary repair will not rewrite shared Codex auth.');
|
|
834
1111
|
else if (status.provider_configured && !status.provider_contract_ok)
|
|
835
1112
|
lines.push('', 'Run: sks codex-lb repair to rewrite the provider block to the current codex-lb App contract.');
|
|
836
|
-
else if (status.
|
|
837
|
-
lines.push('', '
|
|
838
|
-
else if (status.ok && !status.
|
|
839
|
-
lines.push('', 'Run
|
|
840
|
-
else if (status.ok &&
|
|
841
|
-
lines.push('', '
|
|
1113
|
+
else if (status.desktop_mode === 'desktop-native-bridge' && !status.auth_usable_for_codex_app)
|
|
1114
|
+
lines.push('', 'Sign in with ChatGPT OAuth, then run: sks codex-lb use-desktop-full.');
|
|
1115
|
+
else if (status.ok && status.desktop_mode === 'cli-provider' && !status.selected)
|
|
1116
|
+
lines.push('', 'CLI provider is stored but unselected. Run `sks codex-lb use-cli` for CLI use or `sks codex-lb use-desktop-full` for managed Desktop routing.');
|
|
1117
|
+
else if (status.ok && status.desktop_mode === 'desktop-dual-auth-compat')
|
|
1118
|
+
lines.push('', 'Compatibility routing is active. Prefer `sks codex-lb use-desktop-full`; use `sks codex-lb disable` to remove managed Desktop routing.');
|
|
842
1119
|
else if (status.ok)
|
|
843
|
-
lines.push('', 'Status:
|
|
1120
|
+
lines.push('', 'Status: configured; no ordinary repair needed.');
|
|
844
1121
|
else if (!status.ok && status.base_url && status.env_key_configured)
|
|
845
|
-
lines.push('', 'Run: sks codex-lb repair
|
|
1122
|
+
lines.push('', 'Run: sks codex-lb repair. To change routing explicitly, use `use-desktop-full`, `use-cli`, or `disable`.');
|
|
846
1123
|
else if (!status.ok)
|
|
847
1124
|
lines.push('', 'Run: sks codex-lb setup --host <domain> --api-key-stdin');
|
|
848
1125
|
if (backupPresent)
|
|
849
|
-
lines.push('
|
|
1126
|
+
lines.push('Legacy OAuth backup detected; use `sks codex-lb migrate-legacy-desktop --restart-app` instead of background auth switching.');
|
|
850
1127
|
return `${lines.join('\n')}\n`;
|
|
851
1128
|
}
|
|
852
1129
|
export function formatCodexLbRepairResultText(result = {}) {
|
|
853
1130
|
const lines = [
|
|
854
|
-
'codex-lb provider
|
|
1131
|
+
'codex-lb credential/provider configuration repaired without changing Codex shared auth.',
|
|
855
1132
|
`Config: ${result.config_path}`,
|
|
856
1133
|
`Key env: ${result.env_path}`
|
|
857
1134
|
];
|
|
858
|
-
if (result.auth_reconcile?.status === '
|
|
859
|
-
lines.push(
|
|
860
|
-
else if (result.auth_reconcile?.status === '
|
|
861
|
-
lines.push('
|
|
862
|
-
else if (['apikey_auth_active', 'apikey_forced'].includes(result.auth_reconcile?.status))
|
|
863
|
-
lines.push('Codex App auth: codex-lb API-key auth.json is active.');
|
|
1135
|
+
if (result.auth_reconcile?.status === 'oauth_untouched')
|
|
1136
|
+
lines.push('Codex App auth: unchanged.');
|
|
1137
|
+
else if (result.auth_reconcile?.status === 'legacy_migration_required')
|
|
1138
|
+
lines.push('Legacy Desktop auth routing: run `sks codex-lb migrate-legacy-desktop --restart-app` explicitly.');
|
|
864
1139
|
return `${lines.join('\n')}\n`;
|
|
865
1140
|
}
|
|
866
1141
|
function codexLbProviderBaseUrl(text = '') {
|
|
@@ -895,6 +1170,10 @@ function codexLbProviderEnvKey(text = '') {
|
|
|
895
1170
|
const block = String(text || '').match(/(^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
896
1171
|
return block.match(/(^|\n)\s*env_key\s*=\s*"([^"]+)"/)?.[2] || '';
|
|
897
1172
|
}
|
|
1173
|
+
function codexLbProviderHasSeparateGatewayAuth(text = '') {
|
|
1174
|
+
const block = String(text || '').match(/(^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
1175
|
+
return /X-Codex-LB-API-Key/.test(block) && /CODEX_LB_API_KEY/.test(block);
|
|
1176
|
+
}
|
|
898
1177
|
function codexLbFastModeConfigStatus(text = '') {
|
|
899
1178
|
const globalServiceTier = topLevelTomlString(text, 'service_tier');
|
|
900
1179
|
const profileBlock = String(text || '').match(/(^|\n)\[profiles\.sks-fast-high\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
@@ -921,111 +1200,214 @@ function tomlTableString(text = '', table, key) {
|
|
|
921
1200
|
return block.match(new RegExp(`(^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]+)"\\s*(?:#.*)?(?=\\n|$)`))?.[2] || '';
|
|
922
1201
|
}
|
|
923
1202
|
export async function repairCodexLbAuth(opts = {}) {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1203
|
+
const home = opts.home || process.env.HOME || os.homedir();
|
|
1204
|
+
let status = await codexLbStatus({ ...opts, home });
|
|
1205
|
+
const authPath = opts.authPath || status.auth_path || codexAuthPath(home);
|
|
1206
|
+
const authBefore = await captureCodexAuthSnapshot({ home, authPath });
|
|
1207
|
+
const currentConfig = await readText(status.config_path, '');
|
|
1208
|
+
const legacyEnv = await loadCodexLbEnv({ ...opts, home });
|
|
1209
|
+
const legacyDetection = await detectLegacyCodexLbDesktopState({
|
|
1210
|
+
home,
|
|
1211
|
+
configPath: status.config_path,
|
|
1212
|
+
authPath,
|
|
1213
|
+
...(status.base_url ? { remoteBaseUrl: status.base_url } : {}),
|
|
1214
|
+
...(legacyEnv.secret_api_key
|
|
1215
|
+
? { expectedGatewayApiKey: legacyEnv.secret_api_key }
|
|
1216
|
+
: {})
|
|
928
1217
|
});
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
? await probeCodexLbToolOutputRecovery({
|
|
934
|
-
baseUrl: status.base_url,
|
|
935
|
-
...(typeof opts.toolOutputRecoveryFetch === 'function' ? { fetchImpl: opts.toolOutputRecoveryFetch } : {}),
|
|
936
|
-
timeoutMs: Number(opts.toolOutputRecoveryTimeoutMs || 4_000),
|
|
937
|
-
allowUnverified: opts.allowUnverifiedToolOutputRecovery === true
|
|
938
|
-
|| codexLbToolOutputRecoveryOverrideAcknowledged({ env: opts.env || process.env })
|
|
939
|
-
})
|
|
940
|
-
: status.tool_output_recovery;
|
|
941
|
-
if (status.base_url && toolOutputRecovery?.ok !== true) {
|
|
1218
|
+
const legacySharedAuthSelected = status.selected === true
|
|
1219
|
+
&& status.auth_mode === 'apikey'
|
|
1220
|
+
&& status.shared_openai_routing?.managed === true;
|
|
1221
|
+
if (legacyDetection.legacy_destructive_mode || legacySharedAuthSelected) {
|
|
942
1222
|
return {
|
|
943
1223
|
ok: false,
|
|
944
|
-
status: '
|
|
1224
|
+
status: 'legacy_migration_required',
|
|
1225
|
+
reason: 'legacy_desktop_auth_routing_requires_explicit_migration',
|
|
1226
|
+
config_path: status.config_path,
|
|
1227
|
+
env_path: status.env_path,
|
|
1228
|
+
base_url: status.base_url,
|
|
945
1229
|
codex_lb: status,
|
|
946
|
-
|
|
947
|
-
|
|
1230
|
+
auth_reconcile: {
|
|
1231
|
+
status: 'legacy_migration_required',
|
|
1232
|
+
reason: 'run_sks_codex_lb_migrate_legacy_desktop',
|
|
1233
|
+
auth_path: authPath
|
|
1234
|
+
},
|
|
1235
|
+
codex_login: {
|
|
1236
|
+
ok: true,
|
|
1237
|
+
status: 'not_required',
|
|
1238
|
+
reason: 'ordinary_repair_never_changes_shared_codex_auth'
|
|
1239
|
+
},
|
|
1240
|
+
tool_catalog: {
|
|
1241
|
+
ok: true,
|
|
1242
|
+
required: false,
|
|
1243
|
+
status: 'not_bound_during_repair',
|
|
1244
|
+
config_changed: false
|
|
1245
|
+
}
|
|
948
1246
|
};
|
|
949
1247
|
}
|
|
950
|
-
const currentConfig = await readText(status.config_path, '');
|
|
951
1248
|
if (!status.env_key_configured && status.base_url && (status.provider_configured || status.selected || status.env_base_url_configured)) {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1249
|
+
if (status.auth_mode === 'apikey') {
|
|
1250
|
+
return {
|
|
1251
|
+
ok: false,
|
|
1252
|
+
status: 'legacy_migration_required',
|
|
1253
|
+
reason: 'shared_codex_api_key_cannot_be_assumed_to_be_the_codex_lb_gateway_key',
|
|
1254
|
+
config_path: status.config_path,
|
|
1255
|
+
env_path: status.env_path,
|
|
1256
|
+
base_url: status.base_url,
|
|
1257
|
+
codex_lb: status,
|
|
1258
|
+
auth_reconcile: {
|
|
1259
|
+
status: 'legacy_migration_required',
|
|
1260
|
+
reason: 'run_sks_codex_lb_setup_with_a_separate_gateway_key_or_migrate_legacy_desktop',
|
|
1261
|
+
auth_path: authPath
|
|
1262
|
+
},
|
|
1263
|
+
codex_login: {
|
|
1264
|
+
ok: true,
|
|
1265
|
+
status: 'not_required',
|
|
1266
|
+
reason: 'ordinary_repair_never_reads_gateway_credentials_from_shared_codex_auth'
|
|
1267
|
+
},
|
|
1268
|
+
tool_catalog: {
|
|
1269
|
+
ok: true,
|
|
1270
|
+
required: false,
|
|
1271
|
+
status: 'not_bound_during_repair',
|
|
1272
|
+
config_changed: false
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
957
1275
|
}
|
|
958
1276
|
}
|
|
959
|
-
|
|
960
|
-
const repairPreserveThirdParty = Boolean(repairCurrentProvider)
|
|
961
|
-
&& repairCurrentProvider !== 'codex-lb'
|
|
962
|
-
&& opts.forceCodexLbApiKeyAuth !== true
|
|
963
|
-
&& opts.authMode !== 'codex-lb';
|
|
964
|
-
if (status.env_key_configured && status.base_url && (!status.provider_contract_ok || (!status.selected && !repairPreserveThirdParty) || legacyAuthMigrated || hasTopLevelCodexModeLock(currentConfig) || (opts.forceCodexLbApiKeyAuth === true && !status.ok))) {
|
|
965
|
-
await ensureDir(path.dirname(status.config_path));
|
|
966
|
-
let next = upsertCodexLbConfig(currentConfig, status.base_url, !repairPreserveThirdParty);
|
|
967
|
-
next = normalizeCodexFastModeUiConfig(next, {
|
|
968
|
-
forceFastMode: opts.forceFastMode === true || opts.forceCodexLbApiKeyAuth === true
|
|
969
|
-
});
|
|
970
|
-
const safeWrite = await safeWriteCodexConfigToml(status.config_path, currentConfig, next, 'codex-lb-repair');
|
|
971
|
-
configRepaired = safeWrite.ok && safeWrite.changed === true;
|
|
972
|
-
status = await codexLbStatus({ ...opts, probeToolOutputRecovery: true });
|
|
973
|
-
}
|
|
974
|
-
const canRepairAuthMode = (opts.forceCodexLbApiKeyAuth === true || status.codex_lb_key_in_shared_auth === true)
|
|
975
|
-
&& status.provider_contract_ok
|
|
976
|
-
&& status.env_key_configured
|
|
977
|
-
&& Boolean(status.base_url);
|
|
978
|
-
if (!status.ok && !canRepairAuthMode) {
|
|
1277
|
+
if (!status.env_key_configured || !status.base_url) {
|
|
979
1278
|
return {
|
|
980
1279
|
ok: false,
|
|
981
|
-
status: !status.env_key_configured ? 'missing_env_key' :
|
|
1280
|
+
status: !status.env_key_configured ? 'missing_env_key' : 'missing_base_url',
|
|
982
1281
|
config_path: status.config_path,
|
|
983
1282
|
env_path: status.env_path,
|
|
984
|
-
codex_lb: status
|
|
1283
|
+
codex_lb: status,
|
|
1284
|
+
auth_reconcile: {
|
|
1285
|
+
status: 'oauth_untouched',
|
|
1286
|
+
reason: 'ordinary_repair_never_changes_shared_codex_auth',
|
|
1287
|
+
auth_path: authPath
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
const configBeforeRepair = await readText(status.config_path, '');
|
|
1292
|
+
const desktopMode = managedCodexLbDesktopMode(configBeforeRepair);
|
|
1293
|
+
let nextConfig = configBeforeRepair;
|
|
1294
|
+
try {
|
|
1295
|
+
if (desktopMode === 'desktop-native-bridge') {
|
|
1296
|
+
const bridgeBaseUrl = topLevelTomlString(configBeforeRepair, 'openai_base_url');
|
|
1297
|
+
if (!bridgeBaseUrl)
|
|
1298
|
+
throw new Error('managed_desktop_bridge_missing_base_url');
|
|
1299
|
+
nextConfig = upsertCodexLbNativeDesktopConfig(configBeforeRepair, {
|
|
1300
|
+
bridgeBaseUrl,
|
|
1301
|
+
remoteBaseUrl: status.base_url
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
else if (desktopMode === 'desktop-dual-auth-compat') {
|
|
1305
|
+
nextConfig = upsertCodexLbCompatDesktopConfig(configBeforeRepair, {
|
|
1306
|
+
remoteBaseUrl: status.base_url
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
else {
|
|
1310
|
+
nextConfig = upsertCodexLbCliProviderConfig(configBeforeRepair, {
|
|
1311
|
+
remoteBaseUrl: status.base_url,
|
|
1312
|
+
selectGlobally: status.selected === true
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
catch (error) {
|
|
1317
|
+
return {
|
|
1318
|
+
ok: false,
|
|
1319
|
+
status: 'managed_desktop_routing_conflict',
|
|
1320
|
+
config_path: status.config_path,
|
|
1321
|
+
env_path: status.env_path,
|
|
1322
|
+
base_url: status.base_url,
|
|
1323
|
+
codex_lb: status,
|
|
1324
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
await ensureDir(path.dirname(status.config_path));
|
|
1328
|
+
const configWrite = await safeWriteCodexConfigToml(status.config_path, configBeforeRepair, nextConfig, 'codex-lb-safe-repair', { verifyUnchangedBeforeWrite: true });
|
|
1329
|
+
if (!configWrite.ok) {
|
|
1330
|
+
return {
|
|
1331
|
+
ok: false,
|
|
1332
|
+
status: configWrite.status,
|
|
1333
|
+
config_path: status.config_path,
|
|
1334
|
+
env_path: status.env_path,
|
|
1335
|
+
base_url: status.base_url,
|
|
1336
|
+
codex_lb: status,
|
|
1337
|
+
error: 'codex_lb_config_repair_failed'
|
|
985
1338
|
};
|
|
986
1339
|
}
|
|
987
|
-
await migrateCodexAuthKeyFormat({ home: opts.home });
|
|
988
1340
|
const codexEnvironment = await syncCodexLbProviderEnvironment(status, opts);
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
}
|
|
1001
|
-
const
|
|
1002
|
-
const
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1341
|
+
const authAfter = await captureCodexAuthSnapshot({ home, authPath });
|
|
1342
|
+
let authInvariantError = null;
|
|
1343
|
+
try {
|
|
1344
|
+
await assertDesktopAuthUnchangedBySks(authBefore, authAfter);
|
|
1345
|
+
}
|
|
1346
|
+
catch (error) {
|
|
1347
|
+
authInvariantError = error instanceof Error ? error.message : String(error);
|
|
1348
|
+
}
|
|
1349
|
+
if (authInvariantError) {
|
|
1350
|
+
const writtenConfig = await readText(status.config_path, '');
|
|
1351
|
+
await safeWriteCodexConfigToml(status.config_path, writtenConfig, configBeforeRepair, 'codex-lb-safe-repair-auth-invariant-rollback', { verifyUnchangedBeforeWrite: true });
|
|
1352
|
+
}
|
|
1353
|
+
const finalStatus = await codexLbStatus({ ...opts, home });
|
|
1354
|
+
const toolOutputRecovery = opts.probeToolOutputRecovery === true && status.selected
|
|
1355
|
+
? await probeCodexLbToolOutputRecovery({
|
|
1356
|
+
baseUrl: status.base_url,
|
|
1357
|
+
...(typeof opts.toolOutputRecoveryFetch === 'function' ? { fetchImpl: opts.toolOutputRecoveryFetch } : {}),
|
|
1358
|
+
timeoutMs: Number(opts.toolOutputRecoveryTimeoutMs || 4_000),
|
|
1359
|
+
allowUnverified: opts.allowUnverifiedToolOutputRecovery === true
|
|
1360
|
+
|| codexLbToolOutputRecoveryOverrideAcknowledged({ env: opts.env || process.env })
|
|
1361
|
+
})
|
|
1362
|
+
: codexLbToolOutputRecoveryNotChecked(status.selected === true);
|
|
1363
|
+
const authReconcile = {
|
|
1364
|
+
status: authInvariantError ? 'failed' : 'oauth_untouched',
|
|
1365
|
+
reason: authInvariantError || 'ordinary_repair_never_changes_shared_codex_auth',
|
|
1366
|
+
auth_path: authPath,
|
|
1367
|
+
...(authInvariantError ? { error: authInvariantError } : {})
|
|
1368
|
+
};
|
|
1369
|
+
const codexLogin = {
|
|
1370
|
+
ok: !authInvariantError,
|
|
1371
|
+
status: authInvariantError ? 'desktop_auth_byte_invariant_failed' : 'not_required',
|
|
1372
|
+
reason: authInvariantError || 'gateway_credentials_are_independent_from_codex_login',
|
|
1373
|
+
error: authInvariantError
|
|
1374
|
+
};
|
|
1375
|
+
const toolCatalog = {
|
|
1376
|
+
schema: 'sks.codex-lb-tool-catalog-selection.v1',
|
|
1377
|
+
ok: true,
|
|
1378
|
+
required: false,
|
|
1379
|
+
status: desktopMode === 'desktop-native-bridge'
|
|
1380
|
+
? 'not_bound_for_desktop_native'
|
|
1381
|
+
: 'not_bound_during_repair',
|
|
1382
|
+
config_changed: false,
|
|
1383
|
+
selected: finalStatus.selected,
|
|
1384
|
+
blockers: []
|
|
1385
|
+
};
|
|
1386
|
+
const ok = Boolean(configWrite.ok && codexEnvironment.ok && !authInvariantError);
|
|
1015
1387
|
return {
|
|
1016
1388
|
ok,
|
|
1017
|
-
status: ok
|
|
1389
|
+
status: ok
|
|
1390
|
+
? configWrite.changed
|
|
1391
|
+
? 'repaired'
|
|
1392
|
+
: finalStatus.selected
|
|
1393
|
+
? 'present'
|
|
1394
|
+
: 'present_unselected'
|
|
1395
|
+
: authInvariantError
|
|
1396
|
+
? 'desktop_auth_byte_invariant_failed'
|
|
1397
|
+
: codexEnvironment.status,
|
|
1018
1398
|
config_path: status.config_path,
|
|
1019
1399
|
env_path: status.env_path,
|
|
1020
1400
|
base_url: status.base_url,
|
|
1021
|
-
config_repaired:
|
|
1022
|
-
legacy_auth_migrated:
|
|
1023
|
-
legacy_auth_path:
|
|
1401
|
+
config_repaired: configWrite.changed === true,
|
|
1402
|
+
legacy_auth_migrated: false,
|
|
1403
|
+
legacy_auth_path: null,
|
|
1024
1404
|
auth_reconcile: authReconcile,
|
|
1025
1405
|
codex_lb: finalStatus,
|
|
1026
1406
|
codex_environment: codexEnvironment,
|
|
1027
1407
|
codex_login: codexLogin,
|
|
1028
|
-
tool_catalog: toolCatalog
|
|
1408
|
+
tool_catalog: toolCatalog,
|
|
1409
|
+
tool_output_recovery: toolOutputRecovery,
|
|
1410
|
+
error: authInvariantError || codexEnvironment.error || null
|
|
1029
1411
|
};
|
|
1030
1412
|
}
|
|
1031
1413
|
export async function ensureCodexLbAuthDuringInstall(opts = {}) {
|
|
@@ -1034,87 +1416,10 @@ export async function ensureCodexLbAuthDuringInstall(opts = {}) {
|
|
|
1034
1416
|
const status = await codexLbStatus(opts);
|
|
1035
1417
|
if (!status.selected && !status.provider_configured && !status.env_key_configured)
|
|
1036
1418
|
return { status: 'not_configured', codex_lb: status };
|
|
1037
|
-
|
|
1038
|
-
return {
|
|
1039
|
-
ok: true,
|
|
1040
|
-
status: 'present_unselected',
|
|
1041
|
-
reason: 'chatgpt_oauth_active_codex_lb_unselected',
|
|
1042
|
-
config_path: status.config_path,
|
|
1043
|
-
env_path: status.env_path,
|
|
1044
|
-
base_url: status.base_url,
|
|
1045
|
-
codex_lb: status
|
|
1046
|
-
};
|
|
1047
|
-
}
|
|
1048
|
-
await migrateCodexAuthKeyFormat({ home: opts.home });
|
|
1049
|
-
if (status.ok && (!status.selected || !status.provider_contract_ok))
|
|
1050
|
-
return repairCodexLbAuth(opts);
|
|
1051
|
-
if (!status.ok) {
|
|
1052
|
-
if (status.base_url && (status.env_key_configured || status.provider_configured || status.selected || status.env_base_url_configured))
|
|
1053
|
-
return repairCodexLbAuth(opts);
|
|
1054
|
-
return { status: status.env_key_configured ? 'missing_base_url' : 'missing_env_key', codex_lb: status, config_path: status.config_path, env_path: status.env_path };
|
|
1055
|
-
}
|
|
1056
|
-
const codexEnvironment = await syncCodexLbProviderEnvironment(status, opts);
|
|
1057
|
-
const loadedEnv = await loadCodexLbEnv({
|
|
1419
|
+
return repairCodexLbAuth({
|
|
1058
1420
|
...opts,
|
|
1059
|
-
|
|
1060
|
-
envPath: status.env_path
|
|
1421
|
+
probeToolOutputRecovery: false
|
|
1061
1422
|
});
|
|
1062
|
-
const apiKey = String(opts.apiKey || loadedEnv.secret_api_key || '').trim();
|
|
1063
|
-
const toolCatalog = await ensureCodexLbToolCatalogSelection({
|
|
1064
|
-
home: opts.home || process.env.HOME || os.homedir(),
|
|
1065
|
-
configPath: status.config_path,
|
|
1066
|
-
baseUrl: String(status.base_url || ''),
|
|
1067
|
-
apiKey
|
|
1068
|
-
}, opts);
|
|
1069
|
-
const codexLogin = await maybeSyncCodexLbSharedLogin(apiKey, { ...opts, home: opts.home || process.env.HOME || os.homedir(), force: true });
|
|
1070
|
-
const authReconcile = await reconcileCodexLbAuthConflict({ ...opts, status }).catch((err) => ({ status: 'failed', reason: 'exception', error: err.message }));
|
|
1071
|
-
const finalStatus = await codexLbStatus(opts);
|
|
1072
|
-
const finalReady = finalStatus.selected === true
|
|
1073
|
-
&& finalStatus.provider_ready === true
|
|
1074
|
-
&& finalStatus.auth_routing_coherent === true
|
|
1075
|
-
&& finalStatus.shared_openai_routing?.safe === true;
|
|
1076
|
-
const baseOk = Boolean(codexEnvironment.ok
|
|
1077
|
-
&& codexLogin.ok
|
|
1078
|
-
&& (toolCatalog.required === false || toolCatalog.ok === true));
|
|
1079
|
-
const ok = baseOk && finalReady;
|
|
1080
|
-
return {
|
|
1081
|
-
ok,
|
|
1082
|
-
status: ok ? 'present' : baseOk ? 'final_readiness_failed' : (codexEnvironment.status || codexLogin.status),
|
|
1083
|
-
config_path: status.config_path,
|
|
1084
|
-
env_path: status.env_path,
|
|
1085
|
-
base_url: status.base_url,
|
|
1086
|
-
codex_lb: finalStatus,
|
|
1087
|
-
codex_environment: codexEnvironment,
|
|
1088
|
-
codex_login: codexLogin,
|
|
1089
|
-
tool_catalog: toolCatalog,
|
|
1090
|
-
auth_reconcile: authReconcile,
|
|
1091
|
-
error: codexEnvironment.error || codexLogin.error || null
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
async function restoreCodexLbEnvFromSharedLogin(status = {}, opts = {}) {
|
|
1095
|
-
const home = opts.home || process.env.HOME || os.homedir();
|
|
1096
|
-
const authPath = opts.authPath || codexAuthPath(home);
|
|
1097
|
-
const envPath = opts.envPath || status.env_path || codexLbEnvPath(home);
|
|
1098
|
-
const authText = await readText(authPath, '');
|
|
1099
|
-
const apiKey = parseCodexSharedLoginApiKey(authText);
|
|
1100
|
-
if (!apiKey)
|
|
1101
|
-
return { ok: false, status: 'missing_legacy_login_key', auth_path: authPath, env_path: envPath };
|
|
1102
|
-
const baseUrl = status.base_url || parseCodexLbEnvBaseUrl(await readText(envPath, ''));
|
|
1103
|
-
if (!baseUrl)
|
|
1104
|
-
return { ok: false, status: 'missing_base_url', auth_path: authPath, env_path: envPath };
|
|
1105
|
-
await ensureDir(path.dirname(envPath));
|
|
1106
|
-
await writeTextAtomic(envPath, `export CODEX_LB_BASE_URL=${shellSingleQuote(normalizeCodexLbBaseUrl(baseUrl))}\nexport CODEX_LB_API_KEY=${shellSingleQuote(apiKey)}\n`, { mode: 0o600 });
|
|
1107
|
-
await fsp.chmod(envPath, 0o600).catch(() => { });
|
|
1108
|
-
const metadataPath = opts.metadataPath || codexLbMetadataPath(home);
|
|
1109
|
-
await writeTextAtomic(metadataPath, `${JSON.stringify({
|
|
1110
|
-
schema: 'sks.codex-lb-metadata.v1',
|
|
1111
|
-
base_url: normalizeCodexLbBaseUrl(baseUrl),
|
|
1112
|
-
updated_at: new Date().toISOString(),
|
|
1113
|
-
source: 'legacy-login-migration',
|
|
1114
|
-
api_key: { redacted: true, sha256: await sha256Text(apiKey) }
|
|
1115
|
-
}, null, 2)}\n`, { mode: 0o600 });
|
|
1116
|
-
await fsp.chmod(metadataPath, 0o600).catch(() => { });
|
|
1117
|
-
return { ok: true, status: 'migrated_login_cache', auth_path: authPath, env_path: envPath, base_url: normalizeCodexLbBaseUrl(baseUrl) };
|
|
1118
1423
|
}
|
|
1119
1424
|
function hasChatgptOAuthTokens(text = '') {
|
|
1120
1425
|
try {
|
|
@@ -1164,30 +1469,6 @@ function codexAuthModeSummary(text = '') {
|
|
|
1164
1469
|
catch { }
|
|
1165
1470
|
return { mode: 'unknown', codex_app_usable: false, summary: 'unrecognized auth.json shape' };
|
|
1166
1471
|
}
|
|
1167
|
-
async function migrateCodexAuthKeyFormat(opts = {}) {
|
|
1168
|
-
const home = opts.home || process.env.HOME || os.homedir();
|
|
1169
|
-
const authPath = opts.authPath || codexAuthPath(home);
|
|
1170
|
-
const text = await readText(authPath, '');
|
|
1171
|
-
if (!text.trim())
|
|
1172
|
-
return { status: 'skipped', reason: 'empty' };
|
|
1173
|
-
try {
|
|
1174
|
-
const parsed = JSON.parse(text);
|
|
1175
|
-
if (parsed?.auth_mode !== 'apikey')
|
|
1176
|
-
return { status: 'skipped', reason: 'not_apikey' };
|
|
1177
|
-
if (parsed.OPENAI_API_KEY)
|
|
1178
|
-
return { status: 'skipped', reason: 'already_migrated' };
|
|
1179
|
-
const legacyKey = parsed.key || parsed.api_key || parsed.apiKey || parsed.openai_api_key;
|
|
1180
|
-
if (!legacyKey)
|
|
1181
|
-
return { status: 'skipped', reason: 'no_key_found' };
|
|
1182
|
-
const replacement = `${JSON.stringify({ auth_mode: 'apikey', OPENAI_API_KEY: legacyKey })}\n`;
|
|
1183
|
-
await writeTextAtomic(authPath, replacement, { mode: 0o600 });
|
|
1184
|
-
await fsp.chmod(authPath, 0o600).catch(() => { });
|
|
1185
|
-
return { status: 'migrated', auth_path: authPath };
|
|
1186
|
-
}
|
|
1187
|
-
catch {
|
|
1188
|
-
return { status: 'skipped', reason: 'parse_error' };
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
1472
|
export async function reconcileCodexLbAuthConflict(opts = {}) {
|
|
1192
1473
|
const home = opts.home || process.env.HOME || os.homedir();
|
|
1193
1474
|
const status = opts.status || await codexLbStatus({ ...opts, home });
|
|
@@ -1394,8 +1675,7 @@ export async function reconcileCodexLbAuthConflict(opts = {}) {
|
|
|
1394
1675
|
const unselected = await unselectCodexLbProvider({
|
|
1395
1676
|
...opts,
|
|
1396
1677
|
home,
|
|
1397
|
-
authPath
|
|
1398
|
-
allowActiveSharedAuthTransition: true
|
|
1678
|
+
authPath
|
|
1399
1679
|
});
|
|
1400
1680
|
if (!unselected.ok) {
|
|
1401
1681
|
await writeTextAtomic(authPath, authText, { mode: 0o600 });
|
|
@@ -1462,21 +1742,24 @@ export async function unselectCodexLbProvider(opts = {}) {
|
|
|
1462
1742
|
const envLoad = await loadCodexLbEnv({ ...opts, home, envPath });
|
|
1463
1743
|
const authPath = opts.authPath || codexAuthPath(home);
|
|
1464
1744
|
const authApiKey = parseCodexAuthApiKey(await readText(authPath, ''));
|
|
1465
|
-
const
|
|
1466
|
-
|
|
1745
|
+
const managedCatalogPath = codexLbToolCatalogPath(opts.codexHome || path.join(home, '.codex'));
|
|
1746
|
+
const managedCatalogSelected = topLevelTomlString(current, 'model_catalog_json') === managedCatalogPath;
|
|
1747
|
+
const providerBaseUrl = codexLbProviderBaseUrl(current) || envLoad.base_url || '';
|
|
1748
|
+
const sharedOpenAiRouting = codexLbSharedOpenAiRoutingState(current, providerBaseUrl);
|
|
1749
|
+
const managedSharedOpenAiRouting = sharedOpenAiRouting.status === 'matched' && sharedOpenAiRouting.managed;
|
|
1750
|
+
const codexLbRoutingOwned = hasTopLevelCodexLbSelected(current)
|
|
1751
|
+
|| /\[model_providers\.codex-lb\]/.test(current)
|
|
1752
|
+
|| managedCatalogSelected
|
|
1753
|
+
|| managedSharedOpenAiRouting;
|
|
1754
|
+
if (authApiKey && codexLbRoutingOwned) {
|
|
1467
1755
|
return {
|
|
1468
1756
|
ok: false,
|
|
1469
1757
|
status: 'failed',
|
|
1470
1758
|
reason: 'shared_codex_lb_auth_active',
|
|
1471
|
-
provider_error: 'refusing to unselect codex-lb while
|
|
1759
|
+
provider_error: 'refusing to unselect codex-lb while API-key auth could fall through to built-in OpenAI',
|
|
1472
1760
|
config_path: configPath
|
|
1473
1761
|
};
|
|
1474
1762
|
}
|
|
1475
|
-
const managedCatalogPath = codexLbToolCatalogPath(opts.codexHome || path.join(home, '.codex'));
|
|
1476
|
-
const managedCatalogSelected = topLevelTomlString(current, 'model_catalog_json') === managedCatalogPath;
|
|
1477
|
-
const providerBaseUrl = codexLbProviderBaseUrl(current) || envLoad.base_url || '';
|
|
1478
|
-
const sharedOpenAiRouting = codexLbSharedOpenAiRoutingState(current, providerBaseUrl);
|
|
1479
|
-
const managedSharedOpenAiRouting = sharedOpenAiRouting.status === 'matched' && sharedOpenAiRouting.managed;
|
|
1480
1763
|
if (!hasTopLevelCodexLbSelected(current) && !managedCatalogSelected && !managedSharedOpenAiRouting)
|
|
1481
1764
|
return { ok: true, status: 'not_selected', config_path: configPath };
|
|
1482
1765
|
try {
|
|
@@ -1549,8 +1832,7 @@ export async function releaseCodexLbAuthHold(opts = {}) {
|
|
|
1549
1832
|
...opts,
|
|
1550
1833
|
home,
|
|
1551
1834
|
configPath,
|
|
1552
|
-
authPath
|
|
1553
|
-
allowActiveSharedAuthTransition: true
|
|
1835
|
+
authPath
|
|
1554
1836
|
});
|
|
1555
1837
|
provider = providerDeselectionOutcome(unselected);
|
|
1556
1838
|
}
|
|
@@ -1591,8 +1873,7 @@ export async function releaseCodexLbAuthHold(opts = {}) {
|
|
|
1591
1873
|
...opts,
|
|
1592
1874
|
home,
|
|
1593
1875
|
configPath,
|
|
1594
|
-
authPath
|
|
1595
|
-
allowActiveSharedAuthTransition: true
|
|
1876
|
+
authPath
|
|
1596
1877
|
});
|
|
1597
1878
|
provider = providerDeselectionOutcome(unselected);
|
|
1598
1879
|
}
|
|
@@ -1642,8 +1923,7 @@ export async function releaseCodexLbAuthHold(opts = {}) {
|
|
|
1642
1923
|
...opts,
|
|
1643
1924
|
home,
|
|
1644
1925
|
configPath,
|
|
1645
|
-
authPath
|
|
1646
|
-
allowActiveSharedAuthTransition: true
|
|
1926
|
+
authPath
|
|
1647
1927
|
});
|
|
1648
1928
|
provider = providerDeselectionOutcome(unselected);
|
|
1649
1929
|
}
|
|
@@ -1705,108 +1985,46 @@ export async function maybePromptCodexLbSetupForLaunch(args = [], opts = {}) {
|
|
|
1705
1985
|
return { status: 'skipped' };
|
|
1706
1986
|
const allowUnverifiedToolOutputRecovery = opts.allowUnverifiedToolOutputRecovery === true
|
|
1707
1987
|
|| codexLbToolOutputRecoveryOverrideAcknowledged({ args, env: opts.env || process.env });
|
|
1708
|
-
let status = await codexLbStatus({
|
|
1709
|
-
|
|
1710
|
-
probeToolOutputRecovery: true,
|
|
1711
|
-
allowUnverifiedToolOutputRecovery
|
|
1712
|
-
});
|
|
1713
|
-
if (status.selected && status.tool_output_recovery?.ok !== true) {
|
|
1988
|
+
let status = await codexLbStatus({ ...opts, probeToolOutputRecovery: false });
|
|
1989
|
+
if (status.legacy_migration_required) {
|
|
1714
1990
|
return {
|
|
1715
|
-
status: '
|
|
1991
|
+
status: 'legacy_migration_required',
|
|
1716
1992
|
ok: false,
|
|
1717
1993
|
codex_lb: status,
|
|
1718
|
-
|
|
1719
|
-
blockers: status.tool_output_recovery.blockers,
|
|
1720
|
-
operator_actions: status.tool_output_recovery.operator_actions,
|
|
1721
|
-
bypass_codex_lb: false
|
|
1994
|
+
blockers: ['run_sks_codex_lb_migrate_legacy_desktop']
|
|
1722
1995
|
};
|
|
1723
1996
|
}
|
|
1724
|
-
if (status.env_key_configured && status.base_url
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
blockers: status.tool_output_recovery.blockers,
|
|
1745
|
-
operator_actions: status.tool_output_recovery.operator_actions,
|
|
1746
|
-
bypass_codex_lb: false
|
|
1747
|
-
};
|
|
1748
|
-
}
|
|
1749
|
-
if (!status.ok)
|
|
1750
|
-
return { status: 'repair_failed', ok: false, repair: repaired, codex_lb: status };
|
|
1751
|
-
if (!repaired.ok && repaired.error && promptedRestore)
|
|
1752
|
-
console.log(`codex-lb provider restored, but launch environment sync reported: ${repaired.error}`);
|
|
1753
|
-
else if (!repaired.ok && promptedRestore)
|
|
1754
|
-
console.log(`codex-lb provider restored, but provider auth sync reported: ${repaired.status}`);
|
|
1755
|
-
else if (repaired.config_repaired && promptedRestore)
|
|
1756
|
-
console.log(`codex-lb provider restored: ${status.base_url}`);
|
|
1757
|
-
}
|
|
1758
|
-
if (status.ok) {
|
|
1759
|
-
const codexEnvironment = await syncCodexLbProviderEnvironment(status, opts);
|
|
1760
|
-
if (codexEnvironment.status === 'synced')
|
|
1761
|
-
console.log('codex-lb provider auth synced for this user session.');
|
|
1762
|
-
const apiKey = parseCodexLbEnvKey(await readText(status.env_path, ''));
|
|
1763
|
-
const toolCatalog = await ensureCodexLbToolCatalogSelection({
|
|
1764
|
-
home: opts.home || process.env.HOME || os.homedir(),
|
|
1765
|
-
configPath: status.config_path,
|
|
1766
|
-
baseUrl: String(status.base_url || ''),
|
|
1767
|
-
apiKey
|
|
1768
|
-
}, opts);
|
|
1769
|
-
if (toolCatalog.required !== false && toolCatalog.ok !== true) {
|
|
1770
|
-
return { status: 'repair_failed', ok: false, codex_lb: status, codex_environment: codexEnvironment, tool_catalog: toolCatalog };
|
|
1771
|
-
}
|
|
1772
|
-
const codexLogin = await maybeSyncCodexLbSharedLogin(apiKey, { ...opts, home: opts.home || process.env.HOME || os.homedir() });
|
|
1773
|
-
if (codexLogin.status === 'synced')
|
|
1774
|
-
console.log('codex-lb auth synced with Codex CLI login cache.');
|
|
1775
|
-
const chainHealth = await checkCodexLbResponseChain(status, opts);
|
|
1776
|
-
if (!chainHealth.ok && chainHealth.chain_unhealthy) {
|
|
1777
|
-
if (chainHealth.status === 'previous_response_not_found') {
|
|
1778
|
-
console.log('codex-lb response chain check: previous_response_id not persisted by the load balancer (this is normal for stateless deployments). Keeping codex-lb active.');
|
|
1779
|
-
return { status: 'present', ...status, codex_environment: codexEnvironment, codex_login: codexLogin, tool_catalog: toolCatalog, chain_health: chainHealth };
|
|
1780
|
-
}
|
|
1781
|
-
console.log(`codex-lb response chain check failed (${chainHealth.status}${chainHealth.error ? `: ${chainHealth.error}` : ''}).`);
|
|
1782
|
-
if (process.env.SKS_CODEX_LB_AUTOBYPASS === '1') {
|
|
1783
|
-
console.log('SKS_CODEX_LB_AUTOBYPASS=1 set; bypassing codex-lb to ChatGPT OAuth for this launch.');
|
|
1784
|
-
return { status: 'chain_unhealthy', ...status, ok: false, codex_environment: codexEnvironment, codex_login: codexLogin, tool_catalog: toolCatalog, chain_health: chainHealth, bypass_codex_lb: true };
|
|
1785
|
-
}
|
|
1786
|
-
if (canAskYesNo()) {
|
|
1787
|
-
const answer = (await askPostinstallQuestion('Use codex-lb anyway, or fall back to ChatGPT OAuth? [LB/oauth] ')).trim().toLowerCase();
|
|
1788
|
-
if (/^(oauth|o|chatgpt|fall ?back|n|no|아니|아니요|ㄴ)$/.test(answer)) {
|
|
1789
|
-
console.log('Falling back to ChatGPT OAuth for this launch. Re-enable codex-lb anytime with `sks codex-lb repair`.');
|
|
1790
|
-
return { status: 'chain_unhealthy', ...status, ok: false, codex_environment: codexEnvironment, codex_login: codexLogin, tool_catalog: toolCatalog, chain_health: chainHealth, bypass_codex_lb: true };
|
|
1791
|
-
}
|
|
1792
|
-
console.log('Keeping codex-lb active. To switch back to ChatGPT OAuth: `sks codex-lb release`.');
|
|
1793
|
-
return { status: 'present', ...status, codex_environment: codexEnvironment, codex_login: codexLogin, tool_catalog: toolCatalog, chain_health: chainHealth };
|
|
1794
|
-
}
|
|
1795
|
-
console.log('Non-interactive launch + chain check failure. Keeping codex-lb active. Set SKS_CODEX_LB_AUTOBYPASS=1 to auto-bypass to ChatGPT OAuth.');
|
|
1796
|
-
return { status: 'present', ...status, codex_environment: codexEnvironment, codex_login: codexLogin, tool_catalog: toolCatalog, chain_health: chainHealth };
|
|
1797
|
-
}
|
|
1798
|
-
return { status: 'present', ...status, codex_environment: codexEnvironment, codex_login: codexLogin, tool_catalog: toolCatalog, chain_health: chainHealth };
|
|
1997
|
+
if (status.env_key_configured && status.base_url) {
|
|
1998
|
+
const repaired = await repairCodexLbAuth({
|
|
1999
|
+
...opts,
|
|
2000
|
+
allowUnverifiedToolOutputRecovery,
|
|
2001
|
+
probeToolOutputRecovery: false
|
|
2002
|
+
});
|
|
2003
|
+
status = await codexLbStatus({ ...opts, probeToolOutputRecovery: false });
|
|
2004
|
+
if (!repaired.ok)
|
|
2005
|
+
return { status: repaired.status, ok: false, repair: repaired, codex_lb: status };
|
|
2006
|
+
const codexEnvironment = repaired.codex_environment || await syncCodexLbProviderEnvironment(status, opts);
|
|
2007
|
+
return {
|
|
2008
|
+
status: status.desktop_mode === 'cli-provider' && !status.selected ? 'continued_to_codex' : 'present',
|
|
2009
|
+
...status,
|
|
2010
|
+
codex_environment: codexEnvironment,
|
|
2011
|
+
...(repaired.codex_login ? { codex_login: repaired.codex_login } : {}),
|
|
2012
|
+
...(repaired.tool_catalog ? { tool_catalog: repaired.tool_catalog } : {}),
|
|
2013
|
+
reason: status.desktop_mode === 'cli-provider' && !status.selected
|
|
2014
|
+
? 'unselected_cli_provider_available_for_explicit_use'
|
|
2015
|
+
: 'explicit_managed_desktop_routing_preserved'
|
|
2016
|
+
};
|
|
1799
2017
|
}
|
|
1800
2018
|
if (!canAskYesNo())
|
|
1801
2019
|
return { status: 'non_interactive', codex_lb: status };
|
|
1802
|
-
const useCodexLb = (await askPostinstallQuestion('\ncodex-lb is not configured
|
|
2020
|
+
const useCodexLb = (await askPostinstallQuestion('\ncodex-lb is not configured. Store credentials and an unselected CLI provider now? [y/N] ')).trim();
|
|
1803
2021
|
if (!/^(y|yes|예|네|응)$/i.test(useCodexLb))
|
|
1804
2022
|
return { status: 'continued_to_codex' };
|
|
1805
2023
|
const host = (await askPostinstallQuestion('codex-lb host domain [http://127.0.0.1:2455]: ')).trim() || 'http://127.0.0.1:2455';
|
|
1806
2024
|
const apiKey = (await askPostinstallQuestion('codex-lb API key: ')).trim();
|
|
1807
2025
|
const configured = await configureCodexLb({ ...opts, host, apiKey, allowUnverifiedToolOutputRecovery });
|
|
1808
2026
|
if (configured.ok)
|
|
1809
|
-
console.log(`codex-lb
|
|
2027
|
+
console.log(`codex-lb credentials stored: ${configured.base_url}. Use \`sks codex-lb use-cli\`, \`use-desktop-full\`, or \`disable\` explicitly.`);
|
|
1810
2028
|
else
|
|
1811
2029
|
console.log('codex-lb setup skipped: API key was empty.');
|
|
1812
2030
|
return configured;
|
|
@@ -1913,39 +2131,9 @@ async function inspectCodexLbMacLaunchEnvironment(baseUrl = '', opts = {}) {
|
|
|
1913
2131
|
: []
|
|
1914
2132
|
};
|
|
1915
2133
|
}
|
|
1916
|
-
async function maybeSyncCodexLbSharedLogin(apiKey, opts = {}) {
|
|
1917
|
-
if (!apiKey)
|
|
1918
|
-
return { ok: false, status: 'missing_env_key' };
|
|
1919
|
-
if (!shouldSyncCodexLbSharedLogin(opts)) {
|
|
1920
|
-
return { ok: true, status: 'skipped', reason: 'codex-lb repair preserved the current Codex App auth; run `sks codex-lb use-codex-lb` to switch the app to codex-lb API-key auth.' };
|
|
1921
|
-
}
|
|
1922
|
-
return syncCodexApiKeyLogin(apiKey, opts);
|
|
1923
|
-
}
|
|
1924
|
-
function shouldSyncCodexLbSharedLogin(opts = {}) {
|
|
1925
|
-
return opts.syncCodexLogin === true || process.env.SKS_CODEX_LB_SYNC_CODEX_LOGIN === '1';
|
|
1926
|
-
}
|
|
1927
|
-
async function syncCodexApiKeyLogin(apiKey, opts = {}) {
|
|
1928
|
-
const home = opts.home || process.env.HOME || os.homedir();
|
|
1929
|
-
const codexHome = opts.codexHome || path.join(home, '.codex');
|
|
1930
|
-
const codexBin = opts.codexBin || (await getCodexInfo().catch(() => EMPTY_CODEX_INFO)).bin || await which('codex').catch(() => null);
|
|
1931
|
-
if (!codexBin)
|
|
1932
|
-
return { ok: false, status: 'codex_missing' };
|
|
1933
|
-
await ensureDir(codexHome);
|
|
1934
|
-
const env = { HOME: home, CODEX_HOME: codexHome, CODEX_LB_API_KEY: apiKey };
|
|
1935
|
-
if (!opts.force) {
|
|
1936
|
-
const current = await runProcess(codexBin, ['login', 'status'], { env, timeoutMs: 10000, maxOutputBytes: 8192 });
|
|
1937
|
-
if (current.code === 0 && !/not logged in/i.test(`${current.stdout}\n${current.stderr}`))
|
|
1938
|
-
return { ok: true, status: 'present' };
|
|
1939
|
-
}
|
|
1940
|
-
const login = await runProcess(codexBin, ['login', '--with-api-key'], { input: `${apiKey}\n`, env, timeoutMs: 15000, maxOutputBytes: 8192 });
|
|
1941
|
-
if (login.code === 0)
|
|
1942
|
-
return { ok: true, status: 'synced' };
|
|
1943
|
-
return { ok: false, status: 'login_failed', error: redactSecretText(login.stderr || login.stdout || 'codex login failed', [apiKey]).trim() };
|
|
1944
|
-
}
|
|
1945
2134
|
export { checkContext7, ensureCodexCliTool, ensureMadLaunchDependencies, ensureRelatedCliTools, ensureZellijCliTool, formatMadLaunchDependencyAction, maybePromptCodexUpdateForLaunch, maybePromptSksUpdateForLaunch, shouldAutoApproveInstall } from './install-tool-helpers.js';
|
|
1946
2135
|
export { codexFastModeDesktopStatus, ensureGlobalCodexFastModeDuringInstall, normalizeCodexFastModeUiConfig, safeWriteCodexConfigToml } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
1947
2136
|
export { checkRequiredSkills, context7GlobalMcpStatus, ensureCodexImagegenDuringInstall, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureSksCommandDuringInstall, globalCodexSkillsRoot, selftestSksShimRepair } from './install-helpers-install-support.js';
|
|
1948
2137
|
export { askPostinstallQuestion, codexLbConfigPath, codexLbEnvPath, normalizeCodexLbBaseUrl } from './install-helpers-codex-lb-shared.js';
|
|
1949
2138
|
export { ensureGlobalCodexAppGlmProfile, ensureStoredOpenRouterProviderDuringInstall, upsertCodexAppGlmConfig, upsertCodexLbConfig } from './install-helpers-codex-lb-config.js';
|
|
1950
2139
|
export { checkCodexLbResponseChain } from './install-helpers-codex-lb-chain.js';
|
|
1951
|
-
export { selftestCodexLb } from './install-helpers-codex-lb-selftest.js';
|