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,44 +1,40 @@
1
- import { probeEvidence } from './probe-evidence.js';
2
- export function runImageGenerationProbe(input) {
3
- const outputEventSeen = (input.events || []).some(isImageOutputEvent);
4
- const attempted = input.attempted === true || Boolean(input.events?.length);
5
- const cliTransportVerified = input.cliTransportAccepted === true
6
- && outputEventSeen
7
- && input.artifactMaterialized === true;
8
- const blockers = [
9
- ...(input.blockers || []),
10
- ...(attempted && input.toolAdvertised === false ? ['image_tool_not_advertised'] : []),
11
- ...(attempted && input.requestToolsPresent === false ? ['image_tools_omitted_by_responses_lite'] : []),
12
- ...(attempted && !outputEventSeen ? ['image_output_event_filtered'] : []),
13
- ...((input.level === 'deep' || input.cliTransportAccepted === true)
14
- && outputEventSeen && input.artifactMaterialized !== true
15
- ? ['image_artifact_not_materialized']
16
- : [])
17
- ];
18
- return probeEvidence({
19
- advertised: input.manifestRouteAdvertised === true || input.toolAdvertised === true,
20
- attempted,
21
- verified: (input.level === 'deep'
22
- && outputEventSeen
23
- && input.artifactMaterialized === true)
24
- || cliTransportVerified,
25
- fixture: input.fixture,
26
- source: input.level === 'deep' ? 'deep_probe' : attempted ? 'transport' : 'manifest',
27
- unsupported: input.manifestRouteAdvertised === false && input.toolAdvertised === false,
28
- blockers,
29
- warnings: outputEventSeen && input.level !== 'deep' && !cliTransportVerified
30
- ? ['image_event_transport_seen_without_real_desktop_artifact']
31
- : [],
32
- evidence: {
33
- route: input.route || null,
34
- tool_advertised: input.toolAdvertised === true,
35
- request_tools_present: input.requestToolsPresent === true,
36
- output_image_event_seen: outputEventSeen,
37
- artifact_materialized: input.artifactMaterialized === true,
38
- cli_transport_accepted: input.cliTransportAccepted === true,
39
- fixture: input.fixture === true
40
- }
41
- }, input.checkedAt);
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { isAbsolute } from 'node:path';
3
+ import { sha256 } from '../../fsx.js';
4
+ import { capabilityProbeResultV3 } from './probe-evidence.js';
5
+ export function runImageGenerationProbeV3(input) {
6
+ if (input.requestedLevel !== 'deep' || input.attempted !== true || input.fixture === true) {
7
+ return capabilityProbeResultV3({
8
+ ...input,
9
+ capability: 'image_generation',
10
+ scope: `provider:${input.providerId}`,
11
+ stage: 'preflight',
12
+ state: 'not_attempted',
13
+ source: input.advertised ? 'manifest' : 'config',
14
+ warnings: input.fixture ? ['image_fixture_not_live_evidence'] : [],
15
+ recoveryAction: 'run_deep_verification',
16
+ evidence: {
17
+ advertised: input.advertised === true,
18
+ fixture: input.fixture === true,
19
+ reason: input.fixture ? 'fixture_not_live_evidence' : 'deep_verification_not_run'
20
+ }
21
+ });
22
+ }
23
+ if (input.outputEventSeen !== true) {
24
+ return imageFailure(input, 'feature_response', 'image_output_event_filtered');
25
+ }
26
+ const artifact = validateImageArtifact(input.artifactPath, input.artifactSha256);
27
+ if (!artifact.ok)
28
+ return imageFailure(input, 'artifact_validation', artifact.blocker, artifact.evidence);
29
+ return capabilityProbeResultV3({
30
+ ...input,
31
+ capability: 'image_generation',
32
+ scope: `provider:${input.providerId}`,
33
+ stage: 'complete',
34
+ state: 'verified',
35
+ source: 'artifact',
36
+ evidence: { output_event_seen: true, ...artifact.evidence }
37
+ });
42
38
  }
43
39
  export function isImageOutputEvent(value) {
44
40
  if (!value || typeof value !== 'object' || Array.isArray(value))
@@ -51,3 +47,64 @@ export function isImageOutputEvent(value) {
51
47
  return Boolean(item && typeof item === 'object' && !Array.isArray(item)
52
48
  && /image_generation|output_image/i.test(String(item.type || '')));
53
49
  }
50
+ function imageFailure(input, stage, blocker, evidence = {}) {
51
+ return capabilityProbeResultV3({
52
+ ...input,
53
+ capability: 'image_generation',
54
+ scope: `provider:${input.providerId}`,
55
+ stage,
56
+ state: 'blocked',
57
+ terminal: true,
58
+ rootCause: blocker,
59
+ blockers: [blocker],
60
+ retryable: true,
61
+ recoveryAction: 'run_deep_verification',
62
+ source: stage === 'artifact_validation' ? 'artifact' : 'deep_probe',
63
+ evidence: { output_event_seen: input.outputEventSeen === true, ...evidence }
64
+ });
65
+ }
66
+ function validateImageArtifact(artifactPath, expectedSha256) {
67
+ if (!artifactPath || !isAbsolute(artifactPath)) {
68
+ return { ok: false, blocker: 'image_artifact_path_invalid', evidence: { artifact_path: artifactPath || null } };
69
+ }
70
+ if (!expectedSha256 || !/^[a-f0-9]{64}$/i.test(expectedSha256)) {
71
+ return { ok: false, blocker: 'image_artifact_digest_invalid', evidence: { artifact_path: artifactPath } };
72
+ }
73
+ try {
74
+ const stat = statSync(artifactPath);
75
+ if (!stat.isFile() || stat.size <= 0) {
76
+ return { ok: false, blocker: 'image_artifact_not_materialized', evidence: { artifact_path: artifactPath } };
77
+ }
78
+ const bytes = readFileSync(artifactPath);
79
+ const format = imageArtifactFormat(bytes);
80
+ if (!format) {
81
+ return { ok: false, blocker: 'image_artifact_format_invalid', evidence: { artifact_path: artifactPath } };
82
+ }
83
+ const actualSha256 = sha256(bytes);
84
+ if (actualSha256 !== expectedSha256.toLowerCase()) {
85
+ return {
86
+ ok: false,
87
+ blocker: 'image_artifact_digest_mismatch',
88
+ evidence: { artifact_path: artifactPath, artifact_sha256: actualSha256, artifact_bytes: stat.size, artifact_format: format }
89
+ };
90
+ }
91
+ return {
92
+ ok: true,
93
+ evidence: { artifact_path: artifactPath, artifact_sha256: actualSha256, artifact_bytes: stat.size, artifact_format: format }
94
+ };
95
+ }
96
+ catch {
97
+ return { ok: false, blocker: 'image_artifact_not_materialized', evidence: { artifact_path: artifactPath } };
98
+ }
99
+ }
100
+ function imageArtifactFormat(bytes) {
101
+ if (bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])))
102
+ return 'png';
103
+ if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
104
+ return 'jpeg';
105
+ if (bytes.length >= 6 && ['GIF87a', 'GIF89a'].includes(bytes.subarray(0, 6).toString('ascii')))
106
+ return 'gif';
107
+ if (bytes.length >= 12 && bytes.subarray(0, 4).toString('ascii') === 'RIFF' && bytes.subarray(8, 12).toString('ascii') === 'WEBP')
108
+ return 'webp';
109
+ return null;
110
+ }
@@ -1,39 +1,38 @@
1
- export function probeEvidence(input, checkedAt) {
2
- const blockers = uniqueStrings(input.blockers);
3
- const warnings = uniqueStrings(input.warnings);
4
- const source = input.source || inferSource(input);
5
- return {
6
- state: signalState(input, blockers),
7
- checked_at: checkedAt,
8
- source,
9
- evidence: { ...(input.evidence || {}) },
10
- blockers,
11
- warnings
12
- };
13
- }
14
- export function signalState(input, blockers = uniqueStrings(input.blockers)) {
15
- if (input.skipped === true)
16
- return 'skipped';
17
- if (input.unsupported === true)
18
- return 'unsupported';
19
- if (blockers.length > 0)
20
- return 'blocked';
21
- if (input.verified === true && input.fixture !== true && input.source !== 'config' && input.source !== 'manifest') {
22
- return 'verified';
23
- }
24
- return 'available_unverified';
25
- }
26
1
  export function uniqueStrings(values = []) {
27
2
  return [...new Set((Array.isArray(values) ? values : [values])
28
3
  .map((value) => String(value || '').trim())
29
4
  .filter(Boolean))];
30
5
  }
31
- function inferSource(input) {
32
- if (input.verified === true)
33
- return 'deep_probe';
34
- if (input.attempted === true)
35
- return 'transport';
36
- if (input.advertised === true)
37
- return 'manifest';
38
- return 'config';
6
+ export function capabilityProbeResultV3(input) {
7
+ const rootCause = input.rootCause ? String(input.rootCause) : null;
8
+ const blockers = uniqueStrings(input.blockers)
9
+ .filter((blocker) => blocker !== 'desktop_bridge_websocket_transport_failed');
10
+ const terminalBlockers = input.terminal === true && rootCause ? [rootCause] : blockers;
11
+ const secondary = input.terminal === true && rootCause
12
+ ? blockers.filter((blocker) => blocker !== rootCause)
13
+ : [];
14
+ return {
15
+ schema: 'sks.capability-probe.v3',
16
+ capability: input.capability,
17
+ scope: input.scope,
18
+ requested_level: input.requestedLevel,
19
+ stage: input.stage,
20
+ state: input.state,
21
+ checked_at: input.checkedAt,
22
+ report_id: input.reportId,
23
+ correlation_id: input.correlationId,
24
+ session_id: input.sessionId,
25
+ attempt_id: input.attemptId || 1,
26
+ terminal: input.terminal === true,
27
+ root_cause: rootCause,
28
+ blockers: terminalBlockers,
29
+ warnings: uniqueStrings([
30
+ ...(input.warnings || []),
31
+ ...secondary.map((blocker) => `secondary_diagnostic:${blocker}`)
32
+ ]),
33
+ retryable: input.retryable === true,
34
+ recovery_action: input.recoveryAction || null,
35
+ source: input.source,
36
+ evidence: { ...(input.evidence || {}) }
37
+ };
39
38
  }
@@ -1,44 +1,70 @@
1
- import { probeEvidence } from './probe-evidence.js';
2
- export function runVoiceRealtimeProbe(input) {
3
- const attempted = input.attempted === true
4
- || input.createRouteVerified !== undefined
5
- || input.websocketUpgraded !== undefined;
6
- const transportComplete = input.createRouteVerified === true
7
- && input.locationReceived === true
8
- && input.locationRewritten === true
9
- && input.websocketUpgraded === true
10
- && input.serverEventSeen === true
11
- && input.cleanClose === true
12
- && input.ownerBindingVerified === true;
13
- const blockers = [
14
- ...(input.blockers || []),
15
- ...(attempted && input.createRouteVerified !== true ? ['realtime_create_unauthorized'] : []),
16
- ...(attempted && input.locationReceived !== true ? ['realtime_location_missing'] : []),
17
- ...(attempted && input.locationReceived === true && input.locationRewritten !== true ? ['realtime_location_not_rewritten'] : []),
18
- ...(attempted && input.websocketUpgraded !== true ? ['realtime_websocket_upgrade_failed'] : []),
19
- ...(attempted && input.websocketUpgraded === true && input.serverEventSeen !== true ? ['realtime_sideband_event_missing'] : []),
20
- ...(attempted && input.ownerBindingVerified !== true ? ['realtime_call_owner_binding_failed'] : [])
21
- ];
22
- return probeEvidence({
23
- advertised: input.routeAdvertised === true,
24
- attempted,
25
- verified: input.level === 'deep' && transportComplete,
26
- fixture: input.fixture,
27
- source: input.level === 'deep' ? 'deep_probe' : attempted ? 'transport' : 'manifest',
28
- unsupported: input.routeAdvertised === false && !attempted,
29
- blockers,
30
- warnings: transportComplete && input.level !== 'deep'
31
- ? ['realtime_transport_verified_without_real_voice_session']
32
- : [],
33
- evidence: {
34
- create_route_verified: input.createRouteVerified === true,
35
- location_received: input.locationReceived === true,
36
- location_rewritten: input.locationRewritten === true,
37
- websocket_upgraded: input.websocketUpgraded === true,
38
- server_event_seen: input.serverEventSeen === true,
39
- clean_close: input.cleanClose === true,
40
- owner_binding_verified: input.ownerBindingVerified === true,
41
- fixture: input.fixture === true
42
- }
43
- }, input.checkedAt);
1
+ import { capabilityProbeResultV3 } from './probe-evidence.js';
2
+ export function runVoiceRealtimeProbeV3(input) {
3
+ if (input.requestedLevel !== 'deep' || input.attempted !== true || input.fixture === true) {
4
+ return capabilityProbeResultV3({
5
+ ...input,
6
+ capability: 'voice_mode',
7
+ scope: `provider:${input.providerId}`,
8
+ stage: 'preflight',
9
+ state: 'not_attempted',
10
+ source: input.advertised ? 'manifest' : 'config',
11
+ warnings: input.fixture ? ['voice_fixture_not_live_evidence'] : [],
12
+ recoveryAction: 'run_deep_verification',
13
+ evidence: { advertised: input.advertised === true, fixture: input.fixture === true }
14
+ });
15
+ }
16
+ const failure = firstVoiceFailure(input);
17
+ if (failure) {
18
+ return capabilityProbeResultV3({
19
+ ...input,
20
+ capability: 'voice_mode',
21
+ scope: `provider:${input.providerId}`,
22
+ stage: failure.stage,
23
+ state: 'blocked',
24
+ terminal: true,
25
+ rootCause: failure.blocker,
26
+ blockers: [failure.blocker],
27
+ retryable: true,
28
+ recoveryAction: 'run_deep_verification',
29
+ source: 'deep_probe',
30
+ evidence: voiceEvidence(input)
31
+ });
32
+ }
33
+ return capabilityProbeResultV3({
34
+ ...input,
35
+ capability: 'voice_mode',
36
+ scope: `provider:${input.providerId}`,
37
+ stage: 'complete',
38
+ state: 'verified',
39
+ source: 'deep_probe',
40
+ evidence: voiceEvidence(input)
41
+ });
42
+ }
43
+ function firstVoiceFailure(input) {
44
+ if (input.createRouteVerified !== true)
45
+ return { stage: 'feature_request', blocker: 'realtime_create_unauthorized' };
46
+ if (input.locationReceived !== true)
47
+ return { stage: 'feature_response', blocker: 'realtime_location_missing' };
48
+ if (input.locationRewritten !== true)
49
+ return { stage: 'feature_response', blocker: 'realtime_location_not_rewritten' };
50
+ if (input.websocketUpgraded !== true)
51
+ return { stage: 'websocket_upgrade', blocker: 'realtime_websocket_upgrade_failed' };
52
+ if (input.serverEventSeen !== true)
53
+ return { stage: 'websocket_protocol', blocker: 'realtime_sideband_event_missing' };
54
+ if (input.cleanClose !== true)
55
+ return { stage: 'websocket_protocol', blocker: 'realtime_clean_close_failed' };
56
+ if (input.ownerBindingVerified !== true)
57
+ return { stage: 'feature_response', blocker: 'realtime_call_owner_binding_failed' };
58
+ return null;
59
+ }
60
+ function voiceEvidence(input) {
61
+ return {
62
+ create_route_verified: input.createRouteVerified === true,
63
+ location_received: input.locationReceived === true,
64
+ location_rewritten: input.locationRewritten === true,
65
+ websocket_upgraded: input.websocketUpgraded === true,
66
+ server_event_seen: input.serverEventSeen === true,
67
+ clean_close: input.cleanClose === true,
68
+ owner_binding_verified: input.ownerBindingVerified === true
69
+ };
44
70
  }
@@ -0,0 +1,127 @@
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { codexLbBaseUrlSecurityBlocker, codexLbEnvPath, codexLbMetadataPath, normalizeCodexLbBaseUrl } from '../codex-lb-env.js';
5
+ import { openRouterSecretPaths, writeStoredOpenRouterKey } from '../../providers/openrouter/openrouter-secret-store.js';
6
+ import { writePrivateTextAtomic } from '../../security/private-credential-file.js';
7
+ import { restorePrivateCredential, snapshotPrivateCredential } from './private-snapshot.js';
8
+ import { resolveProviderCredential } from './resolution.js';
9
+ import { fingerprint, fingerprintFull, providerCode, providerCredentialStatus } from './runtime.js';
10
+ import { withProviderCredentialLock } from './locks.js';
11
+ export async function configureProviderCredential(input) {
12
+ const apiKey = String(input.api_key || '').trim();
13
+ if (!apiKey)
14
+ throw new Error(`${providerCode(input.provider_id)}_credential_empty`);
15
+ const home = path.resolve(input.home || process.env.HOME || os.homedir());
16
+ return withProviderCredentialLock(home, input.provider_id, () => input.provider_id === 'openrouter'
17
+ ? configureOpenRouterCredential(input, apiKey, home)
18
+ : configureCodexLbCredential(input, apiKey, home));
19
+ }
20
+ async function configureOpenRouterCredential(input, apiKey, home) {
21
+ const env = { ...(input.processEnv || process.env), HOME: home };
22
+ const paths = input.openRouterPaths || openRouterSecretPaths(env);
23
+ const secretDir = path.resolve(paths.secretDir);
24
+ const keyPath = path.resolve(paths.keyPath);
25
+ const metadataPath = path.resolve(paths.metadataPath);
26
+ if (keyPath === secretDir
27
+ || metadataPath === secretDir
28
+ || !keyPath.startsWith(`${secretDir}${path.sep}`)
29
+ || !metadataPath.startsWith(`${secretDir}${path.sep}`)) {
30
+ throw new Error('openrouter_credential_path_outside_secret_store');
31
+ }
32
+ await fs.mkdir(secretDir, { recursive: true, mode: 0o700 });
33
+ await fs.chmod(secretDir, 0o700);
34
+ const snapshots = await Promise.all([
35
+ snapshotPrivateCredential(secretDir, keyPath, 'openrouter_api_key'),
36
+ snapshotPrivateCredential(secretDir, metadataPath, 'openrouter_api_key_metadata')
37
+ ]);
38
+ try {
39
+ await writeStoredOpenRouterKey(apiKey, { paths });
40
+ const verificationEnv = { ...env, OPENROUTER_API_KEY: '', SKS_OPENROUTER_API_KEY: '' };
41
+ const credential = await resolveProviderCredential('openrouter', {
42
+ home,
43
+ processEnv: verificationEnv,
44
+ openRouterPaths: paths
45
+ });
46
+ if (!credential.secret || credential.fingerprint !== fingerprint(apiKey)) {
47
+ throw new Error('openrouter_credential_write_verification_failed');
48
+ }
49
+ return {
50
+ schema: 'sks.provider-credential-configuration.v1',
51
+ provider_id: input.provider_id,
52
+ configured: true,
53
+ credential: providerCredentialStatus(credential),
54
+ blockers: []
55
+ };
56
+ }
57
+ catch (error) {
58
+ await rollbackCredentialSnapshots(secretDir, snapshots);
59
+ throw error;
60
+ }
61
+ }
62
+ async function configureCodexLbCredential(input, apiKey, home) {
63
+ const baseUrl = normalizeCodexLbBaseUrl(input.host || '');
64
+ const endpointBlocker = codexLbBaseUrlSecurityBlocker(baseUrl);
65
+ if (!baseUrl || endpointBlocker)
66
+ throw new Error(endpointBlocker || 'codex_lb_base_url_missing');
67
+ const codexHome = path.join(home, '.codex');
68
+ const envPath = path.resolve(input.codexLbEnvPath || codexLbEnvPath(home));
69
+ const metadataPath = path.resolve(input.codexLbMetadataPath || codexLbMetadataPath(home));
70
+ if (![envPath, metadataPath].every((file) => file.startsWith(`${codexHome}${path.sep}`))) {
71
+ throw new Error('codex_lb_credential_path_outside_codex_home');
72
+ }
73
+ const snapshots = await Promise.all([
74
+ snapshotPrivateCredential(codexHome, envPath, 'codex_lb_env_file'),
75
+ snapshotPrivateCredential(codexHome, metadataPath, 'codex_lb_metadata_file')
76
+ ]);
77
+ const keyHash = fingerprintFull(apiKey);
78
+ const envText = `export CODEX_LB_BASE_URL=${shellSingleQuote(baseUrl)}\nexport CODEX_LB_API_KEY=${shellSingleQuote(apiKey)}\n`;
79
+ const metadataText = `${JSON.stringify({
80
+ schema: 'sks.codex-lb-metadata.v1',
81
+ base_url: baseUrl,
82
+ updated_at: new Date().toISOString(),
83
+ source: 'bridge-provider-configure',
84
+ gateway_auth_transport: 'authorization-bearer',
85
+ api_key: { redacted: true, sha256: keyHash }
86
+ }, null, 2)}\n`;
87
+ try {
88
+ await writePrivateTextAtomic(codexHome, metadataPath, metadataText, 'codex_lb_metadata_file');
89
+ await writePrivateTextAtomic(codexHome, envPath, envText, 'codex_lb_env_file');
90
+ const credential = await resolveProviderCredential('codex-lb', {
91
+ home,
92
+ processEnv: input.processEnv || {},
93
+ codexLbEnvPath: envPath,
94
+ codexLbMetadataPath: metadataPath
95
+ });
96
+ if (!credential.secret || credential.fingerprint !== keyHash.slice(0, 16)) {
97
+ throw new Error('codex_lb_credential_write_verification_failed');
98
+ }
99
+ return {
100
+ schema: 'sks.provider-credential-configuration.v1',
101
+ provider_id: input.provider_id,
102
+ configured: true,
103
+ credential: providerCredentialStatus(credential),
104
+ blockers: []
105
+ };
106
+ }
107
+ catch (error) {
108
+ await rollbackCredentialSnapshots(codexHome, snapshots);
109
+ throw error;
110
+ }
111
+ }
112
+ async function rollbackCredentialSnapshots(boundary, snapshots) {
113
+ const rollbackErrors = [];
114
+ for (const snapshot of snapshots.reverse()) {
115
+ try {
116
+ await restorePrivateCredential(boundary, snapshot);
117
+ }
118
+ catch {
119
+ rollbackErrors.push(`credential_configuration_rollback_failed:${snapshot.file}`);
120
+ }
121
+ }
122
+ if (rollbackErrors.length > 0)
123
+ throw new Error(rollbackErrors[0]);
124
+ }
125
+ function shellSingleQuote(value) {
126
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
127
+ }
@@ -0,0 +1,11 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import { withFileLock } from '../../locks/file-lock.js';
4
+ export function withProviderCredentialLock(home, providerId, action) {
5
+ const resolvedHome = path.resolve(home || process.env.HOME || os.homedir());
6
+ return withFileLock({
7
+ lockPath: path.join(resolvedHome, '.codex', 'sks', 'locks', `provider-credential-${providerId}.lock`),
8
+ timeoutMs: 10_000,
9
+ staleMs: 60_000,
10
+ }, action);
11
+ }
@@ -0,0 +1,28 @@
1
+ import fs from 'node:fs/promises';
2
+ import { PrivateCredentialFileError, readPrivateCredentialFile, writePrivateTextAtomic } from '../../security/private-credential-file.js';
3
+ export async function snapshotPrivateCredential(boundary, file, label) {
4
+ try {
5
+ const snapshot = await readPrivateCredentialFile(boundary, file, label, { maxBytes: 1024 * 1024 });
6
+ return { file, label, existed: true, text: snapshot.bytes.toString('utf8') };
7
+ }
8
+ catch (error) {
9
+ if (error instanceof PrivateCredentialFileError && error.code === 'missing') {
10
+ return { file, label, existed: false, text: '' };
11
+ }
12
+ throw error;
13
+ }
14
+ }
15
+ export async function restorePrivateCredential(boundary, snapshot) {
16
+ if (snapshot.existed) {
17
+ await writePrivateTextAtomic(boundary, snapshot.file, snapshot.text, snapshot.label);
18
+ return;
19
+ }
20
+ const stat = await fs.lstat(snapshot.file).catch(() => null);
21
+ if (!stat)
22
+ return;
23
+ const expectedUid = typeof process.getuid === 'function' ? process.getuid() : null;
24
+ if (!stat.isFile() || stat.isSymbolicLink() || (expectedUid !== null && stat.uid !== expectedUid)) {
25
+ throw new Error(`${snapshot.label}_rollback_target_invalid`);
26
+ }
27
+ await fs.unlink(snapshot.file);
28
+ }
@@ -0,0 +1,33 @@
1
+ import { removeStoredCodexLbCredential } from '../codex-lb-env.js';
2
+ import { removeStoredOpenRouterKey } from '../../providers/openrouter/openrouter-secret-store.js';
3
+ import { withProviderCredentialLock } from './locks.js';
4
+ export async function removeProviderCredential(input) {
5
+ return withProviderCredentialLock(input.home, input.provider_id, async () => {
6
+ if (input.provider_id === 'codex-lb') {
7
+ const result = await removeStoredCodexLbCredential({
8
+ confirmed: input.confirmed,
9
+ ...(input.home ? { home: input.home } : {}),
10
+ ...(input.codexLbEnvPath ? { envPath: input.codexLbEnvPath } : {}),
11
+ ...(input.codexLbMetadataPath ? { metadataPath: input.codexLbMetadataPath } : {})
12
+ });
13
+ return {
14
+ schema: 'sks.provider-credential-removal.v1',
15
+ provider_id: input.provider_id,
16
+ removed: result.removed,
17
+ removed_paths: result.removed_paths,
18
+ blockers: result.blockers
19
+ };
20
+ }
21
+ const result = await removeStoredOpenRouterKey({
22
+ confirmed: input.confirmed,
23
+ ...(input.openRouterPaths ? { paths: input.openRouterPaths } : {})
24
+ });
25
+ return {
26
+ schema: 'sks.provider-credential-removal.v1',
27
+ provider_id: input.provider_id,
28
+ removed: result.removed,
29
+ removed_paths: result.removed_paths,
30
+ blockers: result.blockers
31
+ };
32
+ });
33
+ }
@@ -0,0 +1,59 @@
1
+ import { loadCodexLbEnv } from '../codex-lb-env.js';
2
+ import { openRouterSecretPaths, resolveOpenRouterApiKey } from '../../providers/openrouter/openrouter-secret-store.js';
3
+ import { fingerprint, runtimeCredential, unique } from './runtime.js';
4
+ export async function resolveProviderCredential(providerId, options = {}) {
5
+ if (providerId === 'codex-lb') {
6
+ const loaded = await (options.loadCodexLbEnvImpl || loadCodexLbEnv)({
7
+ ...(options.home ? { home: options.home } : {}),
8
+ processEnv: options.processEnv || process.env,
9
+ ...(options.codexLbEnvPath ? { envPath: options.codexLbEnvPath } : {}),
10
+ ...(options.codexLbMetadataPath ? { metadataPath: options.codexLbMetadataPath } : {})
11
+ });
12
+ const secret = loaded.secret_api_key;
13
+ const present = loaded.api_key.present;
14
+ return runtimeCredential({
15
+ provider_id: providerId,
16
+ secret,
17
+ endpoint_url: loaded.base_url,
18
+ source: loaded.api_key.source || null,
19
+ fingerprint: loaded.api_key.fingerprint,
20
+ fallback_state: !present
21
+ ? 'not_configured'
22
+ : loaded.api_key.usable
23
+ ? 'configured_unverified'
24
+ : 'unavailable',
25
+ blockers: unique(loaded.blockers || loaded.credential_binding.blockers),
26
+ warnings: unique(loaded.guidance || []),
27
+ ...(options.validation === undefined ? {} : { validation: options.validation })
28
+ });
29
+ }
30
+ const env = options.processEnv || process.env;
31
+ const resolved = await (options.resolveOpenRouterApiKeyImpl || resolveOpenRouterApiKey)({
32
+ env,
33
+ paths: options.openRouterPaths || openRouterSecretPaths(env)
34
+ });
35
+ const secret = resolved.key;
36
+ return runtimeCredential({
37
+ provider_id: providerId,
38
+ secret,
39
+ endpoint_url: 'https://openrouter.ai/api/v1',
40
+ source: resolved.source,
41
+ fingerprint: secret ? fingerprint(secret) : null,
42
+ fallback_state: secret ? 'configured_unverified' : 'not_configured',
43
+ blockers: secret ? [] : normalizeOpenRouterCredentialBlockers(resolved.blockers),
44
+ warnings: unique(resolved.warnings),
45
+ ...(options.validation === undefined ? {} : { validation: options.validation })
46
+ });
47
+ }
48
+ export async function resolveAllProviderCredentials(options = {}) {
49
+ const [codexLb, openrouter] = await Promise.all([
50
+ resolveProviderCredential('codex-lb', options.codexLb || {}),
51
+ resolveProviderCredential('openrouter', options.openrouter || {})
52
+ ]);
53
+ return { 'codex-lb': codexLb, openrouter };
54
+ }
55
+ function normalizeOpenRouterCredentialBlockers(values) {
56
+ return unique(values.map((value) => value === 'glm_missing_openrouter_key'
57
+ ? 'openrouter_credential_missing'
58
+ : value));
59
+ }
@@ -0,0 +1,55 @@
1
+ import crypto from 'node:crypto';
2
+ export function runtimeCredential(input) {
3
+ const validation = input.validation;
4
+ const state = validation && input.secret
5
+ ? validation.state
6
+ : input.fallback_state;
7
+ const value = {
8
+ schema: 'sks.provider-credential-status.v1',
9
+ provider_id: input.provider_id,
10
+ state,
11
+ source: input.source,
12
+ fingerprint: input.fingerprint,
13
+ checked_at: validation?.checked_at || null,
14
+ blockers: unique([
15
+ ...input.blockers,
16
+ ...(validation?.blockers || [])
17
+ ]),
18
+ warnings: unique([
19
+ ...input.warnings,
20
+ ...(validation?.warnings || [])
21
+ ]),
22
+ endpoint_url: input.endpoint_url
23
+ };
24
+ Object.defineProperty(value, 'secret', {
25
+ value: input.secret,
26
+ enumerable: false,
27
+ configurable: false,
28
+ writable: false
29
+ });
30
+ return value;
31
+ }
32
+ export function providerCredentialStatus(credential) {
33
+ return {
34
+ schema: 'sks.provider-credential-status.v1',
35
+ provider_id: credential.provider_id,
36
+ state: credential.state,
37
+ source: credential.source,
38
+ fingerprint: credential.fingerprint,
39
+ checked_at: credential.checked_at,
40
+ blockers: [...credential.blockers],
41
+ warnings: [...credential.warnings]
42
+ };
43
+ }
44
+ export function fingerprintFull(secret) {
45
+ return crypto.createHash('sha256').update(secret).digest('hex');
46
+ }
47
+ export function fingerprint(secret) {
48
+ return fingerprintFull(secret).slice(0, 16);
49
+ }
50
+ export function providerCode(providerId) {
51
+ return providerId === 'codex-lb' ? 'codex_lb' : 'openrouter';
52
+ }
53
+ export function unique(values) {
54
+ return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))];
55
+ }
@@ -0,0 +1 @@
1
+ export {};