sneakoscope 7.3.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/README.md +129 -10
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/codex-app-command.js +1 -1
  5. package/dist/cli/command-manifest-lite.js +4 -1
  6. package/dist/cli/command-registry.js +2 -0
  7. package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
  8. package/dist/cli/install-helpers-codex-lb-config.js +114 -9
  9. package/dist/cli/install-helpers-install-support.js +6 -16
  10. package/dist/cli/install-helpers.js +788 -600
  11. package/dist/cli/router.js +30 -2
  12. package/dist/commands/codex-app.js +37 -1
  13. package/dist/commands/codex-lb.js +491 -300
  14. package/dist/config/skills-manifest.json +57 -71
  15. package/dist/core/align/align-route.js +127 -0
  16. package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
  17. package/dist/core/codex-app/codex-app-restart.js +83 -0
  18. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
  19. package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
  20. package/dist/core/codex-app/menubar/constants.js +5 -0
  21. package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
  22. package/dist/core/codex-app/menubar/installer.js +18 -0
  23. package/dist/core/codex-app/multi-provider-router.js +22 -10
  24. package/dist/core/codex-app/openrouter-activate.js +26 -1
  25. package/dist/core/codex-app.js +118 -26
  26. package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
  27. package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
  28. package/dist/core/codex-control/codex-task-runner.js +2 -2
  29. package/dist/core/codex-lb/capability-runner.js +204 -0
  30. package/dist/core/codex-lb/capability-types.js +1 -0
  31. package/dist/core/codex-lb/codex-lb-env.js +9 -13
  32. package/dist/core/codex-lb/codex-lb-setup.js +45 -8
  33. package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
  34. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
  35. package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
  36. package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
  37. package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
  38. package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
  39. package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
  40. package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
  41. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
  42. package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
  43. package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
  44. package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
  45. package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
  46. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
  47. package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
  48. package/dist/core/codex-lb/desktop-controller.js +1410 -0
  49. package/dist/core/codex-lb/desktop-mode.js +32 -0
  50. package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
  51. package/dist/core/codex-lb/desktop-service.js +613 -0
  52. package/dist/core/codex-lb/legacy-migration.js +575 -0
  53. package/dist/core/codex-lb/migration-receipt.js +246 -0
  54. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
  55. package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
  56. package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
  57. package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
  58. package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
  59. package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
  60. package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
  61. package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
  62. package/dist/core/codex-native/core-skill-manifest.js +22 -6
  63. package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
  64. package/dist/core/commands/align-command.js +220 -0
  65. package/dist/core/commands/image-ux-review-command.js +280 -68
  66. package/dist/core/commands/naruto-command.js +257 -8
  67. package/dist/core/commands/ppt-command.js +2 -2
  68. package/dist/core/commands/telegram-command.js +402 -34
  69. package/dist/core/commands/triwiki-command.js +11 -1
  70. package/dist/core/commands/triwiki-graph-command.js +160 -0
  71. package/dist/core/commands/wiki-command.js +100 -20
  72. package/dist/core/doctor/skill-legacy-surface.js +1 -3
  73. package/dist/core/errors/message.js +3 -0
  74. package/dist/core/evidence/evidence-freshness.js +14 -0
  75. package/dist/core/feature-fixtures.js +9 -1
  76. package/dist/core/feature-registry.js +5 -3
  77. package/dist/core/fsx.js +7 -3
  78. package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
  79. package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
  80. package/dist/core/hooks-runtime/hook-context.js +15 -2
  81. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
  82. package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
  83. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  84. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  85. package/dist/core/hooks-runtime.js +33 -89
  86. package/dist/core/image-ux-review/callout-extraction.js +6 -0
  87. package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
  88. package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
  89. package/dist/core/image-ux-review.js +230 -12
  90. package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
  91. package/dist/core/imagegen/imagegen-capability.js +10 -4
  92. package/dist/core/imagegen/require-imagegen.js +29 -5
  93. package/dist/core/init/skills.js +25 -13
  94. package/dist/core/init.js +1 -1
  95. package/dist/core/install/installed-package-smoke.js +157 -4
  96. package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
  97. package/dist/core/managed-path-safety.js +8 -10
  98. package/dist/core/mcp-config/backup.js +2 -4
  99. package/dist/core/mcp-config/config-reader.js +2 -4
  100. package/dist/core/mcp-config/scope.js +3 -5
  101. package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
  102. package/dist/core/naruto/context-graph-advisor.js +0 -0
  103. package/dist/core/perf/release-latency-slo.js +12 -6
  104. package/dist/core/pipeline-internals/runtime-core.js +78 -0
  105. package/dist/core/pipeline-internals/runtime-gates.js +5 -2
  106. package/dist/core/policy/latest-version-guidance.js +113 -0
  107. package/dist/core/ppt.js +8 -8
  108. package/dist/core/preflight/parallel-preflight-engine.js +1 -1
  109. package/dist/core/proof/auto-finalize.js +98 -6
  110. package/dist/core/proof/route-proof-gate.js +12 -1
  111. package/dist/core/proof/route-proof-policy.js +8 -8
  112. package/dist/core/release/gate-manifest.js +32 -1
  113. package/dist/core/release/npm-stage-contract.js +21 -0
  114. package/dist/core/release/package-size-budget.js +2 -2
  115. package/dist/core/release/release-gate-contract.js +5 -0
  116. package/dist/core/release/release-proof-io.js +1 -3
  117. package/dist/core/release/release-real-contract.js +5 -4
  118. package/dist/core/release/release-stamp-proof.js +1 -3
  119. package/dist/core/release/stage-publish.js +27 -0
  120. package/dist/core/remote/audit-idempotency.js +50 -18
  121. package/dist/core/remote/codex-public-activity.js +283 -0
  122. package/dist/core/remote/index.js +1 -0
  123. package/dist/core/remote/ssh-worker-client.js +4 -1
  124. package/dist/core/remote/worker.js +206 -27
  125. package/dist/core/research/research-adversarial-review.js +1 -3
  126. package/dist/core/research/research-claim-synthesizer.js +1 -3
  127. package/dist/core/research/research-falsification-runner.js +1 -3
  128. package/dist/core/research/research-source-evidence.js +1 -3
  129. package/dist/core/responses-stream.js +65 -19
  130. package/dist/core/routes/constants.js +1 -1
  131. package/dist/core/routes/design-policy.js +7 -2
  132. package/dist/core/routes/dollar-manifest-lite.js +2 -4
  133. package/dist/core/routes/evidence.js +1 -1
  134. package/dist/core/routes.js +30 -10
  135. package/dist/core/safety/command-contract/contracts.js +2 -0
  136. package/dist/core/safety/command-contract/types.js +1 -1
  137. package/dist/core/search/context-graph-seeds.js +205 -0
  138. package/dist/core/search/context.js +0 -0
  139. package/dist/core/search/provider.js +2 -2
  140. package/dist/core/secret-redaction.js +6 -0
  141. package/dist/core/subagents/naruto-help-contract.js +2 -1
  142. package/dist/core/subagents/naruto-host-credentials.js +161 -0
  143. package/dist/core/subagents/official-subagent-preparation.js +4 -1
  144. package/dist/core/subagents/official-subagent-prompt.js +44 -2
  145. package/dist/core/subagents/official-subagent-runner.js +26 -8
  146. package/dist/core/subagents/triwiki-attention.js +135 -123
  147. package/dist/core/telegram/bot-api.js +85 -10
  148. package/dist/core/telegram/center-snapshot.js +718 -0
  149. package/dist/core/telegram/config.js +158 -31
  150. package/dist/core/telegram/hub.js +288 -33
  151. package/dist/core/telegram/index.js +2 -0
  152. package/dist/core/telegram/ingress-spool.js +419 -0
  153. package/dist/core/telegram/ledgers.js +188 -29
  154. package/dist/core/telegram/messages.js +26 -6
  155. package/dist/core/telegram/owner-lock.js +10 -7
  156. package/dist/core/telegram/runtime-projection.js +38 -3
  157. package/dist/core/telegram/runtime.js +298 -63
  158. package/dist/core/telegram/service.js +137 -8
  159. package/dist/core/telegram/setup.js +291 -73
  160. package/dist/core/text/strings.js +3 -0
  161. package/dist/core/triwiki/code-pack.js +12 -120
  162. package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
  163. package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
  164. package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
  165. package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
  166. package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
  167. package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
  168. package/dist/core/triwiki/context-graph/contracts.js +236 -0
  169. package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
  170. package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
  171. package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
  172. package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
  173. package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
  174. package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
  175. package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
  176. package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
  177. package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
  178. package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
  179. package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
  180. package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
  181. package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
  182. package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
  183. package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
  184. package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
  185. package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
  186. package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
  187. package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
  188. package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
  189. package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
  190. package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
  191. package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
  192. package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
  193. package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
  194. package/dist/core/triwiki/context-graph/graph-index.js +143 -0
  195. package/dist/core/triwiki/context-graph/ids.js +0 -0
  196. package/dist/core/triwiki/context-graph/lint/index.js +0 -0
  197. package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
  198. package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
  199. package/dist/core/triwiki/context-graph/paths.js +116 -0
  200. package/dist/core/triwiki/context-graph/profiles.js +78 -0
  201. package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
  202. package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
  203. package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
  204. package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
  205. package/dist/core/triwiki/context-graph/projections/index.js +7 -0
  206. package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
  207. package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
  208. package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
  209. package/dist/core/triwiki/context-graph/query/explain.js +0 -0
  210. package/dist/core/triwiki/context-graph/query/index.js +178 -0
  211. package/dist/core/triwiki/context-graph/query/load.js +116 -0
  212. package/dist/core/triwiki/context-graph/query/pack.js +117 -0
  213. package/dist/core/triwiki/context-graph/query/rank.js +142 -0
  214. package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
  215. package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
  216. package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
  217. package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
  218. package/dist/core/triwiki/context-graph/query-types.js +29 -0
  219. package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
  220. package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
  221. package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
  222. package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
  223. package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
  224. package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
  225. package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
  226. package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
  227. package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
  228. package/dist/core/verification/context-graph-affected.js +271 -0
  229. package/dist/core/version.js +1 -1
  230. package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
  231. package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
  232. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
  233. package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
  234. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
  235. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
  236. package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
  237. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
  238. package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
  239. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
  240. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
  241. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
  242. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
  243. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
  244. package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
  245. package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
  246. package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
  247. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
  248. package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
  249. package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
  250. package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
  251. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
  252. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
  253. package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
  254. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
  255. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
  256. package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
  257. package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
  258. package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
  259. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
  260. package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
  261. package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
  262. package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
  263. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
  264. package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
  265. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
  266. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
  267. package/dist/scripts/context-graph-check.js +191 -0
  268. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
  269. package/dist/scripts/gate-policy-audit-check.js +1 -1
  270. package/dist/scripts/helper-dedup-check.js +0 -2
  271. package/dist/scripts/installed-package-smoke-check.js +4 -1
  272. package/dist/scripts/latest-version-guidance-check.js +74 -0
  273. package/dist/scripts/release-metadata-check.js +21 -4
  274. package/dist/scripts/release-real-check.js +7 -0
  275. package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
  276. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
  277. package/package.json +8 -3
  278. package/release-gates.v2.json +176 -1
  279. package/runtime-required-scripts.json +30 -0
  280. package/schemas/triwiki/context-graph.schema.json +119 -0
  281. package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
  282. package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
  283. package/dist/core/triwiki/code-index-scanner.js +0 -309
@@ -0,0 +1,613 @@
1
+ import fsp from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { ensureDir, exists, runProcess, which, writeTextAtomic } from '../fsx.js';
5
+ import { loadCodexLbEnv } from './codex-lb-env.js';
6
+ import { DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES, DESKTOP_BRIDGE_LAUNCHD_LABEL, desktopBridgeConfigGeneration, desktopBridgeLaunchdPlistPath, desktopBridgeProcessExists, desktopBridgeStatePath, getDesktopBridgeStatus, preflightDesktopBridge, readDesktopBridgeState, renderDesktopBridgeLaunchdPlist, safeBridgeErrorCode, selectAvailableDesktopBridgePort, startPreparedDesktopBridge } from './desktop-bridge/index.js';
7
+ import { DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT, parseCodexLbGatewayAuthTransport } from './desktop-mode.js';
8
+ export const DEFAULT_CODEX_LB_DESKTOP_BRIDGE_HOST = '127.0.0.1';
9
+ export const DEFAULT_CODEX_LB_DESKTOP_BRIDGE_PORT = 49_152;
10
+ export const CODEX_LB_DESKTOP_BRIDGE_SETTINGS_SCHEMA = 'sks.codex-lb-desktop-bridge-settings.v1';
11
+ export const CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA = 'sks.codex-lb-desktop-bridge-service.v1';
12
+ const DEFAULT_ALLOWED_ORIGINS = ['app://codex'];
13
+ const DEFAULT_CONNECT_TIMEOUT_MS = 10_000;
14
+ const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60_000;
15
+ export function desktopBridgeServicePaths(home = process.env.HOME || os.homedir()) {
16
+ const resolvedHome = path.resolve(home);
17
+ const runtimeDir = path.join(resolvedHome, '.codex', 'sks');
18
+ const logDir = path.join(runtimeDir, 'logs');
19
+ return {
20
+ settings_path: path.join(runtimeDir, 'codex-lb-desktop-bridge-settings.json'),
21
+ state_path: desktopBridgeStatePath(resolvedHome),
22
+ launch_agent_path: desktopBridgeLaunchdPlistPath(resolvedHome),
23
+ stdout_log_path: path.join(logDir, 'codex-lb-desktop-bridge.out.log'),
24
+ stderr_log_path: path.join(logDir, 'codex-lb-desktop-bridge.err.log')
25
+ };
26
+ }
27
+ export function defaultDesktopBridgeServiceSettings(input = {}) {
28
+ return validateDesktopBridgeServiceSettings({
29
+ schema: CODEX_LB_DESKTOP_BRIDGE_SETTINGS_SCHEMA,
30
+ listen_host: input.listen_host || DEFAULT_CODEX_LB_DESKTOP_BRIDGE_HOST,
31
+ listen_port: input.listen_port ?? DEFAULT_CODEX_LB_DESKTOP_BRIDGE_PORT,
32
+ gateway_auth_transport: parseCodexLbGatewayAuthTransport(input.gateway_auth_transport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT),
33
+ allowed_origins: [...(input.allowed_origins || DEFAULT_ALLOWED_ORIGINS)],
34
+ connect_timeout_ms: input.connect_timeout_ms ?? DEFAULT_CONNECT_TIMEOUT_MS,
35
+ idle_timeout_ms: input.idle_timeout_ms ?? DEFAULT_IDLE_TIMEOUT_MS
36
+ });
37
+ }
38
+ export async function resolveDesktopBridgeActivationSettings(options = {}) {
39
+ const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
40
+ const paths = overridePaths(desktopBridgeServicePaths(home), options);
41
+ const persisted = await readDesktopBridgeServiceSettings(paths.settings_path);
42
+ const listenHost = options.settings?.listen_host
43
+ || persisted?.listen_host
44
+ || DEFAULT_CODEX_LB_DESKTOP_BRIDGE_HOST;
45
+ const listenPort = options.settings?.listen_port
46
+ ?? persisted?.listen_port
47
+ ?? await (options.selectAvailablePort || selectAvailableDesktopBridgePort)(listenHost);
48
+ return defaultDesktopBridgeServiceSettings({
49
+ ...(persisted || {}),
50
+ ...(options.settings || {}),
51
+ listen_host: listenHost,
52
+ listen_port: listenPort
53
+ });
54
+ }
55
+ export async function readDesktopBridgeServiceSettings(file) {
56
+ let stat;
57
+ try {
58
+ stat = await fsp.lstat(file);
59
+ }
60
+ catch (error) {
61
+ if (errorCode(error) === 'ENOENT')
62
+ return null;
63
+ throw error;
64
+ }
65
+ if (!stat.isFile() || stat.isSymbolicLink()) {
66
+ throw new Error('desktop_bridge_settings_not_regular_file');
67
+ }
68
+ if ((stat.mode & 0o077) !== 0)
69
+ throw new Error('desktop_bridge_settings_permissions_unsafe');
70
+ const raw = await fsp.readFile(file, 'utf8');
71
+ if (Buffer.byteLength(raw) > 64 * 1024)
72
+ throw new Error('desktop_bridge_settings_too_large');
73
+ let parsed;
74
+ try {
75
+ parsed = JSON.parse(raw);
76
+ }
77
+ catch {
78
+ throw new Error('desktop_bridge_settings_invalid_json');
79
+ }
80
+ return validateDesktopBridgeServiceSettings(parsed);
81
+ }
82
+ export async function writeDesktopBridgeServiceSettings(file, settings) {
83
+ const validated = validateDesktopBridgeServiceSettings(settings);
84
+ await writeTextAtomic(file, `${JSON.stringify(validated, null, 2)}\n`, { mode: 0o600 });
85
+ await fsp.chmod(file, 0o600);
86
+ }
87
+ export async function resolveDesktopBridgeRuntimeConfig(options = {}) {
88
+ const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
89
+ const defaults = desktopBridgeServicePaths(home);
90
+ const paths = overridePaths(defaults, options);
91
+ const persisted = await readDesktopBridgeServiceSettings(paths.settings_path);
92
+ const settings = defaultDesktopBridgeServiceSettings({
93
+ ...(persisted || {}),
94
+ ...(options.settings || {})
95
+ });
96
+ const loadedEnv = await loadCodexLbEnv({
97
+ home,
98
+ ...(options.envPath ? { envPath: options.envPath } : {}),
99
+ ...(options.metadataPath ? { metadataPath: options.metadataPath } : {}),
100
+ ...(options.env ? { processEnv: options.env } : {})
101
+ });
102
+ if (!loadedEnv.base_url || !loadedEnv.secret_api_key || !loadedEnv.configured) {
103
+ throw new Error(loadedEnv.credential_binding.blockers[0]
104
+ || loadedEnv.missing[0]
105
+ || 'codex_lb_credentials_unavailable');
106
+ }
107
+ return {
108
+ config: {
109
+ listenHost: settings.listen_host,
110
+ listenPort: settings.listen_port,
111
+ remoteBaseUrl: loadedEnv.base_url,
112
+ gatewayKey: loadedEnv.secret_api_key,
113
+ gatewayAuthTransport: settings.gateway_auth_transport,
114
+ allowedPathPrefixes: DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES,
115
+ allowedOrigins: settings.allowed_origins,
116
+ connectTimeoutMs: settings.connect_timeout_ms,
117
+ idleTimeoutMs: settings.idle_timeout_ms
118
+ },
119
+ settings,
120
+ loaded_env: loadedEnv,
121
+ paths
122
+ };
123
+ }
124
+ export async function desktopBridgeServiceStatus(options = {}) {
125
+ const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
126
+ const paths = overridePaths(desktopBridgeServicePaths(home), options);
127
+ const service = desktopBridgeLaunchService(options.uid);
128
+ if ((options.platform || process.platform) !== 'darwin') {
129
+ return {
130
+ schema: CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA,
131
+ ok: false,
132
+ supported: false,
133
+ installed: await exists(paths.launch_agent_path),
134
+ loaded: false,
135
+ running: false,
136
+ status: 'unsupported',
137
+ service,
138
+ paths,
139
+ state: await readDesktopBridgeState(paths.state_path).catch(() => null),
140
+ settings: await readDesktopBridgeServiceSettings(paths.settings_path).catch(() => null),
141
+ expected_config_generation: null,
142
+ credential_source: null,
143
+ blockers: ['desktop_bridge_service_requires_macos']
144
+ };
145
+ }
146
+ const installed = await exists(paths.launch_agent_path);
147
+ const settings = await readDesktopBridgeServiceSettings(paths.settings_path).catch(() => null);
148
+ let expectedConfigGeneration = null;
149
+ let credentialSource = null;
150
+ const credentialBlockers = [];
151
+ if (settings) {
152
+ try {
153
+ const runtime = await resolveDesktopBridgeRuntimeConfig({ ...options, home, settingsPath: paths.settings_path });
154
+ expectedConfigGeneration = desktopBridgeConfigGeneration(runtime.config);
155
+ credentialSource = runtime.loaded_env.source;
156
+ }
157
+ catch (error) {
158
+ credentialBlockers.push(safeServiceError(error));
159
+ }
160
+ }
161
+ const bridgeStatus = await getDesktopBridgeStatus({
162
+ statePath: paths.state_path,
163
+ ...(expectedConfigGeneration ? { expectedConfigGeneration } : {}),
164
+ ...(options.processExists ? { processExists: options.processExists } : {})
165
+ });
166
+ const launchd = await inspectLaunchdService(options, service);
167
+ const blockers = [
168
+ ...(settings ? [] : ['desktop_bridge_settings_missing']),
169
+ ...credentialBlockers,
170
+ ...(bridgeStatus.status === 'running' ? [] : [bridgeStatusBlocker(bridgeStatus)]),
171
+ ...(launchd.loaded && !launchd.running ? ['desktop_bridge_launchd_not_running'] : [])
172
+ ].filter(Boolean);
173
+ const running = bridgeStatus.status === 'running';
174
+ return {
175
+ schema: CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA,
176
+ ok: running && blockers.length === 0,
177
+ supported: true,
178
+ installed,
179
+ loaded: launchd.loaded,
180
+ running,
181
+ status: !settings
182
+ ? 'settings_missing'
183
+ : credentialBlockers.length
184
+ ? 'credentials_unavailable'
185
+ : bridgeStatus.status,
186
+ service,
187
+ paths,
188
+ state: bridgeStatus.state,
189
+ settings,
190
+ expected_config_generation: expectedConfigGeneration,
191
+ credential_source: credentialSource,
192
+ blockers
193
+ };
194
+ }
195
+ export async function installAndStartDesktopBridgeService(options = {}) {
196
+ const platform = options.platform || process.platform;
197
+ const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
198
+ const paths = overridePaths(desktopBridgeServicePaths(home), options);
199
+ if (platform !== 'darwin') {
200
+ return desktopBridgeServiceStatus({ ...options, home, platform });
201
+ }
202
+ const settings = await resolveDesktopBridgeActivationSettings({
203
+ ...options,
204
+ home,
205
+ settingsPath: paths.settings_path
206
+ });
207
+ const loadedEnv = await loadCodexLbEnv({
208
+ home,
209
+ ...(options.envPath ? { envPath: options.envPath } : {}),
210
+ ...(options.metadataPath ? { metadataPath: options.metadataPath } : {}),
211
+ ...(options.env ? { processEnv: options.env } : {})
212
+ });
213
+ if (!loadedEnv.base_url || !loadedEnv.secret_api_key || !loadedEnv.configured) {
214
+ return {
215
+ schema: CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA,
216
+ ok: false,
217
+ supported: true,
218
+ installed: await exists(paths.launch_agent_path),
219
+ loaded: false,
220
+ running: false,
221
+ status: 'credentials_unavailable',
222
+ service: desktopBridgeLaunchService(options.uid),
223
+ paths,
224
+ state: await readDesktopBridgeState(paths.state_path).catch(() => null),
225
+ settings,
226
+ expected_config_generation: null,
227
+ credential_source: loadedEnv.source,
228
+ blockers: [
229
+ loadedEnv.credential_binding.blockers[0]
230
+ || loadedEnv.missing[0]
231
+ || 'codex_lb_credentials_unavailable'
232
+ ]
233
+ };
234
+ }
235
+ const config = {
236
+ listenHost: settings.listen_host,
237
+ listenPort: settings.listen_port,
238
+ remoteBaseUrl: loadedEnv.base_url,
239
+ gatewayKey: loadedEnv.secret_api_key,
240
+ gatewayAuthTransport: settings.gateway_auth_transport,
241
+ allowedPathPrefixes: DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES,
242
+ allowedOrigins: settings.allowed_origins,
243
+ connectTimeoutMs: settings.connect_timeout_ms,
244
+ idleTimeoutMs: settings.idle_timeout_ms
245
+ };
246
+ try {
247
+ await preflightDesktopBridge(config);
248
+ }
249
+ catch (error) {
250
+ return failedServiceStatus({
251
+ paths,
252
+ settings,
253
+ service: desktopBridgeLaunchService(options.uid),
254
+ status: 'credentials_unavailable',
255
+ credentialSource: loadedEnv.source,
256
+ expectedConfigGeneration: desktopBridgeConfigGeneration(config),
257
+ blocker: safeBridgeErrorCode(error)
258
+ });
259
+ }
260
+ const launchCommand = await resolveDesktopBridgeLaunchCommand(options);
261
+ if (!launchCommand) {
262
+ return failedServiceStatus({
263
+ paths,
264
+ settings,
265
+ service: desktopBridgeLaunchService(options.uid),
266
+ status: 'settings_missing',
267
+ credentialSource: loadedEnv.source,
268
+ expectedConfigGeneration: desktopBridgeConfigGeneration(config),
269
+ blocker: 'desktop_bridge_sks_executable_missing'
270
+ });
271
+ }
272
+ await Promise.all([
273
+ ensureDir(path.dirname(paths.settings_path)),
274
+ ensureDir(path.dirname(paths.launch_agent_path)),
275
+ ensureDir(path.dirname(paths.stdout_log_path))
276
+ ]);
277
+ await writeDesktopBridgeServiceSettings(paths.settings_path, settings);
278
+ const launchArguments = [
279
+ ...launchCommand.arguments,
280
+ 'codex-lb',
281
+ 'bridge',
282
+ 'serve',
283
+ '--settings',
284
+ paths.settings_path,
285
+ '--json'
286
+ ];
287
+ const plist = renderDesktopBridgeLaunchdPlist({
288
+ executablePath: launchCommand.executable,
289
+ arguments: launchArguments,
290
+ stdoutPath: paths.stdout_log_path,
291
+ stderrPath: paths.stderr_log_path
292
+ });
293
+ await writeTextAtomic(paths.launch_agent_path, plist, { mode: 0o600 });
294
+ await fsp.chmod(paths.launch_agent_path, 0o600);
295
+ const run = options.run || runProcess;
296
+ const launchctl = options.launchctl || '/bin/launchctl';
297
+ const domain = desktopBridgeLaunchDomain(options.uid);
298
+ const service = desktopBridgeLaunchService(options.uid);
299
+ await run(launchctl, ['bootout', service], {
300
+ timeoutMs: 5_000,
301
+ maxOutputBytes: 16 * 1024
302
+ }).catch(() => undefined);
303
+ await run(launchctl, ['bootout', domain, paths.launch_agent_path], {
304
+ timeoutMs: 5_000,
305
+ maxOutputBytes: 16 * 1024
306
+ }).catch(() => undefined);
307
+ await removeStaleBridgeState(paths.state_path, options.processExists);
308
+ const bootstrap = await run(launchctl, ['bootstrap', domain, paths.launch_agent_path], {
309
+ timeoutMs: 10_000,
310
+ maxOutputBytes: 32 * 1024
311
+ }).catch((error) => failedProcess(error));
312
+ if (bootstrap.code !== 0 && !bootstrap.timedOut) {
313
+ return failedServiceStatus({
314
+ paths,
315
+ settings,
316
+ service,
317
+ status: 'missing',
318
+ credentialSource: loadedEnv.source,
319
+ expectedConfigGeneration: desktopBridgeConfigGeneration(config),
320
+ blocker: 'desktop_bridge_launchd_bootstrap_failed'
321
+ });
322
+ }
323
+ await run(launchctl, ['kickstart', '-k', service], {
324
+ timeoutMs: 10_000,
325
+ maxOutputBytes: 32 * 1024
326
+ }).catch(() => undefined);
327
+ const status = await waitForDesktopBridge({
328
+ ...options,
329
+ home,
330
+ platform,
331
+ settingsPath: paths.settings_path,
332
+ statePath: paths.state_path,
333
+ launchAgentPath: paths.launch_agent_path,
334
+ stdoutLogPath: paths.stdout_log_path,
335
+ stderrLogPath: paths.stderr_log_path
336
+ });
337
+ if (!status.ok) {
338
+ await run(launchctl, ['bootout', service], {
339
+ timeoutMs: 5_000,
340
+ maxOutputBytes: 16 * 1024
341
+ }).catch(() => undefined);
342
+ }
343
+ return status;
344
+ }
345
+ export async function bootstrapExistingDesktopBridgeService(options = {}) {
346
+ const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
347
+ const paths = overridePaths(desktopBridgeServicePaths(home), options);
348
+ if ((options.platform || process.platform) !== 'darwin') {
349
+ return desktopBridgeServiceStatus({ ...options, home });
350
+ }
351
+ if (!(await exists(paths.launch_agent_path)) || !(await exists(paths.settings_path))) {
352
+ return desktopBridgeServiceStatus({ ...options, home });
353
+ }
354
+ const run = options.run || runProcess;
355
+ const launchctl = options.launchctl || '/bin/launchctl';
356
+ const domain = desktopBridgeLaunchDomain(options.uid);
357
+ const service = desktopBridgeLaunchService(options.uid);
358
+ await run(launchctl, ['bootout', service], {
359
+ timeoutMs: 5_000,
360
+ maxOutputBytes: 16 * 1024
361
+ }).catch(() => undefined);
362
+ const bootstrap = await run(launchctl, ['bootstrap', domain, paths.launch_agent_path], {
363
+ timeoutMs: 10_000,
364
+ maxOutputBytes: 32 * 1024
365
+ }).catch((error) => failedProcess(error));
366
+ if (bootstrap.code === 0 || bootstrap.timedOut) {
367
+ await run(launchctl, ['kickstart', '-k', service], {
368
+ timeoutMs: 10_000,
369
+ maxOutputBytes: 32 * 1024
370
+ }).catch(() => undefined);
371
+ }
372
+ return waitForDesktopBridge({ ...options, home });
373
+ }
374
+ export async function stopDesktopBridgeService(options = {}) {
375
+ const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
376
+ const paths = overridePaths(desktopBridgeServicePaths(home), options);
377
+ if ((options.platform || process.platform) !== 'darwin') {
378
+ return desktopBridgeServiceStatus({ ...options, home });
379
+ }
380
+ const run = options.run || runProcess;
381
+ const launchctl = options.launchctl || '/bin/launchctl';
382
+ const service = desktopBridgeLaunchService(options.uid);
383
+ await run(launchctl, ['bootout', service], {
384
+ timeoutMs: 8_000,
385
+ maxOutputBytes: 32 * 1024
386
+ }).catch(() => undefined);
387
+ for (let attempt = 0; attempt < 20; attempt += 1) {
388
+ const state = await readDesktopBridgeState(paths.state_path).catch(() => null);
389
+ if (!state || !(options.processExists || desktopBridgeProcessExists)(state.pid))
390
+ break;
391
+ await delay(100);
392
+ }
393
+ await removeStaleBridgeState(paths.state_path, options.processExists);
394
+ if (options.removePlist === true)
395
+ await fsp.unlink(paths.launch_agent_path).catch(() => undefined);
396
+ if (options.removeSettings === true)
397
+ await fsp.unlink(paths.settings_path).catch(() => undefined);
398
+ const status = await desktopBridgeServiceStatus({ ...options, home });
399
+ return {
400
+ ...status,
401
+ ok: !status.running,
402
+ blockers: status.running ? ['desktop_bridge_process_still_running'] : []
403
+ };
404
+ }
405
+ export async function serveDesktopBridge(options = {}) {
406
+ let handle = null;
407
+ try {
408
+ const runtime = await resolveDesktopBridgeRuntimeConfig(options);
409
+ const prepared = await preflightDesktopBridge(runtime.config);
410
+ handle = await startPreparedDesktopBridge(prepared, { statePath: runtime.paths.state_path });
411
+ process.stdout.write(`${JSON.stringify({
412
+ schema: 'sks.codex-lb-desktop-bridge-log.v1',
413
+ event: 'sks.codex_lb.bridge.started',
414
+ pid: handle.state.pid,
415
+ listen_origin: handle.state.listen_origin,
416
+ remote_origin_sha256: handle.state.remote_origin_sha256,
417
+ gateway_auth_transport: handle.state.gateway_auth_transport,
418
+ secret_fields_redacted: true
419
+ })}\n`);
420
+ await waitForShutdownSignal(handle);
421
+ return {
422
+ schema: 'sks.codex-lb-desktop-bridge-serve.v1',
423
+ ok: true,
424
+ status: 'stopped',
425
+ state: handle.state
426
+ };
427
+ }
428
+ catch (error) {
429
+ return {
430
+ schema: 'sks.codex-lb-desktop-bridge-serve.v1',
431
+ ok: false,
432
+ status: 'failed',
433
+ state: handle?.state || null,
434
+ blocker: safeServiceError(error)
435
+ };
436
+ }
437
+ finally {
438
+ if (handle)
439
+ await handle.stop().catch(() => undefined);
440
+ }
441
+ }
442
+ function validateDesktopBridgeServiceSettings(value) {
443
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
444
+ throw new Error('desktop_bridge_settings_invalid');
445
+ }
446
+ const row = value;
447
+ if (row.schema !== CODEX_LB_DESKTOP_BRIDGE_SETTINGS_SCHEMA) {
448
+ throw new Error('desktop_bridge_settings_schema_invalid');
449
+ }
450
+ const listenHost = row.listen_host;
451
+ const listenPort = Number(row.listen_port);
452
+ const gatewayAuthTransport = parseCodexLbGatewayAuthTransport(row.gateway_auth_transport);
453
+ const allowedOrigins = Array.isArray(row.allowed_origins)
454
+ ? row.allowed_origins.map((entry) => String(entry || '').trim()).filter(Boolean)
455
+ : [];
456
+ const connectTimeoutMs = Number(row.connect_timeout_ms);
457
+ const idleTimeoutMs = Number(row.idle_timeout_ms);
458
+ if (listenHost !== '127.0.0.1' && listenHost !== '::1') {
459
+ throw new Error('desktop_bridge_settings_listen_host_invalid');
460
+ }
461
+ if (!Number.isInteger(listenPort) || listenPort < 49_152 || listenPort > 65_535) {
462
+ throw new Error('desktop_bridge_settings_listen_port_invalid');
463
+ }
464
+ if (!allowedOrigins.length)
465
+ throw new Error('desktop_bridge_settings_allowed_origins_empty');
466
+ if (!Number.isFinite(connectTimeoutMs) || connectTimeoutMs < 100 || connectTimeoutMs > 120_000) {
467
+ throw new Error('desktop_bridge_settings_connect_timeout_invalid');
468
+ }
469
+ if (!Number.isFinite(idleTimeoutMs) || idleTimeoutMs < 1_000 || idleTimeoutMs > 86_400_000) {
470
+ throw new Error('desktop_bridge_settings_idle_timeout_invalid');
471
+ }
472
+ return {
473
+ schema: CODEX_LB_DESKTOP_BRIDGE_SETTINGS_SCHEMA,
474
+ listen_host: listenHost,
475
+ listen_port: listenPort,
476
+ gateway_auth_transport: gatewayAuthTransport,
477
+ allowed_origins: [...new Set(allowedOrigins)],
478
+ connect_timeout_ms: connectTimeoutMs,
479
+ idle_timeout_ms: idleTimeoutMs
480
+ };
481
+ }
482
+ function overridePaths(defaults, options) {
483
+ return {
484
+ settings_path: options.settingsPath || defaults.settings_path,
485
+ state_path: options.statePath || defaults.state_path,
486
+ launch_agent_path: options.launchAgentPath || defaults.launch_agent_path,
487
+ stdout_log_path: options.stdoutLogPath || defaults.stdout_log_path,
488
+ stderr_log_path: options.stderrLogPath || defaults.stderr_log_path
489
+ };
490
+ }
491
+ function desktopBridgeLaunchDomain(uid = typeof process.getuid === 'function' ? process.getuid() : 0) {
492
+ return `gui/${uid}`;
493
+ }
494
+ function desktopBridgeLaunchService(uid) {
495
+ return `${desktopBridgeLaunchDomain(uid)}/${DESKTOP_BRIDGE_LAUNCHD_LABEL}`;
496
+ }
497
+ async function resolveDesktopBridgeLaunchCommand(options) {
498
+ if (options.executablePath) {
499
+ const executable = path.resolve(options.executablePath);
500
+ return await exists(executable)
501
+ ? { executable, arguments: [...(options.executableArguments || [])] }
502
+ : null;
503
+ }
504
+ const entry = String(process.argv[1] || '').trim();
505
+ const entryName = path.basename(entry).replace(/\.js$/i, '');
506
+ if (entry
507
+ && (entryName === 'sks' || entryName === 'sneakoscope')
508
+ && await exists(entry)) {
509
+ return {
510
+ executable: path.resolve(process.execPath),
511
+ arguments: [path.resolve(entry)]
512
+ };
513
+ }
514
+ const sks = await which('sks').catch(() => null);
515
+ if (sks)
516
+ return { executable: path.resolve(sks), arguments: [] };
517
+ return null;
518
+ }
519
+ async function inspectLaunchdService(options, service) {
520
+ if ((options.platform || process.platform) !== 'darwin')
521
+ return { loaded: false, running: false };
522
+ const run = options.run || runProcess;
523
+ const launchctl = options.launchctl || '/bin/launchctl';
524
+ const result = await run(launchctl, ['print', service], {
525
+ timeoutMs: 3_000,
526
+ maxOutputBytes: 32 * 1024
527
+ }).catch(() => null);
528
+ if (!result || result.code !== 0)
529
+ return { loaded: false, running: false };
530
+ const text = `${result.stdout || ''}\n${result.stderr || ''}`;
531
+ const state = text.match(/^[ \t]*state = ([^\n]+)/m)?.[1]?.trim() || null;
532
+ const pid = Number(text.match(/^[ \t]*pid = (\d+)/m)?.[1] || 0);
533
+ return { loaded: true, running: state === 'running' && pid > 0 };
534
+ }
535
+ async function waitForDesktopBridge(options) {
536
+ let status = await desktopBridgeServiceStatus(options);
537
+ for (let attempt = 0; attempt < 40 && !status.ok; attempt += 1) {
538
+ await delay(100);
539
+ status = await desktopBridgeServiceStatus(options);
540
+ }
541
+ return status;
542
+ }
543
+ async function removeStaleBridgeState(statePath, processExists) {
544
+ const state = await readDesktopBridgeState(statePath).catch(() => null);
545
+ if (!state || !(processExists || desktopBridgeProcessExists)(state.pid)) {
546
+ await fsp.unlink(statePath).catch(() => undefined);
547
+ }
548
+ }
549
+ async function waitForShutdownSignal(handle) {
550
+ await new Promise((resolve) => {
551
+ let settled = false;
552
+ const finish = () => {
553
+ if (settled)
554
+ return;
555
+ settled = true;
556
+ process.off('SIGINT', finish);
557
+ process.off('SIGTERM', finish);
558
+ resolve();
559
+ };
560
+ process.once('SIGINT', finish);
561
+ process.once('SIGTERM', finish);
562
+ handle.server.once('close', finish);
563
+ });
564
+ }
565
+ function failedServiceStatus(input) {
566
+ return {
567
+ schema: CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA,
568
+ ok: false,
569
+ supported: true,
570
+ installed: false,
571
+ loaded: false,
572
+ running: false,
573
+ status: input.status,
574
+ service: input.service,
575
+ paths: input.paths,
576
+ state: null,
577
+ settings: input.settings,
578
+ expected_config_generation: input.expectedConfigGeneration,
579
+ credential_source: input.credentialSource,
580
+ blockers: [input.blocker]
581
+ };
582
+ }
583
+ function bridgeStatusBlocker(status) {
584
+ if (status.status === 'missing')
585
+ return 'desktop_bridge_state_missing';
586
+ if (status.status === 'running')
587
+ return '';
588
+ return status.blocker;
589
+ }
590
+ function safeServiceError(error) {
591
+ const bridge = safeBridgeErrorCode(error);
592
+ if (bridge !== 'bridge_upstream_unavailable')
593
+ return bridge;
594
+ const message = error instanceof Error ? error.message : String(error);
595
+ return /^[a-z0-9_:-]+$/i.test(message) ? message : 'desktop_bridge_service_failed';
596
+ }
597
+ function failedProcess(error) {
598
+ return {
599
+ code: 1,
600
+ stdout: '',
601
+ stderr: safeServiceError(error),
602
+ stdoutBytes: 0,
603
+ stderrBytes: 0,
604
+ truncated: false,
605
+ timedOut: false
606
+ };
607
+ }
608
+ function errorCode(error) {
609
+ return error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
610
+ }
611
+ function delay(ms) {
612
+ return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
613
+ }