sneakoscope 8.1.2 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +96 -58
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/bin/fast-inline.js +5 -51
  5. package/dist/cli/command-manifest-lite.js +2 -1
  6. package/dist/cli/command-registry.js +2 -1
  7. package/dist/cli/global-mode-router.js +1 -1
  8. package/dist/cli/install-helpers-codex-lb-config.js +112 -914
  9. package/dist/cli/install-helpers-codex-lb-shared.js +3 -44
  10. package/dist/cli/install-helpers-install-support.js +4 -20
  11. package/dist/cli/install-helpers.js +36 -2716
  12. package/dist/cli/router.js +9 -9
  13. package/dist/commands/bridge-command-output.js +111 -0
  14. package/dist/commands/bridge.js +320 -0
  15. package/dist/commands/codex-app.js +39 -123
  16. package/dist/commands/doctor-helpers.js +380 -0
  17. package/dist/commands/doctor-profile.js +3 -0
  18. package/dist/commands/doctor.js +114 -873
  19. package/dist/commands/mad-sks.js +0 -4
  20. package/dist/config/skills-manifest.json +1 -1
  21. package/dist/core/agents/agent-effort-policy.js +11 -21
  22. package/dist/core/agents/agent-recursion-guard.js +26 -0
  23. package/dist/core/agents/agent-runner-codex-exec.js +7 -9
  24. package/dist/core/architecture-hardening/contracts/contracts.js +225 -161
  25. package/dist/core/architecture-hardening/state/state-service.js +49 -13
  26. package/dist/core/auto-review.js +15 -9
  27. package/dist/core/codex/codex-config-guard.js +8 -0
  28. package/dist/core/codex-adapter.js +8 -10
  29. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -29
  30. package/dist/core/codex-app/codex-app-launcher.js +10 -12
  31. package/dist/core/codex-app/codex-app-restart.js +10 -12
  32. package/dist/core/codex-app/menubar/constants.js +2 -5
  33. package/dist/core/codex-app/menubar/global-install.js +3 -2
  34. package/dist/core/codex-app/menubar/installer/result.js +23 -0
  35. package/dist/core/codex-app/menubar/installer/runtime.js +28 -0
  36. package/dist/core/codex-app/menubar/installer.js +5 -96
  37. package/dist/core/codex-app/menubar/migration.js +1 -42
  38. package/dist/core/codex-app/openrouter-provider.js +0 -78
  39. package/dist/core/codex-app.js +97 -360
  40. package/dist/core/codex-control/codex-app-server-runtime-env.js +8 -28
  41. package/dist/core/codex-control/codex-current-image-path-real-probe.js +2 -3
  42. package/dist/core/codex-control/codex-current-web-search-probe.js +2 -3
  43. package/dist/core/codex-control/codex-sdk-adapter.js +5 -48
  44. package/dist/core/codex-control/codex-task-runner.js +16 -60
  45. package/dist/core/codex-control/desktop-bridge-launch-guard.js +227 -0
  46. package/dist/core/codex-exec-output-schema.js +11 -13
  47. package/dist/core/codex-lb/bridge-contracts.js +2 -0
  48. package/dist/core/codex-lb/bridge-runtime-validation/capability.js +104 -0
  49. package/dist/core/codex-lb/bridge-runtime-validation/catalog.js +52 -0
  50. package/dist/core/codex-lb/bridge-runtime-validation/shared.js +109 -0
  51. package/dist/core/codex-lb/bridge-runtime-validation/status.js +250 -0
  52. package/dist/core/codex-lb/bridge-runtime-validation.js +106 -0
  53. package/dist/core/codex-lb/capability-runner.js +505 -201
  54. package/dist/core/codex-lb/codex-lb-env.js +179 -12
  55. package/dist/core/codex-lb/combined-catalog/builder.js +86 -0
  56. package/dist/core/codex-lb/combined-catalog/contracts.js +5 -0
  57. package/dist/core/codex-lb/combined-catalog/normalize.js +170 -0
  58. package/dist/core/codex-lb/combined-catalog/persistence.js +207 -0
  59. package/dist/core/codex-lb/combined-catalog/shared.js +12 -0
  60. package/dist/core/codex-lb/combined-catalog/storage.js +171 -0
  61. package/dist/core/codex-lb/combined-catalog.js +12 -0
  62. package/dist/core/codex-lb/desktop-bridge/client.js +4 -1
  63. package/dist/core/codex-lb/desktop-bridge/header-policy.js +48 -63
  64. package/dist/core/codex-lb/desktop-bridge/http-forward.js +115 -83
  65. package/dist/core/codex-lb/desktop-bridge/launchd.js +20 -13
  66. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +4 -1
  67. package/dist/core/codex-lb/desktop-bridge/security.js +381 -267
  68. package/dist/core/codex-lb/desktop-bridge/server.js +148 -19
  69. package/dist/core/codex-lb/desktop-bridge/state.js +56 -70
  70. package/dist/core/codex-lb/desktop-bridge/types.js +6 -1
  71. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +281 -106
  72. package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +245 -0
  73. package/dist/core/codex-lb/desktop-bridge-migration/historical-toml.js +56 -0
  74. package/dist/core/codex-lb/desktop-bridge-migration/metadata.js +73 -0
  75. package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +52 -0
  76. package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +106 -0
  77. package/dist/core/codex-lb/desktop-bridge-migration/types.js +1 -0
  78. package/dist/core/codex-lb/desktop-bridge-migration.js +238 -0
  79. package/dist/core/codex-lb/desktop-controller-v3/catalog.js +257 -0
  80. package/dist/core/codex-lb/desktop-controller-v3/diagnostics.js +62 -0
  81. package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +203 -0
  82. package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +293 -0
  83. package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +144 -0
  84. package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +182 -0
  85. package/dist/core/codex-lb/desktop-controller-v3/shared.js +279 -0
  86. package/dist/core/codex-lb/desktop-controller-v3/status.js +269 -0
  87. package/dist/core/codex-lb/desktop-controller-v3/types.js +1 -0
  88. package/dist/core/codex-lb/desktop-controller-v3/verification.js +89 -0
  89. package/dist/core/codex-lb/desktop-controller-v3.js +67 -0
  90. package/dist/core/codex-lb/desktop-controller.js +2 -1730
  91. package/dist/core/codex-lb/desktop-service.js +462 -627
  92. package/dist/core/codex-lb/migration-receipt.js +560 -118
  93. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +37 -53
  94. package/dist/core/codex-lb/probes/bridge-probe.js +125 -64
  95. package/dist/core/codex-lb/probes/catalog-probe.js +36 -136
  96. package/dist/core/codex-lb/probes/computer-use-probe.js +63 -39
  97. package/dist/core/codex-lb/probes/image-generation-probe.js +98 -41
  98. package/dist/core/codex-lb/probes/probe-evidence.js +32 -33
  99. package/dist/core/codex-lb/probes/voice-realtime-probe.js +69 -43
  100. package/dist/core/codex-lb/provider-credentials/configuration.js +127 -0
  101. package/dist/core/codex-lb/provider-credentials/locks.js +11 -0
  102. package/dist/core/codex-lb/provider-credentials/private-snapshot.js +28 -0
  103. package/dist/core/codex-lb/provider-credentials/removal.js +33 -0
  104. package/dist/core/codex-lb/provider-credentials/resolution.js +59 -0
  105. package/dist/core/codex-lb/provider-credentials/runtime.js +55 -0
  106. package/dist/core/codex-lb/provider-credentials/types.js +1 -0
  107. package/dist/core/codex-lb/provider-credentials/validation-store.js +137 -0
  108. package/dist/core/codex-lb/provider-credentials.js +5 -0
  109. package/dist/core/codex-lb/provider-registry.js +323 -0
  110. package/dist/core/codex-lb/provider-route-policy.js +118 -0
  111. package/dist/core/codex-lb/request-route-resolver.js +172 -0
  112. package/dist/core/codex-lb/route-index.js +148 -0
  113. package/dist/core/codex-lb/trusted-deep-evidence.js +158 -286
  114. package/dist/core/commands/basic-cli.js +1 -1
  115. package/dist/core/commands/check-command.js +3 -10
  116. package/dist/core/commands/gate-result-contract.js +10 -14
  117. package/dist/core/commands/intent-normalization/normalizer.js +1 -1
  118. package/dist/core/commands/mad-sks-command.js +2 -6
  119. package/dist/core/commands/release-command.js +11 -1
  120. package/dist/core/commands/telegram-command.js +46 -6
  121. package/dist/core/doctor/doctor-readiness-matrix.js +9 -7
  122. package/dist/core/doctor/skill-legacy-surface.js +5 -5
  123. package/dist/core/evidence/v2/evidence-key.js +30 -3
  124. package/dist/core/feature-fixtures.js +8 -1
  125. package/dist/core/feature-registry.js +3 -2
  126. package/dist/core/hooks-runtime/stop-finalization.js +11 -1
  127. package/dist/core/hooks-runtime/subagent-context.js +15 -1
  128. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +1 -1
  129. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  130. package/dist/core/hooks-runtime.js +73 -87
  131. package/dist/core/image-ux-review/imagegen-adapter.js +108 -94
  132. package/dist/core/image-ux-review/real-callout-extractor.js +144 -14
  133. package/dist/core/imagegen/desktop-bridge-imagegen-target.js +77 -0
  134. package/dist/core/imagegen/imagegen-capability.js +43 -151
  135. package/dist/core/init/skills/filesystem.js +89 -0
  136. package/dist/core/init/skills/inventory.js +97 -0
  137. package/dist/core/init/skills/manifest.js +151 -0
  138. package/dist/core/init/skills.js +5 -325
  139. package/dist/core/init.js +0 -63
  140. package/dist/core/install/installed-package-smoke.js +1 -1
  141. package/dist/core/managed-assets/managed-assets-manifest.js +0 -1
  142. package/dist/core/policy/latest-version-guidance.js +15 -2
  143. package/dist/core/preflight/parallel-preflight-engine.js +27 -51
  144. package/dist/core/provider/provider-badge.js +9 -4
  145. package/dist/core/provider/provider-context.js +57 -107
  146. package/dist/core/providers/openrouter/openrouter-account.js +3 -1
  147. package/dist/core/providers/openrouter/openrouter-secret-store.js +91 -22
  148. package/dist/core/release/desktop-bridge-release-evidence.js +457 -0
  149. package/dist/core/release/gate-manifest.js +0 -1
  150. package/dist/core/release/main-push-guard.js +13 -0
  151. package/dist/core/release/main-push-receipt.js +11 -0
  152. package/dist/core/release/physical-release-gates.js +664 -61
  153. package/dist/core/release/publish-preflight.js +3 -3
  154. package/dist/core/release/release-gate-batch-runner.js +19 -6
  155. package/dist/core/release/release-gate-dag.js +33 -6
  156. package/dist/core/release/release-gate-node.js +1 -3
  157. package/dist/core/release/release-gate-output-contract.js +55 -0
  158. package/dist/core/release/release-pack-contract.js +1 -1
  159. package/dist/core/release/release-pack-receipt.js +209 -8
  160. package/dist/core/release/release-real-contract.js +1 -1
  161. package/dist/core/release/stage-publish.js +106 -28
  162. package/dist/core/retention/temp-lease.js +204 -0
  163. package/dist/core/retention.js +18 -195
  164. package/dist/core/routes.js +1 -2
  165. package/dist/core/safety/intent-contract/intent-contract.js +3 -3
  166. package/dist/core/subagents/naruto-host-credentials.js +6 -1
  167. package/dist/core/subagents/official-subagent-config.js +27 -9
  168. package/dist/core/subagents/official-subagent-runner.js +14 -31
  169. package/dist/core/subagents/wave-lifecycle.js +36 -8
  170. package/dist/core/subagents/wave-parent-guidance.js +2 -1
  171. package/dist/core/telegram/doctor.js +2 -2
  172. package/dist/core/telegram/liveness.js +10 -4
  173. package/dist/core/triwiki/context-graph/compiler/index.js +18 -18
  174. package/dist/core/update/update-migration-state/fast-mode-config.js +172 -0
  175. package/dist/core/update/update-migration-state/session-state-split.js +247 -0
  176. package/dist/core/update/update-migration-state/simple-stages.js +46 -0
  177. package/dist/core/update/update-migration-state.js +4 -481
  178. package/dist/core/version.js +1 -1
  179. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +28 -4
  180. package/dist/native/sks-menubar/Sources/OperationModels.swift +112 -2
  181. package/dist/native/sks-menubar/Sources/ProviderRouteExplanation.swift +21 -0
  182. package/dist/native/sks-menubar/Sources/ProvidersBridgeCatalog.swift +140 -0
  183. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +107 -470
  184. package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +27 -197
  185. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +335 -269
  186. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +233 -437
  187. package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +16 -7
  188. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +39 -9
  189. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
  190. package/dist/scripts/canonical-test-runner.js +7 -1
  191. package/dist/scripts/check-runtime-schemas.js +7 -2
  192. package/dist/scripts/codex-sdk-real-smoke-check.js +3 -1
  193. package/dist/scripts/desktop-bridge-capabilities-check.js +123 -0
  194. package/dist/scripts/{codex-lb-desktop-bridge-latency-check.js → desktop-bridge-latency-check.js} +61 -16
  195. package/dist/scripts/desktop-bridge-real-evidence-check.js +64 -0
  196. package/dist/scripts/{codex-lb-desktop-bridge-check.js → desktop-bridge-transport-check.js} +92 -155
  197. package/dist/scripts/desktop-bridge-transport-client.js +116 -0
  198. package/dist/scripts/desktop-bridge-unification-check.js +276 -0
  199. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -8
  200. package/dist/scripts/gate-policy-audit-check.js +52 -2
  201. package/dist/scripts/provider-badge-context-check.js +55 -12
  202. package/dist/scripts/publish-preflight.js +1 -2
  203. package/dist/scripts/release-affected-selector-check.js +1 -2
  204. package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
  205. package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
  206. package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -0
  207. package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
  208. package/dist/scripts/release-gate-batch-runner-check.js +9 -0
  209. package/dist/scripts/release-gate-dag-runner-check.js +6 -0
  210. package/dist/scripts/release-gate-dag-runner.js +4 -2
  211. package/dist/scripts/release-metadata-check.js +16 -3
  212. package/dist/scripts/release-pack-receipt.js +29 -6
  213. package/dist/scripts/release-physical-gates-check.js +39 -0
  214. package/dist/scripts/release-real-check.js +7 -5
  215. package/package.json +6 -8
  216. package/release-gates.v2.json +156 -157
  217. package/runtime-required-scripts.json +11 -11
  218. package/schemas/codex/desktop-bridge-status-v3.schema.json +286 -0
  219. package/schemas/codex/desktop-capabilities-v3.schema.json +198 -0
  220. package/schemas/release/release-gate-node.schema.json +2 -3
  221. package/dist/cli/codex-lb-setup-warning-output.js +0 -17
  222. package/dist/cli/install-helpers-codex-lb-chain.js +0 -529
  223. package/dist/commands/codex-lb.js +0 -1232
  224. package/dist/core/architecture-hardening/migration/migration.js +0 -111
  225. package/dist/core/codex-app/auxiliary-oauth-policy.js +0 -36
  226. package/dist/core/codex-app/catalog-compat/catalog-service.js +0 -104
  227. package/dist/core/codex-app/child-policy/child-policy.js +0 -35
  228. package/dist/core/codex-app/desktop-routing-snapshot.js +0 -233
  229. package/dist/core/codex-app/glm-model-profile.js +0 -1
  230. package/dist/core/codex-app/glm-profile-installer.js +0 -191
  231. package/dist/core/codex-app/glm-profile-schema.js +0 -42
  232. package/dist/core/codex-app/multi-provider-router.js +0 -391
  233. package/dist/core/codex-app/openrouter-activate.js +0 -405
  234. package/dist/core/codex-app/openrouter-model-catalog.js +0 -217
  235. package/dist/core/codex-app/provider-child-policy.js +0 -64
  236. package/dist/core/codex-app/provider-mode.js +0 -91
  237. package/dist/core/codex-app/provider-session-policy.js +0 -85
  238. package/dist/core/codex-app/session-policy/session-pinning.js +0 -68
  239. package/dist/core/codex-control/codex-lb-launch-recovery.js +0 -319
  240. package/dist/core/codex-lb/codex-lb-setup.js +0 -293
  241. package/dist/core/codex-lb/codex-lb-tool-catalog.js +0 -456
  242. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +0 -290
  243. package/dist/core/codex-lb/desktop-center-credentials.js +0 -1083
  244. package/dist/core/codex-lb/desktop-mode.js +0 -32
  245. package/dist/core/codex-lb/desktop-real-evidence.js +0 -168
  246. package/dist/core/codex-lb/legacy-migration.js +0 -576
  247. package/dist/core/codex-lb/native-openai-transport/transport.js +0 -68
  248. package/dist/core/codex-lb/provider-routing/provider-router.js +0 -58
  249. package/dist/core/codex-lb/routing-truth.js +0 -327
  250. package/dist/core/imagegen/codex-lb-imagegen-target.js +0 -92
  251. package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +0 -118
  252. package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +0 -17
  253. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +0 -331
  254. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +0 -260
  255. package/dist/scripts/codex-lb-catalog-passthrough-check.js +0 -120
  256. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +0 -169
  257. package/dist/scripts/codex-lb-desktop-capabilities-check.js +0 -287
  258. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +0 -123
  259. package/dist/scripts/codex-lb-fast-mode-truth-check.js +0 -114
  260. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +0 -152
  261. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +0 -231
@@ -1,576 +0,0 @@
1
- import fsp from 'node:fs/promises';
2
- import { createHash } from 'node:crypto';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { CODEX_LB_DESKTOP_BRIDGE_MARKER, CODEX_LB_MODEL_CATALOG_MARKER, upsertCodexLbCompatDesktopConfig, upsertCodexLbNativeDesktopConfig } from '../../cli/install-helpers-codex-lb-config.js';
6
- import { codexAuthChatgptBackupPath } from '../../cli/install-helpers-codex-lb-shared.js';
7
- import { ensureTrailingNewline, safeWriteCodexConfigToml } from '../codex-runtime/codex-desktop-config-policy.js';
8
- import { messageOf as errorMessage } from '../errors/message.js';
9
- import { ensureDir, readText } from '../fsx.js';
10
- import { escapeRegExp } from '../text/regex.js';
11
- import { assertDesktopAuthUnchangedBySks, assertDesktopOAuthSemanticIdentity, captureCodexAuthSnapshot, codexAuthApiKeyMatches } from './desktop-auth-invariant.js';
12
- import { DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT, parseCodexLbGatewayAuthTransport } from './desktop-mode.js';
13
- import { backupCodexLbMigrationFile, codexLbMigrationReceiptDir, createCodexLbMigrationReceiptId, finalizeCodexLbMigrationReceiptFiles, rollbackCodexLbMigrationReceipt, writeCodexLbMigrationReceipt } from './migration-receipt.js';
14
- const LEGACY_OPENAI_ROUTING_MARKER = '# sks-codex-lb-managed-openai-base-url';
15
- export async function detectLegacyCodexLbDesktopState(input = {}) {
16
- const home = input.home || process.env.HOME || os.homedir();
17
- const configPath = input.configPath || path.join(home, '.codex', 'config.toml');
18
- const authPath = input.authPath || path.join(home, '.codex', 'auth.json');
19
- const oauthBackupPath = input.oauthBackupPath || codexAuthChatgptBackupPath(home);
20
- const receiptDir = input.receiptDir || codexLbMigrationReceiptDir(home);
21
- const [config, auth, backup, priorReceiptPresent, backupFile, gatewayKeyMatches] = await Promise.all([
22
- readText(configPath, ''),
23
- captureCodexAuthSnapshot({ home, authPath }),
24
- captureCodexAuthSnapshot({ home, authPath: oauthBackupPath }),
25
- hasPriorReceipt(receiptDir),
26
- inspectOAuthBackupFile(oauthBackupPath),
27
- input.expectedGatewayApiKey
28
- ? codexAuthApiKeyMatches({
29
- home,
30
- authPath,
31
- expectedApiKey: input.expectedGatewayApiKey
32
- })
33
- : Promise.resolve(null)
34
- ]);
35
- const providerSelected = topLevelTomlString(config, 'model_provider') === 'codex-lb';
36
- const providerBaseUrl = tomlTableString(config, 'model_providers.codex-lb', 'base_url') || null;
37
- const legacyOpenAiRoutingMarkerPresent = hasAnyTopLevelMarker(config, [
38
- LEGACY_OPENAI_ROUTING_MARKER
39
- ]);
40
- const managedOpenAiBaseUrl = hasAnyTopLevelMarker(config, [
41
- LEGACY_OPENAI_ROUTING_MARKER,
42
- CODEX_LB_DESKTOP_BRIDGE_MARKER
43
- ])
44
- ? topLevelTomlString(config, 'openai_base_url') || null
45
- : null;
46
- const expectedRemote = normalizeUrl(input.remoteBaseUrl || providerBaseUrl || '');
47
- const gatewayKeyBindingChecked = Boolean(input.expectedGatewayApiKey);
48
- const managedRemoteRouting = Boolean(managedOpenAiBaseUrl
49
- && expectedRemote
50
- && normalizeUrl(managedOpenAiBaseUrl) === expectedRemote
51
- && !isLoopbackUrl(managedOpenAiBaseUrl));
52
- const catalogPath = topLevelTomlString(config, 'model_catalog_json');
53
- const cliProviderContract = Boolean(providerBaseUrl
54
- && expectedRemote
55
- && normalizeUrl(providerBaseUrl) === expectedRemote
56
- && !isLoopbackUrl(providerBaseUrl)
57
- && tomlTableString(config, 'model_providers.codex-lb', 'env_key') === 'CODEX_LB_API_KEY'
58
- && tomlTableBoolean(config, 'model_providers.codex-lb', 'requires_openai_auth') === false);
59
- const orphanedManagedRemoteRouting = Boolean(!topLevelTomlString(config, 'openai_base_url')
60
- && cliProviderContract
61
- && gatewayKeyMatches === true
62
- && (legacyOpenAiRoutingMarkerPresent
63
- || Boolean(catalogPath && isSksOwnedLegacyCatalogPath(catalogPath, home))
64
- || backup.mode === 'chatgpt_oauth'
65
- || backup.mode === 'mixed'));
66
- const oauthBackupIdentityPresent = backup.semantic_fingerprint !== null;
67
- const oauthBackupValid = ((backup.mode === 'chatgpt_oauth' || backup.mode === 'mixed')
68
- && backupFile.owner_safe
69
- && backupFile.mode_safe
70
- && oauthBackupIdentityPresent);
71
- const recoveryEvidence = oauthBackupValid || priorReceiptPresent;
72
- const legacyDestructiveMode = auth.mode === 'openai_api_key'
73
- && gatewayKeyMatches === true
74
- && ((providerSelected && managedRemoteRouting) || orphanedManagedRemoteRouting);
75
- const blockers = [];
76
- if (auth.mode !== 'openai_api_key')
77
- blockers.push('legacy_auth_not_api_key_only');
78
- if (!gatewayKeyBindingChecked)
79
- blockers.push('legacy_gateway_key_binding_unavailable');
80
- else if (gatewayKeyMatches !== true)
81
- blockers.push('legacy_gateway_key_mismatch');
82
- if (!providerSelected && !orphanedManagedRemoteRouting)
83
- blockers.push('legacy_codex_lb_provider_not_selected');
84
- if (!managedRemoteRouting && !orphanedManagedRemoteRouting) {
85
- blockers.push('legacy_managed_remote_openai_routing_missing');
86
- }
87
- if (!recoveryEvidence)
88
- blockers.push('legacy_oauth_recovery_evidence_missing');
89
- return {
90
- schema: 'sks.codex-lb-legacy-desktop-detection.v1',
91
- legacy_destructive_mode: legacyDestructiveMode,
92
- auth_mode: auth.mode,
93
- auth_path: authPath,
94
- config_path: configPath,
95
- oauth_backup_path: oauthBackupPath,
96
- oauth_backup_valid: oauthBackupValid,
97
- oauth_backup_sha256: backup.sha256,
98
- oauth_backup_owner_safe: backupFile.owner_safe,
99
- oauth_backup_mode_safe: backupFile.mode_safe,
100
- oauth_backup_identity_present: oauthBackupIdentityPresent,
101
- gateway_key_binding_checked: gatewayKeyBindingChecked,
102
- gateway_key_matches: gatewayKeyMatches,
103
- provider_selected: providerSelected,
104
- provider_base_url: providerBaseUrl,
105
- managed_openai_base_url: managedOpenAiBaseUrl,
106
- managed_remote_routing: managedRemoteRouting,
107
- recovery_evidence: recoveryEvidence,
108
- prior_receipt_present: priorReceiptPresent,
109
- blockers
110
- };
111
- }
112
- export async function migrateLegacyCodexLbDesktop(input) {
113
- const home = input.home || process.env.HOME || os.homedir();
114
- const configPath = input.configPath || path.join(home, '.codex', 'config.toml');
115
- const authPath = input.authPath || path.join(home, '.codex', 'auth.json');
116
- const oauthBackupPath = input.oauthBackupPath || codexAuthChatgptBackupPath(home);
117
- const receiptDir = input.receiptDir || codexLbMigrationReceiptDir(home);
118
- const gatewayAuthTransport = parseCodexLbGatewayAuthTransport(input.gatewayAuthTransport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT);
119
- const targetMode = input.targetMode === 'desktop-dual-auth-compat'
120
- ? 'desktop-dual-auth-compat'
121
- : 'desktop-native-bridge';
122
- if (desktopCompatMigrationUnavailable(targetMode)) {
123
- return {
124
- schema: 'sks.codex-lb-legacy-migration.v1',
125
- ok: false,
126
- status: 'failed',
127
- mode: targetMode,
128
- identity_plane: 'unavailable',
129
- routing_plane: 'unchanged',
130
- gateway_auth_transport: gatewayAuthTransport,
131
- oauth_preserved: false,
132
- auth_path: authPath,
133
- config_path: configPath,
134
- receipt_path: null,
135
- detection: await detectLegacyCodexLbDesktopState({
136
- home,
137
- configPath,
138
- authPath,
139
- oauthBackupPath,
140
- receiptDir,
141
- ...(input.remoteBaseUrl ? { remoteBaseUrl: input.remoteBaseUrl } : {}),
142
- ...(input.gatewayApiKey ? { expectedGatewayApiKey: input.gatewayApiKey } : {})
143
- }),
144
- blockers: ['desktop_dual_auth_compat_requires_global_secret_environment'],
145
- error: 'desktop_dual_auth_compat_requires_global_secret_environment'
146
- };
147
- }
148
- const detection = await detectLegacyCodexLbDesktopState({
149
- home,
150
- configPath,
151
- authPath,
152
- oauthBackupPath,
153
- receiptDir,
154
- ...(input.remoteBaseUrl ? { remoteBaseUrl: input.remoteBaseUrl } : {}),
155
- ...(input.gatewayApiKey ? { expectedGatewayApiKey: input.gatewayApiKey } : {})
156
- });
157
- const baseResult = {
158
- schema: 'sks.codex-lb-legacy-migration.v1',
159
- mode: targetMode,
160
- gateway_auth_transport: gatewayAuthTransport,
161
- auth_path: authPath,
162
- config_path: configPath,
163
- detection
164
- };
165
- if (!detection.legacy_destructive_mode) {
166
- return {
167
- ...baseResult,
168
- ok: false,
169
- status: 'not_legacy',
170
- identity_plane: 'unavailable',
171
- routing_plane: 'unchanged',
172
- oauth_preserved: false,
173
- receipt_path: null,
174
- blockers: detection.blockers
175
- };
176
- }
177
- if (!detection.oauth_backup_valid) {
178
- return {
179
- ...baseResult,
180
- ok: false,
181
- status: 'oauth_login_required',
182
- identity_plane: 'unavailable',
183
- routing_plane: 'unchanged',
184
- oauth_preserved: false,
185
- receipt_path: null,
186
- blockers: ['valid_chatgpt_oauth_backup_required', 'run_codex_login']
187
- };
188
- }
189
- const remoteBaseUrl = normalizeUrl(input.remoteBaseUrl || detection.provider_base_url || '');
190
- if (!remoteBaseUrl) {
191
- return {
192
- ...baseResult,
193
- ok: false,
194
- status: 'failed',
195
- identity_plane: 'unavailable',
196
- routing_plane: 'unchanged',
197
- oauth_preserved: false,
198
- receipt_path: null,
199
- blockers: ['missing_remote_base_url'],
200
- error: 'missing_remote_base_url'
201
- };
202
- }
203
- if (!input.restartApp) {
204
- return {
205
- ...baseResult,
206
- ok: false,
207
- status: 'restart_required',
208
- identity_plane: 'unavailable',
209
- routing_plane: 'unchanged',
210
- oauth_preserved: false,
211
- receipt_path: null,
212
- blockers: ['codex_app_restart_required', 'rerun_with_restart_app']
213
- };
214
- }
215
- if (!input.quitApp) {
216
- return {
217
- ...baseResult,
218
- ok: false,
219
- status: 'restart_required',
220
- identity_plane: 'unavailable',
221
- routing_plane: 'unchanged',
222
- oauth_preserved: false,
223
- receipt_path: null,
224
- blockers: ['codex_app_quit_required', 'rerun_with_restart_app']
225
- };
226
- }
227
- const quitApp = await input.quitApp();
228
- if (quitApp.ok !== true || quitApp.skipped === true) {
229
- return {
230
- ...baseResult,
231
- ok: false,
232
- status: 'failed',
233
- identity_plane: 'unavailable',
234
- routing_plane: 'unchanged',
235
- oauth_preserved: false,
236
- receipt_path: null,
237
- quit_app: quitApp,
238
- blockers: ['codex_app_quiescence_required'],
239
- error: `codex_app_quit_failed:${quitApp.status || 'failed'}`
240
- };
241
- }
242
- const quiescedDetection = await detectLegacyCodexLbDesktopState({
243
- home,
244
- configPath,
245
- authPath,
246
- oauthBackupPath,
247
- receiptDir,
248
- ...(input.remoteBaseUrl ? { remoteBaseUrl: input.remoteBaseUrl } : {}),
249
- ...(input.gatewayApiKey ? { expectedGatewayApiKey: input.gatewayApiKey } : {})
250
- });
251
- const stateStableAfterQuit = quiescedDetection.legacy_destructive_mode
252
- && quiescedDetection.gateway_key_matches === true
253
- && quiescedDetection.oauth_backup_valid
254
- && quiescedDetection.oauth_backup_sha256 === detection.oauth_backup_sha256
255
- && quiescedDetection.provider_base_url === detection.provider_base_url
256
- && quiescedDetection.managed_openai_base_url === detection.managed_openai_base_url;
257
- if (!stateStableAfterQuit) {
258
- return {
259
- ...baseResult,
260
- detection: quiescedDetection,
261
- ok: false,
262
- status: 'failed',
263
- identity_plane: 'unavailable',
264
- routing_plane: 'unchanged',
265
- oauth_preserved: false,
266
- receipt_path: null,
267
- quit_app: quitApp,
268
- blockers: ['legacy_state_changed_during_quiescence', ...quiescedDetection.blockers],
269
- error: 'legacy_state_changed_during_quiescence'
270
- };
271
- }
272
- const receiptId = createCodexLbMigrationReceiptId();
273
- const backupDir = path.join(receiptDir, receiptId, 'files');
274
- const currentConfig = await readText(configPath, '');
275
- const catalogPath = topLevelTomlString(currentConfig, 'model_catalog_json');
276
- const paths = [
277
- { path: configPath, owned: false },
278
- { path: authPath, owned: false },
279
- ...(catalogPath ? [{ path: catalogPath, owned: isSksOwnedLegacyCatalogPath(catalogPath, home) }] : []),
280
- ...(input.bridgeSettingsPath ? [{ path: input.bridgeSettingsPath, owned: true }] : []),
281
- ...(input.bridgeLaunchAgentPath ? [{ path: input.bridgeLaunchAgentPath, owned: true }] : []),
282
- ...(input.bridgeStatePath ? [{ path: input.bridgeStatePath, owned: true }] : [])
283
- ];
284
- const backups = [];
285
- let bridgeAttempted = false;
286
- let restartAttempted = false;
287
- try {
288
- for (const file of uniquePaths(paths)) {
289
- backups.push(await backupCodexLbMigrationFile(file.path, backupDir, file.owned));
290
- }
291
- if (!input.gatewayApiKey
292
- || !await codexAuthApiKeyMatches({
293
- home,
294
- authPath,
295
- expectedApiKey: input.gatewayApiKey
296
- })) {
297
- throw new Error('legacy_gateway_key_changed_before_auth_restore');
298
- }
299
- const oauthBytes = await fsp.readFile(oauthBackupPath);
300
- if (sha256(oauthBytes) !== quiescedDetection.oauth_backup_sha256) {
301
- throw new Error('oauth_backup_changed_before_restore');
302
- }
303
- await writeBufferAtomic(authPath, oauthBytes);
304
- const oauthBaseline = await captureCodexAuthSnapshot({ home, authPath });
305
- if (oauthBaseline.mode !== 'chatgpt_oauth' && oauthBaseline.mode !== 'mixed') {
306
- throw new Error('restored_oauth_backup_invalid');
307
- }
308
- assertDesktopOAuthSemanticIdentity(await captureCodexAuthSnapshot({ home, authPath: oauthBackupPath }), oauthBaseline);
309
- let configForMigration = currentConfig;
310
- configForMigration = removeTopLevelTomlKey(configForMigration, 'model_provider');
311
- configForMigration = removeTopLevelTomlKey(configForMigration, 'openai_base_url');
312
- configForMigration = removeTopLevelMarker(configForMigration, LEGACY_OPENAI_ROUTING_MARKER);
313
- if (catalogPath && isSksOwnedLegacyCatalogPath(catalogPath, home)) {
314
- configForMigration = removeTopLevelTomlKey(configForMigration, 'model_catalog_json');
315
- configForMigration = removeTopLevelMarker(configForMigration, CODEX_LB_MODEL_CATALOG_MARKER);
316
- }
317
- const nextConfig = targetMode === 'desktop-dual-auth-compat'
318
- ? upsertCodexLbCompatDesktopConfig(configForMigration, { remoteBaseUrl })
319
- : upsertCodexLbNativeDesktopConfig(configForMigration, {
320
- bridgeBaseUrl: input.bridgeBaseUrl,
321
- remoteBaseUrl
322
- });
323
- const configWrite = await safeWriteCodexConfigToml(configPath, currentConfig, nextConfig, 'codex-lb-legacy-desktop-migration', { verifyUnchangedBeforeWrite: true });
324
- if (!configWrite.ok)
325
- throw new Error(`legacy_config_write_failed:${configWrite.status}`);
326
- const afterConfigBeforeRestart = await captureCodexAuthSnapshot({ home, authPath });
327
- await assertDesktopAuthUnchangedBySks(oauthBaseline, afterConfigBeforeRestart);
328
- let bridge;
329
- if (targetMode === 'desktop-native-bridge') {
330
- bridgeAttempted = Boolean(input.startBridge);
331
- bridge = input.startBridge ? await input.startBridge() : undefined;
332
- if (bridge?.ok === false)
333
- throw new Error(`desktop_bridge_start_failed:${bridge.status || 'failed'}`);
334
- }
335
- else if (input.stopBridge) {
336
- bridge = await input.stopBridge();
337
- if (bridge?.ok === false)
338
- throw new Error(`desktop_bridge_stop_failed:${bridge.status || 'failed'}`);
339
- }
340
- const beforeRestart = await captureCodexAuthSnapshot({ home, authPath });
341
- await assertDesktopAuthUnchangedBySks(oauthBaseline, beforeRestart);
342
- restartAttempted = true;
343
- const restartApp = await input.restartApp();
344
- if (restartApp.ok !== true)
345
- throw new Error(`codex_app_restart_failed:${restartApp.status || 'failed'}`);
346
- const afterRestart = await captureCodexAuthSnapshot({ home, authPath });
347
- assertDesktopOAuthSemanticIdentity(oauthBaseline, afterRestart);
348
- const capability = input.verifyCapabilities ? await input.verifyCapabilities() : undefined;
349
- if (capability && !capability.ok) {
350
- throw new Error(`desktop_capability_verification_failed:${(capability.blockers || []).join(',') || 'failed'}`);
351
- }
352
- const routingPlane = targetMode === 'desktop-dual-auth-compat'
353
- ? 'desktop_dual_auth_compat'
354
- : 'desktop_native_bridge';
355
- const capabilitySummary = {
356
- ...(input.capabilitySummary || {}),
357
- ...(capability?.summary || {}),
358
- gateway_auth_transport: gatewayAuthTransport,
359
- identity_plane: 'chatgpt_oauth',
360
- routing_plane: routingPlane,
361
- oauth_backup_sha256: quiescedDetection.oauth_backup_sha256 || 'unavailable'
362
- };
363
- const receipt = {
364
- schema: 'sks.codex-lb-migration-receipt.v1',
365
- id: receiptId,
366
- created_at: new Date().toISOString(),
367
- from_mode: 'legacy-destructive-api-key-auth',
368
- to_mode: targetMode,
369
- files: await finalizeCodexLbMigrationReceiptFiles(backups),
370
- bridge_state_path: input.bridgeStatePath || null,
371
- oauth_preserved: true,
372
- capability_summary: capabilitySummary
373
- };
374
- const receiptPath = await writeCodexLbMigrationReceipt(receipt, { receiptDir });
375
- return {
376
- ...baseResult,
377
- detection: quiescedDetection,
378
- ok: true,
379
- status: 'migrated',
380
- identity_plane: 'chatgpt_oauth',
381
- routing_plane: routingPlane,
382
- oauth_preserved: true,
383
- receipt_path: receiptPath,
384
- receipt,
385
- ...(bridge ? { bridge } : {}),
386
- quit_app: quitApp,
387
- restart_app: restartApp,
388
- capability_summary: capabilitySummary,
389
- blockers: []
390
- };
391
- }
392
- catch (error) {
393
- const rollbackQuit = restartAttempted
394
- ? await input.quitApp().catch((quitError) => ({
395
- ok: false,
396
- status: 'failed',
397
- skipped: false,
398
- error: errorMessage(quitError)
399
- }))
400
- : undefined;
401
- if (rollbackQuit && (rollbackQuit.ok !== true || rollbackQuit.skipped === true)) {
402
- return {
403
- ...baseResult,
404
- detection: quiescedDetection,
405
- ok: false,
406
- status: 'failed',
407
- identity_plane: 'chatgpt_oauth',
408
- routing_plane: 'desktop_native_bridge',
409
- oauth_preserved: true,
410
- receipt_path: null,
411
- quit_app: quitApp,
412
- rollback_quit_app: rollbackQuit,
413
- blockers: [
414
- 'legacy_migration_failed',
415
- 'legacy_migration_rollback_quiescence_failed'
416
- ],
417
- error: errorMessage(error)
418
- };
419
- }
420
- const bridgeStop = bridgeAttempted && input.stopBridge
421
- ? await input.stopBridge().catch((stopError) => ({
422
- ok: false,
423
- status: 'failed',
424
- error: errorMessage(stopError)
425
- }))
426
- : undefined;
427
- if (bridgeAttempted
428
- && (!bridgeStop
429
- || bridgeStop.ok !== true
430
- || bridgeStop.running === true)) {
431
- return {
432
- ...baseResult,
433
- detection: quiescedDetection,
434
- ok: false,
435
- status: 'failed',
436
- identity_plane: 'chatgpt_oauth',
437
- routing_plane: 'desktop_native_bridge',
438
- oauth_preserved: true,
439
- receipt_path: null,
440
- ...(bridgeStop ? { bridge_stop: bridgeStop } : {}),
441
- quit_app: quitApp,
442
- ...(rollbackQuit ? { rollback_quit_app: rollbackQuit } : {}),
443
- blockers: [
444
- 'legacy_migration_failed',
445
- 'legacy_migration_bridge_stop_unverified',
446
- 'legacy_migration_manual_recovery_required'
447
- ],
448
- error: errorMessage(error)
449
- };
450
- }
451
- const provisionalReceipt = {
452
- schema: 'sks.codex-lb-migration-receipt.v1',
453
- id: receiptId,
454
- created_at: new Date().toISOString(),
455
- from_mode: 'legacy-destructive-api-key-auth',
456
- to_mode: 'desktop-native-bridge',
457
- files: await finalizeCodexLbMigrationReceiptFiles(backups),
458
- bridge_state_path: input.bridgeStatePath || null,
459
- oauth_preserved: false,
460
- capability_summary: {}
461
- };
462
- const rollback = await rollbackCodexLbMigrationReceipt({ receipt: provisionalReceipt });
463
- return {
464
- ...baseResult,
465
- detection: quiescedDetection,
466
- ok: false,
467
- status: 'failed',
468
- identity_plane: 'unavailable',
469
- routing_plane: 'unchanged',
470
- oauth_preserved: false,
471
- receipt_path: null,
472
- ...(bridgeStop ? { bridge_stop: bridgeStop } : {}),
473
- quit_app: quitApp,
474
- ...(rollbackQuit ? { rollback_quit_app: rollbackQuit } : {}),
475
- blockers: [
476
- 'legacy_migration_failed',
477
- ...(bridgeStop?.ok === false ? ['legacy_migration_bridge_stop_failed'] : []),
478
- ...(rollback.ok ? [] : ['legacy_migration_rollback_failed'])
479
- ],
480
- rollback,
481
- error: errorMessage(error)
482
- };
483
- }
484
- }
485
- async function hasPriorReceipt(receiptDir) {
486
- try {
487
- return (await fsp.readdir(receiptDir)).some((entry) => entry.endsWith('.json'));
488
- }
489
- catch {
490
- return false;
491
- }
492
- }
493
- async function inspectOAuthBackupFile(filePath) {
494
- try {
495
- const stat = await fsp.lstat(filePath);
496
- const expectedUid = typeof process.getuid === 'function' ? process.getuid() : stat.uid;
497
- return {
498
- owner_safe: stat.isFile() && !stat.isSymbolicLink() && stat.uid === expectedUid,
499
- mode_safe: (stat.mode & 0o777) === 0o600
500
- };
501
- }
502
- catch {
503
- return {
504
- owner_safe: false,
505
- mode_safe: false
506
- };
507
- }
508
- }
509
- function uniquePaths(paths) {
510
- const seen = new Map();
511
- for (const entry of paths) {
512
- const resolved = path.resolve(entry.path);
513
- seen.set(resolved, (seen.get(resolved) || false) || entry.owned);
514
- }
515
- return [...seen].map(([filePath, owned]) => ({ path: filePath, owned }));
516
- }
517
- function topLevelTomlString(text, key) {
518
- const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
519
- return topLevel.match(new RegExp(`(?:^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]+)"\\s*(?:#.*)?(?=\\n|$)`))?.[1] || '';
520
- }
521
- function tomlTableString(text, table, key) {
522
- const escapedTable = escapeRegExp(table);
523
- const block = String(text || '').match(new RegExp(`(?:^|\\n)\\[${escapedTable}\\]([\\s\\S]*?)(?=\\n\\[[^\\]]+\\]|\\s*$)`))?.[1] || '';
524
- return block.match(new RegExp(`(?:^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]+)"\\s*(?:#.*)?(?=\\n|$)`))?.[1] || '';
525
- }
526
- function tomlTableBoolean(text, table, key) {
527
- const escapedTable = escapeRegExp(table);
528
- const block = String(text || '').match(new RegExp(`(?:^|\\n)\\[${escapedTable}\\]([\\s\\S]*?)(?=\\n\\[[^\\]]+\\]|\\s*$)`))?.[1] || '';
529
- const value = block.match(new RegExp(`(?:^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*(true|false)\\s*(?:#.*)?(?=\\n|$)`))?.[1];
530
- return value === 'true' ? true : value === 'false' ? false : null;
531
- }
532
- function hasAnyTopLevelMarker(text, markers) {
533
- const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
534
- return topLevel.split(/\r?\n/).some((line) => markers.includes(line.trim()));
535
- }
536
- function removeTopLevelTomlKey(text, key) {
537
- const lines = String(text || '').split('\n');
538
- const firstTable = lines.findIndex((line) => /^\s*\[.+\]\s*$/.test(line));
539
- const end = firstTable === -1 ? lines.length : firstTable;
540
- const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
541
- return ensureTrailingNewline(lines.filter((line, index) => index >= end || !keyPattern.test(line)).join('\n'));
542
- }
543
- function removeTopLevelMarker(text, marker) {
544
- const lines = String(text || '').split('\n');
545
- const firstTable = lines.findIndex((line) => /^\s*\[.+\]\s*$/.test(line));
546
- const end = firstTable === -1 ? lines.length : firstTable;
547
- return ensureTrailingNewline(lines.filter((line, index) => index >= end || line.trim() !== marker).join('\n'));
548
- }
549
- function isSksOwnedLegacyCatalogPath(catalogPath, home) {
550
- return path.resolve(catalogPath) === path.resolve(home, '.codex', 'sks-codex-lb-tool-catalog.json');
551
- }
552
- function normalizeUrl(value) {
553
- return String(value || '').trim().replace(/\/+$/, '');
554
- }
555
- function isLoopbackUrl(value) {
556
- return /^https?:\/\/(?:localhost|127(?:\.\d{1,3}){3}|\[::1\])(?::|\/|$)/i.test(value);
557
- }
558
- function sha256(value) {
559
- return createHash('sha256').update(value).digest('hex');
560
- }
561
- async function writeBufferAtomic(filePath, bytes) {
562
- await ensureDir(path.dirname(filePath));
563
- const tempPath = `${filePath}.${process.pid}.${Date.now().toString(36)}.tmp`;
564
- try {
565
- await fsp.writeFile(tempPath, bytes, { mode: 0o600, flag: 'wx' });
566
- await fsp.rename(tempPath, filePath);
567
- await fsp.chmod(filePath, 0o600);
568
- }
569
- catch (error) {
570
- await fsp.rm(tempPath, { force: true }).catch(() => { });
571
- throw error;
572
- }
573
- }
574
- function desktopCompatMigrationUnavailable(mode) {
575
- return mode === 'desktop-dual-auth-compat';
576
- }
@@ -1,68 +0,0 @@
1
- export const NATIVE_OPENAI_PROVIDER_ID = 'openai';
2
- export const FORBIDDEN_INBOUND_CREDENTIAL_HEADERS = new Set([
3
- 'authorization',
4
- 'proxy-authorization',
5
- 'x-api-key',
6
- 'api-key',
7
- 'x-codex-lb-api-key',
8
- 'openai-organization',
9
- 'cookie',
10
- 'set-cookie'
11
- ]);
12
- export function createNativeOpenAiTransportContract(input) {
13
- if (input.nativeProviderId !== NATIVE_OPENAI_PROVIDER_ID)
14
- throw new Error('native_transport_external_provider_forbidden');
15
- const origin = assertLoopbackHttpOrigin(input.listenOrigin);
16
- return {
17
- schema: 'sks.native-openai-transport.v1',
18
- native_provider_id: NATIVE_OPENAI_PROVIDER_ID,
19
- mode: input.mode,
20
- credential_class: input.mode === 'codex-lb' ? 'codex-lb-api-key' : 'openrouter-api-key',
21
- listen_origin: origin,
22
- http_passthrough: true,
23
- websocket_passthrough: input.mode === 'codex-lb'
24
- };
25
- }
26
- export function buildProviderUpstreamHeaders(contract, inbound, upstreamCredential) {
27
- const credential = String(upstreamCredential || '').trim();
28
- if (!credential)
29
- throw new Error('native_transport_upstream_credential_missing');
30
- const headers = {};
31
- for (const [rawName, value] of Object.entries(inbound)) {
32
- const name = rawName.toLowerCase();
33
- if (value === undefined || FORBIDDEN_INBOUND_CREDENTIAL_HEADERS.has(name))
34
- continue;
35
- if (/[^a-z0-9!#$%&'*+.^_`|~-]/.test(name))
36
- throw new Error('native_transport_header_name_invalid');
37
- headers[name] = value;
38
- }
39
- if (contract.mode === 'codex-lb')
40
- headers['x-codex-lb-api-key'] = credential;
41
- else
42
- headers.authorization = `Bearer ${credential}`;
43
- return headers;
44
- }
45
- export function assertNativeFeaturePassthrough(contract, input) {
46
- if (!input.path.startsWith('/') || /[\r\n\0]/.test(input.path))
47
- throw new Error('native_transport_path_invalid');
48
- if (input.protocol === 'websocket' && !contract.websocket_passthrough) {
49
- throw new Error('native_transport_websocket_unsupported_for_mode');
50
- }
51
- }
52
- export function preserveNativeMetadata(metadata) {
53
- return structuredClone(metadata);
54
- }
55
- function assertLoopbackHttpOrigin(value) {
56
- let parsed;
57
- try {
58
- parsed = new URL(value);
59
- }
60
- catch {
61
- throw new Error('native_transport_loopback_invalid');
62
- }
63
- const host = parsed.hostname.replace(/^\[|\]$/g, '').toLowerCase();
64
- if (parsed.protocol !== 'http:' || !['127.0.0.1', '::1', 'localhost'].includes(host) || parsed.username || parsed.password) {
65
- throw new Error('native_transport_loopback_required');
66
- }
67
- return parsed.origin;
68
- }