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,58 +0,0 @@
1
- import { credentialClassForMode, parseProviderPolicySnapshot } from '../../architecture-hardening/contracts/contracts.js';
2
- export function decideProviderRoute(input) {
3
- const policy = parseProviderPolicySnapshot(input.policy);
4
- const base = {
5
- schema: 'sks.provider-route-decision.v1',
6
- mode: policy.mode,
7
- credential_class: policy.credential_class,
8
- failover_allowed: false
9
- };
10
- if (input.requestedMode !== policy.mode)
11
- return blocked(base, 'provider_route_cross_mode_forbidden');
12
- if (input.credential.status !== 'ready')
13
- return blocked(base, `provider_route_credential_${input.credential.status}`);
14
- const model = String(input.model || '').trim();
15
- if (!model)
16
- return blocked(base, 'provider_route_model_invalid');
17
- const isOpenRouterModel = model.includes('/');
18
- if ((policy.mode === 'openrouter') !== isOpenRouterModel)
19
- return blocked(base, 'provider_route_model_family_mismatch');
20
- if (!policy.allowed_models.includes(model))
21
- return blocked(base, 'provider_route_model_not_allowed');
22
- return {
23
- ...base,
24
- ok: true,
25
- upstream: policy.mode,
26
- model,
27
- source_label: policy.mode === 'chatgpt-oauth' ? 'ChatGPT OAuth' : policy.mode === 'codex-lb' ? 'Codex LB' : 'OpenRouter',
28
- blockers: []
29
- };
30
- }
31
- export async function resolveCredentialForRoute(decision, port) {
32
- if (!decision.ok)
33
- throw new Error(decision.blockers[0] || 'provider_route_blocked');
34
- const credential = await port.readCredential(decision.credential_class);
35
- if (!credential)
36
- throw new Error('provider_route_credential_unavailable');
37
- return credential;
38
- }
39
- export function classifyProviderRouteFailure(status) {
40
- if (status === 401 || status === 403)
41
- return { retry_allowed: false, failover_allowed: false, reason_code: 'provider_route_auth_failed' };
42
- if (status === 402 || status === 429)
43
- return { retry_allowed: false, failover_allowed: false, reason_code: 'provider_route_quota_or_rate_limit' };
44
- if (status >= 500)
45
- return { retry_allowed: false, failover_allowed: false, reason_code: 'provider_route_upstream_failed' };
46
- return { retry_allowed: true, failover_allowed: false, reason_code: 'provider_route_request_failed' };
47
- }
48
- export function emptyProviderCatalogForCredential(input) {
49
- if (input.credential.status !== 'ready')
50
- return [];
51
- return input.models.filter((model) => (input.mode === 'openrouter') === model.includes('/'));
52
- }
53
- function blocked(base, blocker) {
54
- return { ...base, ok: false, upstream: null, model: null, source_label: null, blockers: [blocker] };
55
- }
56
- export function expectedCredentialClass(mode) {
57
- return credentialClassForMode(mode);
58
- }
@@ -1,327 +0,0 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
- import { readJson, writeJsonAtomic } from '../fsx.js';
4
- import { uniqueTruthyStrings as uniqueStrings } from '../text/strings.js';
5
- import { codexLbBaseUrlSecurityBlocker, normalizeCodexLbBaseUrl } from './codex-lb-env.js';
6
- export const CODEX_LB_ROUTING_TRUTH_SCHEMA = 'sks.codex-lb-routing-truth.v1';
7
- export const CODEX_LB_ROUTING_TRUTH_RECEIPT = 'sks-codex-lb-routing-truth.json';
8
- export const CODEX_LB_ROUTING_TRUTH_STAMP = CODEX_LB_ROUTING_TRUTH_RECEIPT;
9
- export const CODEX_LB_ROUTING_TRUTH_STALE_AFTER_MS = 5 * 60 * 1_000;
10
- export function codexLbRoutingTruthReceiptPath(home = process.env.HOME || os.homedir()) {
11
- return path.join(home, '.codex', CODEX_LB_ROUTING_TRUTH_RECEIPT);
12
- }
13
- export const codexLbRoutingTruthStampPath = codexLbRoutingTruthReceiptPath;
14
- export async function measureCodexLbRoutingTruth(options) {
15
- const now = options.now || Date.now;
16
- const measuredAt = options.nowIso?.() || new Date(now()).toISOString();
17
- const baseUrl = normalizeCodexLbBaseUrl(options.baseUrl || '');
18
- const configured = safePublicUrl(baseUrl);
19
- const mode = options.mode || 'cli-provider';
20
- const probeBaseUrl = normalizeCodexLbBaseUrl(options.probeBaseUrl || '');
21
- const bridgeLoopbackProbe = Boolean(probeBaseUrl);
22
- const authTransport = options.authTransport || (mode === 'cli-provider'
23
- ? 'x-codex-lb-api-key'
24
- : 'authorization-bearer');
25
- const base = {
26
- schema: CODEX_LB_ROUTING_TRUTH_SCHEMA,
27
- mode,
28
- selected: options.selected,
29
- fresh: false,
30
- stale_after_ms: CODEX_LB_ROUTING_TRUTH_STALE_AFTER_MS,
31
- age_ms: 0,
32
- configured_base_url: configured?.url || null,
33
- configured_host: configured?.host || null,
34
- actual_url: null,
35
- actual_host: null,
36
- measurement_path: bridgeLoopbackProbe ? 'bridge-loopback' : 'direct',
37
- auth_transport: authTransport,
38
- http_status: null,
39
- measured_at: measuredAt,
40
- checked_at: measuredAt,
41
- latency_ms: null
42
- };
43
- if (!baseUrl) {
44
- return {
45
- ...base,
46
- ok: false,
47
- status: 'missing_base_url',
48
- measured: false,
49
- auth_outcome: 'not_attempted',
50
- blockers: ['codex_lb_missing:CODEX_LB_BASE_URL']
51
- };
52
- }
53
- const transportBlocker = codexLbBaseUrlSecurityBlocker(baseUrl);
54
- if (transportBlocker) {
55
- return {
56
- ...base,
57
- ok: false,
58
- status: 'transport_blocked',
59
- measured: false,
60
- auth_outcome: 'not_attempted',
61
- blockers: [transportBlocker]
62
- };
63
- }
64
- if (bridgeLoopbackProbe && (mode !== 'bridge' || !isHttpLoopbackUrl(probeBaseUrl))) {
65
- return {
66
- ...base,
67
- ok: false,
68
- status: 'transport_blocked',
69
- measured: false,
70
- auth_outcome: 'not_attempted',
71
- blockers: ['codex_lb_bridge_probe_target_not_loopback']
72
- };
73
- }
74
- const apiKey = String(options.apiKey || '').trim();
75
- if (!apiKey) {
76
- return {
77
- ...base,
78
- ok: false,
79
- status: 'missing_api_key',
80
- measured: false,
81
- auth_outcome: 'not_attempted',
82
- blockers: ['codex_lb_missing:CODEX_LB_API_KEY']
83
- };
84
- }
85
- if (options.measure === false) {
86
- return {
87
- ...base,
88
- ok: !options.selected,
89
- status: options.selected ? 'selected_unmeasured' : 'ready_unselected',
90
- measured: false,
91
- auth_outcome: 'not_attempted',
92
- blockers: options.selected ? ['codex_lb_routing_truth_unmeasured'] : []
93
- };
94
- }
95
- if (!options.selected) {
96
- return {
97
- ...base,
98
- ok: true,
99
- status: 'ready_unselected',
100
- measured: false,
101
- auth_outcome: 'not_attempted',
102
- blockers: []
103
- };
104
- }
105
- const measurementBaseUrl = bridgeLoopbackProbe ? probeBaseUrl : baseUrl;
106
- const measurementTarget = safePublicUrl(measurementBaseUrl);
107
- const endpoint = `${measurementBaseUrl.replace(/\/+$/, '')}/models`;
108
- const startedAt = now();
109
- try {
110
- const headers = authTransport === 'x-codex-lb-api-key'
111
- ? { 'X-Codex-LB-API-Key': apiKey }
112
- : { Authorization: `Bearer ${apiKey}` };
113
- const response = await (options.fetchImpl || globalThis.fetch)(endpoint, {
114
- method: 'GET',
115
- headers,
116
- redirect: 'error',
117
- signal: AbortSignal.timeout(Math.max(250, Number(options.timeoutMs || 8_000)))
118
- });
119
- const finishedAt = now();
120
- const actual = safePublicUrl(response.url || endpoint);
121
- const authRejected = response.status === 401 || response.status === 403;
122
- const ok = response.ok && !authRejected && actual?.host === measurementTarget?.host;
123
- const status = authRejected
124
- ? 'auth_rejected'
125
- : response.ok && actual?.host !== measurementTarget?.host
126
- ? 'endpoint_unreachable'
127
- : response.ok
128
- ? 'verified'
129
- : 'http_error';
130
- return {
131
- ...base,
132
- ok,
133
- status,
134
- measured: true,
135
- fresh: true,
136
- actual_url: actual?.url || null,
137
- actual_host: actual?.host || null,
138
- auth_outcome: authRejected ? 'rejected' : response.ok ? 'accepted' : 'indeterminate',
139
- http_status: response.status,
140
- latency_ms: Math.max(0, finishedAt - startedAt),
141
- blockers: ok
142
- ? []
143
- : [authRejected
144
- ? 'codex_lb_auth_rejected'
145
- : response.ok
146
- ? 'codex_lb_actual_host_mismatch'
147
- : `codex_lb_models_http_${response.status}`]
148
- };
149
- }
150
- catch {
151
- const finishedAt = now();
152
- return {
153
- ...base,
154
- ok: false,
155
- status: 'endpoint_unreachable',
156
- measured: true,
157
- fresh: true,
158
- auth_outcome: 'indeterminate',
159
- latency_ms: Math.max(0, finishedAt - startedAt),
160
- blockers: ['codex_lb_endpoint_unreachable']
161
- };
162
- }
163
- }
164
- export async function writeCodexLbRoutingTruthReceipt(truth, options = {}) {
165
- const receiptPath = options.receiptPath || codexLbRoutingTruthReceiptPath(options.home);
166
- const canonical = parseReceipt(truth);
167
- if (!canonical)
168
- throw new Error('codex_lb_routing_truth_invalid');
169
- await writeJsonAtomic(receiptPath, canonical, { mode: 0o600 });
170
- return canonical;
171
- }
172
- export async function readCodexLbRoutingTruthReceipt(options = {}) {
173
- const receiptPath = options.receiptPath || codexLbRoutingTruthReceiptPath(options.home);
174
- const parsed = parseReceipt(await readJson(receiptPath, null));
175
- if (!parsed)
176
- return null;
177
- if (options.expectedMode !== undefined && parsed.mode !== options.expectedMode)
178
- return null;
179
- if (options.expectedSelected !== undefined && parsed.selected !== options.expectedSelected)
180
- return null;
181
- if (options.expectedConfiguredHost !== undefined
182
- && parsed.configured_host !== options.expectedConfiguredHost)
183
- return null;
184
- if (options.expectedAuthTransport !== undefined
185
- && parsed.auth_transport !== options.expectedAuthTransport)
186
- return null;
187
- return withFreshness(parsed, options);
188
- }
189
- export async function readFreshCodexLbRoutingTruthStamp(options = {}) {
190
- const truth = await readCodexLbRoutingTruthReceipt(options);
191
- return truth?.measured === true && truth.fresh === true ? truth : null;
192
- }
193
- export const writeCodexLbRoutingTruthStamp = writeCodexLbRoutingTruthReceipt;
194
- export const readCodexLbRoutingTruthStamp = readCodexLbRoutingTruthReceipt;
195
- export async function measureAndWriteCodexLbRoutingTruth(measureOptions, receiptOptions = {}) {
196
- const truth = await measureCodexLbRoutingTruth(measureOptions);
197
- try {
198
- return await writeCodexLbRoutingTruthReceipt(truth, receiptOptions);
199
- }
200
- catch {
201
- if (!truth.selected)
202
- return truth;
203
- return {
204
- ...truth,
205
- ok: false,
206
- status: 'receipt_write_failed',
207
- observed_status: truth.status,
208
- blockers: uniqueStrings([...truth.blockers, 'codex_lb_routing_truth_receipt_write_failed'])
209
- };
210
- }
211
- }
212
- export function codexLbRoutingTruthIsActive(truth) {
213
- return truth?.selected === true
214
- && truth.measured === true
215
- && truth.fresh === true
216
- && truth.ok === true
217
- && truth.status === 'verified';
218
- }
219
- function withFreshness(truth, options) {
220
- const nowMs = (options.now || Date.now)();
221
- const measuredMs = Date.parse(truth.measured_at);
222
- const staleAfterMs = Math.max(1_000, Number(options.staleAfterMs || CODEX_LB_ROUTING_TRUTH_STALE_AFTER_MS));
223
- const ageMs = Number.isFinite(measuredMs) ? Math.max(0, nowMs - measuredMs) : Number.POSITIVE_INFINITY;
224
- const fresh = truth.measured && Number.isFinite(ageMs) && ageMs <= staleAfterMs;
225
- if (fresh || !truth.selected) {
226
- return { ...truth, fresh, stale_after_ms: staleAfterMs, age_ms: Number.isFinite(ageMs) ? ageMs : null };
227
- }
228
- return {
229
- ...truth,
230
- ok: false,
231
- status: 'stale',
232
- observed_status: truth.status,
233
- fresh: false,
234
- stale_after_ms: staleAfterMs,
235
- age_ms: Number.isFinite(ageMs) ? ageMs : null,
236
- blockers: uniqueStrings([...truth.blockers, 'codex_lb_routing_truth_stale'])
237
- };
238
- }
239
- function parseReceipt(value) {
240
- if (!value || typeof value !== 'object' || Array.isArray(value))
241
- return null;
242
- const input = value;
243
- if (input.schema !== CODEX_LB_ROUTING_TRUTH_SCHEMA)
244
- return null;
245
- const status = String(input.status || '');
246
- const statuses = [
247
- 'verified', 'selected_unmeasured', 'ready_unselected', 'missing_base_url', 'missing_api_key',
248
- 'transport_blocked', 'endpoint_unreachable', 'auth_rejected', 'http_error', 'stale',
249
- 'receipt_write_failed'
250
- ];
251
- const authTransport = String(input.auth_transport || '');
252
- const mode = String(input.mode || 'cli-provider');
253
- const authOutcome = String(input.auth_outcome || '');
254
- const measuredAt = stringOrNull(input.measured_at) || stringOrNull(input.checked_at);
255
- if (!statuses.includes(status)
256
- || !['bridge', 'cli-provider'].includes(mode)
257
- || !['authorization-bearer', 'x-codex-lb-api-key'].includes(authTransport)
258
- || !['accepted', 'rejected', 'not_attempted', 'indeterminate'].includes(authOutcome)
259
- || !measuredAt
260
- || !Array.isArray(input.blockers))
261
- return null;
262
- return {
263
- schema: CODEX_LB_ROUTING_TRUTH_SCHEMA,
264
- ok: input.ok === true,
265
- status,
266
- ...(typeof input.observed_status === 'string'
267
- ? { observed_status: input.observed_status }
268
- : {}),
269
- mode,
270
- selected: input.selected === true,
271
- measured: input.measured === true,
272
- fresh: input.fresh === true,
273
- stale_after_ms: finiteNumberOrNull(input.stale_after_ms) || CODEX_LB_ROUTING_TRUTH_STALE_AFTER_MS,
274
- age_ms: finiteNumberOrNull(input.age_ms),
275
- configured_base_url: safeReceiptUrl(input.configured_base_url),
276
- configured_host: stringOrNull(input.configured_host),
277
- actual_url: safeReceiptUrl(input.actual_url),
278
- actual_host: stringOrNull(input.actual_host),
279
- measurement_path: input.measurement_path === 'bridge-loopback'
280
- ? 'bridge-loopback'
281
- : 'direct',
282
- auth_transport: authTransport,
283
- auth_outcome: authOutcome,
284
- http_status: finiteNumberOrNull(input.http_status),
285
- measured_at: measuredAt,
286
- checked_at: measuredAt,
287
- latency_ms: finiteNumberOrNull(input.latency_ms),
288
- blockers: uniqueStrings(input.blockers.map(String))
289
- };
290
- }
291
- function safeReceiptUrl(input) {
292
- if (typeof input !== 'string')
293
- return null;
294
- return safePublicUrl(input)?.url || null;
295
- }
296
- function stringOrNull(input) {
297
- return typeof input === 'string' && input ? input : null;
298
- }
299
- function finiteNumberOrNull(input) {
300
- return typeof input === 'number' && Number.isFinite(input) && input >= 0 ? input : null;
301
- }
302
- function safePublicUrl(input) {
303
- try {
304
- const parsed = new URL(input);
305
- parsed.username = '';
306
- parsed.password = '';
307
- parsed.search = '';
308
- parsed.hash = '';
309
- return {
310
- url: parsed.toString().replace(/\/$/, ''),
311
- host: parsed.host
312
- };
313
- }
314
- catch {
315
- return null;
316
- }
317
- }
318
- function isHttpLoopbackUrl(input) {
319
- try {
320
- const parsed = new URL(input);
321
- return parsed.protocol === 'http:'
322
- && (parsed.hostname === '127.0.0.1' || parsed.hostname === '::1');
323
- }
324
- catch {
325
- return false;
326
- }
327
- }
@@ -1,92 +0,0 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
- import { readJson, readText } from '../fsx.js';
4
- import { loadCodexLbEnv } from '../codex-lb/codex-lb-env.js';
5
- export const CODEX_LB_IMAGEGEN_TARGET_SCHEMA = 'sks.codex-lb-imagegen-target.v1';
6
- export async function resolveCodexLbImagegenTarget(opts = {}) {
7
- const env = opts.env || process.env;
8
- const home = opts.home || env.HOME || os.homedir();
9
- const codexHome = opts.codexHome || env.CODEX_HOME || path.join(home, '.codex');
10
- const configText = typeof opts.configText === 'string'
11
- ? opts.configText
12
- : await readText(path.join(codexHome, 'config.toml'), '').catch(() => '');
13
- const selected = topLevelTomlString(configText, 'model_provider') === 'codex-lb';
14
- const authTransport = codexLbAuthTransport(configText);
15
- const loaded = await loadCodexLbEnv({
16
- home,
17
- processEnv: {},
18
- envPath: path.join(codexHome, 'sks-codex-lb.env'),
19
- metadataPath: path.join(codexHome, 'sks-codex-lb.json')
20
- }).catch(() => null);
21
- const catalogModels = await readCatalogModels(configText, home, codexHome);
22
- const explicit = String(opts.explicitModel || env.SKS_IMAGEGEN_RESPONSES_MODEL || '').trim();
23
- const configuredModel = topLevelTomlString(configText, 'model');
24
- const { model, model_source } = explicit
25
- ? { model: explicit, model_source: 'explicit' }
26
- : catalogModels.includes(configuredModel)
27
- ? { model: configuredModel, model_source: 'configured_model_in_catalog' }
28
- : catalogModels.length
29
- ? { model: catalogModels[0], model_source: 'catalog_default' }
30
- : { model: null, model_source: null };
31
- return {
32
- schema: CODEX_LB_IMAGEGEN_TARGET_SCHEMA,
33
- selected,
34
- base_url: loaded?.base_url || null,
35
- api_key: loaded?.secret_api_key || null,
36
- api_key_source: loaded?.api_key?.source || null,
37
- auth_transport: authTransport,
38
- model,
39
- model_source,
40
- catalog_models: catalogModels,
41
- blocker: codexLbImagegenBlocker({ selected, loaded, model })
42
- };
43
- }
44
- function codexLbAuthTransport(configText) {
45
- const provider = String(configText || '').match(/(?:^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[1] || '';
46
- const customHeader = /(?:^|\n)\s*env_http_headers\s*=\s*\{[^}]*"X-Codex-LB-API-Key"\s*=\s*"CODEX_LB_API_KEY"[^}]*\}/.test(provider);
47
- const bearerEnvKey = /(?:^|\n)\s*env_key\s*=/.test(provider);
48
- return customHeader && !bearerEnvKey ? 'x-codex-lb-api-key' : 'authorization-bearer';
49
- }
50
- function codexLbImagegenBlocker(state) {
51
- if (!state.selected)
52
- return 'codex_lb_not_selected';
53
- if (!state.loaded?.base_url)
54
- return 'codex_lb_base_url_missing';
55
- if (!state.loaded?.secret_api_key)
56
- return state.loaded?.missing?.includes('CODEX_LB_CREDENTIAL_BINDING')
57
- ? 'codex_lb_credential_binding_invalid'
58
- : 'codex_lb_api_key_missing';
59
- if (!state.model)
60
- return 'codex_lb_imagegen_model_unresolved';
61
- return null;
62
- }
63
- async function readCatalogModels(configText, home, codexHome) {
64
- const configured = topLevelTomlString(configText, 'model_catalog_json');
65
- const candidates = [
66
- configured ? resolveCatalogPath(configured, home, codexHome) : '',
67
- path.join(codexHome, 'sks-codex-lb-tool-catalog.json')
68
- ].filter(Boolean);
69
- for (const file of candidates) {
70
- const payload = await readJson(file, null).catch(() => null);
71
- const rows = Array.isArray(payload?.models) ? payload.models : Array.isArray(payload?.data) ? payload.data : [];
72
- const slugs = rows
73
- .filter((row) => row?.supported_in_api !== false && !hiddenCatalogRow(row))
74
- .map((row) => String(row?.slug || row?.id || row?.model || '').trim())
75
- .filter(Boolean);
76
- if (slugs.length)
77
- return [...new Set(slugs)];
78
- }
79
- return [];
80
- }
81
- function resolveCatalogPath(configured, home, codexHome) {
82
- const expanded = configured.replace(/^~(?=\/|$)/, home);
83
- return path.isAbsolute(expanded) ? expanded : path.resolve(codexHome, expanded);
84
- }
85
- function hiddenCatalogRow(row) {
86
- return ['hide', 'hidden', 'internal', 'none', 'unavailable'].includes(String(row?.visibility || '').trim().toLowerCase());
87
- }
88
- function topLevelTomlString(text, key) {
89
- const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
90
- const re = new RegExp(`(^|\\n)\\s*${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=\\s*"([^"]*)"\\s*(?:#.*)?(?=\\n|$)`);
91
- return topLevel.match(re)?.[2] || '';
92
- }
@@ -1,118 +0,0 @@
1
- import Cocoa
2
-
3
- extension ProvidersViewController {
4
- func makeCliConnectProgressView() -> NSStackView {
5
- cliConnectConfigurationStage.setAccessibilityLabel("Codex LB configuration stage: waiting")
6
- cliConnectConfigurationStage.setAccessibilityIdentifier("sks-center-codex-lb-configuration-stage")
7
- cliConnectRequestStage.setAccessibilityLabel("Codex LB one-request low-token test stage: waiting")
8
- cliConnectRequestStage.setAccessibilityIdentifier("sks-center-codex-lb-request-stage")
9
- cliConnectResponseStage.setAccessibilityLabel("Codex LB response stage: not received")
10
- cliConnectResponseStage.setAccessibilityIdentifier("sks-center-codex-lb-response-stage")
11
- cliConnectResult.setAccessibilityLabel("Codex LB connection proof result: not run")
12
- cliConnectResult.setAccessibilityIdentifier("sks-center-codex-lb-connect-result")
13
- let title = NativeView.sectionTitle("Connection Proof")
14
- title.setAccessibilityIdentifier("sks-center-codex-lb-connect-heading")
15
- let stack = NSStackView(views: [title, cliConnectProgress, cliConnectConfigurationStage, cliConnectRequestStage, cliConnectResponseStage, cliConnectResult])
16
- stack.orientation = .vertical
17
- stack.alignment = .width
18
- stack.spacing = 6
19
- stack.setAccessibilityRole(.group)
20
- stack.setAccessibilityLabel("Codex LB connection proof")
21
- stack.setAccessibilityIdentifier("sks-center-codex-lb-connect-proof")
22
- return stack
23
- }
24
-
25
- func renderCliConnectStages(
26
- progress: Double,
27
- configuration: String,
28
- request: String,
29
- response: String,
30
- result: String,
31
- tone: NSColor
32
- ) {
33
- cliConnectProgress.doubleValue = min(3, max(0, progress))
34
- cliConnectProgress.setAccessibilityValue("Stage \(Int(cliConnectProgress.doubleValue)) of 3")
35
- cliConnectConfigurationStage.stringValue = "1. Configuration · \(configuration)"
36
- cliConnectConfigurationStage.setAccessibilityLabel("Codex LB configuration stage: \(configuration)")
37
- cliConnectRequestStage.stringValue = "2. One-request low-token test · \(request)"
38
- cliConnectRequestStage.setAccessibilityLabel("Codex LB one-request low-token test stage: \(request)")
39
- cliConnectResponseStage.stringValue = "3. Response · \(response)"
40
- cliConnectResponseStage.setAccessibilityLabel("Codex LB response stage: \(response)")
41
- cliConnectResult.stringValue = result
42
- cliConnectResult.textColor = tone
43
- cliConnectResult.setAccessibilityLabel("Codex LB connection proof result: \(result)")
44
- }
45
-
46
- @objc func testConnection() {
47
- runConnectTest()
48
- }
49
-
50
- func runConnectTest() {
51
- guard !connectTestInFlight else {
52
- cliProviderStatus.stringValue = "A Codex LB connection proof is already running."
53
- return
54
- }
55
- guard !busy else { cliProviderStatus.stringValue = "Another provider action is already running."; return }
56
- guard let snapshot = operations.begin(kind: "codex-lb-connect-test", mutationGroup: nil, summary: "Run Codex LB connect test") else {
57
- cliProviderStatus.stringValue = "Another guarded operation is already running. Wait or open Diagnostics."
58
- return
59
- }
60
- connectTestInFlight = true
61
- setBusy(true)
62
- cliProviderStatus.stringValue = "Codex LB mode \(codexLbSelectedNow ? "selected" : "selection unconfirmed") · sending one bounded low-token request…"
63
- renderCliConnectStages(
64
- progress: 2,
65
- configuration: codexLbSelectedNow ? "CLI provider selected" : "selection unconfirmed",
66
- request: "sending one bounded request…",
67
- response: "waiting",
68
- result: "Waiting for structured proof. Connectivity has not been claimed.",
69
- tone: .secondaryLabelColor
70
- )
71
- _ = operations.update(snapshot, state: .running, stage: "one-request-low-token-test", progress: 2.0 / 3.0, summary: "Run Codex LB connect test")
72
- processClient.run(["codex-lb", "connect-test", "--json"], timeout: NativeView.mutationTimeout) { [weak self] result in
73
- guard let self = self else { return }
74
- self.connectTestInFlight = false
75
- self.setBusy(false)
76
- let parsed = self.json(result.output)
77
- let proof = parsed.flatMap(CodexLbConnectTestTruth.success(from:))
78
- let proved = result.code == 0 && proof != nil
79
- _ = self.operations.update(snapshot, state: proved ? .succeeded : .failed, stage: "complete", progress: 1, summary: proved ? "Codex LB connection proved" : "Codex LB connection proof failed")
80
- if let proof = proof, result.code == 0 {
81
- self.codexLbSelectedNow = true
82
- self.codexLbProvedNow = true
83
- self.cliProviderStatus.stringValue = "Codex LB mode selected · connection proved by one live response."
84
- ControlKit.setBadge(self.activeProviderBadge, text: "Codex LB · active · model \(proof.model) · \(proof.latencyMs) ms", tone: .ok)
85
- self.renderCliConnectStages(
86
- progress: 3,
87
- configuration: "CLI provider selected",
88
- request: "completed · \(proof.outputTokens) output tokens",
89
- response: "received · id \(proof.responseId)",
90
- result: proof.renderedSummary,
91
- tone: .systemGreen
92
- )
93
- return
94
- }
95
- self.codexLbProvedNow = false
96
- let selected = self.codexLbSelectedNow
97
- let code = self.publicFailureCode(parsed)
98
- let failure = parsed.map(CodexLbConnectTestTruth.validationFailure(from:))
99
- ?? "structured connect-test JSON was not returned"
100
- self.cliProviderStatus.stringValue = selected
101
- ? "Codex LB mode is selected, but connection proof failed · \(code) · \(failure)."
102
- : "Codex LB connection proof failed and provider selection is unconfirmed · \(code) · \(failure)."
103
- if selected {
104
- ControlKit.setBadge(self.activeProviderBadge, text: "Codex LB · selected · \(code) · connection proof failed", tone: .warning)
105
- }
106
- self.renderCliConnectStages(
107
- progress: 3,
108
- configuration: selected ? "CLI provider remains selected" : "selection unconfirmed",
109
- request: "completed without valid proof",
110
- response: "failed",
111
- result: selected
112
- ? "\(code) · Mode is selected, but connection proof failed · \(failure). Use Run Connect Test to retry."
113
- : "\(code) · Connection proof failed · \(failure). Confirm the provider, then retry.",
114
- tone: .systemRed
115
- )
116
- }
117
- }
118
- }
@@ -1,17 +0,0 @@
1
- import Cocoa
2
-
3
- extension ProvidersViewController {
4
- func refreshFastStatus() {
5
- if !busy { fastStatus.stringValue = "Codex Fast: checking the official service-tier setting…" }
6
- processClient.run(["fast-mode", "status", "--json"], timeout: NativeView.statusTimeout) { [weak self] result in
7
- guard let self = self else { return }
8
- guard result.code == 0, let json = self.json(result.output),
9
- let global = json["global"] as? [String: Any], let on = global["on"] as? Bool else {
10
- self.fastStatus.stringValue = "Codex Fast: unavailable — no state was assumed."
11
- return
12
- }
13
- let tier = global["service_tier"] as? String ?? (on ? "fast" : "default")
14
- self.fastStatus.stringValue = "Codex Fast: \(on ? "On" : "Off") · official service_tier=\(tier) · model and reasoning remain separate."
15
- }
16
- }
17
- }