@remodex/rmx 1.0.2

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 (588) hide show
  1. package/AGENTS_INSTALL.md +91 -0
  2. package/LICENSE +21 -0
  3. package/README.md +242 -0
  4. package/assets/architecture.png +0 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/claude-code-models.gif +0 -0
  7. package/assets/codex-app-picker.png +0 -0
  8. package/bin/ocx.mjs +584 -0
  9. package/bin/package-main.mjs +9 -0
  10. package/gui/dist/assets/index-CZqebSPQ.css +1 -0
  11. package/gui/dist/assets/index-CkETtt7P.js +71 -0
  12. package/gui/dist/favicon.png +0 -0
  13. package/gui/dist/fonts/google-sans-cyrillic.woff2 +0 -0
  14. package/gui/dist/fonts/google-sans-latin.woff2 +0 -0
  15. package/gui/dist/icons.svg +24 -0
  16. package/gui/dist/index.html +25 -0
  17. package/gui/dist/logo.png +0 -0
  18. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  19. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  20. package/gui/dist/provider-icons/claude-color.svg +1 -0
  21. package/gui/dist/provider-icons/cline-color.svg +16 -0
  22. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  23. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  24. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  25. package/gui/dist/provider-icons/cursor-color.svg +2 -0
  26. package/gui/dist/provider-icons/deepseek-color.svg +1 -0
  27. package/gui/dist/provider-icons/discord.svg +1 -0
  28. package/gui/dist/provider-icons/firepass-color.svg +1 -0
  29. package/gui/dist/provider-icons/fireworks-color.svg +1 -0
  30. package/gui/dist/provider-icons/gemini-color.svg +1 -0
  31. package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
  32. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  33. package/gui/dist/provider-icons/grok.svg +1 -0
  34. package/gui/dist/provider-icons/groq-color.svg +1 -0
  35. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  36. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  37. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  38. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  39. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  40. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  41. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  42. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  43. package/gui/dist/provider-icons/openai.svg +1 -0
  44. package/gui/dist/provider-icons/opencode.svg +2 -0
  45. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  46. package/gui/dist/provider-icons/pi.svg +21 -0
  47. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  48. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  49. package/gui/dist/provider-icons/telegram.svg +1 -0
  50. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  51. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  52. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  53. package/package.json +118 -0
  54. package/src/AGENTS.md +28 -0
  55. package/src/adapters/anthropic-image-guard.ts +251 -0
  56. package/src/adapters/anthropic-image-normalize.ts +518 -0
  57. package/src/adapters/anthropic.ts +1205 -0
  58. package/src/adapters/azure.ts +36 -0
  59. package/src/adapters/base.ts +83 -0
  60. package/src/adapters/client-fingerprint.ts +59 -0
  61. package/src/adapters/command-code.ts +453 -0
  62. package/src/adapters/cursor/arg-codec.ts +38 -0
  63. package/src/adapters/cursor/arg-normalize.ts +104 -0
  64. package/src/adapters/cursor/cursor-errors.ts +165 -0
  65. package/src/adapters/cursor/discovery.ts +276 -0
  66. package/src/adapters/cursor/effort-map.ts +139 -0
  67. package/src/adapters/cursor/exec-policy.ts +88 -0
  68. package/src/adapters/cursor/framing.ts +250 -0
  69. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  70. package/src/adapters/cursor/kv-store.ts +52 -0
  71. package/src/adapters/cursor/live-models.ts +153 -0
  72. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  73. package/src/adapters/cursor/live-transport.ts +1235 -0
  74. package/src/adapters/cursor/mcp-config.ts +42 -0
  75. package/src/adapters/cursor/mcp-manager.ts +333 -0
  76. package/src/adapters/cursor/message-mapper.ts +49 -0
  77. package/src/adapters/cursor/native-exec-common.ts +59 -0
  78. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  79. package/src/adapters/cursor/native-exec-fs.ts +332 -0
  80. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  81. package/src/adapters/cursor/native-exec-network.ts +43 -0
  82. package/src/adapters/cursor/native-exec-shell.ts +548 -0
  83. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  84. package/src/adapters/cursor/native-exec.ts +604 -0
  85. package/src/adapters/cursor/protobuf-events.ts +735 -0
  86. package/src/adapters/cursor/protobuf-request.ts +719 -0
  87. package/src/adapters/cursor/request-builder.ts +280 -0
  88. package/src/adapters/cursor/thread-continuity.ts +67 -0
  89. package/src/adapters/cursor/tool-definitions.ts +621 -0
  90. package/src/adapters/cursor/transport-retry.ts +132 -0
  91. package/src/adapters/cursor/transport.ts +57 -0
  92. package/src/adapters/cursor/types.ts +59 -0
  93. package/src/adapters/cursor.ts +196 -0
  94. package/src/adapters/google-antigravity-replay.ts +520 -0
  95. package/src/adapters/google-antigravity-wire.ts +140 -0
  96. package/src/adapters/google-errors.ts +85 -0
  97. package/src/adapters/google-http.ts +100 -0
  98. package/src/adapters/google-tool-schema.ts +173 -0
  99. package/src/adapters/google-truncation.ts +24 -0
  100. package/src/adapters/google-wire-compiler.ts +232 -0
  101. package/src/adapters/google.ts +859 -0
  102. package/src/adapters/identity.ts +77 -0
  103. package/src/adapters/image.ts +23 -0
  104. package/src/adapters/kiro-constants.ts +16 -0
  105. package/src/adapters/kiro-errors.ts +208 -0
  106. package/src/adapters/kiro-events.ts +197 -0
  107. package/src/adapters/kiro-images.ts +129 -0
  108. package/src/adapters/kiro-retry.ts +312 -0
  109. package/src/adapters/kiro-thinking.ts +104 -0
  110. package/src/adapters/kiro-tool-fallback.ts +36 -0
  111. package/src/adapters/kiro-tools.ts +224 -0
  112. package/src/adapters/kiro-truncation.ts +33 -0
  113. package/src/adapters/kiro-wire.ts +129 -0
  114. package/src/adapters/kiro.ts +1924 -0
  115. package/src/adapters/mimo-free.ts +263 -0
  116. package/src/adapters/openai-chat.ts +1265 -0
  117. package/src/adapters/openai-responses.ts +1309 -0
  118. package/src/adapters/run-turn-queue.ts +114 -0
  119. package/src/adapters/tool-catalog-nudge.ts +71 -0
  120. package/src/adapters/upstream-http-error.ts +48 -0
  121. package/src/android-remote/assets.ts +218 -0
  122. package/src/android-remote/attachments.ts +168 -0
  123. package/src/android-remote/auth.ts +237 -0
  124. package/src/android-remote/cloudflare-provisioning.ts +409 -0
  125. package/src/android-remote/cloudflare-secret.ts +106 -0
  126. package/src/android-remote/cloudflare-tunnel.ts +488 -0
  127. package/src/android-remote/cloudflared.ts +286 -0
  128. package/src/android-remote/codex-app-server.ts +565 -0
  129. package/src/android-remote/desktop-history-page.ts +936 -0
  130. package/src/android-remote/desktop-ipc.ts +3643 -0
  131. package/src/android-remote/desktop-ownership-store.ts +98 -0
  132. package/src/android-remote/desktop-project-registration.ts +129 -0
  133. package/src/android-remote/desktop-session-stream.ts +1110 -0
  134. package/src/android-remote/desktop-workspace-state.ts +443 -0
  135. package/src/android-remote/file-change-parser.ts +112 -0
  136. package/src/android-remote/gateway.ts +9108 -0
  137. package/src/android-remote/mutation-store.ts +299 -0
  138. package/src/android-remote/projection.ts +1780 -0
  139. package/src/android-remote/queued-turn-store.ts +248 -0
  140. package/src/android-remote/session-command-recovery.ts +1384 -0
  141. package/src/android-remote/store.ts +466 -0
  142. package/src/android-remote/thread-reconciliation.ts +133 -0
  143. package/src/android-remote/thread-source-paths.ts +307 -0
  144. package/src/android-remote/thread-stream.ts +546 -0
  145. package/src/android-remote/turn-activity.ts +235 -0
  146. package/src/android-remote/user-message-identity.ts +94 -0
  147. package/src/bridge.ts +1793 -0
  148. package/src/chat/inbound.ts +295 -0
  149. package/src/chat/outbound.ts +821 -0
  150. package/src/claude/agents-inject.ts +267 -0
  151. package/src/claude/alias.ts +149 -0
  152. package/src/claude/auth-detect.ts +229 -0
  153. package/src/claude/auth-mode-migration.ts +32 -0
  154. package/src/claude/auth-mode.ts +62 -0
  155. package/src/claude/context-windows.ts +189 -0
  156. package/src/claude/desktop-3p-guard.ts +35 -0
  157. package/src/claude/desktop-3p-paths.ts +84 -0
  158. package/src/claude/desktop-3p.ts +601 -0
  159. package/src/claude/desktop-health.ts +26 -0
  160. package/src/claude/desktop-profile.ts +263 -0
  161. package/src/claude/gateway-cache.ts +70 -0
  162. package/src/claude/inbound-debug.ts +163 -0
  163. package/src/claude/inbound.ts +519 -0
  164. package/src/claude/model-info.ts +154 -0
  165. package/src/claude/outbound.ts +898 -0
  166. package/src/cli/access.ts +108 -0
  167. package/src/cli/account-api.ts +296 -0
  168. package/src/cli/account-auth.ts +250 -0
  169. package/src/cli/account-catalog-refresh.ts +14 -0
  170. package/src/cli/account-extended.ts +476 -0
  171. package/src/cli/account-main.ts +317 -0
  172. package/src/cli/account.ts +297 -0
  173. package/src/cli/agent-driven.ts +70 -0
  174. package/src/cli/agent.ts +184 -0
  175. package/src/cli/catalog-prewarm.ts +27 -0
  176. package/src/cli/claude-desktop.ts +211 -0
  177. package/src/cli/claude.ts +302 -0
  178. package/src/cli/codex-shim-autorestore.ts +45 -0
  179. package/src/cli/codex-shim-readiness.ts +69 -0
  180. package/src/cli/combo.ts +124 -0
  181. package/src/cli/config-command.ts +183 -0
  182. package/src/cli/debug.ts +228 -0
  183. package/src/cli/desktop-first-run.ts +25 -0
  184. package/src/cli/doctor.ts +1022 -0
  185. package/src/cli/export-command.ts +201 -0
  186. package/src/cli/help.ts +370 -0
  187. package/src/cli/index.ts +1565 -0
  188. package/src/cli/init.ts +224 -0
  189. package/src/cli/integrations.ts +225 -0
  190. package/src/cli/interactive-confirm.ts +133 -0
  191. package/src/cli/internal-dispatch.ts +35 -0
  192. package/src/cli/launcher-context.ts +77 -0
  193. package/src/cli/models-runtime.ts +224 -0
  194. package/src/cli/models.ts +340 -0
  195. package/src/cli/observe.ts +170 -0
  196. package/src/cli/opencode.ts +587 -0
  197. package/src/cli/provider-runtime.ts +179 -0
  198. package/src/cli/provider.ts +476 -0
  199. package/src/cli/ready.ts +301 -0
  200. package/src/cli/route-policy.ts +92 -0
  201. package/src/cli/runtime-api.ts +328 -0
  202. package/src/cli/star-prompt.ts +211 -0
  203. package/src/cli/status-oauth.ts +78 -0
  204. package/src/cli/status.ts +321 -0
  205. package/src/cli/system-command.ts +196 -0
  206. package/src/cli/system-restart-client.ts +146 -0
  207. package/src/cli/tray-proxy.ts +205 -0
  208. package/src/cli/v2.ts +200 -0
  209. package/src/cli.ts +10 -0
  210. package/src/clients/config-export.ts +1109 -0
  211. package/src/codex/account-id.ts +34 -0
  212. package/src/codex/account-label.ts +34 -0
  213. package/src/codex/account-lifecycle.ts +172 -0
  214. package/src/codex/account-namespace-match.ts +63 -0
  215. package/src/codex/account-namespaces.ts +195 -0
  216. package/src/codex/account-pause.ts +20 -0
  217. package/src/codex/account-priority.ts +83 -0
  218. package/src/codex/account-runtime-state.ts +31 -0
  219. package/src/codex/account-store.ts +517 -0
  220. package/src/codex/account-usability.ts +40 -0
  221. package/src/codex/admission.ts +263 -0
  222. package/src/codex/app-server-processes.ts +799 -0
  223. package/src/codex/auth-api.ts +2098 -0
  224. package/src/codex/auth-collision.ts +107 -0
  225. package/src/codex/auth-context.ts +480 -0
  226. package/src/codex/autostart-health.ts +156 -0
  227. package/src/codex/catalog/account-models.ts +67 -0
  228. package/src/codex/catalog/aggregation.ts +471 -0
  229. package/src/codex/catalog/bundled.ts +533 -0
  230. package/src/codex/catalog/effort.ts +432 -0
  231. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  232. package/src/codex/catalog/kinds.ts +2 -0
  233. package/src/codex/catalog/metadata.ts +287 -0
  234. package/src/codex/catalog/native-models.ts +7 -0
  235. package/src/codex/catalog/parsing.ts +503 -0
  236. package/src/codex/catalog/provider-fetch.ts +2267 -0
  237. package/src/codex/catalog/sync.ts +1606 -0
  238. package/src/codex/catalog-admission.ts +197 -0
  239. package/src/codex/catalog-refresh-status.ts +87 -0
  240. package/src/codex/catalog-write-serialization.ts +242 -0
  241. package/src/codex/catalog.ts +15 -0
  242. package/src/codex/codex-write-lock.ts +384 -0
  243. package/src/codex/convergence-types.ts +593 -0
  244. package/src/codex/convergence.ts +580 -0
  245. package/src/codex/custom-model-catalog-migration.ts +176 -0
  246. package/src/codex/data/upstream-models.json +830 -0
  247. package/src/codex/desired-state.ts +230 -0
  248. package/src/codex/desktop-client-processes.ts +521 -0
  249. package/src/codex/exec-invocation.ts +22 -0
  250. package/src/codex/features.ts +1091 -0
  251. package/src/codex/generation.ts +202 -0
  252. package/src/codex/history-job.ts +347 -0
  253. package/src/codex/history-lock.ts +242 -0
  254. package/src/codex/history-migration-guardian.ts +115 -0
  255. package/src/codex/history-provider.ts +1075 -0
  256. package/src/codex/history-transition.ts +105 -0
  257. package/src/codex/history-worker.ts +204 -0
  258. package/src/codex/home.ts +206 -0
  259. package/src/codex/inject-coordination.ts +257 -0
  260. package/src/codex/inject.ts +1857 -0
  261. package/src/codex/injected-marker.ts +79 -0
  262. package/src/codex/integration-record.ts +266 -0
  263. package/src/codex/internal/catalog-writer.ts +203 -0
  264. package/src/codex/internal/history-writer.ts +105 -0
  265. package/src/codex/journal.ts +172 -0
  266. package/src/codex/main-account-cache.ts +56 -0
  267. package/src/codex/main-account.ts +40 -0
  268. package/src/codex/management-convergence.ts +114 -0
  269. package/src/codex/model-cache.ts +267 -0
  270. package/src/codex/native-main-admission.ts +47 -0
  271. package/src/codex/native-main-auth-temp.ts +187 -0
  272. package/src/codex/native-main-claim.ts +167 -0
  273. package/src/codex/native-main-lock-file.ts +162 -0
  274. package/src/codex/native-main-owner.ts +329 -0
  275. package/src/codex/native-profile-api.ts +247 -0
  276. package/src/codex/native-profile-manager.ts +1531 -0
  277. package/src/codex/native-profile-processes.ts +121 -0
  278. package/src/codex/native-profile-recovery.ts +99 -0
  279. package/src/codex/native-profile-stage-store.ts +387 -0
  280. package/src/codex/native-profile-startup.ts +348 -0
  281. package/src/codex/native-profile-store.ts +855 -0
  282. package/src/codex/native-profile-types.ts +120 -0
  283. package/src/codex/native-residue.ts +691 -0
  284. package/src/codex/paths.ts +78 -0
  285. package/src/codex/plugins-doctor.ts +242 -0
  286. package/src/codex/pool-rotation.ts +295 -0
  287. package/src/codex/project-config-warnings.ts +426 -0
  288. package/src/codex/prompt-journal.ts +311 -0
  289. package/src/codex/prompt-layers.ts +967 -0
  290. package/src/codex/prompt-lock.ts +143 -0
  291. package/src/codex/provider-adoption.ts +242 -0
  292. package/src/codex/quota-rejection.ts +224 -0
  293. package/src/codex/quota.ts +494 -0
  294. package/src/codex/refresh.ts +60 -0
  295. package/src/codex/routing.ts +1855 -0
  296. package/src/codex/runtime.ts +659 -0
  297. package/src/codex/shim.ts +1215 -0
  298. package/src/codex/subagent-defaults.ts +557 -0
  299. package/src/codex/subagent-model-fallback.ts +560 -0
  300. package/src/codex/sync.ts +238 -0
  301. package/src/codex/transition-state.ts +612 -0
  302. package/src/codex/upstream-host-health.ts +368 -0
  303. package/src/codex/user-identity.ts +374 -0
  304. package/src/codex/warmup.ts +192 -0
  305. package/src/codex/websocket-registry.ts +100 -0
  306. package/src/codex/write-coordination.ts +114 -0
  307. package/src/combos/failover.ts +140 -0
  308. package/src/combos/index.ts +44 -0
  309. package/src/combos/request.ts +64 -0
  310. package/src/combos/resolve.ts +232 -0
  311. package/src/combos/types.ts +392 -0
  312. package/src/config.ts +3270 -0
  313. package/src/generated/model-metadata.ts +144 -0
  314. package/src/github/star-state.ts +203 -0
  315. package/src/grok/inject.ts +540 -0
  316. package/src/grok/inspect.ts +45 -0
  317. package/src/grok/status.ts +127 -0
  318. package/src/grok/sync.ts +66 -0
  319. package/src/images/artifacts.ts +516 -0
  320. package/src/images/fulfill-video.ts +163 -0
  321. package/src/images/fulfill.ts +149 -0
  322. package/src/images/index.ts +4 -0
  323. package/src/images/loop.ts +922 -0
  324. package/src/images/plan.ts +133 -0
  325. package/src/images/synthetic-tool.ts +133 -0
  326. package/src/images/types.ts +41 -0
  327. package/src/images/xai-client.ts +141 -0
  328. package/src/images/xai-video-client.ts +163 -0
  329. package/src/index.ts +22 -0
  330. package/src/integrations/config-io.ts +151 -0
  331. package/src/integrations/journal.ts +315 -0
  332. package/src/integrations/merge.ts +135 -0
  333. package/src/integrations/native/ownership-preflight.ts +202 -0
  334. package/src/integrations/ownership.ts +111 -0
  335. package/src/integrations/registry.ts +108 -0
  336. package/src/integrations/serialize.ts +235 -0
  337. package/src/integrations/state.ts +290 -0
  338. package/src/integrations/store.ts +103 -0
  339. package/src/integrations/writer.ts +492 -0
  340. package/src/lib/abort.ts +146 -0
  341. package/src/lib/admin-secrets.ts +25 -0
  342. package/src/lib/admission.ts +83 -0
  343. package/src/lib/app-owned-memory-stores.ts +173 -0
  344. package/src/lib/app-owned-memory.ts +265 -0
  345. package/src/lib/bounded-body.ts +242 -0
  346. package/src/lib/bun-binary-validator.d.mts +3 -0
  347. package/src/lib/bun-binary-validator.mjs +18 -0
  348. package/src/lib/bun-runtime.ts +184 -0
  349. package/src/lib/bun-stream-caps.ts +127 -0
  350. package/src/lib/config-ownership.ts +438 -0
  351. package/src/lib/crash-guard.ts +344 -0
  352. package/src/lib/debug-log-buffer.ts +83 -0
  353. package/src/lib/debug-settings.ts +108 -0
  354. package/src/lib/debug.ts +31 -0
  355. package/src/lib/destination-policy.ts +316 -0
  356. package/src/lib/errors.ts +364 -0
  357. package/src/lib/eventstream-decoder.ts +253 -0
  358. package/src/lib/gcp-adc.ts +341 -0
  359. package/src/lib/injection-debug-log.ts +58 -0
  360. package/src/lib/local-management-attestation.ts +51 -0
  361. package/src/lib/open-url.ts +25 -0
  362. package/src/lib/pinned-http.ts +182 -0
  363. package/src/lib/privacy.ts +20 -0
  364. package/src/lib/process-control.ts +168 -0
  365. package/src/lib/provider-environment.ts +470 -0
  366. package/src/lib/provider-outbound.ts +203 -0
  367. package/src/lib/provider-url.ts +14 -0
  368. package/src/lib/proxy-env.ts +18 -0
  369. package/src/lib/redact.ts +510 -0
  370. package/src/lib/remodex-home.ts +616 -0
  371. package/src/lib/retry-after.ts +55 -0
  372. package/src/lib/service-secrets.ts +178 -0
  373. package/src/lib/shadow-call.ts +54 -0
  374. package/src/lib/sidecar-tracker.ts +52 -0
  375. package/src/lib/sse-decoder.ts +364 -0
  376. package/src/lib/state-store-registrations.ts +109 -0
  377. package/src/lib/state-store-sweeper.ts +184 -0
  378. package/src/lib/system-restart-contract.ts +73 -0
  379. package/src/lib/test-home-guard.ts +98 -0
  380. package/src/lib/token-estimate.ts +69 -0
  381. package/src/lib/translator-budget.ts +366 -0
  382. package/src/lib/upstream-reachability.ts +91 -0
  383. package/src/lib/upstream-retry.ts +508 -0
  384. package/src/lib/win-exec.ts +115 -0
  385. package/src/lib/win-paths.ts +68 -0
  386. package/src/lib/windows-elevation.ts +705 -0
  387. package/src/lib/windows-secret-acl.ts +817 -0
  388. package/src/lib/windows-user-principal.ts +283 -0
  389. package/src/lib/winsw.ts +402 -0
  390. package/src/model-sources.ts +73 -0
  391. package/src/oauth/anthropic-routing.ts +594 -0
  392. package/src/oauth/anthropic.ts +177 -0
  393. package/src/oauth/callback-server.ts +294 -0
  394. package/src/oauth/chatgpt.ts +150 -0
  395. package/src/oauth/command-code.ts +239 -0
  396. package/src/oauth/cursor.ts +231 -0
  397. package/src/oauth/github-copilot.ts +428 -0
  398. package/src/oauth/google-antigravity.ts +230 -0
  399. package/src/oauth/health.ts +443 -0
  400. package/src/oauth/index.ts +1280 -0
  401. package/src/oauth/key-providers.ts +128 -0
  402. package/src/oauth/kimi.ts +213 -0
  403. package/src/oauth/kiro-credentials.ts +726 -0
  404. package/src/oauth/kiro.ts +621 -0
  405. package/src/oauth/local-token-detect.ts +121 -0
  406. package/src/oauth/log.ts +48 -0
  407. package/src/oauth/login-cli.ts +163 -0
  408. package/src/oauth/pkce.ts +15 -0
  409. package/src/oauth/store.ts +655 -0
  410. package/src/oauth/token-guardian.ts +309 -0
  411. package/src/oauth/types.ts +62 -0
  412. package/src/oauth/xai.ts +241 -0
  413. package/src/providers/alibaba-region-backup.ts +75 -0
  414. package/src/providers/alibaba-region-migration.ts +156 -0
  415. package/src/providers/alibaba-region-startup.ts +36 -0
  416. package/src/providers/antigravity-models.ts +317 -0
  417. package/src/providers/api-keys.ts +140 -0
  418. package/src/providers/base-url-choices.ts +64 -0
  419. package/src/providers/codex-capacity.ts +288 -0
  420. package/src/providers/command-code-efforts.ts +85 -0
  421. package/src/providers/context-cap.ts +73 -0
  422. package/src/providers/derive.ts +451 -0
  423. package/src/providers/free-directory.ts +187 -0
  424. package/src/providers/github-copilot-transport.ts +56 -0
  425. package/src/providers/google-vertex-location.ts +14 -0
  426. package/src/providers/key-failover.ts +271 -0
  427. package/src/providers/kiro-models.ts +67 -0
  428. package/src/providers/label.ts +19 -0
  429. package/src/providers/model-discovery-limits.ts +16 -0
  430. package/src/providers/model-discovery.ts +361 -0
  431. package/src/providers/openai-sidecar.ts +235 -0
  432. package/src/providers/openai-tier-startup.ts +27 -0
  433. package/src/providers/openai-tiers.ts +301 -0
  434. package/src/providers/openai-virtual-models.ts +83 -0
  435. package/src/providers/openrouter-routing.ts +102 -0
  436. package/src/providers/provider-id-rewrite.ts +179 -0
  437. package/src/providers/quota.ts +1942 -0
  438. package/src/providers/reasoning-capabilities.ts +336 -0
  439. package/src/providers/registry.ts +2375 -0
  440. package/src/providers/slug-codec.ts +74 -0
  441. package/src/providers/xai-transport.ts +149 -0
  442. package/src/reasoning-effort.ts +243 -0
  443. package/src/responses/compaction.ts +124 -0
  444. package/src/responses/hosted-tool-policy.ts +9 -0
  445. package/src/responses/parser.ts +714 -0
  446. package/src/responses/reasoning-envelope.ts +60 -0
  447. package/src/responses/reasoning-replay-cache.ts +106 -0
  448. package/src/responses/schema.ts +159 -0
  449. package/src/responses/spill-store.ts +431 -0
  450. package/src/responses/state.ts +1039 -0
  451. package/src/responses/tool-groups.ts +19 -0
  452. package/src/router.ts +802 -0
  453. package/src/routing/analytics.ts +377 -0
  454. package/src/routing/capability.ts +205 -0
  455. package/src/routing/cost.ts +77 -0
  456. package/src/routing/evaluator.ts +444 -0
  457. package/src/routing/health.ts +401 -0
  458. package/src/routing/history/cursor.ts +43 -0
  459. package/src/routing/history/indexer.ts +605 -0
  460. package/src/routing/history/schema.ts +72 -0
  461. package/src/routing/profile-namespace.ts +15 -0
  462. package/src/routing/profile.ts +424 -0
  463. package/src/routing/quota.ts +145 -0
  464. package/src/routing/request-evidence.ts +45 -0
  465. package/src/routing/trace.ts +686 -0
  466. package/src/server/adapter-resolve.ts +83 -0
  467. package/src/server/auth-cors.ts +606 -0
  468. package/src/server/chat-completions.ts +379 -0
  469. package/src/server/claude-messages.ts +980 -0
  470. package/src/server/effort-policy.ts +251 -0
  471. package/src/server/github-copilot-responses-repair.ts +338 -0
  472. package/src/server/gui-static.ts +152 -0
  473. package/src/server/image-retry.ts +42 -0
  474. package/src/server/images.ts +485 -0
  475. package/src/server/index.ts +1633 -0
  476. package/src/server/lifecycle.ts +482 -0
  477. package/src/server/live.ts +609 -0
  478. package/src/server/management/agent-settings-routes.ts +1180 -0
  479. package/src/server/management/android-remote-routes.ts +390 -0
  480. package/src/server/management/api-access.ts +141 -0
  481. package/src/server/management/api-key-usage.ts +167 -0
  482. package/src/server/management/body.ts +35 -0
  483. package/src/server/management/combo-routes.ts +244 -0
  484. package/src/server/management/config-routes.ts +602 -0
  485. package/src/server/management/context.ts +88 -0
  486. package/src/server/management/integration-routes.ts +538 -0
  487. package/src/server/management/logs-usage-routes.ts +516 -0
  488. package/src/server/management/model-routes.ts +519 -0
  489. package/src/server/management/model-rows.ts +143 -0
  490. package/src/server/management/native-integration-routes.ts +781 -0
  491. package/src/server/management/oauth-account-routes.ts +573 -0
  492. package/src/server/management/provider-routes.ts +781 -0
  493. package/src/server/management/request-history-routes.ts +191 -0
  494. package/src/server/management/routing-analytics-routes.ts +74 -0
  495. package/src/server/management/routing-profile-routes.ts +384 -0
  496. package/src/server/management/shared.ts +277 -0
  497. package/src/server/management/sidebar-routes.ts +106 -0
  498. package/src/server/management/sync-response.ts +69 -0
  499. package/src/server/management/system-restart.ts +433 -0
  500. package/src/server/management/system-routes.ts +141 -0
  501. package/src/server/management/usage-summary-cache.ts +86 -0
  502. package/src/server/management-api.ts +269 -0
  503. package/src/server/management-auth.ts +353 -0
  504. package/src/server/memory-watchdog.ts +156 -0
  505. package/src/server/port-reclaim.ts +307 -0
  506. package/src/server/ports.ts +156 -0
  507. package/src/server/proxy-liveness.ts +326 -0
  508. package/src/server/proxy-stop.ts +92 -0
  509. package/src/server/readiness.ts +99 -0
  510. package/src/server/relay-eager.ts +353 -0
  511. package/src/server/relay.ts +1179 -0
  512. package/src/server/request-decompress.ts +132 -0
  513. package/src/server/request-log-conversation.ts +168 -0
  514. package/src/server/request-log.ts +1072 -0
  515. package/src/server/responses/collaboration.ts +409 -0
  516. package/src/server/responses/compact.ts +710 -0
  517. package/src/server/responses/core.ts +3561 -0
  518. package/src/server/responses/encrypted-payload.ts +308 -0
  519. package/src/server/responses/fetch-helpers.ts +171 -0
  520. package/src/server/responses/passthrough-error.ts +78 -0
  521. package/src/server/responses/policy-fallback.ts +152 -0
  522. package/src/server/responses/terminal-guard.ts +230 -0
  523. package/src/server/responses/upstream-error.ts +48 -0
  524. package/src/server/responses-image-gen-repair.ts +132 -0
  525. package/src/server/responses-item-id-repair.ts +272 -0
  526. package/src/server/responses-json-events.ts +52 -0
  527. package/src/server/responses-model-rewrite.ts +29 -0
  528. package/src/server/responses-snapshot-repair.ts +621 -0
  529. package/src/server/responses.ts +10 -0
  530. package/src/server/search.ts +181 -0
  531. package/src/server/sse-frame-buffer.ts +292 -0
  532. package/src/server/sse-payload-rewrite.ts +263 -0
  533. package/src/server/startup-action-control.ts +308 -0
  534. package/src/server/startup-health-cache.ts +119 -0
  535. package/src/server/system-env.ts +418 -0
  536. package/src/server/windows-tcp-drop.ts +184 -0
  537. package/src/server/windows-tray-control.ts +41 -0
  538. package/src/server/ws-bridge.ts +470 -0
  539. package/src/service-manager-probe.ts +824 -0
  540. package/src/service.ts +3011 -0
  541. package/src/stall-timeout.ts +20 -0
  542. package/src/storage/cleanup-job.ts +57 -0
  543. package/src/storage/cleanup.ts +3085 -0
  544. package/src/storage/policy-job.ts +457 -0
  545. package/src/storage/policy-scheduler.ts +40 -0
  546. package/src/storage/policy-worker.ts +59 -0
  547. package/src/storage/policy.ts +527 -0
  548. package/src/storage/restore-job.ts +299 -0
  549. package/src/storage/restore-worker.ts +58 -0
  550. package/src/storage/scanner.ts +238 -0
  551. package/src/storage/storage-mutation-coordinator.ts +139 -0
  552. package/src/storage/worker-lifecycle.ts +215 -0
  553. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  554. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  555. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  556. package/src/tray/assets/opencodex-tray.png +0 -0
  557. package/src/tray/windows-tray.ps1 +364 -0
  558. package/src/tray/windows.ts +738 -0
  559. package/src/types.ts +1531 -0
  560. package/src/update/badge.ts +72 -0
  561. package/src/update/desktop-release.ts +1620 -0
  562. package/src/update/index.ts +402 -0
  563. package/src/update/job.ts +1906 -0
  564. package/src/update/notify.ts +261 -0
  565. package/src/update/npm-cache-preflight.d.mts +47 -0
  566. package/src/update/npm-cache-preflight.mjs +201 -0
  567. package/src/update/npm-invocation.d.mts +23 -0
  568. package/src/update/npm-invocation.mjs +94 -0
  569. package/src/update/tray-update-plan.d.mts +18 -0
  570. package/src/update/tray-update-plan.mjs +38 -0
  571. package/src/usage/cost.ts +0 -0
  572. package/src/usage/debug.ts +97 -0
  573. package/src/usage/expected-prices.ts +283 -0
  574. package/src/usage/log.ts +695 -0
  575. package/src/usage/summary.ts +585 -0
  576. package/src/usage/totals.ts +14 -0
  577. package/src/vision/anthropic-describe.ts +185 -0
  578. package/src/vision/describe.ts +127 -0
  579. package/src/vision/index.ts +558 -0
  580. package/src/vision/reasoning.ts +55 -0
  581. package/src/web-search/anthropic-executor.ts +189 -0
  582. package/src/web-search/executor.ts +105 -0
  583. package/src/web-search/format-result.ts +89 -0
  584. package/src/web-search/index.ts +196 -0
  585. package/src/web-search/loop.ts +791 -0
  586. package/src/web-search/parse.ts +235 -0
  587. package/src/web-search/progress-stream.ts +342 -0
  588. package/src/web-search/synthetic-tool.ts +47 -0
@@ -0,0 +1,824 @@
1
+ /**
2
+ * What the service manager says about this machine, read-only and fail-closed.
3
+ *
4
+ * The question this answers is NOT "is a job loaded". Installation writes the
5
+ * definition BEFORE the state file (`service.ts` install paths) and embeds
6
+ * `CODEX_HOME`/`OPENCODEX_HOME` inside it, so an interrupted reinstall leaves a
7
+ * valid state file for one home beside an installed definition for another. A
8
+ * probe that only asked about registration would call that owned. On macOS it is
9
+ * worse: a logged-out user has the plist on disk with no GUI domain at all, so
10
+ * registration reports nothing while a foreign definition sits right there.
11
+ *
12
+ * So the probe reads the DEFINITION, parses the homes out of it, and reports
13
+ * what it saw. Comparing those homes is the caller's job — a probe that returned
14
+ * a verdict would be deciding ownership from half the evidence.
15
+ *
16
+ * Every command here is read-only and bounded. The user's proxy runs under a
17
+ * service manager while this executes; a probe that could start, stop or reload
18
+ * anything is not a probe. An unbounded one is not much better, since a wedged
19
+ * manager would hold the event loop.
20
+ */
21
+ import { spawnSync } from "node:child_process";
22
+ import { existsSync, lstatSync, readFileSync } from "node:fs";
23
+ import { homedir } from "node:os";
24
+ import { join, win32 as win32Path } from "node:path";
25
+ import {
26
+ resolveTrustedWindowsSchtasksExe,
27
+ resolveTrustedWindowsSystemDirectory,
28
+ } from "./lib/windows-elevation";
29
+ import { WINSW_SERVICE_ID } from "./lib/winsw";
30
+ import { REMODEX_HOME_DIRNAME } from "./lib/remodex-home";
31
+
32
+ /** Short: this runs inside admission, and a slow answer is the same as none. */
33
+ export const SERVICE_PROBE_TIMEOUT_MS = 2_000;
34
+
35
+ export type ServiceManagerBackend = "launchd" | "systemd" | "scheduler" | "winsw";
36
+
37
+ export interface ServiceManagerClaim {
38
+ readonly backend: ServiceManagerBackend;
39
+ readonly definitionPath: string;
40
+ /**
41
+ * The homes the definition names. `null` means the definition deliberately
42
+ * omits that key, which is different from naming a different one: an install
43
+ * that ran without `CODEX_HOME` set writes no such key at all.
44
+ */
45
+ readonly homes: {
46
+ readonly codexHome: string | null;
47
+ readonly opencodexHome: string | null;
48
+ };
49
+ readonly registration: "present" | "absent";
50
+ }
51
+
52
+ export type ServiceManagerInstallation =
53
+ | { readonly kind: "absent" }
54
+ | { readonly kind: "present"; readonly claims: readonly ServiceManagerClaim[] }
55
+ | { readonly kind: "conflict"; readonly claims: readonly ServiceManagerClaim[] }
56
+ | { readonly kind: "unknown"; readonly reason: string };
57
+
58
+ /** Injected so a test can observe the EXACT argv production emits. */
59
+ export interface ProbeRunner {
60
+ (file: string, args: readonly string[]): {
61
+ status: number | null;
62
+ stdout: string;
63
+ stderr: string;
64
+ timedOut: boolean;
65
+ spawnFailed: boolean;
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Windows probe runner: preserves schtasks stdout/stderr as raw bytes so the
71
+ * UTF-16LE task XML is not corrupted by a UTF-8 decode.
72
+ */
73
+ export type RawProbeRunner = (file: string, args: readonly string[]) => {
74
+ status: number | null;
75
+ stdout: Buffer;
76
+ stderr: Buffer;
77
+ timedOut: boolean;
78
+ spawnFailed: boolean;
79
+ };
80
+
81
+ export const defaultProbeRunner: ProbeRunner = (file, args) => {
82
+ const result = spawnSync(file, [...args], {
83
+ encoding: "utf8",
84
+ windowsHide: true,
85
+ timeout: SERVICE_PROBE_TIMEOUT_MS,
86
+ });
87
+ return {
88
+ status: result.status,
89
+ stdout: String(result.stdout ?? ""),
90
+ stderr: String(result.stderr ?? ""),
91
+ // `signal` is SIGTERM when the timeout fired; a spawn failure sets `error`.
92
+ timedOut: result.signal !== null && result.error === undefined,
93
+ spawnFailed: result.error !== undefined,
94
+ };
95
+ };
96
+
97
+ export const defaultRawProbeRunner: RawProbeRunner = (file, args) => {
98
+ const result = spawnSync(file, [...args], {
99
+ encoding: "buffer",
100
+ windowsHide: true,
101
+ timeout: SERVICE_PROBE_TIMEOUT_MS,
102
+ });
103
+ return {
104
+ status: result.status,
105
+ stdout: Buffer.isBuffer(result.stdout) ? result.stdout : Buffer.alloc(0),
106
+ stderr: Buffer.isBuffer(result.stderr) ? result.stderr : Buffer.alloc(0),
107
+ timedOut: result.signal !== null && result.error === undefined,
108
+ spawnFailed: result.error !== undefined,
109
+ };
110
+ };
111
+
112
+ export interface ProbeDeps {
113
+ readonly run?: ProbeRunner;
114
+ /** Raw-buffer runner for bounded Windows service-manager queries. */
115
+ readonly runRaw?: RawProbeRunner;
116
+ readonly platform?: NodeJS.Platform;
117
+ readonly uid?: number;
118
+ readonly home?: string;
119
+ /** Effective Remodex config dir (OPENCODEX_HOME). Overrides `<home>/.remodex`. */
120
+ readonly configDir?: string;
121
+ /** Test seam for WinSW SCM status. Production uses bounded trusted `sc.exe query`. */
122
+ readonly winswStatus?: () => "started" | "stopped" | "nonexistent" | "unknown";
123
+ }
124
+
125
+ const LABEL = "com.opencodex.proxy";
126
+ const TASK = "opencodex-proxy";
127
+
128
+ /**
129
+ * `launchctl print` exits 113 for a service that is not there and 112 when the
130
+ * domain itself cannot be reached — measured against nonexistent targets rather
131
+ * than assumed. Only 113 is an answer; everything else is a failure to ask.
132
+ */
133
+ const LAUNCHCTL_NO_SUCH_SERVICE = 113;
134
+ /**
135
+ * 112 is an answer about the DOMAIN, not the label.
136
+ *
137
+ * Measured on macOS 27.0: querying a domain with no service name at all still
138
+ * returns it, and a domain that does not exist cannot be running one of our
139
+ * jobs. Treating it as "could not ask" would refuse every write on a fresh
140
+ * headless Mac — no GUI domain, and no installation either.
141
+ */
142
+ const LAUNCHCTL_NO_SUCH_DOMAIN = 112;
143
+
144
+ /**
145
+ * Residue on disk, distinguished from a path that could not be read.
146
+ *
147
+ * `existsSync` answers "no" for a dangling symlink and for a path whose parent
148
+ * denies traversal. Both are residue, not absence — only ENOENT is absence.
149
+ */
150
+ function artifactPresence(path: string): "present" | "absent" | "unreadable" {
151
+ try {
152
+ lstatSync(path);
153
+ return "present";
154
+ } catch (error) {
155
+ return (error as NodeJS.ErrnoException)?.code === "ENOENT" ? "absent" : "unreadable";
156
+ }
157
+ }
158
+
159
+ function unknown(reason: string): ServiceManagerInstallation {
160
+ return { kind: "unknown", reason };
161
+ }
162
+
163
+ /** Pull `<key>NAME</key><string>VALUE</string>` out of a plist body. */
164
+ function plistEnvValue(body: string, key: string): string | null {
165
+ const match = body.match(
166
+ new RegExp(`<key>\\s*${key}\\s*</key>\\s*<string>([^<]*)</string>`),
167
+ );
168
+ return match ? match[1] : null;
169
+ }
170
+
171
+ /** Pull `Environment="NAME=VALUE"` (quoted or bare) out of a systemd unit. */
172
+ function unitEnvValue(body: string, key: string): string | null {
173
+ for (const line of body.split("\n")) {
174
+ const match = line.match(new RegExp(`^\\s*Environment=\\s*"?${key}=([^"\\n]*)"?\\s*$`));
175
+ if (match) return match[1];
176
+ }
177
+ return null;
178
+ }
179
+
180
+ function inspectLaunchd(deps: Required<Pick<ProbeDeps, "run" | "uid" | "home">>): ServiceManagerInstallation {
181
+ const definitionPath = join(deps.home, "Library", "LaunchAgents", `${LABEL}.plist`);
182
+
183
+ /*
184
+ * BOTH domains, because they are independent and hold separate service sets.
185
+ * Measured on macOS 27.0: the shipped agent answers 0 under `gui/<uid>` and
186
+ * 113 under `user/<uid>`. Asking only one leaves the other free to hold a job
187
+ * this probe would then call absent.
188
+ */
189
+ let registration: "present" | "absent" = "absent";
190
+ let unreachableDomains = 0;
191
+ for (const domain of [`gui/${deps.uid}`, `user/${deps.uid}`]) {
192
+ const printed = deps.run("/bin/launchctl", ["print", `${domain}/${LABEL}`]);
193
+ if (printed.spawnFailed || printed.timedOut) {
194
+ return unknown(`launchctl could not be asked: ${printed.timedOut ? "timed out" : printed.stderr.trim()}`);
195
+ }
196
+ if (printed.status === 0) { registration = "present"; break; }
197
+ if (printed.status === LAUNCHCTL_NO_SUCH_SERVICE) continue;
198
+ if (printed.status === LAUNCHCTL_NO_SUCH_DOMAIN) { unreachableDomains += 1; continue; }
199
+ return unknown(`launchctl print exited ${String(printed.status)}: ${printed.stderr.trim()}`);
200
+ }
201
+
202
+ const definition = artifactPresence(definitionPath);
203
+ if (definition === "absent") {
204
+ // No file. A registration without one means launchd holds a definition whose
205
+ // file is gone — real, and not something to resolve unattended.
206
+ if (registration === "present") {
207
+ return unknown("launchd has a job loaded but its plist is missing");
208
+ }
209
+ /*
210
+ * Nothing staged, and every domain either answered "no such service" or does
211
+ * not exist. 112 is an answer ABOUT THE DOMAIN and is label-independent —
212
+ * querying a domain with no service name at all returns it — so an
213
+ * unreachable domain cannot be hiding a job of ours. Calling this `unknown`
214
+ * instead would refuse every write on a fresh headless Mac, which has no
215
+ * GUI domain and no installation either.
216
+ */
217
+ void unreachableDomains;
218
+ return { kind: "absent" };
219
+ }
220
+
221
+ let body: string;
222
+ try {
223
+ body = readFileSync(definitionPath, "utf-8");
224
+ } catch (error) {
225
+ // Present-but-unreadable cannot supply homes, and `present` without homes
226
+ // would compare equal to nothing and read as agreement.
227
+ return unknown(`the launchd plist exists but could not be read: ${String(error)}`);
228
+ }
229
+
230
+ return {
231
+ kind: "present",
232
+ claims: [{
233
+ backend: "launchd",
234
+ definitionPath,
235
+ homes: {
236
+ codexHome: plistEnvValue(body, "CODEX_HOME"),
237
+ opencodexHome: plistEnvValue(body, "OPENCODEX_HOME"),
238
+ },
239
+ registration,
240
+ }],
241
+ };
242
+ }
243
+
244
+ function systemdProperty(out: string, key: string): string | null {
245
+ for (const line of out.split("\n")) {
246
+ const match = line.match(new RegExp(`^${key}=(.*)$`));
247
+ if (match) return match[1].trim();
248
+ }
249
+ return null;
250
+ }
251
+
252
+ function inspectSystemd(deps: Required<Pick<ProbeDeps, "run" | "home">>): ServiceManagerInstallation {
253
+ const definitionPath = join(deps.home, ".config", "systemd", "user", `${TASK}.service`);
254
+
255
+ /*
256
+ * All four properties in one call. LoadState alone is not enough — it is
257
+ * orthogonal to ActiveState — and neither says whether the LOADED bytes match
258
+ * the file. NeedDaemonReload is that signal, and this repository already
259
+ * documents it as the systemd analogue of launchd's stale plist.
260
+ */
261
+ const shown = deps.run("systemctl", [
262
+ "--user", "show", TASK,
263
+ "-p", "LoadState", "-p", "ActiveState", "-p", "FragmentPath", "-p", "NeedDaemonReload",
264
+ ]);
265
+ if (shown.spawnFailed || shown.timedOut) {
266
+ return unknown(`systemctl could not be asked: ${shown.timedOut ? "timed out" : shown.stderr.trim()}`);
267
+ }
268
+ if (shown.status !== 0) {
269
+ // A missing unit still exits ZERO and says not-found; a non-zero status means
270
+ // the question never reached the bus.
271
+ return unknown(`systemctl show exited ${String(shown.status)}: ${shown.stderr.trim()}`);
272
+ }
273
+
274
+ const loadState = systemdProperty(shown.stdout, "LoadState");
275
+ const activeState = systemdProperty(shown.stdout, "ActiveState");
276
+ const fragmentPath = systemdProperty(shown.stdout, "FragmentPath");
277
+ const needReload = systemdProperty(shown.stdout, "NeedDaemonReload");
278
+ if (loadState === null || activeState === null || needReload === null) {
279
+ return unknown("systemctl show did not report the properties it was asked for");
280
+ }
281
+ if (needReload === "yes") {
282
+ return unknown("systemd has a stale definition loaded; it needs daemon-reload");
283
+ }
284
+
285
+ const registration: "present" | "absent" =
286
+ loadState === "not-found" && activeState === "inactive" && !fragmentPath ? "absent" : "present";
287
+
288
+ if (artifactPresence(definitionPath) === "absent") {
289
+ return registration === "absent"
290
+ ? { kind: "absent" }
291
+ : unknown("systemd knows this unit but its file is missing");
292
+ }
293
+
294
+ let body: string;
295
+ try {
296
+ body = readFileSync(definitionPath, "utf-8");
297
+ } catch (error) {
298
+ return unknown(`the systemd unit exists but could not be read: ${String(error)}`);
299
+ }
300
+
301
+ return {
302
+ kind: "present",
303
+ claims: [{
304
+ backend: "systemd",
305
+ definitionPath,
306
+ homes: {
307
+ codexHome: unitEnvValue(body, "CODEX_HOME"),
308
+ opencodexHome: unitEnvValue(body, "OPENCODEX_HOME"),
309
+ },
310
+ registration,
311
+ }],
312
+ };
313
+ }
314
+
315
+ /**
316
+ * Windows: walk the scheduled-task definition chain and report the homes it names.
317
+ *
318
+ * The chain is not one file: the task XML names only the launcher, the launcher
319
+ * (VBS) names only the batch wrapper, and the homes live in the wrapper's
320
+ * `set "CODEX_HOME=..."` / `set "OPENCODEX_HOME=..."` lines. Parsing the XML
321
+ * and stopping would find no homes and read that as agreement, so the walk goes
322
+ * all the way to the wrapper.
323
+ *
324
+ * A `set` line is OMITTED by `buildWindowsServiceScript` when the value was
325
+ * unset at install time (windowsBatchSet returns null for empty values), so a
326
+ * missing home stays `null` — the same contract the launchd/systemd probes use —
327
+ * and a definition that names no homes cannot be mistaken for agreement.
328
+ *
329
+ * Registration is answered by bounded `schtasks` queries so a definition staged
330
+ * on disk but never registered is still visible (the interrupted-install case).
331
+ * Every failure to ask is `unknown`, never absence.
332
+ */
333
+ function windowsTaskName(): string {
334
+ return "opencodex-proxy";
335
+ }
336
+
337
+ function windowsConfigDirPath(deps: { home: string; configDir?: string }): string {
338
+ if (deps.configDir) return deps.configDir;
339
+ const canonical = join(deps.home, REMODEX_HOME_DIRNAME);
340
+ // A legacy service definition must remain discoverable until the runtime can
341
+ // migrate it. Prefer the canonical root whenever it has service assets.
342
+ if (
343
+ existsSync(join(canonical, "opencodex-service-task.xml"))
344
+ || existsSync(join(canonical, "winsw", `${WINSW_SERVICE_ID}.xml`))
345
+ ) {
346
+ return canonical;
347
+ }
348
+ const legacy = join(deps.home, ".opencodex");
349
+ if (
350
+ existsSync(join(legacy, "opencodex-service-task.xml"))
351
+ || existsSync(join(legacy, "winsw", `${WINSW_SERVICE_ID}.xml`))
352
+ ) {
353
+ return legacy;
354
+ }
355
+ return canonical;
356
+ }
357
+
358
+ /** Decode an on-disk Windows text asset (task XML, VBS), which is UTF-16LE (often BOM-prefixed). */
359
+ function decodeWindowsText(buffer: Buffer): string {
360
+ if (buffer.length === 0) return "";
361
+ const bomUtf16Le = buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xfe;
362
+ const bomUtf16Be = buffer.length >= 2 && buffer[0] === 0xfe && buffer[1] === 0xff;
363
+ const looksUtf16Le = buffer.length >= 4
364
+ && buffer[1] === 0x00
365
+ && buffer[3] === 0x00
366
+ && buffer[0] !== 0x00;
367
+ if (bomUtf16Le || looksUtf16Le) {
368
+ return buffer.toString("utf16le").replace(/^\uFEFF/, "").trim();
369
+ }
370
+ if (bomUtf16Be) {
371
+ const swapped = Buffer.alloc(buffer.length - 2);
372
+ for (let i = 2; i + 1 < buffer.length; i += 2) {
373
+ swapped[i - 2] = buffer[i + 1]!;
374
+ swapped[i - 1] = buffer[i]!;
375
+ }
376
+ return swapped.toString("utf16le").trim();
377
+ }
378
+ return buffer.toString("utf8").replace(/^\uFEFF/, "").trim();
379
+ }
380
+
381
+ /** Decode the XML entities emitted by the service-definition writers. */
382
+ function decodeXmlEntities(value: string): string {
383
+ return value
384
+ .replace(/&quot;/g, '"')
385
+ .replace(/&lt;/g, "<")
386
+ .replace(/&gt;/g, ">")
387
+ .replace(/&apos;/g, "'")
388
+ .replace(/&amp;/g, "&");
389
+ }
390
+
391
+ /** Pull the launcher path out of the task XML `<Arguments>` element. */
392
+ function windowsTaskArguments(xml: string): string | null {
393
+ const match = /<Arguments[^>]*>\s*([^<]*?)\s*<\/Arguments>/i.exec(xml);
394
+ return match ? decodeXmlEntities(match[1]!.trim()) : null;
395
+ }
396
+
397
+ /**
398
+ * Pull the wrapper path out of a VBS `shell.Run` line.
399
+ *
400
+ * `buildWindowsLauncherVbs` escapes a `"` inside a VBS string literal by
401
+ * doubling it, so a wrapper `C:\...\opencodex-service.cmd` is emitted as
402
+ * `shell.Run """C:\...\opencodex-service.cmd""", 0, True`.
403
+ */
404
+ function vbsWrappedCommand(body: string): string | null {
405
+ const match = /\.Run\s+"""([^"]*)"""/.exec(body);
406
+ if (match) {
407
+ const unwrapped = match[1]!.trim();
408
+ if (unwrapped.length > 0) return unwrapped;
409
+ }
410
+ const plain = /\.Run\s+"([^"]+)"/.exec(body);
411
+ return plain ? plain[1]!.trim() : null;
412
+ }
413
+
414
+ /** Pull one `set "NAME=value"` out of a batch wrapper. */
415
+ function batchSetValue(body: string, name: string): string | null {
416
+ const match = new RegExp(`^\\s*set\\s+"${name}=([^"]*)"\\s*$`, "im").exec(body);
417
+ return match ? match[1]!.trim() : null;
418
+ }
419
+
420
+ /** Resolve generated batch env indirection before comparing homes. */
421
+ function decodeBatchPathValue(
422
+ value: string,
423
+ env: Record<string, string | undefined> = process.env,
424
+ ): string {
425
+ const escapedPercent = "\u0000";
426
+ const tokens: Record<string, string | undefined> = {
427
+ USERPROFILE: env.USERPROFILE,
428
+ APPDATA: env.APPDATA,
429
+ LOCALAPPDATA: env.LOCALAPPDATA,
430
+ SYSTEMROOT: env.SystemRoot,
431
+ };
432
+ return value
433
+ .replace(/%%/g, escapedPercent)
434
+ .replace(/%([A-Za-z][A-Za-z0-9_]*)%/g, (whole, name: string) => {
435
+ const resolved = tokens[name.toUpperCase()];
436
+ return resolved === undefined ? whole : resolved;
437
+ })
438
+ .replaceAll(escapedPercent, "%");
439
+ }
440
+
441
+ /** Validate the generated wrapper before interpreting omitted optional homes. */
442
+ function wrapperLooksGenerated(body: string): boolean {
443
+ return /:loop\s*[\s\S]*^"%OCX_BUN%" "%OCX_CLI%" start\b[^\r\n]*$/im.test(body);
444
+ }
445
+
446
+ function normalizeWindowsPath(value: string): string {
447
+ return win32Path.normalize(value.replace(/\//g, "\\")).replace(/[\\]+$/, "").toLowerCase();
448
+ }
449
+
450
+ /** True only when a definition-provided path remains inside the effective OPENCODEX_HOME. */
451
+ function windowsPathInsideConfigDir(candidate: string, configDir: string): boolean {
452
+ const root = normalizeWindowsPath(configDir);
453
+ const path = normalizeWindowsPath(candidate);
454
+ const relative = win32Path.relative(root, path);
455
+ return relative === "" || (relative !== ".." && !relative.startsWith("..\\") && !win32Path.isAbsolute(relative));
456
+ }
457
+
458
+ /** Parse the first CSV field emitted by schtasks `/fo CSV`. */
459
+ function csvFirstField(line: string): string {
460
+ const trimmed = line.trim();
461
+ if (!trimmed.startsWith('"')) return (trimmed.split(",", 1)[0] ?? "").trim();
462
+ let value = "";
463
+ for (let i = 1; i < trimmed.length; i += 1) {
464
+ const ch = trimmed[i]!;
465
+ if (ch !== '"') {
466
+ value += ch;
467
+ continue;
468
+ }
469
+ if (trimmed[i + 1] === '"') {
470
+ value += '"';
471
+ i += 1;
472
+ continue;
473
+ }
474
+ break;
475
+ }
476
+ return value;
477
+ }
478
+
479
+ function windowsTaskListContains(body: string, taskName: string): boolean {
480
+ const target = taskName.toLowerCase();
481
+ return body.split(/\r?\n/).some(line => {
482
+ const field = csvFirstField(line).replace(/\//g, "\\").replace(/^\\+/, "");
483
+ return field.toLowerCase() === target;
484
+ });
485
+ }
486
+
487
+ /** English hosts provide a decisive fast path; other locales fall back to a full listing. */
488
+ const SCHTASKS_TASK_NOT_FOUND_EN = /cannot find the file specified/i;
489
+
490
+ /**
491
+ * Registration state of the scheduled task.
492
+ *
493
+ * The `/xml` query gives the authoritative registered definition. A nonzero
494
+ * result is locale-dependent. English's task-not-found message is decisive; all
495
+ * other nonzero responses use a bounded full listing as the locale-neutral
496
+ * fallback, and only a successful list without our task proves absence.
497
+ */
498
+ function probeWindowsTaskRegistration(deps: Required<Pick<ProbeDeps, "runRaw">>): {
499
+ registered: "present" | "absent" | "unknown";
500
+ registeredXml: string;
501
+ } {
502
+ let schtasks: string;
503
+ try {
504
+ schtasks = resolveTrustedWindowsSchtasksExe();
505
+ } catch {
506
+ return { registered: "unknown", registeredXml: "" };
507
+ }
508
+
509
+ const queried = deps.runRaw(schtasks, ["/query", "/tn", windowsTaskName(), "/xml"]);
510
+ if (queried.spawnFailed || queried.timedOut) return { registered: "unknown", registeredXml: "" };
511
+ if (queried.status === 0) {
512
+ const registeredXml = decodeWindowsText(queried.stdout) || decodeWindowsText(queried.stderr);
513
+ return registeredXml
514
+ ? { registered: "present", registeredXml }
515
+ : { registered: "unknown", registeredXml: "" };
516
+ }
517
+
518
+ const queryText = `${decodeWindowsText(queried.stdout)}\n${decodeWindowsText(queried.stderr)}`;
519
+ if (queried.status !== null && SCHTASKS_TASK_NOT_FOUND_EN.test(queryText)) {
520
+ return { registered: "absent", registeredXml: "" };
521
+ }
522
+
523
+ const listed = deps.runRaw(schtasks, ["/query", "/fo", "CSV", "/nh"]);
524
+ if (listed.spawnFailed || listed.timedOut || listed.status !== 0) {
525
+ return { registered: "unknown", registeredXml: "" };
526
+ }
527
+ const listing = decodeWindowsText(listed.stdout) || decodeWindowsText(listed.stderr);
528
+ return windowsTaskListContains(listing, windowsTaskName())
529
+ ? { registered: "unknown", registeredXml: "" }
530
+ : { registered: "absent", registeredXml: "" };
531
+ }
532
+
533
+ type WinswRegistration = "present" | "absent" | "unknown";
534
+
535
+ /** Query WinSW registration through trusted System32 sc.exe; never execute the user-writable WinSW binary. */
536
+ function probeWinswRegistration(
537
+ deps: Required<Pick<ProbeDeps, "runRaw">> & Pick<ProbeDeps, "winswStatus">,
538
+ ): WinswRegistration {
539
+ if (deps.winswStatus) {
540
+ const injected = deps.winswStatus();
541
+ if (injected === "started" || injected === "stopped") return "present";
542
+ if (injected === "nonexistent") return "absent";
543
+ return "unknown";
544
+ }
545
+
546
+ let sc: string;
547
+ try {
548
+ sc = join(resolveTrustedWindowsSystemDirectory(), "sc.exe");
549
+ if (artifactPresence(sc) !== "present") return "unknown";
550
+ } catch {
551
+ return "unknown";
552
+ }
553
+
554
+ const queried = deps.runRaw(sc, ["query", WINSW_SERVICE_ID]);
555
+ if (queried.spawnFailed || queried.timedOut) return "unknown";
556
+ if (queried.status === 0) return "present";
557
+
558
+ // ERROR_SERVICE_DOES_NOT_EXIST (1060) is locale-invariant. Search raw byte
559
+ // text so localized OEM output cannot affect the numeric classification.
560
+ const text = `${queried.stdout.toString("latin1")}\n${queried.stderr.toString("latin1")}`;
561
+ return /\b1060\b/.test(text) ? "absent" : "unknown";
562
+ }
563
+
564
+ function inspectWindows(
565
+ deps: Required<Pick<ProbeDeps, "runRaw" | "home">> & Pick<ProbeDeps, "configDir" | "winswStatus">,
566
+ ): ServiceManagerInstallation {
567
+ const configDir = windowsConfigDirPath(deps);
568
+ const taskXmlPath = join(configDir, "opencodex-service-task.xml");
569
+ const task = artifactPresence(taskXmlPath);
570
+ const winsw = walkWinswChain(deps);
571
+ const winswInstalled = winsw.kind === "present" && winsw.claims[0].registration === "present";
572
+ const winswStaged = winsw.kind === "present" && winsw.claims[0].registration === "absent";
573
+
574
+ let xml = "";
575
+ if (task !== "absent") {
576
+ try {
577
+ xml = decodeWindowsText(readFileSync(taskXmlPath));
578
+ } catch (error) {
579
+ return unknown(`the scheduled-task XML exists but could not be read: ${String(error)}`);
580
+ }
581
+ }
582
+
583
+ const registration = probeWindowsTaskRegistration(deps);
584
+ if (registration.registered === "unknown") {
585
+ return unknown("Task Scheduler could not be asked whether opencodex-proxy is registered");
586
+ }
587
+ const schedulerRegistered = registration.registered === "present";
588
+
589
+ // Two live registrations are a conflict even if the staging copy of the task
590
+ // XML disappeared. The authoritative `/query /xml` definition is sufficient
591
+ // to walk the scheduler chain without inventing homes.
592
+ if (winswInstalled && schedulerRegistered) {
593
+ if (!registration.registeredXml.trim()) {
594
+ return unknown("Task Scheduler is registered but its definition XML could not be read");
595
+ }
596
+ const registeredWalk = walkWindowsChain(deps, registration.registeredXml, taskXmlPath);
597
+ if (registeredWalk.kind !== "present") return registeredWalk;
598
+ return {
599
+ kind: "conflict",
600
+ claims: [
601
+ winsw.claims[0],
602
+ { ...registeredWalk.claims[0], registration: "present" },
603
+ ],
604
+ };
605
+ }
606
+
607
+ if (winswInstalled) {
608
+ // A staged scheduler definition beside a registered WinSW service is an
609
+ // interrupted backend switch, not proof that WinSW alone owns the machine.
610
+ if (task !== "absent") {
611
+ return unknown("a scheduled-task definition is staged while the native WinSW service is registered");
612
+ }
613
+ return winsw;
614
+ }
615
+
616
+ if (winsw.kind === "unknown") return winsw;
617
+
618
+ // Staged-but-unregistered WinSW remains evidence. If Scheduler is also live
619
+ // or staged, neither half-finished backend switch can be chosen unattended.
620
+ if (winswStaged && (schedulerRegistered || task !== "absent")) {
621
+ return unknown("native WinSW and Task Scheduler definitions overlap during an incomplete backend switch");
622
+ }
623
+ if (winswStaged && task === "absent" && registration.registered === "absent") {
624
+ return winsw;
625
+ }
626
+
627
+ if (task === "absent") {
628
+ return schedulerRegistered
629
+ ? unknown("Task Scheduler holds opencodex-proxy but its task XML is missing")
630
+ : { kind: "absent" };
631
+ }
632
+
633
+ const staged = walkWindowsChain(deps, xml, taskXmlPath);
634
+ if (staged.kind !== "present") return staged;
635
+ const stagedClaim = staged.claims[0];
636
+
637
+ if (schedulerRegistered) {
638
+ if (!registration.registeredXml.trim()) {
639
+ return unknown("Task Scheduler is registered but its definition XML could not be read");
640
+ }
641
+ const registeredWalk = walkWindowsChain(deps, registration.registeredXml, taskXmlPath);
642
+ if (registeredWalk.kind !== "present") return registeredWalk;
643
+ const registeredClaim = registeredWalk.claims[0];
644
+ if (!homesEqual(registeredClaim.homes, stagedClaim.homes)) {
645
+ return unknown("the registered scheduled task names different homes than the staged task definition");
646
+ }
647
+ }
648
+
649
+ return {
650
+ kind: "present",
651
+ claims: [{
652
+ ...stagedClaim,
653
+ registration: schedulerRegistered ? "present" : "absent",
654
+ }],
655
+ };
656
+ }
657
+
658
+ /** Compare two home pairs with Windows path normalization (case, slashes, trailing separators). */
659
+ function homesEqual(
660
+ a: { codexHome: string | null; opencodexHome: string | null },
661
+ b: { codexHome: string | null; opencodexHome: string | null },
662
+ ): boolean {
663
+ const norm = (v: string | null): string | null => {
664
+ if (v === null) return null;
665
+ return v.replace(/[\\/]+$/, "").replace(/\//g, "\\").toLowerCase();
666
+ };
667
+ return norm(a.codexHome) === norm(b.codexHome) && norm(a.opencodexHome) === norm(b.opencodexHome);
668
+ }
669
+
670
+ /**
671
+ * Walk one scheduled-task definition (staged or registered XML) down to the
672
+ * generated batch wrapper and extract the homes it names. Definition-provided
673
+ * paths are followed only inside the effective OPENCODEX_HOME, preventing a
674
+ * foreign task from turning this ownership probe into an arbitrary local/UNC
675
+ * file read while preserving interrupted-reinstall diagnostics within the
676
+ * generated service-asset directory.
677
+ */
678
+ function walkWindowsChain(
679
+ deps: Required<Pick<ProbeDeps, "home">> & Pick<ProbeDeps, "configDir">,
680
+ xml: string,
681
+ definitionPath: string,
682
+ ): ServiceManagerInstallation {
683
+ const configDir = windowsConfigDirPath(deps);
684
+
685
+ const launcherArg = windowsTaskArguments(xml);
686
+ if (!launcherArg) {
687
+ return unknown("the scheduled-task XML names no launcher to run");
688
+ }
689
+ const launcherPath = /"([^"]+)"/.exec(launcherArg)?.[1];
690
+ if (!launcherPath) {
691
+ return unknown("the scheduled-task XML launcher argument is not a quoted path");
692
+ }
693
+ if (!windowsPathInsideConfigDir(launcherPath, configDir)) {
694
+ return unknown(`the scheduled-task XML names ${launcherPath}, outside the expected launcher directory ${configDir}`);
695
+ }
696
+
697
+ const launcher = artifactPresence(launcherPath);
698
+ if (launcher === "absent") {
699
+ return unknown(`the scheduled-task launcher is missing: ${launcherPath}`);
700
+ }
701
+ let launcherBody: string;
702
+ try {
703
+ launcherBody = decodeWindowsText(readFileSync(launcherPath));
704
+ } catch (error) {
705
+ return unknown(`the scheduled-task launcher could not be read: ${String(error)}`);
706
+ }
707
+
708
+ const wrapperPath = vbsWrappedCommand(launcherBody);
709
+ if (!wrapperPath) {
710
+ return unknown(`the launcher ${launcherPath} names no wrapper to run`);
711
+ }
712
+ if (!windowsPathInsideConfigDir(wrapperPath, configDir)) {
713
+ return unknown(`the scheduled-task launcher names ${wrapperPath}, outside the expected wrapper directory ${configDir}`);
714
+ }
715
+
716
+ const wrapper = artifactPresence(wrapperPath);
717
+ if (wrapper === "absent") {
718
+ return unknown(`the launcher wrapper is missing: ${wrapperPath}`);
719
+ }
720
+ let wrapperBody: string;
721
+ try {
722
+ wrapperBody = decodeWindowsText(readFileSync(wrapperPath));
723
+ } catch (error) {
724
+ return unknown(`the launcher wrapper could not be read: ${String(error)}`);
725
+ }
726
+
727
+ if (!wrapperLooksGenerated(wrapperBody)) {
728
+ return unknown(`the launcher wrapper does not look like a generated Remodex service wrapper: ${wrapperPath}`);
729
+ }
730
+
731
+ const rawCodexHome = batchSetValue(wrapperBody, "CODEX_HOME");
732
+ const rawOpencodexHome = batchSetValue(wrapperBody, "OPENCODEX_HOME");
733
+
734
+ return {
735
+ kind: "present",
736
+ claims: [{
737
+ backend: "scheduler",
738
+ definitionPath,
739
+ homes: {
740
+ codexHome: rawCodexHome === null ? null : decodeBatchPathValue(rawCodexHome),
741
+ opencodexHome: rawOpencodexHome === null ? null : decodeBatchPathValue(rawOpencodexHome),
742
+ },
743
+ registration: "absent",
744
+ }],
745
+ };
746
+ }
747
+
748
+ /**
749
+ * Walk the WinSW native-backend definition. SCM registration is queried via a
750
+ * trusted, bounded `sc.exe query`; the WinSW executable itself is never run by
751
+ * this read-only ownership probe.
752
+ */
753
+ function walkWinswChain(
754
+ deps: Required<Pick<ProbeDeps, "runRaw" | "home">> & Pick<ProbeDeps, "configDir" | "winswStatus">,
755
+ ): ServiceManagerInstallation {
756
+ const configDir = windowsConfigDirPath(deps);
757
+ const exePath = join(configDir, "winsw", `${WINSW_SERVICE_ID}.exe`);
758
+ const xmlPath = join(configDir, "winsw", `${WINSW_SERVICE_ID}.xml`);
759
+ const xml = artifactPresence(xmlPath);
760
+ const exe = artifactPresence(exePath);
761
+ const registration = probeWinswRegistration(deps);
762
+
763
+ if (xml === "absent" && exe === "absent" && registration === "absent") return { kind: "absent" };
764
+ if (registration === "unknown") {
765
+ return unknown("the native WinSW service registration could not be verified");
766
+ }
767
+ if (xml === "absent" || exe === "absent") {
768
+ return unknown("the native WinSW service registration could not be verified");
769
+ }
770
+
771
+ let body: string;
772
+ try {
773
+ body = decodeWindowsText(readFileSync(xmlPath));
774
+ } catch (error) {
775
+ return unknown(`the WinSW XML could not be read: ${String(error)}`);
776
+ }
777
+ if (!winswXmlLooksGenerated(body)) {
778
+ return unknown(`the WinSW XML does not look like a generated Remodex service definition: ${xmlPath}`);
779
+ }
780
+
781
+ const envValue = (name: string): string | null => {
782
+ const tag = new RegExp(`<env\\b[^>]*\\bname=["']${name}["'][^>]*>`, "i").exec(body);
783
+ if (!tag) return null;
784
+ const value = /value=(["'])(.*?)\1/i.exec(tag[0]);
785
+ return value ? decodeXmlEntities(value[2]!) : null;
786
+ };
787
+
788
+ return {
789
+ kind: "present",
790
+ claims: [{
791
+ backend: "winsw",
792
+ definitionPath: exePath,
793
+ homes: {
794
+ codexHome: envValue("CODEX_HOME"),
795
+ opencodexHome: envValue("OPENCODEX_HOME"),
796
+ },
797
+ registration,
798
+ }],
799
+ };
800
+ }
801
+
802
+ /** The generated WinSW XML embeds the SCM id and a `start --port` invocation. */
803
+ function winswXmlLooksGenerated(body: string): boolean {
804
+ return /<id>\s*opencodex-proxy-native\s*<\/id>/i.test(body)
805
+ && /<arguments>.*?start\s+--port\b/i.test(body);
806
+ }
807
+
808
+ export function inspectServiceManagerInstallation(deps: ProbeDeps = {}): ServiceManagerInstallation {
809
+ const platform = deps.platform ?? process.platform;
810
+ const run = deps.run ?? defaultProbeRunner;
811
+ const runRaw = deps.runRaw ?? defaultRawProbeRunner;
812
+ const home = deps.home ?? homedir();
813
+ if (platform === "darwin") return inspectLaunchd({ run, uid: deps.uid ?? process.getuid?.() ?? 0, home });
814
+ if (platform === "linux") return inspectSystemd({ run, home });
815
+ if (platform === "win32") {
816
+ return inspectWindows({
817
+ runRaw,
818
+ home,
819
+ configDir: deps.configDir,
820
+ winswStatus: deps.winswStatus,
821
+ });
822
+ }
823
+ return unknown(`no service manager probe for platform ${platform}`);
824
+ }