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.
Files changed (283) hide show
  1. package/README.md +129 -10
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/codex-app-command.js +1 -1
  5. package/dist/cli/command-manifest-lite.js +4 -1
  6. package/dist/cli/command-registry.js +2 -0
  7. package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
  8. package/dist/cli/install-helpers-codex-lb-config.js +114 -9
  9. package/dist/cli/install-helpers-install-support.js +6 -16
  10. package/dist/cli/install-helpers.js +788 -600
  11. package/dist/cli/router.js +30 -2
  12. package/dist/commands/codex-app.js +37 -1
  13. package/dist/commands/codex-lb.js +491 -300
  14. package/dist/config/skills-manifest.json +57 -71
  15. package/dist/core/align/align-route.js +127 -0
  16. package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
  17. package/dist/core/codex-app/codex-app-restart.js +83 -0
  18. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
  19. package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
  20. package/dist/core/codex-app/menubar/constants.js +5 -0
  21. package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
  22. package/dist/core/codex-app/menubar/installer.js +18 -0
  23. package/dist/core/codex-app/multi-provider-router.js +22 -10
  24. package/dist/core/codex-app/openrouter-activate.js +26 -1
  25. package/dist/core/codex-app.js +118 -26
  26. package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
  27. package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
  28. package/dist/core/codex-control/codex-task-runner.js +2 -2
  29. package/dist/core/codex-lb/capability-runner.js +204 -0
  30. package/dist/core/codex-lb/capability-types.js +1 -0
  31. package/dist/core/codex-lb/codex-lb-env.js +9 -13
  32. package/dist/core/codex-lb/codex-lb-setup.js +45 -8
  33. package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
  34. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
  35. package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
  36. package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
  37. package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
  38. package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
  39. package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
  40. package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
  41. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
  42. package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
  43. package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
  44. package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
  45. package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
  46. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
  47. package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
  48. package/dist/core/codex-lb/desktop-controller.js +1410 -0
  49. package/dist/core/codex-lb/desktop-mode.js +32 -0
  50. package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
  51. package/dist/core/codex-lb/desktop-service.js +613 -0
  52. package/dist/core/codex-lb/legacy-migration.js +575 -0
  53. package/dist/core/codex-lb/migration-receipt.js +246 -0
  54. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
  55. package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
  56. package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
  57. package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
  58. package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
  59. package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
  60. package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
  61. package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
  62. package/dist/core/codex-native/core-skill-manifest.js +22 -6
  63. package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
  64. package/dist/core/commands/align-command.js +220 -0
  65. package/dist/core/commands/image-ux-review-command.js +280 -68
  66. package/dist/core/commands/naruto-command.js +257 -8
  67. package/dist/core/commands/ppt-command.js +2 -2
  68. package/dist/core/commands/telegram-command.js +402 -34
  69. package/dist/core/commands/triwiki-command.js +11 -1
  70. package/dist/core/commands/triwiki-graph-command.js +160 -0
  71. package/dist/core/commands/wiki-command.js +100 -20
  72. package/dist/core/doctor/skill-legacy-surface.js +1 -3
  73. package/dist/core/errors/message.js +3 -0
  74. package/dist/core/evidence/evidence-freshness.js +14 -0
  75. package/dist/core/feature-fixtures.js +9 -1
  76. package/dist/core/feature-registry.js +5 -3
  77. package/dist/core/fsx.js +7 -3
  78. package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
  79. package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
  80. package/dist/core/hooks-runtime/hook-context.js +15 -2
  81. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
  82. package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
  83. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  84. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  85. package/dist/core/hooks-runtime.js +33 -89
  86. package/dist/core/image-ux-review/callout-extraction.js +6 -0
  87. package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
  88. package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
  89. package/dist/core/image-ux-review.js +230 -12
  90. package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
  91. package/dist/core/imagegen/imagegen-capability.js +10 -4
  92. package/dist/core/imagegen/require-imagegen.js +29 -5
  93. package/dist/core/init/skills.js +25 -13
  94. package/dist/core/init.js +1 -1
  95. package/dist/core/install/installed-package-smoke.js +157 -4
  96. package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
  97. package/dist/core/managed-path-safety.js +8 -10
  98. package/dist/core/mcp-config/backup.js +2 -4
  99. package/dist/core/mcp-config/config-reader.js +2 -4
  100. package/dist/core/mcp-config/scope.js +3 -5
  101. package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
  102. package/dist/core/naruto/context-graph-advisor.js +0 -0
  103. package/dist/core/perf/release-latency-slo.js +12 -6
  104. package/dist/core/pipeline-internals/runtime-core.js +78 -0
  105. package/dist/core/pipeline-internals/runtime-gates.js +5 -2
  106. package/dist/core/policy/latest-version-guidance.js +113 -0
  107. package/dist/core/ppt.js +8 -8
  108. package/dist/core/preflight/parallel-preflight-engine.js +1 -1
  109. package/dist/core/proof/auto-finalize.js +98 -6
  110. package/dist/core/proof/route-proof-gate.js +12 -1
  111. package/dist/core/proof/route-proof-policy.js +8 -8
  112. package/dist/core/release/gate-manifest.js +32 -1
  113. package/dist/core/release/npm-stage-contract.js +21 -0
  114. package/dist/core/release/package-size-budget.js +2 -2
  115. package/dist/core/release/release-gate-contract.js +5 -0
  116. package/dist/core/release/release-proof-io.js +1 -3
  117. package/dist/core/release/release-real-contract.js +5 -4
  118. package/dist/core/release/release-stamp-proof.js +1 -3
  119. package/dist/core/release/stage-publish.js +27 -0
  120. package/dist/core/remote/audit-idempotency.js +50 -18
  121. package/dist/core/remote/codex-public-activity.js +283 -0
  122. package/dist/core/remote/index.js +1 -0
  123. package/dist/core/remote/ssh-worker-client.js +4 -1
  124. package/dist/core/remote/worker.js +206 -27
  125. package/dist/core/research/research-adversarial-review.js +1 -3
  126. package/dist/core/research/research-claim-synthesizer.js +1 -3
  127. package/dist/core/research/research-falsification-runner.js +1 -3
  128. package/dist/core/research/research-source-evidence.js +1 -3
  129. package/dist/core/responses-stream.js +65 -19
  130. package/dist/core/routes/constants.js +1 -1
  131. package/dist/core/routes/design-policy.js +7 -2
  132. package/dist/core/routes/dollar-manifest-lite.js +2 -4
  133. package/dist/core/routes/evidence.js +1 -1
  134. package/dist/core/routes.js +30 -10
  135. package/dist/core/safety/command-contract/contracts.js +2 -0
  136. package/dist/core/safety/command-contract/types.js +1 -1
  137. package/dist/core/search/context-graph-seeds.js +205 -0
  138. package/dist/core/search/context.js +0 -0
  139. package/dist/core/search/provider.js +2 -2
  140. package/dist/core/secret-redaction.js +6 -0
  141. package/dist/core/subagents/naruto-help-contract.js +2 -1
  142. package/dist/core/subagents/naruto-host-credentials.js +161 -0
  143. package/dist/core/subagents/official-subagent-preparation.js +4 -1
  144. package/dist/core/subagents/official-subagent-prompt.js +44 -2
  145. package/dist/core/subagents/official-subagent-runner.js +26 -8
  146. package/dist/core/subagents/triwiki-attention.js +135 -123
  147. package/dist/core/telegram/bot-api.js +85 -10
  148. package/dist/core/telegram/center-snapshot.js +718 -0
  149. package/dist/core/telegram/config.js +158 -31
  150. package/dist/core/telegram/hub.js +288 -33
  151. package/dist/core/telegram/index.js +2 -0
  152. package/dist/core/telegram/ingress-spool.js +419 -0
  153. package/dist/core/telegram/ledgers.js +188 -29
  154. package/dist/core/telegram/messages.js +26 -6
  155. package/dist/core/telegram/owner-lock.js +10 -7
  156. package/dist/core/telegram/runtime-projection.js +38 -3
  157. package/dist/core/telegram/runtime.js +298 -63
  158. package/dist/core/telegram/service.js +137 -8
  159. package/dist/core/telegram/setup.js +291 -73
  160. package/dist/core/text/strings.js +3 -0
  161. package/dist/core/triwiki/code-pack.js +12 -120
  162. package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
  163. package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
  164. package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
  165. package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
  166. package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
  167. package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
  168. package/dist/core/triwiki/context-graph/contracts.js +236 -0
  169. package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
  170. package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
  171. package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
  172. package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
  173. package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
  174. package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
  175. package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
  176. package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
  177. package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
  178. package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
  179. package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
  180. package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
  181. package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
  182. package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
  183. package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
  184. package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
  185. package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
  186. package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
  187. package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
  188. package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
  189. package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
  190. package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
  191. package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
  192. package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
  193. package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
  194. package/dist/core/triwiki/context-graph/graph-index.js +143 -0
  195. package/dist/core/triwiki/context-graph/ids.js +0 -0
  196. package/dist/core/triwiki/context-graph/lint/index.js +0 -0
  197. package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
  198. package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
  199. package/dist/core/triwiki/context-graph/paths.js +116 -0
  200. package/dist/core/triwiki/context-graph/profiles.js +78 -0
  201. package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
  202. package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
  203. package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
  204. package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
  205. package/dist/core/triwiki/context-graph/projections/index.js +7 -0
  206. package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
  207. package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
  208. package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
  209. package/dist/core/triwiki/context-graph/query/explain.js +0 -0
  210. package/dist/core/triwiki/context-graph/query/index.js +178 -0
  211. package/dist/core/triwiki/context-graph/query/load.js +116 -0
  212. package/dist/core/triwiki/context-graph/query/pack.js +117 -0
  213. package/dist/core/triwiki/context-graph/query/rank.js +142 -0
  214. package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
  215. package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
  216. package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
  217. package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
  218. package/dist/core/triwiki/context-graph/query-types.js +29 -0
  219. package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
  220. package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
  221. package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
  222. package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
  223. package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
  224. package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
  225. package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
  226. package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
  227. package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
  228. package/dist/core/verification/context-graph-affected.js +271 -0
  229. package/dist/core/version.js +1 -1
  230. package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
  231. package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
  232. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
  233. package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
  234. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
  235. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
  236. package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
  237. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
  238. package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
  239. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
  240. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
  241. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
  242. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
  243. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
  244. package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
  245. package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
  246. package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
  247. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
  248. package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
  249. package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
  250. package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
  251. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
  252. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
  253. package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
  254. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
  255. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
  256. package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
  257. package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
  258. package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
  259. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
  260. package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
  261. package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
  262. package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
  263. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
  264. package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
  265. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
  266. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
  267. package/dist/scripts/context-graph-check.js +191 -0
  268. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
  269. package/dist/scripts/gate-policy-audit-check.js +1 -1
  270. package/dist/scripts/helper-dedup-check.js +0 -2
  271. package/dist/scripts/installed-package-smoke-check.js +4 -1
  272. package/dist/scripts/latest-version-guidance-check.js +74 -0
  273. package/dist/scripts/release-metadata-check.js +21 -4
  274. package/dist/scripts/release-real-check.js +7 -0
  275. package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
  276. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
  277. package/package.json +8 -3
  278. package/release-gates.v2.json +176 -1
  279. package/runtime-required-scripts.json +30 -0
  280. package/schemas/triwiki/context-graph.schema.json +119 -0
  281. package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
  282. package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
  283. package/dist/core/triwiki/code-index-scanner.js +0 -309
@@ -0,0 +1,86 @@
1
+ import fsp from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { readTopLevelTomlString } from '../codex-app/codex-model-catalog.js';
5
+ import { loadCodexLbEnv } from '../codex-lb/codex-lb-env.js';
6
+ export const CODEX_APP_SERVER_PROVIDER_CREDENTIALS_UNAVAILABLE = 'codex_app_server_provider_credentials_unavailable';
7
+ export const CODEX_APP_SERVER_CONFIG_READ_FAILED = 'codex_app_server_config_read_failed';
8
+ export class CodexAppServerRuntimeEnvError extends Error {
9
+ code;
10
+ constructor(code) {
11
+ super(code);
12
+ this.code = code;
13
+ this.name = 'CodexAppServerRuntimeEnvError';
14
+ }
15
+ }
16
+ export function codexAppServerExecutablePath(input) {
17
+ const home = path.resolve(input.home || process.env.HOME || os.homedir());
18
+ const candidates = [
19
+ path.dirname(path.resolve(input.nodeBin)),
20
+ path.join(home, '.local', 'bin'),
21
+ ...(input.inheritedPath || '').split(path.delimiter),
22
+ '/opt/homebrew/bin',
23
+ '/usr/local/bin',
24
+ '/usr/bin',
25
+ '/bin',
26
+ '/usr/sbin',
27
+ '/sbin'
28
+ ];
29
+ return [...new Set(candidates
30
+ .map((entry) => String(entry || '').trim())
31
+ .filter((entry) => path.isAbsolute(entry)))]
32
+ .join(path.delimiter);
33
+ }
34
+ export async function prepareCodexAppServerRuntimeEnv(input = {}) {
35
+ const sourceEnv = input.env || process.env;
36
+ const home = path.resolve(input.home || sourceEnv.HOME || os.homedir());
37
+ const codexHome = path.resolve(input.codexHome || sourceEnv.CODEX_HOME || path.join(home, '.codex'));
38
+ let configText = input.configText;
39
+ if (configText === undefined) {
40
+ const configPath = path.join(codexHome, 'config.toml');
41
+ const readConfigText = input.readConfigTextImpl
42
+ ?? ((file) => fsp.readFile(file, 'utf8'));
43
+ try {
44
+ configText = await readConfigText(configPath);
45
+ }
46
+ catch (error) {
47
+ if (error?.code === 'ENOENT')
48
+ configText = '';
49
+ else
50
+ throw new CodexAppServerRuntimeEnvError(CODEX_APP_SERVER_CONFIG_READ_FAILED);
51
+ }
52
+ }
53
+ const selectedProvider = readTopLevelTomlString(configText, 'model_provider');
54
+ const env = {
55
+ ...sourceEnv,
56
+ PATH: codexAppServerExecutablePath({
57
+ nodeBin: input.nodeBin || process.execPath,
58
+ home,
59
+ ...(sourceEnv.PATH === undefined ? {} : { inheritedPath: sourceEnv.PATH })
60
+ })
61
+ };
62
+ if (selectedProvider !== 'codex-lb') {
63
+ delete env.CODEX_LB_API_KEY;
64
+ delete env.CODEX_LB_BASE_URL;
65
+ return env;
66
+ }
67
+ const load = input.loadCodexLbEnvImpl || loadCodexLbEnv;
68
+ let loaded = null;
69
+ try {
70
+ loaded = await load({
71
+ home,
72
+ processEnv: {},
73
+ envPath: path.join(codexHome, 'sks-codex-lb.env'),
74
+ metadataPath: path.join(codexHome, 'sks-codex-lb.json')
75
+ });
76
+ }
77
+ catch {
78
+ throw new CodexAppServerRuntimeEnvError(CODEX_APP_SERVER_PROVIDER_CREDENTIALS_UNAVAILABLE);
79
+ }
80
+ if (!loaded?.configured || !loaded.secret_api_key || !loaded.base_url) {
81
+ throw new CodexAppServerRuntimeEnvError(CODEX_APP_SERVER_PROVIDER_CREDENTIALS_UNAVAILABLE);
82
+ }
83
+ env.CODEX_LB_API_KEY = loaded.secret_api_key;
84
+ env.CODEX_LB_BASE_URL = loaded.base_url;
85
+ return env;
86
+ }
@@ -1,18 +1,34 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { nowIso, PACKAGE_VERSION } from '../fsx.js';
3
3
  import { resolveCodexRuntime } from '../codex-runtime/resolve-codex-runtime.js';
4
+ export class CodexAppServerRequestError extends Error {
5
+ method;
6
+ kind;
7
+ rpcCode;
8
+ constructor(method, kind, message, rpcCode = null) {
9
+ super(message);
10
+ this.method = method;
11
+ this.kind = kind;
12
+ this.rpcCode = rpcCode;
13
+ this.name = 'CodexAppServerRequestError';
14
+ }
15
+ }
4
16
  export class CodexAppServerV2Client {
5
17
  command;
6
18
  args;
7
19
  env;
8
20
  cwd;
9
21
  timeoutMs;
22
+ maxFrameBytes;
23
+ maxNotifications;
24
+ maxNotificationBytes;
10
25
  currentTimeProvider;
11
26
  approvalPolicy;
12
27
  child = null;
13
28
  nextId = 1;
14
29
  pending = new Map();
15
30
  notifications = [];
31
+ notificationBytes = 0;
16
32
  listeners = new Set();
17
33
  stdoutBuffer = '';
18
34
  stderr = '';
@@ -22,6 +38,9 @@ export class CodexAppServerV2Client {
22
38
  this.env = options.env || process.env;
23
39
  this.cwd = options.cwd || process.cwd();
24
40
  this.timeoutMs = Number(options.timeoutMs || 20_000);
41
+ this.maxFrameBytes = Math.max(1_024, Math.min(32 * 1024 * 1024, options.maxFrameBytes ?? 8 * 1024 * 1024));
42
+ this.maxNotifications = Math.max(16, Math.min(8_192, options.maxNotifications ?? 2_048));
43
+ this.maxNotificationBytes = Math.max(1_024, Math.min(64 * 1024 * 1024, options.maxNotificationBytes ?? 4 * 1024 * 1024));
25
44
  this.currentTimeProvider = options.currentTimeProvider || (() => new Date());
26
45
  this.approvalPolicy = options.approvalPolicy || {};
27
46
  }
@@ -57,6 +76,12 @@ export class CodexAppServerV2Client {
57
76
  async readThread(threadId, includeTurns = false) {
58
77
  return await this.request('thread/read', { threadId, includeTurns });
59
78
  }
79
+ async listThreadTurns(threadId, params = {}) {
80
+ return await this.request('thread/turns/list', {
81
+ threadId,
82
+ ...normalizeThreadListParams(params)
83
+ });
84
+ }
60
85
  async startTurn(params = {}) {
61
86
  return await this.request('turn/start', params);
62
87
  }
@@ -131,9 +156,9 @@ export class CodexAppServerV2Client {
131
156
  if (this.stderr.length > 64 * 1024)
132
157
  this.stderr = this.stderr.slice(-64 * 1024);
133
158
  });
134
- this.child.on('error', (err) => this.rejectAll(err));
159
+ this.child.on('error', (err) => this.rejectAll(err, 'transport'));
135
160
  this.child.on('close', (code, signal) => {
136
- this.rejectAll(new Error(`Codex app-server exited before response (code ${code ?? signal ?? 'unknown'}). ${this.stderr.trim()}`.trim()));
161
+ this.rejectAll(new Error(`Codex app-server exited before response (code ${code ?? signal ?? 'unknown'}). ${this.stderr.trim()}`.trim()), 'process_exit');
137
162
  });
138
163
  }
139
164
  request(method, params) {
@@ -143,7 +168,7 @@ export class CodexAppServerV2Client {
143
168
  return new Promise((resolve, reject) => {
144
169
  const timer = setTimeout(() => {
145
170
  this.pending.delete(id);
146
- reject(new Error(`Codex app-server request timed out: ${method}. ${this.stderr.trim()}`.trim()));
171
+ reject(new CodexAppServerRequestError(method, 'timeout', `Codex app-server request timed out: ${method}. ${this.stderr.trim()}`.trim()));
147
172
  }, this.timeoutMs);
148
173
  timer.unref?.();
149
174
  this.pending.set(id, { method, resolve, reject, timer });
@@ -158,9 +183,17 @@ export class CodexAppServerV2Client {
158
183
  this.stdoutBuffer += chunk.toString('utf8');
159
184
  const lines = this.stdoutBuffer.split(/\r?\n/);
160
185
  this.stdoutBuffer = lines.pop() || '';
186
+ if (Buffer.byteLength(this.stdoutBuffer, 'utf8') > this.maxFrameBytes) {
187
+ this.abortProtocol('codex_app_server_frame_too_large');
188
+ return;
189
+ }
161
190
  for (const line of lines) {
162
191
  if (!line.trim())
163
192
  continue;
193
+ if (Buffer.byteLength(line, 'utf8') > this.maxFrameBytes) {
194
+ this.abortProtocol('codex_app_server_frame_too_large');
195
+ return;
196
+ }
164
197
  let message;
165
198
  try {
166
199
  message = JSON.parse(line);
@@ -176,7 +209,18 @@ export class CodexAppServerV2Client {
176
209
  }
177
210
  else {
178
211
  const event = { ...message, received_at: nowIso() };
179
- this.notifications.push(event);
212
+ const eventBytes = notificationByteLength(event);
213
+ if (eventBytes <= this.maxNotificationBytes) {
214
+ this.notifications.push(event);
215
+ this.notificationBytes += eventBytes;
216
+ while (this.notifications.length > this.maxNotifications
217
+ || this.notificationBytes > this.maxNotificationBytes) {
218
+ const removed = this.notifications.shift();
219
+ if (!removed)
220
+ break;
221
+ this.notificationBytes = Math.max(0, this.notificationBytes - notificationByteLength(removed));
222
+ }
223
+ }
180
224
  for (const listener of this.listeners) {
181
225
  try {
182
226
  listener(event);
@@ -247,15 +291,16 @@ export class CodexAppServerV2Client {
247
291
  return;
248
292
  this.pending.delete(id);
249
293
  clearTimeout(pending.timer);
250
- if (message.error)
251
- pending.reject(new Error(jsonRpcErrorMessage(pending.method, message.error)));
294
+ if (message.error) {
295
+ pending.reject(new CodexAppServerRequestError(pending.method, 'rpc_rejection', jsonRpcErrorMessage(pending.method, message.error), jsonRpcErrorCode(message.error)));
296
+ }
252
297
  else
253
298
  pending.resolve(message.result);
254
299
  }
255
- rejectAll(err) {
300
+ rejectAll(err, kind) {
256
301
  for (const [id, pending] of this.pending.entries()) {
257
302
  clearTimeout(pending.timer);
258
- pending.reject(err);
303
+ pending.reject(new CodexAppServerRequestError(pending.method, kind, err.message));
259
304
  this.pending.delete(id);
260
305
  }
261
306
  }
@@ -270,6 +315,16 @@ export class CodexAppServerV2Client {
270
315
  write(message) {
271
316
  this.child?.stdin.write(`${JSON.stringify(message)}\n`);
272
317
  }
318
+ abortProtocol(code) {
319
+ const child = this.child;
320
+ this.child = null;
321
+ this.stdoutBuffer = '';
322
+ this.rejectAll(new Error(code), 'protocol_overflow');
323
+ if (!child)
324
+ return;
325
+ child.stdin.end();
326
+ child.kill('SIGTERM');
327
+ }
273
328
  }
274
329
  export async function createCodexAppServerV2Client(options = {}) {
275
330
  const runtime = await resolveCodexRuntime({
@@ -287,6 +342,12 @@ export async function createCodexAppServerV2Client(options = {}) {
287
342
  clientOptions.env = options.env;
288
343
  if (options.timeoutMs !== undefined)
289
344
  clientOptions.timeoutMs = options.timeoutMs;
345
+ if (options.maxFrameBytes !== undefined)
346
+ clientOptions.maxFrameBytes = options.maxFrameBytes;
347
+ if (options.maxNotifications !== undefined)
348
+ clientOptions.maxNotifications = options.maxNotifications;
349
+ if (options.maxNotificationBytes !== undefined)
350
+ clientOptions.maxNotificationBytes = options.maxNotificationBytes;
290
351
  if (options.currentTimeProvider !== undefined)
291
352
  clientOptions.currentTimeProvider = options.currentTimeProvider;
292
353
  if (options.approvalPolicy !== undefined)
@@ -317,6 +378,12 @@ function jsonRpcErrorMessage(method, error) {
317
378
  return `${method}: ${String(error.message)}`;
318
379
  return `${method}: ${JSON.stringify(error)}`;
319
380
  }
381
+ function jsonRpcErrorCode(error) {
382
+ if (!error || typeof error !== 'object' || !('code' in error))
383
+ return null;
384
+ const code = Number(error.code);
385
+ return Number.isSafeInteger(code) ? code : null;
386
+ }
320
387
  function isTurnCompletionEvent(event, threadId, turnId) {
321
388
  if (String(event.method || '') !== 'turn/completed')
322
389
  return false;
@@ -329,3 +396,6 @@ function isTurnCompletionEvent(event, threadId, turnId) {
329
396
  return String(params.threadId || '') === threadId
330
397
  && (!turnId || String(completedTurn.id || params.turnId || '') === turnId);
331
398
  }
399
+ function notificationByteLength(event) {
400
+ return Buffer.byteLength(JSON.stringify(event), 'utf8');
401
+ }
@@ -595,12 +595,12 @@ async function ensurePythonCodexLbConfig(env, config) {
595
595
  'approval_policy = "never"',
596
596
  '',
597
597
  '[model_providers.codex-lb]',
598
- 'name = "openai"',
598
+ 'name = "codex-lb"',
599
599
  `base_url = ${tomlQuote(lbBaseUrl)}`,
600
600
  'wire_api = "responses"',
601
601
  'env_key = "CODEX_LB_API_KEY"',
602
602
  'supports_websockets = true',
603
- 'requires_openai_auth = true',
603
+ 'requires_openai_auth = false',
604
604
  ''
605
605
  ].join('\n');
606
606
  const configPath = path.join(codexHome, 'config.toml');
@@ -0,0 +1,204 @@
1
+ import { runAuxiliarySurfacesProbe } from './probes/auxiliary-surfaces-probe.js';
2
+ import { runBridgeProbe, gatewayAuthTransportEvidence } from './probes/bridge-probe.js';
3
+ import { runCatalogProbe } from './probes/catalog-probe.js';
4
+ import { runComputerUseProbe } from './probes/computer-use-probe.js';
5
+ import { runImageGenerationProbe } from './probes/image-generation-probe.js';
6
+ import { probeEvidence } from './probes/probe-evidence.js';
7
+ import { runVoiceRealtimeProbe } from './probes/voice-realtime-probe.js';
8
+ import { CODEX_LB_DEEP_EVIDENCE_VALIDATION_SCHEMA } from './trusted-deep-evidence.js';
9
+ const REQUIRED_FOR_FULL = [
10
+ 'gateway_auth_transport',
11
+ 'provider_identity',
12
+ 'bridge',
13
+ 'catalog',
14
+ 'model_picker',
15
+ 'fast_mode',
16
+ 'image_generation',
17
+ 'computer_use',
18
+ 'browser_use',
19
+ 'voice_mode',
20
+ 'plugins',
21
+ 'auxiliary_surfaces'
22
+ ];
23
+ export function runCodexLbDesktopCapabilityReport(input) {
24
+ const level = input.level || 'shallow';
25
+ const checkedAt = input.checkedAt || new Date().toISOString();
26
+ const manifest = input.manifest || null;
27
+ const catalog = runCatalogProbe({
28
+ mode: input.mode,
29
+ level,
30
+ checkedAt,
31
+ manifest,
32
+ ...(input.catalog || {})
33
+ });
34
+ const providerIdentityBlockers = [
35
+ ...(input.providerIdentity?.blockers || []),
36
+ ...(input.providerIdentity?.requiresOauth === true && input.oauthPreserved !== true
37
+ ? ['chatgpt_oauth_identity_not_preserved']
38
+ : [])
39
+ ];
40
+ const providerIdentity = probeEvidence({
41
+ ...(input.providerIdentity || {}),
42
+ blockers: providerIdentityBlockers,
43
+ evidence: {
44
+ ...(input.providerIdentity?.evidence || {}),
45
+ oauth_required: input.providerIdentity?.requiresOauth === true,
46
+ oauth_preserved: input.oauthPreserved === true
47
+ }
48
+ }, checkedAt);
49
+ const bridge = runBridgeProbe({
50
+ mode: input.mode,
51
+ level,
52
+ checkedAt,
53
+ manifest,
54
+ ...(input.bridge || {})
55
+ });
56
+ const reportWithoutOverall = {
57
+ schema: 'sks.codex-lb-desktop-capabilities.v2',
58
+ mode: input.mode,
59
+ configured: input.configured === true,
60
+ oauth_preserved: input.oauthPreserved === true,
61
+ gateway_auth_transport: gatewayAuthTransportEvidence({
62
+ checkedAt,
63
+ ...(input.gatewayAuth || {})
64
+ }),
65
+ provider_identity: providerIdentity,
66
+ bridge,
67
+ catalog: catalog.catalog,
68
+ model_picker: catalog.model_picker,
69
+ fast_mode: catalog.fast_mode,
70
+ text_responses: probeEvidence(input.textResponses || {
71
+ skipped: true,
72
+ evidence: { reason: 'text_responses_probe_not_run' }
73
+ }, checkedAt),
74
+ image_generation: runImageGenerationProbe({
75
+ level,
76
+ checkedAt,
77
+ manifestRouteAdvertised: manifestFlag(manifest, 'routes', 'images_generations')
78
+ ?? manifestFlag(manifest, 'routes', 'responses_http')
79
+ ?? undefined,
80
+ toolAdvertised: manifestFlag(manifest, 'tools', 'image_generation') ?? undefined,
81
+ ...(input.imageGeneration || {})
82
+ }),
83
+ computer_use: runComputerUseProbe({
84
+ level,
85
+ checkedAt,
86
+ toolAdvertised: manifestFlag(manifest, 'tools', 'computer_use_passthrough') ?? undefined,
87
+ ...(input.computerUse || {})
88
+ }),
89
+ browser_use: probeEvidence(input.browserUse || {
90
+ source: 'config',
91
+ evidence: {
92
+ independent_from_computer_use: true,
93
+ independent_from_auth_mode: true,
94
+ reason: 'browser_use_probe_not_run'
95
+ }
96
+ }, checkedAt),
97
+ voice_mode: runVoiceRealtimeProbe({
98
+ level,
99
+ checkedAt,
100
+ routeAdvertised: manifestFlag(manifest, 'routes', 'realtime_calls') ?? undefined,
101
+ ...(input.voiceMode || {})
102
+ }),
103
+ plugins: probeEvidence(input.plugins || {
104
+ source: 'config',
105
+ evidence: {
106
+ independent_from_auth_mode: true
107
+ }
108
+ }, checkedAt),
109
+ auxiliary_surfaces: runAuxiliarySurfacesProbe({
110
+ level,
111
+ checkedAt,
112
+ routesAdvertised: auxiliaryRoutesAdvertised(manifest),
113
+ ...(input.auxiliarySurfaces || {})
114
+ })
115
+ };
116
+ return {
117
+ ...reportWithoutOverall,
118
+ deep_evidence_validation: input.deepEvidenceValidation || {
119
+ schema: CODEX_LB_DEEP_EVIDENCE_VALIDATION_SCHEMA,
120
+ state: 'available_unverified',
121
+ trusted: false,
122
+ evidence: null,
123
+ producer_id: null,
124
+ created_at: null,
125
+ content_sha256: null,
126
+ trust_anchor_id: null,
127
+ blockers: ['codex_lb_deep_evidence_missing'],
128
+ warnings: []
129
+ },
130
+ overall: overallCapabilityState(reportWithoutOverall)
131
+ };
132
+ }
133
+ export function overallCapabilityState(report) {
134
+ if (report.mode === 'cli-provider' || report.mode === 'disabled')
135
+ return 'available_unverified';
136
+ const required = report.mode === 'desktop-native-bridge'
137
+ ? REQUIRED_FOR_FULL
138
+ : REQUIRED_FOR_FULL.filter((key) => key !== 'bridge');
139
+ const states = required.map((key) => report[key].state);
140
+ if (states.some((state) => state === 'blocked'))
141
+ return 'blocked';
142
+ if (states.some((state) => state === 'unsupported'))
143
+ return 'unsupported';
144
+ if (report.configured && states.every((state) => state === 'verified'))
145
+ return 'verified';
146
+ return 'available_unverified';
147
+ }
148
+ export function shapeCodexLbDesktopCapabilityStatus(report) {
149
+ const entries = capabilityEntries(report);
150
+ return {
151
+ schema: 'sks.codex-lb-desktop-capability-status.v2',
152
+ ready: report.overall === 'verified',
153
+ state: report.overall,
154
+ configured: report.configured,
155
+ oauth_preserved: report.oauth_preserved,
156
+ gateway_auth_transport: String(report.gateway_auth_transport.evidence.configured_gateway_auth_transport || 'unknown'),
157
+ verified: entries.filter(([, value]) => value.state === 'verified').map(([key]) => key),
158
+ available_unverified: entries.filter(([, value]) => value.state === 'available_unverified').map(([key]) => key),
159
+ blocked: Object.fromEntries(entries
160
+ .filter(([, value]) => value.state === 'blocked')
161
+ .map(([key, value]) => [key, value.blockers])),
162
+ unsupported: entries.filter(([, value]) => value.state === 'unsupported').map(([key]) => key),
163
+ skipped: entries.filter(([, value]) => value.state === 'skipped').map(([key]) => key)
164
+ };
165
+ }
166
+ function capabilityEntries(report) {
167
+ const keys = [
168
+ 'gateway_auth_transport',
169
+ 'provider_identity',
170
+ 'bridge',
171
+ 'catalog',
172
+ 'model_picker',
173
+ 'fast_mode',
174
+ 'text_responses',
175
+ 'image_generation',
176
+ 'computer_use',
177
+ 'browser_use',
178
+ 'voice_mode',
179
+ 'plugins',
180
+ 'auxiliary_surfaces'
181
+ ];
182
+ return keys.map((key) => [key, report[key]]);
183
+ }
184
+ function manifestFlag(manifest, section, key) {
185
+ const row = manifest?.[section];
186
+ if (!row || typeof row !== 'object' || Array.isArray(row))
187
+ return null;
188
+ const value = row[key];
189
+ return typeof value === 'boolean' ? value : null;
190
+ }
191
+ function auxiliaryRoutesAdvertised(manifest) {
192
+ if (!manifest)
193
+ return undefined;
194
+ const flags = [
195
+ manifestFlag(manifest, 'routes', 'files'),
196
+ manifestFlag(manifest, 'routes', 'transcribe'),
197
+ manifestFlag(manifest, 'routes', 'images_edits'),
198
+ manifestFlag(manifest, 'routes', 'thread_goal'),
199
+ manifestFlag(manifest, 'routes', 'memories_trace_summarize'),
200
+ manifestFlag(manifest, 'routes', 'safety_arc'),
201
+ manifestFlag(manifest, 'routes', 'agent_identities_jwks')
202
+ ];
203
+ return flags.some((flag) => flag === true);
204
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -14,9 +14,6 @@ if r != errSecSuccess{FileHandle.standardError.write(Data("keychain_status=\\(r)
14
14
  export function codexLbEnvPath(home = process.env.HOME || os.homedir()) {
15
15
  return path.join(String(home || os.homedir()), '.codex', 'sks-codex-lb.env');
16
16
  }
17
- export function legacyCodexLbEnvPath(home = process.env.HOME || os.homedir()) {
18
- return path.join(String(home || os.homedir()), '.codex', 'sks.env');
19
- }
20
17
  export function codexLbMetadataPath(home = process.env.HOME || os.homedir()) {
21
18
  return path.join(String(home || os.homedir()), '.codex', 'sks-codex-lb.json');
22
19
  }
@@ -65,9 +62,13 @@ export async function readCodexLbModelCatalog(opts = {}) {
65
62
  };
66
63
  }
67
64
  const fetchImpl = opts.fetchImpl || fetch;
65
+ const gatewayAuthTransport = opts.gatewayAuthTransport || 'x-codex-lb-api-key';
66
+ const gatewayHeaders = gatewayAuthTransport === 'authorization-bearer-compat'
67
+ ? { Authorization: `Bearer ${loaded.secret_api_key}` }
68
+ : { 'X-Codex-LB-API-Key': loaded.secret_api_key };
68
69
  try {
69
70
  const response = await fetchImpl(`${loaded.base_url}/models`, {
70
- headers: { Authorization: `Bearer ${loaded.secret_api_key}` },
71
+ headers: gatewayHeaders,
71
72
  redirect: 'error',
72
73
  signal: AbortSignal.timeout(Math.max(250, Number(opts.timeoutMs || 5000)))
73
74
  });
@@ -159,24 +160,19 @@ export function codexLbBaseUrlSecurityBlocker(input) {
159
160
  export async function loadCodexLbEnv(opts = {}) {
160
161
  const home = opts.home || process.env.HOME || os.homedir();
161
162
  const metadataPath = opts.metadataPath || codexLbMetadataPath(home);
162
- const envPaths = [
163
- opts.envPath || codexLbEnvPath(home),
164
- opts.legacyEnvPath || legacyCodexLbEnvPath(home)
165
- ];
166
- const sourcePriority = ['process.env', 'env-file', 'keychain', 'legacy-env-file'];
163
+ const envPaths = [opts.envPath || codexLbEnvPath(home)];
164
+ const sourcePriority = ['env-file', 'keychain', 'process.env'];
167
165
  if (opts.allowProjectSecrets)
168
166
  sourcePriority.push('project-local');
169
167
  const processEnv = pickEnv(opts.processEnv || process.env);
170
168
  const envFile = await readEnvFile(envPaths[0]);
171
- const legacyEnv = await readEnvFile(envPaths[1]);
172
169
  const keychain = await readMacKeychain(opts);
173
170
  const projectLocal = opts.allowProjectSecrets ? await readEnvFile(path.join(opts.root || process.cwd(), '.env')) : {};
174
171
  const metadata = await readCodexLbCredentialMetadata(metadataPath);
175
172
  const apiKeyCandidates = [
176
- { source: 'process.env', apiKey: processEnv.apiKey },
177
173
  { source: 'env-file', apiKey: envFile.apiKey },
178
174
  { source: 'keychain', apiKey: keychain.apiKey },
179
- { source: 'legacy-env-file', apiKey: legacyEnv.apiKey },
175
+ { source: 'process.env', apiKey: processEnv.apiKey },
180
176
  ...(opts.allowProjectSecrets ? [{ source: 'project-local', apiKey: String(projectLocal.apiKey || '') }] : [])
181
177
  ].filter((candidate) => Boolean(candidate.apiKey));
182
178
  const candidateFingerprints = await Promise.all(apiKeyCandidates.map(async (candidate) => ({
@@ -188,7 +184,7 @@ export async function loadCodexLbEnv(opts = {}) {
188
184
  : candidateFingerprints[0];
189
185
  const keySource = selectedApiKey?.source || 'missing';
190
186
  const apiKey = selectedApiKey?.apiKey || '';
191
- const configuredBaseUrl = normalizeCodexLbBaseUrl(processEnv.baseUrl || envFile.baseUrl || legacyEnv.baseUrl || projectLocal.baseUrl || '');
187
+ const configuredBaseUrl = normalizeCodexLbBaseUrl(envFile.baseUrl || processEnv.baseUrl || projectLocal.baseUrl || '');
192
188
  const apiKeySha256 = selectedApiKey?.sha256 || '';
193
189
  const binding = evaluateCodexLbCredentialBinding({
194
190
  metadata,
@@ -2,22 +2,59 @@ import os from 'node:os';
2
2
  import path from 'node:path';
3
3
  import { ensureDir, exists, readText, writeTextAtomic } from '../fsx.js';
4
4
  import { codexLbEnvPath, codexLbMetadataPath, normalizeCodexLbBaseUrl } from './codex-lb-env.js';
5
+ import { DEFAULT_CODEX_LB_DESKTOP_MODE, DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT } from './desktop-mode.js';
5
6
  export function buildCodexLbSetupPlan(answers, opts = {}) {
6
7
  const home = opts.home || process.env.HOME || os.homedir();
7
8
  const baseUrl = normalizeCodexLbBaseUrl(answers.host_or_base_url);
8
9
  const configPath = opts.configPath || path.join(home, '.codex', 'config.toml');
9
10
  const envPath = opts.envPath || codexLbEnvPath(home);
10
11
  const metadataPath = opts.metadataPath || codexLbMetadataPath(home);
12
+ const desktopMode = answers.desktop_mode || DEFAULT_CODEX_LB_DESKTOP_MODE;
13
+ const gatewayAuthTransport = answers.gateway_auth_transport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT;
11
14
  const blockers = [];
12
15
  if (!baseUrl)
13
16
  blockers.push('missing_host_or_base_url');
14
17
  if (answers.install_shell_profile !== 'skip' && !answers.write_env_file)
15
18
  blockers.push('shell_profile_snippet_requires_env_file');
16
- const actions = [
17
- { type: 'write_config_provider', target: configPath, effect: 'write or update [model_providers.codex-lb] with name="openai", the normalized base URL, CODEX_LB_API_KEY env_key, WebSocket support, and requires_openai_auth=true for Codex App routing' }
18
- ];
19
- if (answers.use_as_default_provider) {
20
- actions.push({ type: 'select_default_provider', target: configPath, effect: 'set top-level model_provider = "codex-lb"' });
19
+ const actions = [];
20
+ if (desktopMode === 'desktop-native-bridge') {
21
+ actions.push({
22
+ type: 'configure_desktop_native_bridge',
23
+ target: configPath,
24
+ effect: `keep built-in OpenAI and ChatGPT OAuth, route model traffic through a loopback bridge, and use explicit gateway auth transport ${gatewayAuthTransport}`
25
+ });
26
+ actions.push({
27
+ type: 'start_desktop_bridge',
28
+ target: 'SKS Codex Desktop bridge',
29
+ effect: 'start the loopback-only bridge without changing Codex Desktop auth'
30
+ });
31
+ actions.push({
32
+ type: 'verify_oauth_preserved',
33
+ target: path.join(home, '.codex', 'auth.json'),
34
+ effect: 'verify byte identity before App restart and semantic OAuth identity afterward'
35
+ });
36
+ }
37
+ else if (desktopMode === 'desktop-dual-auth-compat') {
38
+ if (gatewayAuthTransport !== 'x-codex-lb-api-key') {
39
+ blockers.push('desktop_compat_requires_x_codex_lb_api_key_transport');
40
+ }
41
+ actions.push({
42
+ type: 'configure_desktop_compat_provider',
43
+ target: configPath,
44
+ effect: 'configure exact provider name "OpenAI", retain ChatGPT OAuth, and send the separate LB key only with X-Codex-LB-API-Key'
45
+ });
46
+ actions.push({
47
+ type: 'verify_oauth_preserved',
48
+ target: path.join(home, '.codex', 'auth.json'),
49
+ effect: 'verify Desktop configuration did not change shared OAuth bytes'
50
+ });
51
+ }
52
+ else if (desktopMode === 'cli-provider') {
53
+ actions.push({
54
+ type: 'write_cli_provider',
55
+ target: configPath,
56
+ effect: 'write an unselected CLI-only codex-lb provider using CODEX_LB_API_KEY without changing Desktop OAuth'
57
+ });
21
58
  }
22
59
  if (answers.write_env_file) {
23
60
  actions.push({ type: 'write_env_file', target: envPath, effect: 'write CODEX_LB_BASE_URL and redacted CODEX_LB_API_KEY env loader with chmod 0600' });
@@ -32,7 +69,7 @@ export function buildCodexLbSetupPlan(answers, opts = {}) {
32
69
  actions.push({ type: 'install_shell_profile_snippet', target: profileTargets(home, answers.install_shell_profile).join(', '), effect: `install managed shell snippet for ${answers.install_shell_profile}` });
33
70
  }
34
71
  if (answers.run_health_check) {
35
- actions.push({ type: 'run_health_check', target: 'codex-lb response chain', effect: 'run codex-lb health check after apply' });
72
+ actions.push({ type: 'run_capability_check', target: 'codex-lb response chain', effect: 'run codex-lb capability check after apply' });
36
73
  }
37
74
  actions.push({ type: 'write_metadata', target: metadataPath, effect: 'write redacted setup metadata and key fingerprint with chmod 0600' });
38
75
  const selectedModes = selectedCodexLbPersistenceModes(answers);
@@ -106,8 +143,8 @@ export function codexLbPersistenceSummary({ selectedModes = [], appliedModes = [
106
143
  const warnings = effective === 'process_only_ephemeral'
107
144
  ? [
108
145
  'process_only_ephemeral',
109
- 'next_shell_requires_setup_or_env',
110
- 'Codex App GUI launch may not see credentials'
146
+ 'next_session_requires_center_or_setup',
147
+ 'Save credentials in SKS Center (or sks codex-lb setup --write-env-file --keychain) so Desktop can load them automatically'
111
148
  ]
112
149
  : [];
113
150
  return {