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
@@ -0,0 +1,239 @@
1
+ // ── The File Explorer's watcher ──
2
+ // Tells the page which of the folders it shows changed on disk, and when the
3
+ // git state of the project may have, without the page asking again and again.
4
+ // It costs nothing while nothing changes: the operating system reports changes,
5
+ // nothing is polled.
6
+ //
7
+ // Two ways to watch, by what the system offers:
8
+ // recursive one watcher on the project's folder (macOS and Windows have it
9
+ // natively). Every change below the folder arrives; the ones the
10
+ // page did not ask about are dropped here.
11
+ // folders one watcher per folder the page shows (Linux, where a recursive
12
+ // watch is a watch per sub-folder), plus one on .git.
13
+ // Changes are collected for a moment and sent together. Dependencies are
14
+ // injected: the tests drive it with real folders and with a fake `watch`.
15
+ //
16
+ // A watcher that exists is not a watch that works: the system may have no
17
+ // watches left (ENOSPC on Linux), or lose one it had. `state()` says what is
18
+ // really watched, and `onState` when that changes, so the page can read again
19
+ // by itself where nothing will tell it.
20
+ //
21
+ // A watch that failed is not asked for again at once. A watch that is lost
22
+ // each time it is opened would otherwise be opened, lost and opened again as
23
+ // fast as the page and the system can answer one another. It is tried again
24
+ // after a wait that doubles (a second, two, four, ... a minute at most) and
25
+ // starts over once a watch has held for a minute; until then it stays a watch
26
+ // that is not there, whatever folders the page shows meanwhile.
27
+
28
+ import { existsSync, watch as fsWatch } from 'node:fs';
29
+ import { dirname, join, relative, sep } from 'node:path';
30
+
31
+ export const MAX_WATCHED_DIRS = 600;
32
+ /** A watch that failed is tried again after this long, then after twice that, and so on. */
33
+ export const RETRY_FIRST_MS = 1000;
34
+ /** ... and never less often than this. */
35
+ export const RETRY_MAX_MS = 60000;
36
+ /** A watch that held this long was no part of a run of failures: the next wait is the first one again. */
37
+ export const RETRY_QUIET_MS = 60000;
38
+ /** The files of .git whose change means "commit, stage, checkout or merge": the git state is read again. */
39
+ const GIT_STATE_FILES = /^(index|HEAD|ORIG_HEAD|MERGE_HEAD|CHERRY_PICK_HEAD|REVERT_HEAD|packed-refs)$/;
40
+ /** A folder that is not there (any more) is no watch that failed: its next listing says what is true. */
41
+ const NOT_THERE = new Set(['ENOENT', 'ENOTDIR']);
42
+
43
+ /** Which way this system is watched. */
44
+ export function watchStrategy(platform = process.platform) {
45
+ return platform === 'darwin' || platform === 'win32' ? 'recursive' : 'folders';
46
+ }
47
+
48
+ /**
49
+ * @param {object} o
50
+ * @param {string} o.root the project's folder, canonical
51
+ * @param {'recursive' | 'folders'} [o.strategy]
52
+ * @param {(change: { dirs: string[], git: boolean }) => void} o.onChange `dirs`: watched folders (canonical) whose
53
+ * entries changed; `git`: the git state may have changed
54
+ * @param {(error: Error) => void} [o.onError] the watch on the project's folder was lost
55
+ * @param {(state: { scope: 'tree' | 'folders', live: 'full' | 'partial' | 'none' }) => void} [o.onState] what is
56
+ * really watched changed (see `state()`); not called for the state it starts in
57
+ * @param {(relativePath: string) => boolean} [o.ignoresGit] a path git ignores: a change there is no change of state
58
+ * @param {typeof fsWatch} [o.watch] @param {(path: string) => boolean} [o.exists]
59
+ * @param {number} [o.debounceMs] @param {number} [o.maxWaitMs]
60
+ * @param {() => number} [o.now] @param {(fn: () => void, ms: number) => any} [o.setTimer] @param {(timer: any) => void} [o.clearTimer]
61
+ * the clock of the waits between two attempts at a watch that failed (a test moves it by hand)
62
+ * @returns {{ setDirs: (dirs: string[]) => void, close: () => void, count: () => number, strategy: string,
63
+ * state: () => { scope: 'tree' | 'folders', live: 'full' | 'partial' | 'none' } }}
64
+ */
65
+ export function createTreeWatcher({
66
+ root, strategy = watchStrategy(), onChange, onError = () => {}, onState = () => {}, ignoresGit = () => false,
67
+ watch = fsWatch, exists = existsSync, debounceMs = 120, maxWaitMs = 500,
68
+ now = () => Date.now(), setTimer = (fn, ms) => setTimeout(fn, ms), clearTimer = (timer) => clearTimeout(timer),
69
+ }) {
70
+ let closed = false;
71
+ let wanted = new Set([root]); // the folders the page shows
72
+ let capped = false; // the page shows more folders than are watched
73
+ const watchers = new Map(); // what is watched → FSWatcher
74
+ const failed = new Set(); // what should be watched and is not (no watches left, a watch that was lost)
75
+ const openedAt = new Map(); // what is watched → when that watch was opened
76
+ let retryMs = 0; // the wait that is running, or ran last
77
+ let retryAt = 0; // nothing that failed is asked for again before this
78
+ let retryTimer = null;
79
+ let refused = false; // the system refused a watch in the pass that is under way
80
+ let said = null; // the `live` that was last reported
81
+ const pending = new Set();
82
+ let pendingGit = false;
83
+ let timer = null;
84
+ let firstAt = 0;
85
+
86
+ const slashed = (path) => (sep === '/' ? path : path.split(sep).join('/'));
87
+
88
+ function flush() {
89
+ timer = null;
90
+ if (closed || (!pending.size && !pendingGit)) return;
91
+ const change = { dirs: [...pending], git: pendingGit };
92
+ pending.clear();
93
+ pendingGit = false;
94
+ try { onChange(change); } catch { /* a listener's failure is not the watcher's */ }
95
+ }
96
+
97
+ // Sent a moment after the last change, and never later than `maxWaitMs` after the first of a burst.
98
+ function schedule() {
99
+ const now = Date.now();
100
+ if (!timer) firstAt = now;
101
+ else clearTimeout(timer);
102
+ const wait = Math.max(0, Math.min(debounceMs, firstAt + maxWaitMs - now));
103
+ timer = setTimeout(flush, wait);
104
+ timer.unref?.();
105
+ }
106
+
107
+ // One change, by its path below the root ('' is the root itself).
108
+ function note(rel) {
109
+ if (closed) return;
110
+ const path = slashed(rel || '');
111
+ if (path === '.git' || path.startsWith('.git/')) {
112
+ const inside = path.slice(5);
113
+ if (GIT_STATE_FILES.test(inside) || inside.startsWith('refs/')) pendingGit = true;
114
+ } else if (!path || !ignoresGit(path)) {
115
+ pendingGit = true;
116
+ }
117
+ const parent = path.includes('/') ? join(root, dirname(path)) : root;
118
+ if (wanted.has(parent)) pending.add(parent);
119
+ // A watched folder that was itself removed or renamed: whoever shows it reads it again and finds out.
120
+ const self = path ? join(root, path) : root;
121
+ if (self !== parent && wanted.has(self)) pending.add(self);
122
+ if (pending.size || pendingGit) schedule();
123
+ }
124
+
125
+ /**
126
+ * What is really watched. `scope`: every change below the project (`tree`), or only changes in the folders the
127
+ * page shows and in git's own files (`folders`: a change inside a closed folder is not seen). `live`: `full`
128
+ * when all of that is watched, `partial` when some of it could not be, `none` when the project's folder is not.
129
+ */
130
+ const state = () => ({
131
+ scope: strategy === 'recursive' ? 'tree' : 'folders',
132
+ live: failed.has(root) || !watchers.has(root) ? 'none' : failed.size || capped ? 'partial' : 'full',
133
+ });
134
+ function report() {
135
+ if (closed) return;
136
+ const is = state();
137
+ if (is.live === said) return;
138
+ said = is.live;
139
+ try { onState(is); } catch { /* a listener's failure is not the watcher's */ }
140
+ }
141
+
142
+ // Something that should be watched is not: everything that failed is asked for again together, after a wait.
143
+ // One wait covers whatever fails while it runs; the next one is twice as long, unless the watch that failed
144
+ // had held for a while (`heldSince`): that was no part of a run of failures.
145
+ function backOff(heldSince) {
146
+ const at = now();
147
+ if (retryTimer !== null && at < retryAt) return;
148
+ if (heldSince !== undefined && at - heldSince >= RETRY_QUIET_MS) retryMs = 0;
149
+ retryMs = retryMs ? Math.min(retryMs * 2, RETRY_MAX_MS) : RETRY_FIRST_MS;
150
+ retryAt = at + retryMs;
151
+ if (retryTimer !== null) clearTimer(retryTimer);
152
+ retryTimer = setTimer(() => { retryTimer = null; if (closed) return; sync(); report(); }, retryMs);
153
+ retryTimer?.unref?.();
154
+ }
155
+
156
+ function open(key, path, options, handler) {
157
+ if (watchers.has(key) || closed) return;
158
+ // Still in its wait: the folders the page shows do not ask the system again for a watch it just refused or lost.
159
+ if (failed.has(key) && now() < retryAt) return;
160
+ let watcher;
161
+ try { watcher = watch(path, { persistent: false, ...options }, handler); }
162
+ catch (error) {
163
+ // A folder that is gone says so in its next listing. Anything else (no watches left) is a change nobody
164
+ // will be told of: it is counted, and the page reads again by itself.
165
+ if (key === root || !NOT_THERE.has(error?.code)) { failed.add(key); refused = true; }
166
+ return;
167
+ }
168
+ failed.delete(key);
169
+ openedAt.set(key, now());
170
+ watcher.on?.('error', () => {
171
+ try { watcher.close(); } catch { /* already closed */ }
172
+ if (watchers.get(key) !== watcher) return; // let go of since: not a watch that was lost
173
+ watchers.delete(key);
174
+ if (closed) return;
175
+ // Tried again after its wait; until then it is a watch that is not there.
176
+ if (key === root || exists(path)) { failed.add(key); backOff(openedAt.get(key)); }
177
+ report();
178
+ if (key === root) { try { onError(new Error('The watch on the project folder was lost.')); } catch { /* as above */ } } else note(relative(root, path));
179
+ });
180
+ watchers.set(key, watcher);
181
+ }
182
+
183
+ function sync() {
184
+ if (closed) return;
185
+ refused = false;
186
+ try { place(); } finally { if (refused) backOff(); }
187
+ }
188
+
189
+ function place() {
190
+ if (strategy === 'recursive') {
191
+ open(root, root, { recursive: true }, (type, name) => note(name ? String(name) : ''));
192
+ return;
193
+ }
194
+ for (const [key, watcher] of watchers) {
195
+ if (key === '.git' || wanted.has(key)) continue;
196
+ try { watcher.close(); } catch { /* already closed */ }
197
+ watchers.delete(key);
198
+ openedAt.delete(key);
199
+ }
200
+ for (const key of failed) if (key !== '.git' && !wanted.has(key)) failed.delete(key);
201
+ for (const dir of wanted) {
202
+ const rel = relative(root, dir);
203
+ open(dir, dir, {}, (type, name) => note(name ? join(rel, String(name)) : rel));
204
+ }
205
+ open('.git', join(root, '.git'), {}, (type, name) => { if (name) note(join('.git', String(name))); });
206
+ }
207
+
208
+ sync();
209
+ said = state().live;
210
+
211
+ return {
212
+ strategy,
213
+ /** The folders the page shows now (canonical paths, the root or below it). */
214
+ setDirs(dirs) {
215
+ const next = new Set([root]);
216
+ capped = false;
217
+ for (const dir of dirs || []) {
218
+ if (typeof dir !== 'string' || !(dir === root || dir.startsWith(root.endsWith(sep) ? root : root + sep)) || next.has(dir)) continue;
219
+ if (next.size >= MAX_WATCHED_DIRS) { capped = strategy !== 'recursive'; break; }
220
+ next.add(dir);
221
+ }
222
+ wanted = next;
223
+ sync();
224
+ report();
225
+ },
226
+ state,
227
+ count: () => watchers.size,
228
+ close() {
229
+ closed = true;
230
+ if (timer) clearTimeout(timer);
231
+ timer = null;
232
+ if (retryTimer !== null) clearTimer(retryTimer);
233
+ retryTimer = null;
234
+ for (const watcher of watchers.values()) { try { watcher.close(); } catch { /* already closed */ } }
235
+ watchers.clear();
236
+ pending.clear();
237
+ },
238
+ };
239
+ }
@@ -0,0 +1,339 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — bzip2, read
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Git for Windows publishes its full installation as a .tar.bz2, and Node has
6
+ // no bzip2. A Windows 10 tar.exe may not have one either (older builds stop at
7
+ // "unable to run program bzip2 -d"), so SynaBun reads the file itself, the same
8
+ // way on the build machine and on the user's: this decoder, then the tar
9
+ // reader in install.js.
10
+ //
11
+ // The whole format of bzip2 1.0 (Julian Seward's): one stream or several
12
+ // concatenated, each "BZh1".."BZh9", blocks of Huffman-coded move-to-front
13
+ // values with their runs, the inverse Burrows-Wheeler transform, the final run
14
+ // decoding. Every block's CRC is compared with the one it carries, and every
15
+ // stream's combined CRC with its trailer: a block that decodes to anything else
16
+ // than what was compressed stops the read. A "randomised" block (written only
17
+ // by bzip2 0.9.0, before 1998) is refused rather than guessed at.
18
+ //
19
+ // What it hands out is the decompressed bytes, block by block
20
+ // (bunzip2Blocks: a generator, so a caller can stop at any block, and see how
21
+ // far into the file it got). Every problem is a Bzip2Error.
22
+ //
23
+ // No imports: install.js and the extraction worker load it, and so does a
24
+ // build, from the application it stages.
25
+
26
+ export class Bzip2Error extends Error {
27
+ constructor(message) {
28
+ super(message);
29
+ this.name = 'Bzip2Error';
30
+ this.code = 'unpack';
31
+ }
32
+ }
33
+
34
+ const BLOCK_MAGIC_HI = 0x314159; // π
35
+ const BLOCK_MAGIC_LO = 0x265359;
36
+ const END_MAGIC_HI = 0x177245; // √π
37
+ const END_MAGIC_LO = 0x385090;
38
+ const MAX_GROUPS = 6;
39
+ const MAX_ALPHA = 258;
40
+ const MAX_CODE_LEN = 20;
41
+ const MAX_SELECTORS = 18002;
42
+ const RUNA = 0;
43
+ const RUNB = 1;
44
+ const GROUP_SIZE = 50;
45
+
46
+ /** CRC-32 as bzip2 has it: polynomial 0x04c11db7, most significant bit first. */
47
+ const CRC_TABLE = (() => {
48
+ const table = new Uint32Array(256);
49
+ for (let i = 0; i < 256; i++) {
50
+ let c = i << 24;
51
+ for (let k = 0; k < 8; k++) c = (c & 0x80000000) ? (c << 1) ^ 0x04c11db7 : c << 1;
52
+ table[i] = c >>> 0;
53
+ }
54
+ return table;
55
+ })();
56
+
57
+ /** Reads a Buffer bit by bit, most significant bit first. */
58
+ class BitReader {
59
+ constructor(buffer) {
60
+ this.buffer = buffer;
61
+ this.pos = 0; // the next byte to take
62
+ this.bits = 0; // what is held, in the low `count` bits
63
+ this.count = 0;
64
+ }
65
+
66
+ /** `n` bits (1 to 24) as a number. */
67
+ read(n) {
68
+ while (this.count < n) {
69
+ if (this.pos >= this.buffer.length) throw new Bzip2Error('The archive ends inside a compressed block.');
70
+ this.bits = ((this.bits << 8) | this.buffer[this.pos++]) >>> 0;
71
+ this.count += 8;
72
+ }
73
+ this.count -= n;
74
+ const value = (this.bits >>> this.count) & ((1 << n) - 1);
75
+ this.bits &= (1 << this.count) - 1;
76
+ return value;
77
+ }
78
+
79
+ bit() {
80
+ if (this.count === 0) {
81
+ if (this.pos >= this.buffer.length) throw new Bzip2Error('The archive ends inside a compressed block.');
82
+ this.bits = this.buffer[this.pos++];
83
+ this.count = 8;
84
+ }
85
+ this.count--;
86
+ const value = (this.bits >>> this.count) & 1;
87
+ this.bits &= (1 << this.count) - 1;
88
+ return value;
89
+ }
90
+
91
+ read32() {
92
+ return ((this.read(16) << 16) | this.read(16)) >>> 0;
93
+ }
94
+
95
+ /** Drop what is left of the current byte (a stream ends on a byte boundary). */
96
+ align() {
97
+ this.bits = 0;
98
+ this.count = 0;
99
+ }
100
+
101
+ get done() {
102
+ return this.count === 0 && this.pos >= this.buffer.length;
103
+ }
104
+ }
105
+
106
+ /** Canonical Huffman decoding tables of one group, as bzip2's hbCreateDecodeTables makes them. */
107
+ function decodeTables(lengths, alphaSize) {
108
+ let minLen = 32;
109
+ let maxLen = 0;
110
+ for (let i = 0; i < alphaSize; i++) {
111
+ if (lengths[i] > maxLen) maxLen = lengths[i];
112
+ if (lengths[i] < minLen) minLen = lengths[i];
113
+ }
114
+ const perm = new Int32Array(MAX_ALPHA);
115
+ const base = new Int32Array(MAX_CODE_LEN + 2);
116
+ const limit = new Int32Array(MAX_CODE_LEN + 1).fill(-1);
117
+ let pp = 0;
118
+ for (let len = minLen; len <= maxLen; len++) for (let j = 0; j < alphaSize; j++) if (lengths[j] === len) perm[pp++] = j;
119
+ for (let i = 0; i < alphaSize; i++) base[lengths[i] + 1]++;
120
+ for (let i = 1; i < base.length; i++) base[i] += base[i - 1];
121
+ let vec = 0;
122
+ for (let len = minLen; len <= maxLen; len++) {
123
+ vec += base[len + 1] - base[len];
124
+ limit[len] = vec - 1;
125
+ vec <<= 1;
126
+ }
127
+ for (let len = minLen + 1; len <= maxLen; len++) base[len] = ((limit[len - 1] + 1) << 1) - base[len];
128
+ return { minLen, maxLen, perm, base, limit };
129
+ }
130
+
131
+ /** The next symbol of a group's code. */
132
+ function nextSymbol(reader, table) {
133
+ let len = table.minLen;
134
+ let code = reader.read(len);
135
+ while (len <= table.maxLen && code > table.limit[len]) {
136
+ len++;
137
+ code = (code << 1) | reader.bit();
138
+ }
139
+ if (len > table.maxLen) throw new Bzip2Error('The archive holds a code its tables do not know.');
140
+ const index = code - table.base[len];
141
+ if (index < 0 || index >= MAX_ALPHA) throw new Bzip2Error('The archive holds a code its tables do not know.');
142
+ return table.perm[index];
143
+ }
144
+
145
+ /**
146
+ * One block, from just after its magic: its bytes (the final run decoding done) and the CRC it carries.
147
+ * `tt` is the block's workspace, as large as the stream's block size.
148
+ */
149
+ function decodeBlock(reader, tt, blockSize) {
150
+ const storedCrc = reader.read32();
151
+ if (reader.bit()) throw new Bzip2Error('The archive has a randomised block (bzip2 0.9.0), which this reader does not read.');
152
+ const origPtr = reader.read(24);
153
+
154
+ // Which byte values the block uses.
155
+ const used = [];
156
+ const inUse16 = reader.read(16);
157
+ for (let i = 0; i < 16; i++) {
158
+ if (!(inUse16 & (0x8000 >>> i))) continue;
159
+ const bits = reader.read(16);
160
+ for (let j = 0; j < 16; j++) if (bits & (0x8000 >>> j)) used.push(i * 16 + j);
161
+ }
162
+ if (!used.length) throw new Bzip2Error('The archive has a block that uses no byte value.');
163
+ const alphaSize = used.length + 2;
164
+ const eob = used.length + 1;
165
+
166
+ // The Huffman groups and which one codes each run of fifty symbols.
167
+ const groups = reader.read(3);
168
+ if (groups < 2 || groups > MAX_GROUPS) throw new Bzip2Error('The archive has a block with an impossible number of code tables.');
169
+ const selectorCount = reader.read(15);
170
+ if (selectorCount < 1) throw new Bzip2Error('The archive has a block without code selectors.');
171
+ const mtfGroups = [0, 1, 2, 3, 4, 5].slice(0, groups);
172
+ const selectors = new Uint8Array(Math.min(selectorCount, MAX_SELECTORS));
173
+ for (let i = 0; i < selectorCount; i++) {
174
+ let j = 0;
175
+ while (reader.bit()) {
176
+ j++;
177
+ if (j >= groups) throw new Bzip2Error('The archive has a block with an impossible code selector.');
178
+ }
179
+ // Selectors past the most a block can use are read and dropped, as bzip2 1.0.8 does.
180
+ if (i >= MAX_SELECTORS) continue;
181
+ const value = mtfGroups[j];
182
+ for (let k = j; k > 0; k--) mtfGroups[k] = mtfGroups[k - 1];
183
+ mtfGroups[0] = value;
184
+ selectors[i] = value;
185
+ }
186
+
187
+ const tables = [];
188
+ for (let t = 0; t < groups; t++) {
189
+ const lengths = new Uint8Array(alphaSize);
190
+ let current = reader.read(5);
191
+ for (let i = 0; i < alphaSize; i++) {
192
+ for (;;) {
193
+ if (current < 1 || current > MAX_CODE_LEN) throw new Bzip2Error('The archive has a block with an impossible code length.');
194
+ if (!reader.bit()) break;
195
+ current += reader.bit() ? -1 : 1;
196
+ }
197
+ lengths[i] = current;
198
+ }
199
+ tables.push(decodeTables(lengths, alphaSize));
200
+ }
201
+
202
+ // The move-to-front values and their runs: the block before the inverse transform.
203
+ const counts = new Int32Array(256);
204
+ const order = new Uint8Array(256);
205
+ for (let i = 0; i < 256; i++) order[i] = i;
206
+ let length = 0;
207
+ let selector = 0;
208
+ let left = 0;
209
+ let table = null;
210
+ const next = () => {
211
+ if (left === 0) {
212
+ if (selector >= selectors.length) throw new Bzip2Error('The archive has a block that runs past its code selectors.');
213
+ table = tables[selectors[selector++]];
214
+ left = GROUP_SIZE;
215
+ }
216
+ left--;
217
+ return nextSymbol(reader, table);
218
+ };
219
+ let symbol = next();
220
+ for (;;) {
221
+ if (symbol === eob) break;
222
+ if (symbol === RUNA || symbol === RUNB) {
223
+ let run = 0;
224
+ let weight = 1;
225
+ do {
226
+ run += (symbol === RUNA ? 1 : 2) * weight;
227
+ weight <<= 1;
228
+ if (run > blockSize) throw new Bzip2Error('The archive has a block larger than its stream says blocks are.');
229
+ symbol = next();
230
+ } while (symbol === RUNA || symbol === RUNB);
231
+ const value = used[order[0]];
232
+ if (length + run > blockSize) throw new Bzip2Error('The archive has a block larger than its stream says blocks are.');
233
+ counts[value] += run;
234
+ tt.fill(value, length, length + run);
235
+ length += run;
236
+ continue;
237
+ }
238
+ if (length >= blockSize) throw new Bzip2Error('The archive has a block larger than its stream says blocks are.');
239
+ const index = symbol - 1;
240
+ const front = order[index];
241
+ order.copyWithin(1, 0, index);
242
+ order[0] = front;
243
+ const value = used[front];
244
+ counts[value]++;
245
+ tt[length++] = value;
246
+ symbol = next();
247
+ }
248
+ if (origPtr >= length) throw new Bzip2Error('The archive has a block whose start lies outside it.');
249
+
250
+ // The inverse Burrows-Wheeler transform: each byte's successor in the upper 24 bits.
251
+ const start = new Int32Array(256);
252
+ for (let i = 0, sum = 0; i < 256; i++) { start[i] = sum; sum += counts[i]; }
253
+ for (let i = 0; i < length; i++) {
254
+ const value = tt[i] & 0xff;
255
+ tt[start[value]++] |= i << 8;
256
+ }
257
+
258
+ // The final run decoding (four equal bytes and a count), with the CRC over what comes out.
259
+ let out = Buffer.allocUnsafe(Math.max(1024, length + (length >>> 2)));
260
+ let at = 0;
261
+ let crc = 0xffffffff;
262
+ const room = (need) => {
263
+ const bigger = Buffer.allocUnsafe(Math.max(need, out.length * 2));
264
+ out.copy(bigger, 0, 0, at);
265
+ out = bigger;
266
+ };
267
+ let pos = tt[origPtr] >>> 8;
268
+ let last = -1;
269
+ let same = 0;
270
+ for (let i = 0; i < length; i++) {
271
+ const entry = tt[pos];
272
+ const byte = entry & 0xff;
273
+ pos = entry >>> 8;
274
+ if (same === 4) {
275
+ if (at + byte > out.length) room(at + byte);
276
+ for (let k = 0; k < byte; k++) {
277
+ out[at++] = last;
278
+ crc = (crc << 8) ^ CRC_TABLE[((crc >>> 24) ^ last) & 0xff];
279
+ }
280
+ same = 0;
281
+ last = -1;
282
+ continue;
283
+ }
284
+ if (byte === last) same++;
285
+ else { last = byte; same = 1; }
286
+ if (at === out.length) room(at + 1);
287
+ out[at++] = byte;
288
+ crc = (crc << 8) ^ CRC_TABLE[((crc >>> 24) ^ byte) & 0xff];
289
+ }
290
+ crc = (~crc) >>> 0;
291
+ if (crc !== storedCrc) throw new Bzip2Error('A block of the archive does not decode to what was compressed (CRC mismatch).');
292
+ return { data: out.subarray(0, at), crc };
293
+ }
294
+
295
+ /**
296
+ * The decompressed bytes of `buffer` (one or more concatenated bzip2 streams), one block at a time. `progress`,
297
+ * when given, is told after each block how far into `buffer` the reader is.
298
+ * @param {Buffer} buffer
299
+ * @param {{ progress?: (consumed: number, total: number) => void }} [o]
300
+ */
301
+ export function* bunzip2Blocks(buffer, { progress } = {}) {
302
+ const reader = new BitReader(buffer);
303
+ let streams = 0;
304
+ while (!reader.done) {
305
+ // Zero bytes after the last stream are padding some writers leave; anything else has to be a stream.
306
+ if (streams > 0 && buffer.subarray(reader.pos).every((byte) => byte === 0)) break;
307
+ if (reader.read(8) !== 0x42 || reader.read(8) !== 0x5a || reader.read(8) !== 0x68) throw new Bzip2Error(streams ? 'The archive has something after its last stream.' : 'The archive is not a bzip2 file.');
308
+ const level = reader.read(8) - 0x30;
309
+ if (level < 1 || level > 9) throw new Bzip2Error('The archive names an impossible block size.');
310
+ const blockSize = level * 100000;
311
+ const tt = new Uint32Array(blockSize);
312
+ let combined = 0;
313
+ for (;;) {
314
+ const hi = reader.read(24);
315
+ const lo = reader.read(24);
316
+ if (hi === BLOCK_MAGIC_HI && lo === BLOCK_MAGIC_LO) {
317
+ const block = decodeBlock(reader, tt, blockSize);
318
+ combined = (((combined << 1) | (combined >>> 31)) ^ block.crc) >>> 0;
319
+ progress?.(reader.pos, buffer.length);
320
+ if (block.data.length) yield block.data;
321
+ continue;
322
+ }
323
+ if (hi === END_MAGIC_HI && lo === END_MAGIC_LO) {
324
+ if (reader.read32() !== combined) throw new Bzip2Error('The archive does not decode to what was compressed (stream CRC mismatch).');
325
+ reader.align();
326
+ break;
327
+ }
328
+ throw new Bzip2Error('The archive has something that is not a bzip2 block.');
329
+ }
330
+ streams++;
331
+ }
332
+ if (!streams) throw new Bzip2Error('The archive is empty.');
333
+ }
334
+
335
+ /** All of `buffer`, decompressed (tests and small files). */
336
+ export function bunzip2(buffer) {
337
+ const parts = [...bunzip2Blocks(buffer)];
338
+ return Buffer.concat(parts);
339
+ }
@@ -0,0 +1,73 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Git Bash for the user's own `claude`
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Every Claude Code that SynaBun starts gets its Git Bash in its environment
6
+ // (resolver.js gitBashChildEnv). A `claude` the user starts in another
7
+ // terminal does not: on a PC whose only Git Bash is SynaBun's copy, that one
8
+ // is named in the `env` block of ~/.claude/settings.json, which Claude Code
9
+ // applies before it looks for Git Bash (checked in its 2.1.302 build:
10
+ // applySafeConfigEnvironmentVariables applies the user settings' env in full,
11
+ // in init(), before the Git Bash lookup).
12
+ //
13
+ // SynaBun's copy is in use env.CLAUDE_CODE_GIT_BASH_PATH = its bash.exe,
14
+ // unless the user put a value of their own there
15
+ // it is not a value that is SynaBun's (a path inside one of
16
+ // its copies) is removed; any other is left
17
+ //
18
+ // Windows only. The file is read and written by the caller's functions
19
+ // (server.js readClaudeSettingsForWrite / writeClaudeSettings: a file that is
20
+ // not JSON is never written over, and the write is atomic). Never throws.
21
+
22
+ import { GIT_BASH_ENV, insideAny } from './resolver.js';
23
+
24
+ /**
25
+ * Bring the user's settings in line with the Git Bash in use.
26
+ * @param {object} o
27
+ * @param {{ source: string|null, path: string|null }} o.resolution resolver.js resolveGitBash()
28
+ * @param {string} o.settingsPath ~/.claude/settings.json
29
+ * @param {(path: string) => object} o.read {} when the file does not exist; throws when it is not JSON
30
+ * @param {(path: string, settings: object) => void} o.write
31
+ * @param {string[]} o.ownRoots SynaBun's own Git folders
32
+ * @returns {{ state: 'skipped' | 'unchanged' | 'written' | 'removed' | 'kept-user-value' | 'error', value?: string|null, message?: string }}
33
+ */
34
+ export function syncClaudeSettingsGitBash({ resolution, settingsPath, read, write, ownRoots = [], platform = process.platform }) {
35
+ if (platform !== 'win32' || !settingsPath) return { state: 'skipped' };
36
+ const ours = resolution?.source === 'bundled' || resolution?.source === 'downloaded' ? resolution.path : null;
37
+ let settings;
38
+ try { settings = read(settingsPath); } catch (error) {
39
+ return { state: 'error', message: String(error?.message || error) };
40
+ }
41
+ if (!settings || typeof settings !== 'object' || Array.isArray(settings)) return { state: 'error', message: `${settingsPath} does not hold a settings object.` };
42
+ const env = settings.env && typeof settings.env === 'object' && !Array.isArray(settings.env) ? settings.env : null;
43
+ const current = typeof env?.[GIT_BASH_ENV] === 'string' ? env[GIT_BASH_ENV] : null;
44
+ const isOurs = current !== null && insideAny(current, ownRoots);
45
+
46
+ if (ours) {
47
+ if (current === ours) return { state: 'unchanged', value: ours };
48
+ // A value of the user's own (one that does not work, or the resolver would have taken it) is theirs to change.
49
+ if (current !== null && !isOurs) return { state: 'kept-user-value', value: current };
50
+ settings.env = { ...(env || {}), [GIT_BASH_ENV]: ours };
51
+ } else {
52
+ if (!isOurs) return { state: 'unchanged', value: current };
53
+ const rest = { ...env };
54
+ delete rest[GIT_BASH_ENV];
55
+ if (Object.keys(rest).length) settings.env = rest;
56
+ else delete settings.env;
57
+ }
58
+ try {
59
+ write(settingsPath, settings);
60
+ } catch (error) {
61
+ return { state: 'error', message: String(error?.message || error) };
62
+ }
63
+ return ours ? { state: 'written', value: ours } : { state: 'removed', value: null };
64
+ }
65
+
66
+ /** The user's own value of CLAUDE_CODE_GIT_BASH_PATH in their settings, or null (what the resolver weighs). */
67
+ export function settingsGitBashValue({ settingsPath, read }) {
68
+ try {
69
+ const settings = read(settingsPath);
70
+ const value = settings?.env?.[GIT_BASH_ENV];
71
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
72
+ } catch { return null; }
73
+ }
@@ -0,0 +1,34 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Git for Windows: the unpacking, in a thread of its own
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // install.js unpackInWorker() starts this with what unpackAndCheck() takes and
6
+ // a shared flag the server raises to stop it. The reading of the .tar.bz2, the
7
+ // writing of some ten thousand files and the digest of what was written take
8
+ // about a minute of CPU: here, they never hold the server's event loop. The
9
+ // thread answers with messages: `step`, `progress` (a few times a second at
10
+ // most), then `done` with the result or `error` with a GitBashError's code.
11
+
12
+ import { parentPort, workerData } from 'node:worker_threads';
13
+ import { asGitBashError, unpackAndCheck } from './install.js';
14
+
15
+ const { flag, ...options } = workerData;
16
+ let last = 0;
17
+
18
+ try {
19
+ const result = unpackAndCheck({
20
+ ...options,
21
+ stopped: () => Atomics.load(flag, 0) === 1,
22
+ onStep: (step) => parentPort.postMessage({ type: 'step', step }),
23
+ onProgress: (progress) => {
24
+ const now = Date.now();
25
+ if (progress.consumed !== progress.total && now - last < 250) return;
26
+ last = now;
27
+ parentPort.postMessage({ type: 'progress', progress });
28
+ },
29
+ });
30
+ parentPort.postMessage({ type: 'done', result });
31
+ } catch (failure) {
32
+ const error = asGitBashError(failure, 'unpack', 'The archive could not be unpacked.');
33
+ parentPort.postMessage({ type: 'error', code: error.code, message: error.message, detail: error.detail || null });
34
+ }