synabun 2.0.0 → 2.0.7

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 (507) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/NOTICE +5 -0
  3. package/README.md +2 -1
  4. package/THIRD-PARTY-LICENSES.md +59 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +153 -18
  8. package/hooks/claude-code/pre-compact.mjs +11 -0
  9. package/hooks/claude-code/prompt-submit.mjs +60 -184
  10. package/hooks/claude-code/session-start.mjs +73 -16
  11. package/hooks/claude-code/shared.mjs +26 -3
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/lib/child-path.js +303 -0
  14. package/lib/claude-hooks.js +2 -1
  15. package/lib/data-home-migration.js +2 -0
  16. package/lib/desktop-app.js +265 -0
  17. package/lib/external-tools.js +7 -3
  18. package/lib/log-redact.js +102 -0
  19. package/lib/packaged-runtime.js +138 -11
  20. package/lib/start-launcher.js +88 -1
  21. package/mcp-server/dist/http.d.ts +3 -1
  22. package/mcp-server/dist/http.d.ts.map +1 -1
  23. package/mcp-server/dist/http.js +15 -4
  24. package/mcp-server/dist/http.js.map +1 -1
  25. package/mcp-server/dist/index.d.ts.map +1 -1
  26. package/mcp-server/dist/index.js +9 -6
  27. package/mcp-server/dist/index.js.map +1 -1
  28. package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
  29. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
  30. package/mcp-server/dist/services/budget-judgments.js +153 -0
  31. package/mcp-server/dist/services/budget-judgments.js.map +1 -0
  32. package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
  33. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  34. package/mcp-server/dist/services/codex-config-heal.js +41 -4
  35. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  36. package/mcp-server/dist/services/identity.d.ts +11 -0
  37. package/mcp-server/dist/services/identity.d.ts.map +1 -1
  38. package/mcp-server/dist/services/identity.js +24 -0
  39. package/mcp-server/dist/services/identity.js.map +1 -1
  40. package/mcp-server/dist/services/neural-interface.d.ts +13 -0
  41. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  42. package/mcp-server/dist/services/neural-interface.js +34 -2
  43. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  44. package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
  45. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
  46. package/mcp-server/dist/services/typesafe-config.js +4 -1
  47. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  48. package/mcp-server/dist/tools/agents.d.ts +10 -7
  49. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  50. package/mcp-server/dist/tools/agents.js +34 -13
  51. package/mcp-server/dist/tools/agents.js.map +1 -1
  52. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
  53. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
  54. package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
  55. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
  56. package/mcp-server/dist/tui.js +2 -1
  57. package/mcp-server/dist/tui.js.map +1 -1
  58. package/mcp-server/dist/version.d.ts +5 -0
  59. package/mcp-server/dist/version.d.ts.map +1 -0
  60. package/mcp-server/dist/version.js +21 -0
  61. package/mcp-server/dist/version.js.map +1 -0
  62. package/mcp-server/package-lock.json +2 -2
  63. package/mcp-server/package.json +1 -3
  64. package/mcp-server/run.mjs +15 -9
  65. package/mcp-server/src/http.ts +18 -3
  66. package/mcp-server/src/index.ts +9 -6
  67. package/mcp-server/src/services/budget-judgments.ts +183 -0
  68. package/mcp-server/src/services/codex-config-heal.ts +35 -4
  69. package/mcp-server/src/services/identity.ts +27 -0
  70. package/mcp-server/src/services/neural-interface.ts +34 -2
  71. package/mcp-server/src/services/typesafe-config.ts +4 -1
  72. package/mcp-server/src/tools/agents.ts +33 -14
  73. package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
  74. package/mcp-server/src/tui.ts +2 -1
  75. package/mcp-server/src/version.ts +22 -0
  76. package/neural-interface/i18n/en.json +2126 -209
  77. package/neural-interface/i18n/pt-BR.json +2123 -207
  78. package/neural-interface/lib/account-usage.js +163 -0
  79. package/neural-interface/lib/assistant-api.js +316 -57
  80. package/neural-interface/lib/assistant-brains/claude.js +19 -2
  81. package/neural-interface/lib/assistant-brains/codex.js +3 -0
  82. package/neural-interface/lib/assistant-brains/opencode.js +17 -4
  83. package/neural-interface/lib/assistant-budget-planner.js +390 -0
  84. package/neural-interface/lib/assistant-budget-split.js +408 -0
  85. package/neural-interface/lib/assistant-budget.js +94 -95
  86. package/neural-interface/lib/assistant-catalog.js +179 -27
  87. package/neural-interface/lib/assistant-config.js +91 -14
  88. package/neural-interface/lib/assistant-dispatch.js +494 -138
  89. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  90. package/neural-interface/lib/assistant-jev.js +26 -3
  91. package/neural-interface/lib/assistant-memory.js +24 -10
  92. package/neural-interface/lib/assistant-persona.js +73 -12
  93. package/neural-interface/lib/assistant-plan-permissions.js +8 -1
  94. package/neural-interface/lib/assistant-pricing.js +21 -6
  95. package/neural-interface/lib/assistant-route-accounts.js +14 -0
  96. package/neural-interface/lib/assistant-route-gate.js +19 -3
  97. package/neural-interface/lib/assistant-router.js +373 -42
  98. package/neural-interface/lib/assistant-runtime.js +488 -59
  99. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  100. package/neural-interface/lib/assistant-test-density.js +142 -0
  101. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  102. package/neural-interface/lib/assistant-user-context.js +908 -0
  103. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  104. package/neural-interface/lib/assistant-user-learn.js +404 -0
  105. package/neural-interface/lib/assistant-user-threads.js +226 -0
  106. package/neural-interface/lib/assistant-user-writer.js +183 -0
  107. package/neural-interface/lib/augmented-path.js +317 -53
  108. package/neural-interface/lib/browser-background-tab.js +134 -0
  109. package/neural-interface/lib/browser-screenshot.js +678 -61
  110. package/neural-interface/lib/claude-accounts.js +342 -31
  111. package/neural-interface/lib/claude-agent-bridge.js +123 -9
  112. package/neural-interface/lib/claude-executable.js +6 -1
  113. package/neural-interface/lib/claude-model-catalog.js +33 -5
  114. package/neural-interface/lib/claude-panel-auth.js +221 -0
  115. package/neural-interface/lib/claude-profile-api-key.js +62 -0
  116. package/neural-interface/lib/claude-temporary.js +46 -7
  117. package/neural-interface/lib/cli-install-api.js +137 -0
  118. package/neural-interface/lib/cli-install.js +797 -0
  119. package/neural-interface/lib/cli-path.js +130 -0
  120. package/neural-interface/lib/cli-resolver.js +744 -0
  121. package/neural-interface/lib/cli-update-planner.js +143 -7
  122. package/neural-interface/lib/cloudflared/install.js +533 -0
  123. package/neural-interface/lib/cloudflared/manager.js +545 -0
  124. package/neural-interface/lib/cloudflared/pins.js +115 -0
  125. package/neural-interface/lib/cloudflared/pins.json +63 -0
  126. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  127. package/neural-interface/lib/codex-account-auth.js +178 -0
  128. package/neural-interface/lib/codex-accounts.js +489 -0
  129. package/neural-interface/lib/codex-capabilities.js +2 -1
  130. package/neural-interface/lib/codex-compat-rpc.js +8 -2
  131. package/neural-interface/lib/codex-mcp-approvals.js +5 -1
  132. package/neural-interface/lib/codex-panel-auth.js +62 -0
  133. package/neural-interface/lib/codex-session-settings.js +1 -1
  134. package/neural-interface/lib/codex-settings-schema.js +120 -0
  135. package/neural-interface/lib/desktop/config.js +3 -0
  136. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  137. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  138. package/neural-interface/lib/editor-completion.js +714 -0
  139. package/neural-interface/lib/editor-config.js +149 -0
  140. package/neural-interface/lib/editor-files-api.js +268 -0
  141. package/neural-interface/lib/editor-git-api.js +134 -0
  142. package/neural-interface/lib/file-explorer-api.js +1039 -0
  143. package/neural-interface/lib/file-explorer-system.js +355 -0
  144. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  145. package/neural-interface/lib/file-explorer-watch.js +239 -0
  146. package/neural-interface/lib/git-bash/bzip2.js +339 -0
  147. package/neural-interface/lib/git-bash/claude-settings.js +73 -0
  148. package/neural-interface/lib/git-bash/extract-worker.js +34 -0
  149. package/neural-interface/lib/git-bash/install.js +529 -0
  150. package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
  151. package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
  152. package/neural-interface/lib/git-bash/manager.js +268 -0
  153. package/neural-interface/lib/git-bash/pins.js +206 -0
  154. package/neural-interface/lib/git-bash/pins.json +34 -0
  155. package/neural-interface/lib/git-bash/resolver.js +194 -0
  156. package/neural-interface/lib/git-diff-api.js +103 -0
  157. package/neural-interface/lib/git-read.js +234 -0
  158. package/neural-interface/lib/greeting/api.js +89 -0
  159. package/neural-interface/lib/greeting/budgets.js +90 -0
  160. package/neural-interface/lib/greeting/engine.js +461 -0
  161. package/neural-interface/lib/greeting/facts.js +320 -0
  162. package/neural-interface/lib/greeting/sources.js +252 -0
  163. package/neural-interface/lib/greeting/writer.js +139 -0
  164. package/neural-interface/lib/http-guards.js +189 -13
  165. package/neural-interface/lib/install-details.js +145 -0
  166. package/neural-interface/lib/machine-names.js +88 -0
  167. package/neural-interface/lib/mcp-installer.js +14 -2
  168. package/neural-interface/lib/memory-api.js +31 -7
  169. package/neural-interface/lib/native-loop-providers.js +26 -8
  170. package/neural-interface/lib/native-loop-runtime.js +9 -4
  171. package/neural-interface/lib/os-path.js +319 -0
  172. package/neural-interface/lib/plugin-installer.js +4 -2
  173. package/neural-interface/lib/project-storage.js +5 -7
  174. package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
  175. package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
  176. package/neural-interface/lib/pty-host/core.js +2 -1
  177. package/neural-interface/lib/pty-host/manager.js +27 -2
  178. package/neural-interface/lib/remote-policy.js +20 -11
  179. package/neural-interface/lib/request-gate.js +589 -0
  180. package/neural-interface/lib/session-title-generator.js +4 -3
  181. package/neural-interface/lib/system-about.js +340 -0
  182. package/neural-interface/lib/system-activity.js +66 -0
  183. package/neural-interface/lib/terminal-env.js +11 -3
  184. package/neural-interface/lib/terminal-path.js +55 -0
  185. package/neural-interface/lib/ui-blob-api.js +108 -0
  186. package/neural-interface/lib/ui-blob-store.js +68 -0
  187. package/neural-interface/lib/virtual-socket.js +12 -1
  188. package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
  189. package/neural-interface/lib/whatsapp/bridge.js +78 -34
  190. package/neural-interface/lib/whatsapp/commands.js +13 -14
  191. package/neural-interface/lib/whatsapp/config.js +134 -20
  192. package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
  193. package/neural-interface/lib/whatsapp/host-core.js +168 -35
  194. package/neural-interface/lib/whatsapp/inbound.js +5 -3
  195. package/neural-interface/lib/whatsapp/local-file.js +524 -0
  196. package/neural-interface/lib/whatsapp/manager.js +92 -32
  197. package/neural-interface/lib/whatsapp/paths.js +11 -1
  198. package/neural-interface/lib/whatsapp/protocol.js +65 -5
  199. package/neural-interface/lib/whatsapp/redact.js +63 -0
  200. package/neural-interface/lib/whatsapp/service.js +343 -91
  201. package/neural-interface/lib/whatsapp/transcription.js +463 -0
  202. package/neural-interface/package-lock.json +701 -2
  203. package/neural-interface/package.json +13 -2
  204. package/neural-interface/public/index.html +2 -0
  205. package/neural-interface/public/index2d.html +2 -0
  206. package/neural-interface/public/install-app.html +9 -6
  207. package/neural-interface/public/offline.html +19 -3
  208. package/neural-interface/public/onboarding.html +107 -2
  209. package/neural-interface/public/shared/account-i18n.js +7 -0
  210. package/neural-interface/public/shared/account-menu.js +539 -0
  211. package/neural-interface/public/shared/account-usage.js +19 -0
  212. package/neural-interface/public/shared/account-view.js +335 -0
  213. package/neural-interface/public/shared/agent-runtime-options.js +50 -14
  214. package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
  215. package/neural-interface/public/shared/assistant/asst-api.js +32 -2
  216. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  217. package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
  218. package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
  219. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  220. package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
  221. package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
  222. package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
  223. package/neural-interface/public/shared/assistant/asst-render.js +197 -23
  224. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  225. package/neural-interface/public/shared/assistant/asst-route.js +95 -343
  226. package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
  227. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  228. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  229. package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
  230. package/neural-interface/public/shared/assistant/asst-state.js +186 -2
  231. package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
  232. package/neural-interface/public/shared/blob-store.js +111 -22
  233. package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
  234. package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
  235. package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
  236. package/neural-interface/public/shared/cdx/cdx-panel.js +80 -118
  237. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  238. package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
  239. package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
  240. package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
  241. package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
  242. package/neural-interface/public/shared/claude-account-view.js +3 -0
  243. package/neural-interface/public/shared/cli-install-view.js +392 -0
  244. package/neural-interface/public/shared/cli-status.js +282 -22
  245. package/neural-interface/public/shared/constants.js +6 -1
  246. package/neural-interface/public/shared/cp/cp-events.js +2 -1
  247. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  248. package/neural-interface/public/shared/cp/cp-styles.js +61 -0
  249. package/neural-interface/public/shared/desktop-bridge.js +39 -0
  250. package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
  251. package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
  252. package/neural-interface/public/shared/desktop-titlebar.js +153 -0
  253. package/neural-interface/public/shared/desktop-update-view.js +462 -0
  254. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  255. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  256. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  257. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  258. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  259. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  260. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  261. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  262. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  263. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  264. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  265. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  266. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  267. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  268. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  269. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  270. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  271. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  272. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  273. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  274. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  275. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  276. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  277. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  278. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  279. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  280. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  281. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  282. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  283. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  284. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  285. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  286. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  287. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  288. package/neural-interface/public/shared/file-links.js +339 -0
  289. package/neural-interface/public/shared/git-bash-view.js +145 -0
  290. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  291. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  292. package/neural-interface/public/shared/html-shell.js +161 -165
  293. package/neural-interface/public/shared/i18n.js +19 -1
  294. package/neural-interface/public/shared/install-details-view.js +86 -0
  295. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  296. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  297. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
  298. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  299. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
  300. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
  301. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  302. package/neural-interface/public/shared/pwa-install-page.js +32 -2
  303. package/neural-interface/public/shared/pwa-install.css +3 -3
  304. package/neural-interface/public/shared/pwa-install.js +100 -1
  305. package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
  306. package/neural-interface/public/shared/settings/settings-about.js +254 -0
  307. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  308. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  309. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  310. package/neural-interface/public/shared/settings/settings-ia.js +33 -4
  311. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  312. package/neural-interface/public/shared/showcase-privacy.js +489 -0
  313. package/neural-interface/public/shared/start-bridge.js +238 -19
  314. package/neural-interface/public/shared/storage.js +30 -3
  315. package/neural-interface/public/shared/styles.css +1710 -770
  316. package/neural-interface/public/shared/synabun-mascot.js +68 -58
  317. package/neural-interface/public/shared/tunnel-view.js +167 -0
  318. package/neural-interface/public/shared/ui-claude-panel.js +369 -229
  319. package/neural-interface/public/shared/ui-cli-banner.js +355 -0
  320. package/neural-interface/public/shared/ui-detail.js +15 -0
  321. package/neural-interface/public/shared/ui-explorer.js +22 -0
  322. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  323. package/neural-interface/public/shared/ui-file-links.js +103 -0
  324. package/neural-interface/public/shared/ui-git-bash.js +124 -0
  325. package/neural-interface/public/shared/ui-help.js +26 -0
  326. package/neural-interface/public/shared/ui-invite.js +126 -116
  327. package/neural-interface/public/shared/ui-judgments.js +10 -1
  328. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  329. package/neural-interface/public/shared/ui-loading.js +187 -19
  330. package/neural-interface/public/shared/ui-menubar.js +25 -33
  331. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  332. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  333. package/neural-interface/public/shared/ui-navbar.js +48 -22
  334. package/neural-interface/public/shared/ui-provider-auth.js +104 -0
  335. package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
  336. package/neural-interface/public/shared/ui-sessions.js +9 -3
  337. package/neural-interface/public/shared/ui-settings.js +509 -490
  338. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  339. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
  340. package/neural-interface/public/shared/ui-sync.js +22 -0
  341. package/neural-interface/public/shared/ui-terminal.js +40 -5
  342. package/neural-interface/public/shared/ui-tool-updates.js +5 -0
  343. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  344. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  345. package/neural-interface/public/shared/ui-update.js +594 -5
  346. package/neural-interface/public/shared/ui-whatsapp.js +21 -6
  347. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  348. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  349. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  350. package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
  351. package/neural-interface/public/shared/worker-end.js +253 -0
  352. package/neural-interface/public/sw.js +2 -2
  353. package/neural-interface/public/variant/2d/main.js +10 -1
  354. package/neural-interface/public/variant/3d/main.js +10 -1
  355. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  356. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  357. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  358. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  359. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  360. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  361. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  362. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  363. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  364. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  365. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  366. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  367. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  368. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  369. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  370. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  371. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  372. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  373. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  374. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  375. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  376. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  377. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  378. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  379. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  380. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  381. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  382. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  383. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  384. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  385. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  386. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  387. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  388. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  389. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  390. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  391. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  392. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  393. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  394. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  395. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  396. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  397. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  398. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  399. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  400. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  401. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  402. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  403. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  404. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  405. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  406. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  407. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  408. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  409. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  410. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  411. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  412. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  413. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  414. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  415. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  416. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  417. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  418. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  419. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  420. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  421. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  422. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  423. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  424. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  425. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  426. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  427. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  428. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  429. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  430. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  431. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  432. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  433. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  434. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  435. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  436. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  437. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  438. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  439. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  440. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  441. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  442. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  443. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  444. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  445. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  446. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  447. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  448. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  449. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  450. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  451. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  452. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  453. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  454. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  455. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  456. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  457. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  458. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  459. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  460. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  461. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  462. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  463. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  464. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  465. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  466. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  467. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  468. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  469. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  470. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  471. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  472. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  473. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  474. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  475. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  476. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  477. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  478. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  479. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  480. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  481. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  482. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  483. package/neural-interface/scripts/build-vendor.mjs +78 -2
  484. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  485. package/neural-interface/server.js +2085 -1629
  486. package/neural-interface/templates/rulesets/README.md +1 -1
  487. package/package.json +5 -8
  488. package/setup.js +53 -15
  489. package/benchmarks/browser-live-results.json +0 -79
  490. package/benchmarks/browser-v2-results.json +0 -258
  491. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  492. package/neural-interface/public/category-logos/claude.svg +0 -1
  493. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  494. package/neural-interface/public/category-logos/openai.svg +0 -1
  495. package/neural-interface/public/category-logos/synabun.png +0 -0
  496. package/neural-interface/public/claude.svg +0 -1
  497. package/neural-interface/public/gallery.svg +0 -6
  498. package/neural-interface/public/leonardoai.svg +0 -1
  499. package/neural-interface/public/mcp.svg +0 -4
  500. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  501. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  502. package/neural-interface/public/sessionicon.svg +0 -8
  503. package/neural-interface/public/toolbar-album.svg +0 -8
  504. package/neural-interface/public/toolbar-question.svg +0 -6
  505. package/neural-interface/public/toolbar-sessions.svg +0 -6
  506. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  507. package/skills/synabun/synabunicon.png +0 -0
@@ -10,13 +10,24 @@
10
10
  // resources are symlinked back to ~/.claude so skills/commands/settings stay in
11
11
  // sync; identity-bearing files stay isolated.
12
12
  //
13
- // The `default` account is the ambient ~/.claude and never gets an env override.
13
+ // Default follows CLAUDE_CONFIG_DIR when set, otherwise ~/.claude.
14
+ //
15
+ // One account, two credentials, one mode (docs/claude-sidepanel.md → Claude
16
+ // accounts). Each account can hold a Claude subscription login (Claude Code's
17
+ // own OAuth, inside that account's config directory) and an API key (SynaBun's
18
+ // `.synabun-api-key` file in the same directory). Its mode, "subscription" or
19
+ // "apiKey", decides what its sessions use; when only one credential exists,
20
+ // that one is the mode. describe() is the one reading of an account's state:
21
+ // the account list, the panel's sign-in screens, its composer picker, the
22
+ // Assistant's brain picker, the catalog and worker dispatch all show it.
23
+ // launchFor() is the one place a Claude Code process gets its credential.
14
24
 
15
25
  import {
16
- copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, symlinkSync, writeFileSync,
26
+ existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync,
17
27
  } from 'node:fs';
18
28
  import { dirname, resolve } from 'node:path';
19
29
  import { homedir } from 'node:os';
30
+ import { readClaudeProfileApiKey, saveClaudeProfileApiKey, clearClaudeProfileApiKey } from './claude-profile-api-key.js';
20
31
 
21
32
  export const DEFAULT_ACCOUNT_ID = 'default';
22
33
  // `rules` carries SynaBun's rules file (rules/synabun.md) to accounts created
@@ -26,6 +37,18 @@ export const SHARED_LINKS = ['settings.json', 'CLAUDE.md', 'rules', 'commands',
26
37
  export const ISOLATED_ITEMS = ['.credentials.json', '.claude.json', 'projects', 'todos', 'history.jsonl', 'statsig', 'shell-snapshots', 'plans'];
27
38
  const CLAUDE_JSON_WHITELIST = ['mcpServers', 'theme', 'editorMode', 'preferredNotifChannel', 'autoUpdates', 'verbose'];
28
39
 
40
+ export const CLAUDE_AUTH_MODES = Object.freeze(['subscription', 'apiKey']);
41
+ // Everything inherited from the server's own environment that would decide a
42
+ // Claude Code process's credential or backend. Removed from every account
43
+ // launch: the account and its mode decide, never what SynaBun was started with.
44
+ export const CLAUDE_CREDENTIAL_ENV = Object.freeze([
45
+ 'ANTHROPIC_API_KEY', 'ANTHROPIC_API_KEY_FILE_DESCRIPTOR', 'ANTHROPIC_AUTH_TOKEN',
46
+ 'CLAUDE_CODE_OAUTH_TOKEN', 'CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR',
47
+ 'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_VERTEX', 'CLAUDE_CODE_USE_FOUNDRY',
48
+ ]);
49
+ // A subscription check older than this is asked again when the list is read.
50
+ const STATUS_MAX_AGE_MS = 5 * 60_000;
51
+
29
52
  export class ClaudeAccountError extends Error {
30
53
  constructor(code, message, status = 400) {
31
54
  super(message);
@@ -49,11 +72,22 @@ function sanitizeLabel(value, fallback = 'Claude account') {
49
72
  const label = String(value || '').replace(/[\r\n\t]/g, ' ').trim().slice(0, 80);
50
73
  return label || fallback;
51
74
  }
75
+ const text = (value, max = 200) => (typeof value === 'string' && value.trim() ? value.trim().slice(0, max) : null);
76
+
77
+ /**
78
+ * Claude Code's global config file (`.claude.json`, where `oauthAccount` lives)
79
+ * for a config directory. An explicit CLAUDE_CONFIG_DIR keeps it inside that
80
+ * directory; the ambient default keeps it in the home directory, next to
81
+ * ~/.claude rather than inside it (the CLI's own layout).
82
+ */
83
+ export function claudeGlobalConfigFile(configDir, { ambient = false, homeDir = homedir() } = {}) {
84
+ return ambient ? resolve(homeDir, '.claude.json') : resolve(configDir, '.claude.json');
85
+ }
52
86
 
53
87
  /** Read the OAuth identity Claude Code records after login. */
54
- export function readClaudeIdentity(configDir) {
55
- if (!configDir) return null;
56
- const data = readJson(resolve(configDir, '.claude.json'), null);
88
+ export function readClaudeIdentity(configDir, file = null) {
89
+ if (!configDir && !file) return null;
90
+ const data = readJson(file || resolve(configDir, '.claude.json'), null);
57
91
  const account = data?.oauthAccount;
58
92
  if (!account || typeof account !== 'object') return null;
59
93
  return {
@@ -63,58 +97,312 @@ export function readClaudeIdentity(configDir) {
63
97
  };
64
98
  }
65
99
 
100
+ /** What an API key is shown as: never the key, never its prefix. */
101
+ export function apiKeyHint(key) {
102
+ return typeof key === 'string' && key.length >= 8 ? `…${key.slice(-4)}` : null;
103
+ }
104
+
105
+ /**
106
+ * The mode an account's sessions use, from its stored choice and the
107
+ * credentials it holds. `subscription` is true (signed in), false (confirmed
108
+ * signed out) or null (not known yet). A stored key with an unconfirmed
109
+ * subscription keeps the key unless the user chose the subscription; both
110
+ * present with no choice keeps the key, which is what SynaBun did before modes.
111
+ */
112
+ export function resolveClaudeAuth({ preferredMode = null, hasKey = false, subscription = null } = {}) {
113
+ const preferred = CLAUDE_AUTH_MODES.includes(preferredMode) ? preferredMode : null;
114
+ let mode = null;
115
+ if (hasKey && subscription === true) mode = preferred || 'apiKey';
116
+ else if (hasKey) mode = preferred === 'subscription' && subscription !== false ? 'subscription' : 'apiKey';
117
+ else if (subscription !== false) mode = 'subscription';
118
+ const signedIn = mode === 'apiKey' ? true : mode === 'subscription' ? subscription : false;
119
+ return { mode, signedIn, switchable: hasKey && subscription === true };
120
+ }
121
+
122
+ /** A launch environment applied to a base: inherited credentials out, the account's in. */
123
+ export function applyClaudeLaunchEnv(base, launch) {
124
+ const env = { ...(base || {}) };
125
+ for (const key of launch?.unset || CLAUDE_CREDENTIAL_ENV) delete env[key];
126
+ return Object.assign(env, launch?.env || {});
127
+ }
128
+
66
129
  export function createClaudeAccounts({
67
130
  dataHome,
68
131
  homeDir = homedir(),
132
+ env = process.env,
69
133
  accountsRoot = null,
70
134
  log = () => {},
71
135
  now = Date.now,
72
136
  pollIntervalMs = 2000,
137
+ // async (id) => Claude Code's own `auth status --json` for that account
138
+ // ({ loggedIn, email, orgName, subscriptionType, authMethod }), or null.
139
+ readNativeStatus = null,
140
+ statusTimeoutMs = 20_000,
73
141
  } = {}) {
74
142
  if (!dataHome) throw new Error('createClaudeAccounts requires dataHome');
75
143
  const registryPath = resolve(dataHome, 'data', 'claude-accounts.json');
76
- const defaultHome = resolve(homeDir, '.claude');
144
+ const configDir = env.CLAUDE_CONFIG_DIR;
145
+ const defaultHome = resolve(configDir || resolve(homeDir, '.claude'));
146
+ const defaultConfigFile = claudeGlobalConfigFile(defaultHome, { ambient: !configDir, homeDir });
77
147
  const root = accountsRoot || resolve(homeDir, '.claude-accounts');
148
+ // id → the last answer of Claude Code's own status check (never a credential).
149
+ const nativeStatus = new Map();
150
+ const refreshing = new Map();
151
+ const listeners = new Set();
152
+ let nativeReader = typeof readNativeStatus === 'function' ? readNativeStatus : null;
78
153
 
154
+ function configFileOf(id, home) { return id === DEFAULT_ACCOUNT_ID ? defaultConfigFile : resolve(home, '.claude.json'); }
79
155
  function defaultAccount() {
80
- const identity = readClaudeIdentity(defaultHome);
156
+ const identity = readClaudeIdentity(defaultHome, defaultConfigFile);
81
157
  return { id: DEFAULT_ACCOUNT_ID, label: 'Default', email: identity?.email || null, organization: identity?.organization || null, home: defaultHome, builtin: true, createdAt: null };
82
158
  }
83
159
  function load() {
84
160
  const data = readJson(registryPath, null);
85
161
  const accounts = Array.isArray(data?.accounts) ? data.accounts.filter((row) => row?.id && row.id !== DEFAULT_ACCOUNT_ID) : [];
86
- return { accounts, defaultId: DEFAULT_ACCOUNT_ID };
162
+ const defaults = data?.default && typeof data.default === 'object' ? data.default : {};
163
+ return { accounts, defaultId: DEFAULT_ACCOUNT_ID, default: defaults };
164
+ }
165
+ function save(data) {
166
+ writeJsonAtomic(registryPath, { accounts: data.accounts, defaultId: DEFAULT_ACCOUNT_ID, default: data.default || {}, updatedAt: new Date(now()).toISOString() });
167
+ }
168
+ function storedMode(data, id) {
169
+ const mode = id === DEFAULT_ACCOUNT_ID ? data.default?.authMode : data.accounts.find((row) => row.id === id)?.authMode;
170
+ return CLAUDE_AUTH_MODES.includes(mode) ? mode : null;
87
171
  }
88
- function save(data) { writeJsonAtomic(registryPath, { accounts: data.accounts, defaultId: DEFAULT_ACCOUNT_ID, updatedAt: new Date(now()).toISOString() }); }
89
172
 
90
- function list() {
173
+ // Terminal login needs only registry metadata, without reading CLI identity files.
174
+ function profileFor(id) {
175
+ if (id === DEFAULT_ACCOUNT_ID) return { id, home: defaultHome };
176
+ return load().accounts.find(row => row.id === id) || null;
177
+ }
178
+ function keyHome(id) {
179
+ const row = profileFor(id);
180
+ if (!row || row.id !== id) throw new ClaudeAccountError('ACCOUNT_NOT_FOUND', 'Unknown Claude account', 404);
181
+ return row.home;
182
+ }
183
+ function readApiKey(id) { return readClaudeProfileApiKey(keyHome(id)); }
184
+ function saveApiKey(id, key) {
185
+ saveClaudeProfileApiKey(keyHome(id), key);
186
+ // A key just pasted is what the user wants this account to use.
187
+ writeMode(id, 'apiKey');
188
+ emit(id);
189
+ }
190
+ function clearApiKey(id) {
191
+ clearClaudeProfileApiKey(keyHome(id));
91
192
  const data = load();
92
- const rows = data.accounts.map((row) => {
93
- const identity = readClaudeIdentity(row.home);
94
- return { ...row, email: identity?.email || row.email || null, organization: identity?.organization || row.organization || null, builtin: false, loggedIn: !!identity?.email };
95
- });
96
- const base = defaultAccount();
97
- return [{ ...base, loggedIn: !!base.email }, ...rows];
193
+ if (storedMode(data, id) === 'apiKey') writeMode(id, null, data);
194
+ emit(id);
98
195
  }
99
- function listForClient() {
100
- return list().map(({ id, label, email, organization, builtin, loggedIn, createdAt }) => ({ id, label, email, organization, isDefault: id === DEFAULT_ACCOUNT_ID, builtin, loggedIn, createdAt }));
196
+ // The key itself only for a launch; everything else sees whether one is there.
197
+ function keyState(id) {
198
+ try {
199
+ const key = readApiKey(id);
200
+ return key ? { present: true, hint: apiKeyHint(key), error: null, key } : { present: false, hint: null, error: null, key: null };
201
+ } catch (error) {
202
+ if (error?.code === 'ACCOUNT_NOT_FOUND') throw error;
203
+ return { present: false, hint: null, error: 'unreadable', key: null };
204
+ }
205
+ }
206
+ function fileChangedSince(file, at) {
207
+ try { return statSync(file).mtimeMs > at; } catch { return false; }
208
+ }
209
+ function subscriptionOf(id, home) {
210
+ const file = configFileOf(id, home);
211
+ const status = nativeStatus.get(id);
212
+ // Claude Code rewrites its global config when it signs in or out, so a file
213
+ // newer than the last check outranks that check until it is asked again.
214
+ if (status && !fileChangedSince(file, status.wall)) {
215
+ return {
216
+ signedIn: status.loggedIn, email: status.email, organization: status.organization,
217
+ plan: status.plan, method: status.method, checkedAt: status.checkedAt, checked: true,
218
+ };
219
+ }
220
+ // Its own record of an OAuth login, if any: a login is likely, its absence proves nothing.
221
+ const identity = readClaudeIdentity(home, file);
222
+ const signedIn = identity?.email ? true : status?.loggedIn === false ? false : null;
223
+ return {
224
+ signedIn, email: identity?.email || null, organization: identity?.organization || null,
225
+ plan: identity?.email && status?.loggedIn ? status.plan : null, method: identity?.email ? 'claude.ai' : null,
226
+ checkedAt: status?.checkedAt || null, checked: false,
227
+ };
101
228
  }
102
- function get(id) {
229
+ function describeProfile(profile, data = load()) {
230
+ const id = profile.id;
231
+ const isDefault = id === DEFAULT_ACCOUNT_ID;
232
+ const base = isDefault ? defaultAccount() : profile;
233
+ const key = keyState(id);
234
+ const subscription = subscriptionOf(id, base.home);
235
+ const preferredMode = storedMode(data, id);
236
+ const auth = resolveClaudeAuth({ preferredMode, hasKey: key.present, subscription: subscription.signedIn });
237
+ const identity = auth.mode === 'apiKey' ? key.hint : auth.mode === 'subscription' ? (subscription.email || subscription.organization) : null;
238
+ const row = {
239
+ id, provider: 'claude', label: isDefault ? 'Default' : sanitizeLabel(base.label, 'Claude account'), home: base.home,
240
+ isDefault, builtin: isDefault, createdAt: base.createdAt || null,
241
+ mode: auth.mode, preferredMode, signedIn: auth.signedIn, switchable: auth.switchable, identity: identity || null,
242
+ subscription: { signedIn: subscription.signedIn, email: subscription.email, organization: subscription.organization, plan: subscription.plan, method: subscription.method, checked: subscription.checked, checkedAt: subscription.checkedAt },
243
+ apiKey: { present: key.present, hint: key.hint, error: key.error },
244
+ // The fields older panels read, from the same state.
245
+ loggedIn: auth.signedIn,
246
+ email: auth.mode === 'subscription' ? subscription.email : null,
247
+ organization: auth.mode === 'subscription' ? subscription.organization : null,
248
+ authMethod: auth.mode === 'apiKey' ? 'apiKey' : auth.mode === 'subscription' && subscription.signedIn ? (subscription.method || 'claude.ai') : null,
249
+ // Kept in the registry for older readers; the live record wins.
250
+ registryEmail: isDefault ? null : profile.email || null,
251
+ };
252
+ return { row, key: key.key };
253
+ }
254
+ function describe(id) {
103
255
  const wanted = sanitizeId(id) || DEFAULT_ACCOUNT_ID;
104
- if (wanted === DEFAULT_ACCOUNT_ID) return { ...defaultAccount(), loggedIn: !!defaultAccount().email };
105
- return list().find((row) => row.id === wanted) || null;
256
+ const data = load();
257
+ const profile = wanted === DEFAULT_ACCOUNT_ID ? { id: DEFAULT_ACCOUNT_ID, home: defaultHome } : data.accounts.find((row) => row.id === wanted);
258
+ if (!profile) return null;
259
+ return describeProfile(profile, data).row;
260
+ }
261
+ function list() {
262
+ const data = load();
263
+ return [{ id: DEFAULT_ACCOUNT_ID, home: defaultHome }, ...data.accounts].map((profile) => describeProfile(profile, data).row);
264
+ }
265
+ function clientRow(row) {
266
+ const { home, registryEmail, ...rest } = row;
267
+ return rest;
106
268
  }
269
+ function listForClient() { return list().map(clientRow); }
270
+ function describeForClient(id) { const row = describe(id); return row ? clientRow(row) : null; }
271
+ function get(id) { return describe(id); }
107
272
  const find = get;
108
273
  function homeFor(id) {
109
- const account = get(id);
110
- if (!account || account.id === DEFAULT_ACCOUNT_ID) return null;
111
- return account.home;
274
+ const wanted = sanitizeId(id);
275
+ if (!wanted || wanted === DEFAULT_ACCOUNT_ID) return null;
276
+ return profileFor(wanted)?.home || null;
112
277
  }
113
278
  function envFor(id) {
279
+ // Native auth strips ambient config/auth variables before applying this
280
+ // account's environment. Restore Default's explicit config location too.
281
+ if (id === DEFAULT_ACCOUNT_ID && configDir) return { CLAUDE_CONFIG_DIR: defaultHome };
114
282
  const home = homeFor(id);
115
283
  return home ? { CLAUDE_CONFIG_DIR: home } : {};
116
284
  }
117
285
 
286
+ /**
287
+ * How a Claude Code process runs under an account: `unset` the inherited
288
+ * credential variables, then add `env` (its config directory and, in API-key
289
+ * mode, its key). `env` can carry a secret: pass it to the spawn only, never
290
+ * to a log, a response, an event or a file.
291
+ */
292
+ function launchFor(id) {
293
+ const wanted = typeof id === 'string' && id ? id : DEFAULT_ACCOUNT_ID;
294
+ const data = load();
295
+ const profile = wanted === DEFAULT_ACCOUNT_ID ? { id: DEFAULT_ACCOUNT_ID, home: defaultHome } : data.accounts.find((row) => row.id === wanted);
296
+ if (!profile || !profile.home) {
297
+ return { ok: false, accountId: wanted, code: 'account_unavailable', message: 'That Claude account is no longer set up in SynaBun.' };
298
+ }
299
+ const { row, key } = describeProfile(profile, data);
300
+ const launchEnv = {};
301
+ if (wanted !== DEFAULT_ACCOUNT_ID) launchEnv.CLAUDE_CONFIG_DIR = profile.home;
302
+ else if (configDir) launchEnv.CLAUDE_CONFIG_DIR = defaultHome;
303
+ if (row.mode === 'apiKey' && key) launchEnv.ANTHROPIC_API_KEY = key;
304
+ return {
305
+ ok: true, accountId: wanted, label: row.label, mode: row.mode, signedIn: row.signedIn, identity: row.identity,
306
+ env: launchEnv, unset: [...CLAUDE_CREDENTIAL_ENV],
307
+ };
308
+ }
309
+
310
+ /** Why an account cannot run a session now ('' when it can, or when that is not known yet). */
311
+ function unusableReason(id) {
312
+ const row = describe(id);
313
+ if (!row) return 'That Claude account is no longer set up in SynaBun.';
314
+ if (row.apiKey.error && row.mode !== 'subscription') return `The API key saved for ${row.label} cannot be read. Save it again.`;
315
+ if (row.signedIn === false) return `${row.label} is signed out. Sign in with Claude or add an API key to it first.`;
316
+ return '';
317
+ }
318
+
319
+ function writeMode(id, mode, data = load()) {
320
+ if (id === DEFAULT_ACCOUNT_ID) {
321
+ data.default = { ...(data.default || {}) };
322
+ if (mode) data.default.authMode = mode; else delete data.default.authMode;
323
+ } else {
324
+ const account = data.accounts.find((row) => row.id === id);
325
+ if (!account) throw new ClaudeAccountError('ACCOUNT_NOT_FOUND', `Claude account not found: ${id}`, 404);
326
+ if (mode) account.authMode = mode; else delete account.authMode;
327
+ }
328
+ save(data);
329
+ }
330
+ /** Choose what an account's sessions use. The chosen credential must be there. */
331
+ function setMode(id, mode) {
332
+ const wanted = sanitizeId(id) || DEFAULT_ACCOUNT_ID;
333
+ if (!CLAUDE_AUTH_MODES.includes(mode)) throw new ClaudeAccountError('MODE_INVALID', 'Choose "subscription" or "apiKey".', 400);
334
+ const row = describe(wanted);
335
+ if (!row) throw new ClaudeAccountError('ACCOUNT_NOT_FOUND', `Claude account not found: ${id}`, 404);
336
+ if (mode === 'apiKey' && !row.apiKey.present) throw new ClaudeAccountError('MODE_NEEDS_CREDENTIAL', `Add an API key to ${row.label} first.`, 409);
337
+ if (mode === 'subscription' && row.subscription.signedIn === false) throw new ClaudeAccountError('MODE_NEEDS_CREDENTIAL', `Sign in to ${row.label} with Claude first.`, 409);
338
+ writeMode(wanted, mode);
339
+ emit(wanted);
340
+ return describe(wanted);
341
+ }
342
+
343
+ /** Remember what Claude Code said about an account's own login. */
344
+ function noteNativeStatus(id, status) {
345
+ if (!status || typeof status.loggedIn !== 'boolean') return;
346
+ nativeStatus.set(id, {
347
+ loggedIn: status.loggedIn,
348
+ email: status.loggedIn ? text(status.email) : null,
349
+ organization: status.loggedIn ? text(status.orgName ?? status.organization) : null,
350
+ plan: status.loggedIn ? text(status.subscriptionType ?? status.plan, 40) : null,
351
+ method: status.loggedIn ? text(status.authMethod ?? status.method, 40) : null,
352
+ checkedAt: now(),
353
+ // Compared with the config file's modification time: the clock files use.
354
+ wall: Date.now(),
355
+ });
356
+ }
357
+ function forgetNativeStatus(id) { nativeStatus.delete(id); }
358
+ function setNativeStatusReader(reader) { nativeReader = typeof reader === 'function' ? reader : null; }
359
+ function signature(row) { return row ? JSON.stringify([row.label, row.mode, row.signedIn, row.identity, row.subscription.signedIn, row.subscription.email, row.subscription.plan, row.apiKey.present, row.apiKey.hint, row.preferredMode]) : ''; }
360
+ function checkOne(id) {
361
+ if (refreshing.has(id)) return refreshing.get(id);
362
+ const job = (async () => {
363
+ let timer;
364
+ try {
365
+ const status = await Promise.race([
366
+ Promise.resolve().then(() => nativeReader(id)),
367
+ new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('timeout')), statusTimeoutMs); timer.unref?.(); }),
368
+ ]);
369
+ if (status && typeof status.loggedIn === 'boolean' && profileFor(id)) noteNativeStatus(id, status);
370
+ } catch (error) {
371
+ log('claude-accounts', `status check failed for ${id}: ${String(error?.message || error).slice(0, 120)}`);
372
+ } finally { clearTimeout(timer); refreshing.delete(id); }
373
+ })();
374
+ refreshing.set(id, job);
375
+ return job;
376
+ }
377
+ /**
378
+ * Ask Claude Code for the subscription state of these accounts (all by
379
+ * default): `force` asks again, otherwise only an answer older than
380
+ * `maxAgeMs` or none at all. Listeners hear about every account that changed.
381
+ */
382
+ async function refreshStatus(ids = null, { force = false, maxAgeMs = STATUS_MAX_AGE_MS } = {}) {
383
+ if (!nativeReader) return list();
384
+ const wanted = (Array.isArray(ids) ? ids : [DEFAULT_ACCOUNT_ID, ...load().accounts.map((row) => row.id)]).filter((id) => profileFor(id));
385
+ const before = new Map(wanted.map((id) => [id, signature(describe(id))]));
386
+ await Promise.all(wanted.map((id) => {
387
+ const known = nativeStatus.get(id);
388
+ if (!force && known && now() - known.checkedAt < maxAgeMs && !stale(id, maxAgeMs)) return null;
389
+ return checkOne(id);
390
+ }));
391
+ for (const id of wanted) if (before.get(id) !== signature(describe(id))) emit(id);
392
+ return list();
393
+ }
394
+ function stale(id, maxAgeMs = STATUS_MAX_AGE_MS) {
395
+ const known = nativeStatus.get(id);
396
+ if (!known || now() - known.checkedAt >= maxAgeMs) return true;
397
+ const profile = profileFor(id);
398
+ return !!profile && fileChangedSince(configFileOf(id, profile.home), known.wall);
399
+ }
400
+
401
+ function subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); }
402
+ function emit(accountId, extra = {}) {
403
+ for (const listener of [...listeners]) { try { listener({ accountId, ...extra }); } catch {} }
404
+ }
405
+
118
406
  function linkShared(home) {
119
407
  const linked = [];
120
408
  for (const name of SHARED_LINKS) {
@@ -136,15 +424,17 @@ export function createClaudeAccounts({
136
424
  function seedClaudeJson(home) {
137
425
  const target = resolve(home, '.claude.json');
138
426
  if (existsSync(target)) return false;
139
- const source = readJson(resolve(defaultHome, '.claude.json'), {}) || {};
427
+ // Only shared preferences from Default's global config; never its login.
428
+ const source = readJson(defaultConfigFile, {}) || {};
140
429
  const seeded = { hasCompletedOnboarding: true };
141
430
  for (const key of CLAUDE_JSON_WHITELIST) if (source[key] !== undefined) seeded[key] = source[key];
142
431
  writeJsonAtomic(target, seeded);
143
432
  return true;
144
433
  }
145
434
  function seedHome(id) {
146
- const account = get(id);
147
- if (!account || account.id === DEFAULT_ACCOUNT_ID) throw new ClaudeAccountError('ACCOUNT_NOT_FOUND', `Claude account not found: ${id}`, 404);
435
+ const wanted = sanitizeId(id);
436
+ const account = wanted && wanted !== DEFAULT_ACCOUNT_ID ? profileFor(wanted) : null;
437
+ if (!account) throw new ClaudeAccountError('ACCOUNT_NOT_FOUND', `Claude account not found: ${id}`, 404);
148
438
  mkdirSync(resolve(account.home, 'projects'), { recursive: true });
149
439
  mkdirSync(resolve(account.home, 'todos'), { recursive: true });
150
440
  const linked = linkShared(account.home);
@@ -156,10 +446,14 @@ export function createClaudeAccounts({
156
446
  const data = load();
157
447
  const id = `cacct-${now().toString(36)}${Math.floor(Math.random() * 1296).toString(36).padStart(2, '0')}`;
158
448
  const account = { id, label: sanitizeLabel(label, `Claude ${data.accounts.length + 2}`), email: null, organization: null, home: resolve(root, id), createdAt: new Date(now()).toISOString() };
449
+ if (label && String(label).trim()) account.labelCustom = true;
159
450
  data.accounts.push(account);
160
451
  save(data);
161
452
  const seeded = seedHome(id);
162
- return { ...account, isDefault: false, builtin: false, loggedIn: false, seeded };
453
+ // A directory made a moment ago holds no login: nothing to ask Claude Code.
454
+ noteNativeStatus(id, { loggedIn: false });
455
+ emit(id, { created: true });
456
+ return { ...describe(id), seeded };
163
457
  }
164
458
  function rename(id, label) {
165
459
  const wanted = sanitizeId(id);
@@ -168,7 +462,9 @@ export function createClaudeAccounts({
168
462
  const account = data.accounts.find((row) => row.id === wanted);
169
463
  if (!account) throw new ClaudeAccountError('ACCOUNT_NOT_FOUND', `Claude account not found: ${id}`, 404);
170
464
  account.label = sanitizeLabel(label, account.label);
465
+ account.labelCustom = true;
171
466
  save(data);
467
+ emit(wanted);
172
468
  return get(wanted);
173
469
  }
174
470
  function remove(id, { inUse = () => null } = {}) {
@@ -181,9 +477,11 @@ export function createClaudeAccounts({
181
477
  if (reason) throw new ClaudeAccountError('ACCOUNT_IN_USE', typeof reason === 'string' ? reason : `Claude account ${wanted} is in use`, 409);
182
478
  const [account] = data.accounts.splice(index, 1);
183
479
  save(data);
480
+ nativeStatus.delete(wanted);
184
481
  if (account.home && resolve(account.home).startsWith(resolve(root))) {
185
482
  try { rmSync(account.home, { recursive: true, force: true }); } catch (error) { log('claude-accounts', `remove home failed: ${error?.message || error}`); }
186
483
  }
484
+ emit(wanted, { removed: true });
187
485
  return { ok: true, removed: account.id };
188
486
  }
189
487
  function recordIdentity(id) {
@@ -201,6 +499,16 @@ export function createClaudeAccounts({
201
499
  }
202
500
  return { ...account, ...identity };
203
501
  }
502
+ /** A subscription sign-in just finished: it is what the account uses from now on. */
503
+ function noteSubscriptionSignIn(id, status = null) {
504
+ const wanted = sanitizeId(id) || DEFAULT_ACCOUNT_ID;
505
+ if (!profileFor(wanted)) return null;
506
+ if (status) noteNativeStatus(wanted, status);
507
+ if (wanted !== DEFAULT_ACCOUNT_ID) recordIdentity(wanted);
508
+ writeMode(wanted, 'subscription');
509
+ emit(wanted);
510
+ return describe(wanted);
511
+ }
204
512
  /** Poll for the OAuth identity to appear after a login terminal was opened. */
205
513
  function watchLogin(id, { onLogin = () => {}, onTimeout = () => {}, timeoutMs = 10 * 60_000 } = {}) {
206
514
  const started = now();
@@ -223,8 +531,11 @@ export function createClaudeAccounts({
223
531
  }
224
532
 
225
533
  return {
226
- registryPath, defaultHome, root,
227
- list, listForClient, get, find, homeFor, envFor, create, rename, remove, seedHome, recordIdentity, watchLogin, loginCommand,
228
- readIdentity: (id) => readClaudeIdentity(id === DEFAULT_ACCOUNT_ID ? defaultHome : homeFor(id)),
534
+ registryPath, defaultHome, defaultConfigFile, root,
535
+ list, listForClient, describe, describeForClient, profileFor, readApiKey, saveApiKey, clearApiKey, get, find, homeFor, envFor,
536
+ launchFor, unusableReason, setMode, noteNativeStatus, forgetNativeStatus, noteSubscriptionSignIn, setNativeStatusReader,
537
+ refreshStatus, stale, subscribe,
538
+ create, rename, remove, seedHome, recordIdentity, watchLogin, loginCommand,
539
+ readIdentity: (id) => (id === DEFAULT_ACCOUNT_ID ? readClaudeIdentity(defaultHome, defaultConfigFile) : readClaudeIdentity(homeFor(id))),
229
540
  };
230
541
  }