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,26 +1,108 @@
1
- import { randomInt } from 'node:crypto';
1
+ import { createHash, randomInt, timingSafeEqual } from 'node:crypto';
2
2
  import http, {} from 'node:http';
3
3
  import net, {} from 'node:net';
4
- import { forwardHttp, prepareDesktopBridgeRequestBody } from './http-forward.js';
5
- import { assertAllowedOrigin, assertAllowedPath, assertDesktopBridgeRequestPolicy, assertLoopbackPeer, assertLoopbackListenHost, assertWebSocketUpgrade, prepareDesktopBridgeConfig, safeBridgeErrorCode, validatePreparedDesktopBridgeConfig, } from './security.js';
6
- import { createDesktopBridgePublicState, desktopBridgeStatePath, removeDesktopBridgeStateIfOwned, writeDesktopBridgeState, } from './state.js';
4
+ import { forwardHttp, prepareDesktopBridgeRequest } from './http-forward.js';
5
+ import { assertAllowedOrigin, assertAllowedPath, assertLoopbackPeer, assertLoopbackListenHost, assertWebSocketUpgrade, prepareDesktopBridgeConfig, safeBridgeErrorCode, validatePreparedDesktopBridgeConfig, } from './security.js';
6
+ import { createDesktopBridgePublicState, desktopBridgeListenOrigin, desktopBridgeStatePath, refreshDesktopBridgeState, removeDesktopBridgeStateIfOwned, writeDesktopBridgeState, } from './state.js';
7
7
  import { DesktopBridgeError } from './types.js';
8
+ import { DESKTOP_BRIDGE_CLIENT_PATH_PREFIX, DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH, DESKTOP_BRIDGE_DIAGNOSTIC_PATH, DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL } from './types.js';
8
9
  import { forwardWebSocket } from './websocket-forward.js';
9
- function pathnameFromRequest(req) {
10
+ function authenticateDesktopBridgeClient(req, input) {
11
+ const raw = String(req.url || '/');
12
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw) || raw.startsWith('//') || /[\r\n\0]/.test(raw)) {
13
+ throw new DesktopBridgeError('bridge_request_target_invalid');
14
+ }
15
+ let parsed;
10
16
  try {
11
- return new URL(req.url || '/', 'http://bridge.invalid').pathname;
17
+ parsed = new URL(raw, 'http://bridge.invalid');
12
18
  }
13
19
  catch {
14
20
  throw new DesktopBridgeError('bridge_request_target_invalid');
15
21
  }
22
+ const tokenPrefix = `${DESKTOP_BRIDGE_CLIENT_PATH_PREFIX}/`;
23
+ if (!parsed.pathname.startsWith(tokenPrefix))
24
+ throw new DesktopBridgeError('bridge_client_capability_required');
25
+ const remainder = parsed.pathname.slice(tokenPrefix.length);
26
+ const separator = remainder.indexOf('/');
27
+ const capability = separator > 0 ? remainder.slice(0, separator) : '';
28
+ const canonicalPathname = separator > 0 ? remainder.slice(separator) : '';
29
+ if (!/^[A-Za-z0-9_-]{43}$/.test(capability) || !canonicalPathname.startsWith('/')) {
30
+ throw new DesktopBridgeError('bridge_client_capability_invalid');
31
+ }
32
+ const actual = createHash('sha256').update(capability).digest();
33
+ const expected = Buffer.from(input.clientCapabilitySha256, 'hex');
34
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
35
+ throw new DesktopBridgeError('bridge_client_capability_invalid');
36
+ }
37
+ req.url = `${canonicalPathname}${parsed.search}`;
38
+ return {
39
+ pathname: canonicalPathname,
40
+ clientBasePath: `${DESKTOP_BRIDGE_CLIENT_PATH_PREFIX}/${capability}`,
41
+ };
42
+ }
43
+ function handleDiagnosticWebSocket(req, socket, head) {
44
+ const protocols = String(req.headers['sec-websocket-protocol'] || '').split(',').map((value) => value.trim());
45
+ if (!protocols.includes(DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL))
46
+ throw new DesktopBridgeError('bridge_websocket_protocol_mismatch');
47
+ const key = String(req.headers['sec-websocket-key'] || '');
48
+ const accept = createHash('sha1').update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest('base64');
49
+ socket.write('HTTP/1.1 101 Switching Protocols\r\n'
50
+ + 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
51
+ + `Sec-WebSocket-Accept: ${accept}\r\n`
52
+ + `Sec-WebSocket-Protocol: ${DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL}\r\n\r\n`);
53
+ let buffered = head;
54
+ const consume = (chunk) => {
55
+ if (chunk)
56
+ buffered = Buffer.concat([buffered, chunk]);
57
+ while (buffered.length >= 6) {
58
+ const opcode = buffered[0] & 0x0f;
59
+ const masked = (buffered[1] & 0x80) !== 0;
60
+ const length = buffered[1] & 0x7f;
61
+ if (!masked || length > 125) {
62
+ socket.destroy();
63
+ return;
64
+ }
65
+ if (buffered.length < 6 + length)
66
+ return;
67
+ const mask = buffered.subarray(2, 6);
68
+ const raw = buffered.subarray(6, 6 + length);
69
+ const payload = Buffer.alloc(length);
70
+ for (let index = 0; index < length; index += 1)
71
+ payload[index] = (raw[index] || 0) ^ (mask[index % 4] || 0);
72
+ buffered = buffered.subarray(6 + length);
73
+ if (opcode === 1 || opcode === 2)
74
+ socket.write(Buffer.concat([Buffer.from([0x80 | opcode, length]), payload]));
75
+ else if (opcode === 8) {
76
+ socket.end(Buffer.concat([Buffer.from([0x88, payload.length]), payload]));
77
+ return;
78
+ }
79
+ }
80
+ };
81
+ socket.on('data', consume);
82
+ consume();
16
83
  }
17
84
  function rejectionStatus(code) {
18
85
  if (code === 'bridge_path_not_allowed')
19
86
  return 404;
20
- if (code.includes('origin') || code.includes('peer') || code.includes('loopback'))
87
+ if (code === 'bridge_request_capacity_exhausted')
88
+ return 503;
89
+ if (code === 'bridge_session_pin_persist_failed' || code.startsWith('bridge_upstream_'))
90
+ return 502;
91
+ if (code.includes('origin') || code.includes('peer') || code.includes('loopback') || code.includes('capability'))
21
92
  return 403;
22
93
  return 400;
23
94
  }
95
+ function rejectionStatusText(status) {
96
+ if (status === 404)
97
+ return 'Not Found';
98
+ if (status === 403)
99
+ return 'Forbidden';
100
+ if (status === 502)
101
+ return 'Bad Gateway';
102
+ if (status === 503)
103
+ return 'Service Unavailable';
104
+ return 'Bad Request';
105
+ }
24
106
  function writeBridgeRejection(res, error) {
25
107
  if (res.headersSent) {
26
108
  res.destroy(error instanceof Error ? error : undefined);
@@ -39,13 +121,32 @@ function writeUpgradeRejection(socket, error) {
39
121
  return;
40
122
  const code = safeBridgeErrorCode(error);
41
123
  const status = rejectionStatus(code);
42
- socket.end(`HTTP/1.1 ${status} ${status === 404 ? 'Not Found' : status === 403 ? 'Forbidden' : 'Bad Request'}\r\n`
124
+ socket.end(`HTTP/1.1 ${status} ${rejectionStatusText(status)}\r\n`
43
125
  + 'Content-Type: application/json\r\n'
44
126
  + 'Cache-Control: no-store\r\n'
45
127
  + 'Connection: close\r\n'
46
128
  + '\r\n'
47
129
  + JSON.stringify({ error: { type: 'sks_bridge_rejection', code, message: code } }));
48
130
  }
131
+ function writeDiagnosticHealth(req, res, input) {
132
+ if (req.method !== 'GET')
133
+ throw new DesktopBridgeError('bridge_diagnostic_method_not_allowed');
134
+ const payload = {
135
+ schema: 'sks.desktop-bridge-health.v1',
136
+ runtime: 'desktop-bridge',
137
+ state: 'ready',
138
+ provider_registry_generation: input.providerRegistry?.generation || null,
139
+ route_policy_generation: input.routePolicy?.policy_generation || null,
140
+ catalog_generation: input.routePolicy?.catalog_generation || null,
141
+ secret_fields_redacted: true
142
+ };
143
+ res.writeHead(200, {
144
+ 'content-type': 'application/json',
145
+ 'cache-control': 'no-store',
146
+ connection: 'close'
147
+ });
148
+ res.end(JSON.stringify(payload));
149
+ }
49
150
  async function listenExact(server, host, port) {
50
151
  await new Promise((resolve, reject) => {
51
152
  const onError = (error) => {
@@ -101,23 +202,43 @@ export const preflightDesktopBridge = prepareDesktopBridgeConfig;
101
202
  export async function startPreparedDesktopBridge(input, options = {}) {
102
203
  validatePreparedDesktopBridgeConfig(input);
103
204
  const sockets = new Set();
205
+ const maxConcurrentRequests = input.maxConcurrentRequests ?? 64;
206
+ let activeRequests = 0;
104
207
  const server = http.createServer((req, res) => {
105
208
  void (async () => {
209
+ let admitted = false;
106
210
  try {
211
+ if (activeRequests >= maxConcurrentRequests)
212
+ throw new DesktopBridgeError('bridge_request_capacity_exhausted');
213
+ activeRequests += 1;
214
+ admitted = true;
107
215
  assertLoopbackPeer(req.socket.remoteAddress);
216
+ const authenticated = authenticateDesktopBridgeClient(req, input);
108
217
  assertAllowedOrigin(req.headers, input.allowedOrigins);
109
- assertAllowedPath(pathnameFromRequest(req), input.allowedPathPrefixes);
110
- assertDesktopBridgeRequestPolicy({ headers: req.headers, config: input });
111
- const body = await prepareDesktopBridgeRequestBody(req, input);
112
- await forwardHttp(req, res, input, body);
218
+ if (authenticated.pathname === DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH) {
219
+ writeDiagnosticHealth(req, res, input);
220
+ return;
221
+ }
222
+ assertAllowedPath(authenticated.pathname, input.allowedPathPrefixes);
223
+ const prepared = await prepareDesktopBridgeRequest(req, input);
224
+ await forwardHttp(req, res, input, prepared, `${desktopBridgeListenOrigin(input)}${authenticated.clientBasePath}`);
113
225
  }
114
226
  catch (error) {
115
- req.resume();
227
+ req.pause();
228
+ res.once('finish', () => {
229
+ if (!req.complete)
230
+ req.destroy();
231
+ });
116
232
  writeBridgeRejection(res, error);
117
233
  }
234
+ finally {
235
+ if (admitted)
236
+ activeRequests -= 1;
237
+ }
118
238
  })();
119
239
  });
120
- server.requestTimeout = 0;
240
+ server.maxConnections = input.maxConnections ?? 128;
241
+ server.requestTimeout = input.requestTimeoutMs ?? 30_000;
121
242
  server.headersTimeout = Math.max(5_000, input.connectTimeoutMs);
122
243
  server.keepAliveTimeout = Math.min(input.idleTimeoutMs, 60_000);
123
244
  server.on('connection', (socket) => {
@@ -131,14 +252,15 @@ export async function startPreparedDesktopBridge(input, options = {}) {
131
252
  server.on('upgrade', (req, socket, head) => {
132
253
  try {
133
254
  assertLoopbackPeer(req.socket.remoteAddress);
255
+ const authenticated = authenticateDesktopBridgeClient(req, input);
134
256
  assertAllowedOrigin(req.headers, input.allowedOrigins);
135
- assertAllowedPath(pathnameFromRequest(req), input.allowedPathPrefixes);
136
- assertDesktopBridgeRequestPolicy({ headers: req.headers, config: input });
137
257
  assertWebSocketUpgrade(req.headers, req.method);
138
- if (input.providerMode === 'openrouter') {
139
- throw new DesktopBridgeError('bridge_openrouter_websocket_unsupported');
258
+ if (authenticated.pathname === DESKTOP_BRIDGE_DIAGNOSTIC_PATH) {
259
+ handleDiagnosticWebSocket(req, socket, head);
260
+ return;
140
261
  }
141
- forwardWebSocket(req, socket, head, input);
262
+ assertAllowedPath(authenticated.pathname, input.allowedPathPrefixes);
263
+ void forwardWebSocket(req, socket, head, input, `${desktopBridgeListenOrigin(input)}${authenticated.clientBasePath}`).catch((error) => writeUpgradeRejection(socket, error));
142
264
  }
143
265
  catch (error) {
144
266
  writeUpgradeRejection(socket, error);
@@ -159,6 +281,11 @@ export async function startPreparedDesktopBridge(input, options = {}) {
159
281
  await closeServer(server, sockets);
160
282
  throw error;
161
283
  }
284
+ const freshnessMs = input.stateFreshnessMs ?? 5 * 60_000;
285
+ const heartbeat = statePath ? setInterval(() => {
286
+ void refreshDesktopBridgeState(statePath, state, new Date(), freshnessMs).catch(() => undefined);
287
+ }, Math.max(1_000, Math.floor(freshnessMs / 3))) : null;
288
+ heartbeat?.unref();
162
289
  let stopped = false;
163
290
  const handle = {
164
291
  server,
@@ -169,6 +296,8 @@ export async function startPreparedDesktopBridge(input, options = {}) {
169
296
  if (stopped)
170
297
  return;
171
298
  stopped = true;
299
+ if (heartbeat)
300
+ clearInterval(heartbeat);
172
301
  await closeServer(server, sockets);
173
302
  if (statePath)
174
303
  await removeDesktopBridgeStateIfOwned(statePath, state);
@@ -3,85 +3,73 @@ import fsp from 'node:fs/promises';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { DESKTOP_BRIDGE_STATE_SCHEMA, DesktopBridgeError } from './types.js';
6
+ const DEFAULT_FRESHNESS_MS = 5 * 60_000;
6
7
  export function desktopBridgeStatePath(home = os.homedir()) {
7
- return path.join(home, '.codex', 'sks', 'codex-lb-desktop-bridge.json');
8
- }
9
- export function sha256Hex(value) {
10
- return createHash('sha256').update(value).digest('hex');
8
+ return path.join(home, '.codex', 'sks', 'desktop-bridge-state.json');
11
9
  }
10
+ export function sha256Hex(value) { return createHash('sha256').update(value).digest('hex'); }
12
11
  export function desktopBridgeListenOrigin(config) {
13
- const host = config.listenHost === '::1' ? '[::1]' : config.listenHost;
14
- return `http://${host}:${config.listenPort}`;
12
+ return `http://${config.listenHost === '::1' ? '[::1]' : config.listenHost}:${config.listenPort}`;
15
13
  }
16
14
  export function desktopBridgeConfigGeneration(config) {
17
- const stable = JSON.stringify({
18
- listenHost: config.listenHost,
19
- listenPort: config.listenPort,
20
- providerMode: config.providerMode || 'codex-lb',
21
- allowedModels: [...(config.allowedModels || [])].sort(),
22
- providerPolicy: config.providerPolicy || null,
23
- credentialReadiness: config.credentialReadiness || null,
24
- childPolicy: config.childPolicy || null,
25
- sessionPins: [...(config.sessionPins || [])].sort((left, right) => left.session_id.localeCompare(right.session_id)),
26
- requireSessionPin: config.requireSessionPin === true,
27
- remoteBaseUrl: new URL(config.remoteBaseUrl).toString().replace(/\/$/, ''),
28
- gatewayAuthTransport: config.gatewayAuthTransport,
29
- allowedPathPrefixes: [...config.allowedPathPrefixes],
30
- allowedOrigins: [...config.allowedOrigins],
31
- connectTimeoutMs: config.connectTimeoutMs,
32
- idleTimeoutMs: config.idleTimeoutMs,
33
- });
34
- return sha256Hex(stable);
15
+ const registry = config.providerRegistry;
16
+ return sha256Hex(JSON.stringify({
17
+ listen_host: config.listenHost, listen_port: config.listenPort,
18
+ provider_registry_generation: registry.generation,
19
+ provider_credential_generations: Object.fromEntries(Object.keys(registry.providers).map((id) => [id, registry.providers[id].credential_generation])),
20
+ route_policy_generation: config.routePolicy.policy_generation,
21
+ catalog_generation: config.routePolicy.catalog_generation,
22
+ client_capability_sha256: config.clientCapabilitySha256,
23
+ allowed_paths: [...config.allowedPathPrefixes], allowed_origins: [...config.allowedOrigins].sort(),
24
+ connect_timeout_ms: config.connectTimeoutMs, idle_timeout_ms: config.idleTimeoutMs,
25
+ }));
35
26
  }
36
27
  export function createDesktopBridgePublicState(config, options = {}) {
37
- const listenOrigin = desktopBridgeListenOrigin(config);
38
- const remote = new URL(config.remoteBaseUrl);
39
- const providerMode = config.providerMode || 'codex-lb';
40
- const allowedModels = [...(config.allowedModels || [])].sort();
28
+ const now = options.now ?? new Date();
29
+ const registry = config.providerRegistry;
30
+ const enabled = Object.keys(registry.providers).filter((id) => registry.providers[id].enabled);
31
+ const credentialGenerations = Object.fromEntries(Object.keys(registry.providers).map((id) => [id, registry.providers[id].credential_generation]));
32
+ const listen = desktopBridgeListenOrigin(config);
41
33
  return {
42
- schema: DESKTOP_BRIDGE_STATE_SCHEMA,
43
- pid: options.pid ?? process.pid,
44
- started_at: (options.now ?? new Date()).toISOString(),
45
- listen_origin: listenOrigin,
46
- codex_base_url: providerMode === 'openrouter'
47
- ? `${listenOrigin}/api/v1`
48
- : `${listenOrigin}/backend-api/codex`,
49
- provider_mode: providerMode,
50
- allowed_models_sha256: sha256Hex(JSON.stringify(allowedModels)),
51
- ...(config.providerPolicy ? { provider_policy_sha256: sha256Hex(JSON.stringify(config.providerPolicy)) } : {}),
52
- ...(config.childPolicy ? { child_policy_sha256: sha256Hex(JSON.stringify(config.childPolicy)) } : {}),
53
- session_pin_enforcement: config.requireSessionPin === true ? 'required' : 'compatibility',
54
- remote_origin_sha256: sha256Hex(remote.origin),
55
- gateway_auth_transport: config.gatewayAuthTransport,
56
- config_generation: desktopBridgeConfigGeneration(config),
34
+ schema: DESKTOP_BRIDGE_STATE_SCHEMA, runtime: 'desktop-bridge', pid: options.pid ?? process.pid,
35
+ started_at: now.toISOString(), updated_at: now.toISOString(),
36
+ stale_after: new Date(now.getTime() + (config.stateFreshnessMs ?? DEFAULT_FRESHNESS_MS)).toISOString(),
37
+ listen_origin: listen, codex_base_url: `${listen}/backend-api/codex`, process_generation: randomUUID(),
38
+ provider_registry_generation: registry.generation, route_policy_generation: config.routePolicy.policy_generation,
39
+ catalog_generation: config.routePolicy.catalog_generation, enabled_providers: enabled,
40
+ provider_credential_generations: credentialGenerations, last_verified_probe_ids: [], config_generation: desktopBridgeConfigGeneration(config),
57
41
  };
58
42
  }
43
+ function isGeneration(value) { return typeof value === 'string' && value.length > 0 && value.length <= 256 && !/[\r\n\0]/.test(value); }
59
44
  export function isDesktopBridgePublicState(value) {
60
45
  if (!value || typeof value !== 'object')
61
46
  return false;
62
47
  const row = value;
63
- return row.schema === DESKTOP_BRIDGE_STATE_SCHEMA
64
- && Number.isInteger(row.pid)
65
- && Number(row.pid) > 0
66
- && typeof row.started_at === 'string'
67
- && Number.isFinite(Date.parse(row.started_at))
68
- && typeof row.listen_origin === 'string'
69
- && /^http:\/\/(?:127\.0\.0\.1|\[::1\]):\d+$/.test(row.listen_origin)
70
- && typeof row.codex_base_url === 'string'
71
- && (row.provider_mode === 'codex-lb' || row.provider_mode === 'openrouter')
72
- && row.codex_base_url === (row.provider_mode === 'openrouter'
73
- ? `${row.listen_origin}/api/v1`
74
- : `${row.listen_origin}/backend-api/codex`)
75
- && typeof row.allowed_models_sha256 === 'string'
76
- && /^[a-f0-9]{64}$/.test(row.allowed_models_sha256)
77
- && (row.provider_policy_sha256 === undefined || typeof row.provider_policy_sha256 === 'string' && /^[a-f0-9]{64}$/.test(row.provider_policy_sha256))
78
- && (row.child_policy_sha256 === undefined || typeof row.child_policy_sha256 === 'string' && /^[a-f0-9]{64}$/.test(row.child_policy_sha256))
79
- && (row.session_pin_enforcement === undefined || row.session_pin_enforcement === 'required' || row.session_pin_enforcement === 'compatibility')
80
- && typeof row.remote_origin_sha256 === 'string'
81
- && /^[a-f0-9]{64}$/.test(row.remote_origin_sha256)
82
- && (row.gateway_auth_transport === 'x-codex-lb-api-key' || row.gateway_auth_transport === 'authorization-bearer-compat')
83
- && typeof row.config_generation === 'string'
84
- && /^[a-f0-9]{64}$/.test(row.config_generation);
48
+ if (/"(?:api_?key|secret|authorization|cookie|access_token|refresh_token)"\s*:/i.test(JSON.stringify(row)))
49
+ return false;
50
+ const dates = ['started_at', 'updated_at', 'stale_after'].every((key) => typeof row[key] === 'string' && Number.isFinite(Date.parse(String(row[key]))));
51
+ const credentialRows = row.provider_credential_generations;
52
+ return row.schema === DESKTOP_BRIDGE_STATE_SCHEMA && row.runtime === 'desktop-bridge' && Number.isInteger(row.pid) && Number(row.pid) > 0 && dates
53
+ && typeof row.listen_origin === 'string' && /^http:\/\/(?:127\.0\.0\.1|\[::1\]):\d+$/.test(row.listen_origin)
54
+ && row.codex_base_url === `${row.listen_origin}/backend-api/codex` && isGeneration(row.process_generation)
55
+ && isGeneration(row.provider_registry_generation) && isGeneration(row.route_policy_generation) && isGeneration(row.catalog_generation)
56
+ && Array.isArray(row.enabled_providers) && row.enabled_providers.every((id) => id === 'codex-lb' || id === 'openrouter')
57
+ && !!credentialRows && typeof credentialRows === 'object' && isGeneration(credentialRows['codex-lb']) && isGeneration(credentialRows.openrouter)
58
+ && Array.isArray(row.last_verified_probe_ids) && row.last_verified_probe_ids.every(isGeneration)
59
+ && typeof row.config_generation === 'string' && /^[a-f0-9]{64}$/.test(row.config_generation);
60
+ }
61
+ export async function refreshDesktopBridgeState(file, state, now = new Date(), freshnessMs = DEFAULT_FRESHNESS_MS) {
62
+ const current = await readDesktopBridgeState(file).catch(() => null);
63
+ if (!current || current.schema !== DESKTOP_BRIDGE_STATE_SCHEMA || current.pid !== state.pid
64
+ || current.process_generation !== state.process_generation || current.config_generation !== state.config_generation)
65
+ return false;
66
+ state.updated_at = now.toISOString();
67
+ state.stale_after = new Date(now.getTime() + freshnessMs).toISOString();
68
+ await writeDesktopBridgeState(file, state);
69
+ return true;
70
+ }
71
+ export function isDesktopBridgeStateFresh(state, now = new Date()) {
72
+ return Date.parse(state.stale_after) > now.getTime();
85
73
  }
86
74
  export async function writeDesktopBridgeState(file, state) {
87
75
  if (!isDesktopBridgePublicState(state))
@@ -109,7 +97,7 @@ export async function readDesktopBridgeState(file) {
109
97
  return null;
110
98
  throw error;
111
99
  }
112
- if (!stat.isFile())
100
+ if (!stat.isFile() || stat.isSymbolicLink())
113
101
  throw new DesktopBridgeError('bridge_state_not_file');
114
102
  if ((stat.mode & 0o077) !== 0)
115
103
  throw new DesktopBridgeError('bridge_state_permissions_unsafe');
@@ -131,9 +119,7 @@ export async function removeDesktopBridgeStateIfOwned(file, expected) {
131
119
  const current = await readDesktopBridgeState(file).catch(() => null);
132
120
  if (!current || current.pid !== expected.pid || current.config_generation !== expected.config_generation)
133
121
  return false;
134
- await fsp.unlink(file).catch((error) => {
135
- if (error.code !== 'ENOENT')
136
- throw error;
137
- });
122
+ await fsp.unlink(file).catch((error) => { if (error.code !== 'ENOENT')
123
+ throw error; });
138
124
  return true;
139
125
  }
@@ -1,4 +1,9 @@
1
- export const DESKTOP_BRIDGE_STATE_SCHEMA = 'sks.codex-lb-desktop-bridge.v1';
1
+ export const DESKTOP_BRIDGE_STATE_SCHEMA = 'sks.desktop-bridge-state.v2';
2
+ export const DESKTOP_BRIDGE_REGISTRY_SCHEMA = 'sks.desktop-bridge-provider-registry.v1';
3
+ export const DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH = '/__sks/diagnostics/health';
4
+ export const DESKTOP_BRIDGE_DIAGNOSTIC_PATH = '/__sks/diagnostics/websocket';
5
+ export const DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL = 'sks.desktop-bridge.probe.v2';
6
+ export const DESKTOP_BRIDGE_CLIENT_PATH_PREFIX = '/__sks/client';
2
7
  export const DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES = [
3
8
  '/backend-api/codex/',
4
9
  '/backend-api/files',