synabun 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +1 -1
  3. package/docs/whatsapp.md +11 -11
  4. package/lib/client-config-repair.js +13 -9
  5. package/lib/external-tools.js +6 -5
  6. package/lib/update-snapshot.js +77 -3
  7. package/lib/virustotal.mjs +12 -1
  8. package/mcp-server/dist/index.js +1 -1
  9. package/mcp-server/dist/index.js.map +1 -1
  10. package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
  11. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  12. package/mcp-server/dist/services/budget-judgments.js +5 -3
  13. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  14. package/mcp-server/dist/services/identity.d.ts +2 -2
  15. package/mcp-server/dist/services/identity.js +2 -2
  16. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  17. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  18. package/mcp-server/dist/services/memory-sessions.js +28 -2
  19. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  20. package/mcp-server/dist/services/neural-interface.d.ts +2 -1
  21. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  22. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  23. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  24. package/mcp-server/dist/services/server-instructions.js +2 -1
  25. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  26. package/mcp-server/dist/services/tool-catalog.js +1 -1
  27. package/mcp-server/dist/services/tool-catalog.js.map +1 -1
  28. package/mcp-server/dist/services/tool-list.d.ts +1 -1
  29. package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
  30. package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
  31. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
  32. package/mcp-server/dist/services/tool-runtime.js +1 -1
  33. package/mcp-server/dist/services/tool-runtime.js.map +1 -1
  34. package/mcp-server/dist/services/typesafe-config.js +1 -1
  35. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  36. package/mcp-server/dist/tools/agents.d.ts +8 -8
  37. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  38. package/mcp-server/dist/tools/agents.js +10 -9
  39. package/mcp-server/dist/tools/agents.js.map +1 -1
  40. package/mcp-server/dist/tools/choice.d.ts +6 -0
  41. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  42. package/mcp-server/dist/tools/choice.js +18 -13
  43. package/mcp-server/dist/tools/choice.js.map +1 -1
  44. package/mcp-server/dist/tools/git-tools.js +2 -2
  45. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  46. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  47. package/mcp-server/dist/tools/morelogin.js +9 -6
  48. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  49. package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
  50. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
  51. package/mcp-server/dist/tools/skill-tools.js +11 -8
  52. package/mcp-server/dist/tools/skill-tools.js.map +1 -1
  53. package/mcp-server/package-lock.json +2 -2
  54. package/mcp-server/package.json +1 -1
  55. package/mcp-server/src/index.ts +1 -1
  56. package/mcp-server/src/services/budget-judgments.ts +5 -3
  57. package/mcp-server/src/services/identity.ts +2 -2
  58. package/mcp-server/src/services/memory-sessions.ts +24 -3
  59. package/mcp-server/src/services/neural-interface.ts +2 -1
  60. package/mcp-server/src/services/server-instructions.ts +2 -1
  61. package/mcp-server/src/services/tool-catalog.ts +1 -1
  62. package/mcp-server/src/services/tool-list.ts +1 -1
  63. package/mcp-server/src/services/tool-runtime.ts +2 -2
  64. package/mcp-server/src/services/typesafe-config.ts +1 -1
  65. package/mcp-server/src/tools/agents.ts +10 -9
  66. package/mcp-server/src/tools/choice.ts +17 -12
  67. package/mcp-server/src/tools/git-tools.ts +2 -2
  68. package/mcp-server/src/tools/morelogin.ts +6 -4
  69. package/mcp-server/src/tools/skill-tools.ts +11 -8
  70. package/neural-interface/i18n/en.json +501 -39
  71. package/neural-interface/i18n/pt-BR.json +501 -39
  72. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  73. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  74. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  75. package/neural-interface/lib/antigravity/bypass.js +100 -0
  76. package/neural-interface/lib/antigravity/catalog.js +273 -0
  77. package/neural-interface/lib/antigravity/home.js +260 -0
  78. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  79. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  80. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  81. package/neural-interface/lib/antigravity/policy.js +74 -0
  82. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  83. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  84. package/neural-interface/lib/antigravity/usage.js +43 -0
  85. package/neural-interface/lib/assistant-api.js +1 -0
  86. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  87. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  88. package/neural-interface/lib/assistant-budget-split.js +42 -15
  89. package/neural-interface/lib/assistant-catalog.js +76 -9
  90. package/neural-interface/lib/assistant-config.js +6 -3
  91. package/neural-interface/lib/assistant-dispatch.js +207 -49
  92. package/neural-interface/lib/assistant-envelope.js +153 -1
  93. package/neural-interface/lib/assistant-evidence.js +80 -0
  94. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  95. package/neural-interface/lib/assistant-persona.js +16 -11
  96. package/neural-interface/lib/assistant-plan-permissions.js +15 -0
  97. package/neural-interface/lib/assistant-router.js +237 -35
  98. package/neural-interface/lib/assistant-runtime.js +158 -12
  99. package/neural-interface/lib/assistant-task-prompt.js +10 -8
  100. package/neural-interface/lib/assistant-tools.js +2 -2
  101. package/neural-interface/lib/assistant-usage.js +29 -0
  102. package/neural-interface/lib/backup-service.js +6 -1
  103. package/neural-interface/lib/browser-guard-cdp.js +5 -4
  104. package/neural-interface/lib/browser-network-guard.js +274 -82
  105. package/neural-interface/lib/browser-tool-policy.js +12 -7
  106. package/neural-interface/lib/claude-agent-bridge.js +7 -0
  107. package/neural-interface/lib/claude-history-cache.js +210 -0
  108. package/neural-interface/lib/claude-model-catalog.js +5 -1
  109. package/neural-interface/lib/cli-install-api.js +1 -1
  110. package/neural-interface/lib/cli-install.js +160 -31
  111. package/neural-interface/lib/cli-resolver.js +9 -2
  112. package/neural-interface/lib/cli-update-planner.js +25 -15
  113. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  114. package/neural-interface/lib/codex-worker-app-server.js +25 -6
  115. package/neural-interface/lib/editor-git-api.js +3 -20
  116. package/neural-interface/lib/exec-loop-command.js +9 -2
  117. package/neural-interface/lib/file-explorer-api.js +74 -40
  118. package/neural-interface/lib/file-explorer-watch.js +9 -3
  119. package/neural-interface/lib/git-bash/install.js +52 -6
  120. package/neural-interface/lib/git-bash/manager.js +59 -17
  121. package/neural-interface/lib/git-bash/pins.js +12 -0
  122. package/neural-interface/lib/git-read.js +2 -2
  123. package/neural-interface/lib/git-status-runner.js +390 -0
  124. package/neural-interface/lib/git-status-watchdog.js +40 -0
  125. package/neural-interface/lib/greeting/budgets.js +1 -0
  126. package/neural-interface/lib/greeting/engine.js +2 -2
  127. package/neural-interface/lib/greeting/sources.js +10 -12
  128. package/neural-interface/lib/greeting/writer.js +1 -1
  129. package/neural-interface/lib/mcp-client-config.js +37 -6
  130. package/neural-interface/lib/mcp-external-servers.js +13 -4
  131. package/neural-interface/lib/mcp-installer.js +2 -1
  132. package/neural-interface/lib/memory-session-capture.js +17 -3
  133. package/neural-interface/lib/morelogin.js +147 -25
  134. package/neural-interface/lib/native-loop-providers.js +420 -21
  135. package/neural-interface/lib/native-loop-runtime.js +23 -2
  136. package/neural-interface/lib/opencode-native-boundary.js +11 -2
  137. package/neural-interface/lib/owner-authority.js +1 -1
  138. package/neural-interface/lib/path-confine.js +12 -9
  139. package/neural-interface/lib/peer-identity.js +32 -2
  140. package/neural-interface/lib/pty-host/core.js +28 -1
  141. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  142. package/neural-interface/lib/pty-host/manager.js +12 -1
  143. package/neural-interface/lib/remote-policy.js +1 -1
  144. package/neural-interface/lib/request-gate.js +3 -0
  145. package/neural-interface/lib/rulesets/api.js +6 -5
  146. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  147. package/neural-interface/lib/rulesets/installer.js +89 -10
  148. package/neural-interface/lib/rulesets/render.js +1 -1
  149. package/neural-interface/lib/session-cache-builder.js +64 -1
  150. package/neural-interface/lib/skills/catalog.js +5 -1
  151. package/neural-interface/lib/skills/creation-brief.js +1 -1
  152. package/neural-interface/lib/skills/creation-sources.js +19 -1
  153. package/neural-interface/lib/skills/deploy.js +9 -4
  154. package/neural-interface/lib/skills/draft-confinement.js +3 -1
  155. package/neural-interface/lib/skills/eval-confinement.js +3 -1
  156. package/neural-interface/lib/skills/eval-trace.js +16 -0
  157. package/neural-interface/lib/skills/export.js +5 -2
  158. package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
  159. package/neural-interface/lib/skills/ownership.js +1 -1
  160. package/neural-interface/lib/skills/roots.js +10 -3
  161. package/neural-interface/lib/skills/runtime-checks.js +6 -3
  162. package/neural-interface/lib/skills/service.js +15 -7
  163. package/neural-interface/lib/skills/telemetry.js +6 -0
  164. package/neural-interface/lib/synabun-version.js +1 -1
  165. package/neural-interface/lib/system-activity.js +1 -1
  166. package/neural-interface/lib/whatsapp/brain.js +7 -3
  167. package/neural-interface/lib/whatsapp/cards.js +22 -9
  168. package/neural-interface/lib/whatsapp/config.js +1 -1
  169. package/neural-interface/lib/worker-confinement.js +77 -1
  170. package/neural-interface/lib/worker-question.js +74 -0
  171. package/neural-interface/lib/worker-resume.js +13 -4
  172. package/neural-interface/lib/worker-sandbox.js +46 -16
  173. package/neural-interface/package-lock.json +18 -2
  174. package/neural-interface/package.json +2 -1
  175. package/neural-interface/public/onboarding.html +1 -1
  176. package/neural-interface/public/shared/agent-runtime-options.js +89 -11
  177. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  178. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  179. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  180. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  181. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  182. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  183. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  184. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  185. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  186. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  187. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  188. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  189. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  190. package/neural-interface/public/shared/api.js +4 -4
  191. package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
  192. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  193. package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
  194. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  195. package/neural-interface/public/shared/assistant/asst-control.js +48 -0
  196. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  197. package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
  198. package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
  199. package/neural-interface/public/shared/assistant/asst-panel.js +45 -8
  200. package/neural-interface/public/shared/assistant/asst-render.js +51 -12
  201. package/neural-interface/public/shared/assistant/asst-route.js +237 -57
  202. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  203. package/neural-interface/public/shared/assistant/asst-scenes.js +54 -1
  204. package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
  205. package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
  206. package/neural-interface/public/shared/assistant/asst-state.js +76 -9
  207. package/neural-interface/public/shared/assistant/asst-styles.js +34 -0
  208. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
  209. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  210. package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
  211. package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
  212. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  213. package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
  214. package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
  215. package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
  216. package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
  217. package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
  218. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  219. package/neural-interface/public/shared/cli-install-view.js +7 -4
  220. package/neural-interface/public/shared/cli-status.js +9 -8
  221. package/neural-interface/public/shared/constants.js +3 -2
  222. package/neural-interface/public/shared/context-menu-model.js +1 -0
  223. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  224. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  225. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  226. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  227. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  228. package/neural-interface/public/shared/desktop-update-view.js +1 -0
  229. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  230. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  231. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  232. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  233. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  234. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  235. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  236. package/neural-interface/public/shared/html-shell.js +21 -4
  237. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +27 -7
  238. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  239. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  240. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -0
  241. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
  242. package/neural-interface/public/shared/provider-icons.js +4 -2
  243. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  244. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
  245. package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
  246. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  247. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  248. package/neural-interface/public/shared/skills/ss-creation.js +6 -3
  249. package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
  250. package/neural-interface/public/shared/skills/ss-lint.js +9 -1
  251. package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
  252. package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
  253. package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
  254. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
  255. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
  256. package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
  257. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  258. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  259. package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
  260. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
  261. package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
  262. package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
  263. package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
  264. package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
  265. package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
  266. package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
  267. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
  268. package/neural-interface/public/shared/state.js +1 -1
  269. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  270. package/neural-interface/public/shared/styles.css +354 -34
  271. package/neural-interface/public/shared/text-cache.js +29 -0
  272. package/neural-interface/public/shared/tool-list-picker.js +2 -2
  273. package/neural-interface/public/shared/tool-list-view.js +2 -2
  274. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  275. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  276. package/neural-interface/public/shared/ui-assistant.js +2 -0
  277. package/neural-interface/public/shared/ui-automation-studio.js +20 -12
  278. package/neural-interface/public/shared/ui-claude-panel.js +299 -77
  279. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  280. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  281. package/neural-interface/public/shared/ui-file-links.js +2 -2
  282. package/neural-interface/public/shared/ui-games.js +2 -2
  283. package/neural-interface/public/shared/ui-judgments.js +1 -1
  284. package/neural-interface/public/shared/ui-keybinds.js +8 -2
  285. package/neural-interface/public/shared/ui-link.js +3 -3
  286. package/neural-interface/public/shared/ui-menubar.js +2 -2
  287. package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
  288. package/neural-interface/public/shared/ui-navbar.js +45 -0
  289. package/neural-interface/public/shared/ui-notifications.js +1 -0
  290. package/neural-interface/public/shared/ui-resume.js +9 -7
  291. package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
  292. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  293. package/neural-interface/public/shared/ui-sessions.js +22 -12
  294. package/neural-interface/public/shared/ui-settings.js +90 -65
  295. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  296. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
  297. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  298. package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
  299. package/neural-interface/public/shared/ui-terminal.js +146 -10
  300. package/neural-interface/public/shared/ui-tool-updates.js +3 -3
  301. package/neural-interface/public/shared/ui-tooltip.js +71 -46
  302. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  303. package/neural-interface/public/shared/ui-whiteboard.js +1 -1
  304. package/neural-interface/public/shared/ui-worker-end.js +1 -1
  305. package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
  306. package/neural-interface/public/shared/worker-questions.js +34 -0
  307. package/neural-interface/public/sidepanel-session.html +1 -1
  308. package/neural-interface/server.js +644 -426
  309. package/neural-interface/templates/rulesets/README.md +1 -1
  310. package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
  311. package/neural-interface/templates/rulesets/manifest.json +1 -1
  312. package/npm-shrinkwrap.json +18 -2
  313. package/package.json +2 -1
  314. package/skills/skill-forge/SKILL.md +4 -4
  315. package/skills/synabun/SKILL.md +1 -1
  316. package/skills/synabun/modules/schedule.md +3 -3
  317. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -11,7 +11,10 @@
11
11
  // redirect too), stopped at the pinned size, compared with
12
12
  // the pinned SHA-256 (cloudflared/install.js downloadPinned).
13
13
  // A file that does not match is removed: nothing of it is
14
- // ever unpacked
14
+ // ever unpacked. An installed app carries the release file
15
+ // itself (<resources>\git-archive): it is copied into the
16
+ // work folder and checked the same way (copyPinned), never
17
+ // unpacked from where it lies
15
18
  // extractArchive the .tar.bz2 read by this application itself (bzip2.js,
16
19
  // then the tar reader below), never by a tar program: what a
17
20
  // Windows 10 tar.exe can read differs from build to build.
@@ -34,8 +37,10 @@
34
37
  // Only node: modules and this application's own files here (see pins.js).
35
38
 
36
39
  import { createHash, randomBytes } from 'node:crypto';
40
+ import { pipeline } from 'node:stream/promises';
41
+ import { Transform } from 'node:stream';
37
42
  import {
38
- closeSync, constants, copyFileSync, existsSync, linkSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync,
43
+ closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, linkSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync,
39
44
  renameSync, rmSync, statSync, writeFileSync, writeSync,
40
45
  } from 'node:fs';
41
46
  import { dirname, join } from 'node:path';
@@ -463,6 +468,40 @@ export function unpackInWorker(data, { signal, onProgress = () => {}, onStep = (
463
468
  });
464
469
  }
465
470
 
471
+ /**
472
+ * The release file an installed app carries (`source`), copied into `destination` (the attempt's own work folder)
473
+ * and compared with the pin as it is copied: its size, then its SHA-256. What is unpacked is the copy, so nothing
474
+ * can change the file between the check and the read. A file that does not match is removed and named.
475
+ */
476
+ export async function copyPinned({ source, pin, destination, signal, onProgress = () => {} }) {
477
+ const hash = createHash('sha256');
478
+ let size = 0;
479
+ const count = new Transform({
480
+ transform(chunk, encoding, done) {
481
+ size += chunk.length;
482
+ if (size > pin.size) { done(new GitBashError('checksum', `${source} is larger than the pinned ${pin.file}.`)); return; }
483
+ hash.update(chunk);
484
+ onProgress({ received: size, total: pin.size });
485
+ done(null, chunk);
486
+ },
487
+ });
488
+ try {
489
+ await pipeline(createReadStream(source, { signal }), count, createWriteStream(destination, { flags: 'wx', mode: 0o600 }), { signal });
490
+ } catch (error) {
491
+ // A file that was there already is not this attempt's to remove.
492
+ if (error?.code !== 'EEXIST') rmSync(destination, { force: true });
493
+ if (signal?.aborted) throw cancelled();
494
+ if (error instanceof GitBashError) throw error;
495
+ throw new GitBashError('disk', `The Git for Windows release file this app carries could not be read (${error?.code || error?.message || error}).`);
496
+ }
497
+ const sha256 = hash.digest('hex');
498
+ if (size !== pin.size || sha256 !== pin.sha256) {
499
+ rmSync(destination, { force: true });
500
+ throw new GitBashError('checksum', `${source} is not the pinned ${pin.file} (${size} bytes, sha256 ${sha256}).`, { detail: sha256 });
501
+ }
502
+ return { size, sha256 };
503
+ }
504
+
466
505
  /** Renaming a folder on Windows can meet a file an antivirus scanner holds open for a moment: try again, briefly. */
467
506
  async function renameWithRetry(from, to, { tries = 6, wait = (ms) => new Promise((done) => setTimeout(done, ms)) } = {}) {
468
507
  for (let attempt = 1; ; attempt++) {
@@ -489,9 +528,10 @@ async function renameWithRetry(from, to, { tries = 6, wait = (ms) => new Promise
489
528
  * @param {(p: { phase: string, received?: number, total: number, consumed?: number }) => void} [o.onProgress]
490
529
  * @param {boolean} [o.worker] unpack in a worker thread (true for the server; tests and builds run it inline)
491
530
  * @param {Function} [o.unpack] what unpacks (data, { signal, onProgress, onStep }): unpackInWorker or unpackAndCheck
531
+ * @param {string|null} [o.archive] the release file an installed app carries: used instead of a download
492
532
  * @returns {Promise<{ path: string, version: string }>}
493
533
  */
494
- export async function installGitBash({ pins, pin, root, fetchImpl, signal, onStep = () => {}, onProgress = () => {}, stallMs, worker = true, unpack = null, platform = process.platform, licensesDir }) {
534
+ export async function installGitBash({ pins, pin, root, fetchImpl, signal, onStep = () => {}, onProgress = () => {}, stallMs, worker = true, unpack = null, platform = process.platform, licensesDir, archive = null }) {
495
535
  const directory = join(root, pin.version);
496
536
  let work = null;
497
537
  try {
@@ -501,12 +541,18 @@ export async function installGitBash({ pins, pin, root, fetchImpl, signal, onSte
501
541
  work = join(root, `${WORK_PREFIX}${process.pid}-${randomBytes(6).toString('hex')}`);
502
542
  mkdirSync(work, { mode: 0o700 });
503
543
  const releaseFile = join(work, 'release.part');
504
- onStep('download');
505
- await downloadPinned({ pin, destination: releaseFile, fetchImpl, signal, stallMs, onProgress: ({ received, total }) => onProgress({ phase: 'download', received, total }) });
544
+ if (archive) {
545
+ // The release file this app carries: copied and checked, then unpacked like a download.
546
+ onStep('extract');
547
+ await copyPinned({ source: archive, pin, destination: releaseFile, signal });
548
+ } else {
549
+ onStep('download');
550
+ await downloadPinned({ pin, destination: releaseFile, fetchImpl, signal, stallMs, onProgress: ({ received, total }) => onProgress({ phase: 'download', received, total }) });
551
+ }
506
552
  if (signal?.aborted) throw cancelled();
507
553
  const tree = join(work, 'tree');
508
554
  mkdirSync(tree);
509
- const options = { archive: releaseFile, destination: tree, pins, pin, source: 'downloaded', platform, licensesDir };
555
+ const options = { archive: releaseFile, destination: tree, pins, pin, source: archive ? 'archive' : 'downloaded', platform, licensesDir };
510
556
  const report = ({ consumed, total }) => onProgress({ phase: 'extract', consumed, total });
511
557
  const run = unpack || (worker
512
558
  ? unpackInWorker
@@ -7,23 +7,31 @@
7
7
  // Claude Code started by SynaBun get" (resolver.js) and, on a PC that has
8
8
  // none, puts SynaBun's own copy of Git for Windows in place:
9
9
  //
10
- // bundled inside an installed app: <resources>\git (the Windows x64
11
- // installer carries it; packaging/lib/git-bash.mjs)
12
- // downloaded on an npm install: DATA_HOME/runtime/git/<version>/, fetched
13
- // once from the pinned release (pins.js), checked and unpacked
14
- // by install.js, outside data/ so no backup holds it
10
+ // bundled inside an installed app of an earlier build: <resources>\git
11
+ // downloaded DATA_HOME/runtime/git/<version>/, outside data/ so no backup
12
+ // holds it: on an npm install fetched once from the pinned
13
+ // release (pins.js); in an installed app unpacked from the
14
+ // release file the Windows x64 installer carries
15
+ // (<resources>\git-archive\<file>; packaging/lib/git-bash.mjs),
16
+ // which keeps ~9,600 files out of every install and update. Either
17
+ // way checked and unpacked by install.js, and kept across updates
18
+ // (an update from a build with the tree moves that tree here:
19
+ // packaging/desktop/src/updater/git-adopt.mjs)
15
20
  //
16
- // It downloads only on Windows, only for a processor a release is pinned for,
17
- // and only when neither the PC nor the app has a Git Bash: at the server's
18
- // start (ensure) or at the first Claude Code start that finds none (childEnv),
19
- // once per process. A failed download is not tried again by itself: Settings
21
+ // It downloads (or unpacks the carried release file) only on Windows, only for
22
+ // a processor a release is pinned for, and only when neither the PC nor the app
23
+ // has a Git Bash: at the server's start (ensure) or at the first Claude Code
24
+ // start that finds none (childEnv), once per process. Until it is in place a
25
+ // Claude Code started by SynaBun runs as it does on a PC without Git Bash (its
26
+ // PowerShell fallback); whenReady() waits for it, bounded. A failed download is not tried again by itself: Settings
20
27
  // shows why and has the button that tries again. SYNABUN_GIT_BASH=off turns all
21
28
  // of it off: no download, nothing added to any environment.
22
29
  //
23
30
  // resolution() the Git Bash in use (refreshed when older than half a minute)
24
31
  // childEnv(env) the environment of a Claude Code process SynaBun starts
25
32
  // gitExecutable() git.exe of SynaBun's copy when it is the one in use (SynaBun's own git features)
26
- // ensure() download when there is no Git Bash at all; never rejects
33
+ // ensure() download (or unpack the carried file) when there is no Git Bash at all; never rejects
34
+ // whenReady(ms) resolves the status once no download or unpacking runs, or after `ms`
27
35
  // status() what Settings shows: missing / downloading / ready / failed, and the rest
28
36
  // onChange(fn) told when the Git Bash in use changes (the settings of the user's own `claude` follow it)
29
37
  // housekeep() what an earlier run left in the data home: a stopped attempt's work folder, a version the
@@ -37,7 +45,7 @@ import { packagedRuntime } from '../../../lib/packaged-runtime.js';
37
45
  import { pathEnvValue } from '../augmented-path.js';
38
46
  import { sweepStaleWork } from '../cloudflared/install.js';
39
47
  import { checkInstalled, GitBashError, installGitBash } from './install.js';
40
- import { BUNDLED_FOLDER, gitBashPinFor, inTree, MARKER, readGitBashPins } from './pins.js';
48
+ import { ARCHIVE_FOLDER, BUNDLED_FOLDER, gitBashPinFor, inTree, MARKER, readGitBashPins } from './pins.js';
41
49
  import { gitBashChildEnv, resolveGitBash } from './resolver.js';
42
50
 
43
51
  const VERSION_FOLDER = /^\d+\.\d+\.\d+\.\d+$/;
@@ -100,6 +108,12 @@ export function createGitBashManager({
100
108
  const root = gitRoot(dataHome);
101
109
  const bundledRoot = platform === 'win32' && packaged?.resources ? join(packaged.resources, BUNDLED_FOLDER) : null;
102
110
  const downloadedRoot = pin ? join(root, pin.version) : null;
111
+ // The release file an installed Windows app carries (packaging/lib/git-bash.mjs), unpacked instead of a download.
112
+ const carriedArchive = platform === 'win32' && pin && packaged?.resources ? join(packaged.resources, ARCHIVE_FOLDER, pin.file) : null;
113
+ const archiveThere = () => {
114
+ if (!carriedArchive) return false;
115
+ try { return statSync(carriedArchive).isFile(); } catch { return false; }
116
+ };
103
117
  const ownRoots = [bundledRoot, root].filter(Boolean);
104
118
 
105
119
  const verified = new Map(); // copy root → { sig, result }: a copy is hashed again only when its marker changes
@@ -110,6 +124,7 @@ export function createGitBashManager({
110
124
  let lastError = null;
111
125
  let attempted = false; // a download was started in this process (the first-need trigger fires once)
112
126
  let settings = null; // what the last sync of the user's settings did (noteSettings)
127
+ let earlyStartLogged = false; // a Claude Code started while the copy was being put in place (said once)
113
128
 
114
129
  /** SynaBun's own copies, checked: the app's, then the data home's. */
115
130
  function copies() {
@@ -179,7 +194,7 @@ export function createGitBashManager({
179
194
  return { swept, removed: await pruneOtherVersions() };
180
195
  }
181
196
 
182
- /** Download SynaBun's copy when there is no Git Bash at all. Resolves the status; never rejects. */
197
+ /** Download (or unpack the carried release file of) SynaBun's copy when there is no Git Bash at all. Resolves the status; never rejects. */
183
198
  function ensure({ reason = 'start' } = {}) {
184
199
  if (platform !== 'win32' || !pin || off) return Promise.resolve(status());
185
200
  if (download) return download.promise.then(() => status());
@@ -187,12 +202,23 @@ export function createGitBashManager({
187
202
  attempted = true;
188
203
  lastError = null;
189
204
  const stop = new AbortController();
190
- const job = { stop, step: 'download', progress: { phase: 'download', received: 0, total: pin.size } };
191
- log(`[git-bash] No Git Bash on this PC (${reason}): downloading Git for Windows ${pin.version} (${(pin.size / 1024 / 1024).toFixed(1)} MB) from ${pin.url}`);
192
- job.promise = install({
193
- pins, pin, root, fetchImpl, stallMs, worker, platform: host, signal: stop.signal,
205
+ const archive = archiveThere() ? carriedArchive : null;
206
+ const job = { stop, archive, step: archive ? 'extract' : 'download', progress: archive ? { phase: 'extract', consumed: 0, total: pin.size } : { phase: 'download', received: 0, total: pin.size } };
207
+ const run = (from) => install({
208
+ pins, pin, root, fetchImpl, stallMs, worker, platform: host, signal: stop.signal, archive: from,
194
209
  onStep: (step) => { job.step = step; },
195
210
  onProgress: (progress) => { job.progress = progress; },
211
+ });
212
+ if (archive) log(`[git-bash] No Git Bash on this PC (${reason}): unpacking Git for Windows ${pin.version} from the release file this app carries (${archive}) in the background`);
213
+ else log(`[git-bash] No Git Bash on this PC (${reason}): downloading Git for Windows ${pin.version} (${(pin.size / 1024 / 1024).toFixed(1)} MB) from ${pin.url}`);
214
+ job.promise = run(archive).catch((error) => {
215
+ // A carried file that cannot be used (damaged, unreadable): the pinned download is the same release.
216
+ if (!archive || stop.signal.aborted || error?.code === 'cancelled') throw error;
217
+ log(`[git-bash] The carried release file could not be used (${describe(error).code}): ${describe(error).message} Downloading it instead.`);
218
+ job.archive = null;
219
+ job.step = 'download';
220
+ job.progress = { phase: 'download', received: 0, total: pin.size };
221
+ return run(null);
196
222
  }).then(async (placed) => {
197
223
  log(`[git-bash] Git for Windows ${pin.version} is in place, release file and tree checked: ${placed.path}`);
198
224
  verified.delete(downloadedRoot);
@@ -209,10 +235,24 @@ export function createGitBashManager({
209
235
  return job.promise.then(() => status());
210
236
  }
211
237
 
238
+ /** The status once nothing is being put in place, or after `timeoutMs`, whichever comes first. Never rejects. */
239
+ function whenReady(timeoutMs = 120_000) {
240
+ if (!download) return Promise.resolve(status());
241
+ const job = download;
242
+ return new Promise((done) => {
243
+ const timer = setTimeout(() => done(status()), Math.max(0, timeoutMs));
244
+ job.promise.then(() => { clearTimeout(timer); done(status()); }, () => { clearTimeout(timer); done(status()); });
245
+ });
246
+ }
247
+
212
248
  function childEnv(base = env) {
213
249
  const r = resolution();
214
250
  // The first Claude Code start that finds no Git Bash at all fetches one (once per process; Settings retries).
215
251
  if (platform === 'win32' && pin && !off && !r.source && !download && !attempted) ensure({ reason: 'first-need' });
252
+ if (platform === 'win32' && !r.source && download && !earlyStartLogged) {
253
+ earlyStartLogged = true;
254
+ log('[git-bash] A Claude Code started while Git for Windows is still being put in place: it runs without Git Bash (its PowerShell fallback) until the next start.');
255
+ }
216
256
  return gitBashChildEnv(base, r, { platform });
217
257
  }
218
258
 
@@ -240,7 +280,7 @@ export function createGitBashManager({
240
280
  if (download) {
241
281
  const { phase, received = 0, consumed = 0, total = 0 } = download.progress || {};
242
282
  const done = phase === 'extract' ? consumed : received;
243
- return { ...base, state: 'downloading', step: download.step, progress: { phase, done, total, percent: total ? Math.min(100, Math.floor((done / total) * 100)) : 0 } };
283
+ return { ...base, state: 'downloading', step: download.step, fromArchive: !!download.archive, progress: { phase, done, total, percent: total ? Math.min(100, Math.floor((done / total) * 100)) : 0 } };
244
284
  }
245
285
  if (r.source === 'bundled' || r.source === 'downloaded') return { ...base, state: 'ready' };
246
286
  if (r.source) return { ...base, state: 'system' };
@@ -259,7 +299,9 @@ export function createGitBashManager({
259
299
  childEnv,
260
300
  gitExecutable,
261
301
  ensure,
302
+ whenReady,
262
303
  status,
304
+ carriedArchive,
263
305
  housekeep,
264
306
  cancel: () => { if (!download) return false; download.stop.abort(); return true; },
265
307
  noteSettings: (result) => { settings = result || null; },
@@ -42,6 +42,13 @@ const SOURCE_TREE = 'https://github.com/git-for-windows/git/tree/';
42
42
  /** The folder of a packaged application that holds its Git for Windows, beside `runtime/` and `app/`. */
43
43
  export const BUNDLED_FOLDER = 'git';
44
44
 
45
+ /**
46
+ * The folder of a packaged Windows application that holds the pinned release file itself (`<file>` of the pin):
47
+ * one file in the installer instead of a tree of ten thousand, unpacked into the data home on first need
48
+ * (manager.js) and kept there across updates.
49
+ */
50
+ export const ARCHIVE_FOLDER = 'git-archive';
51
+
45
52
  /** What SynaBun puts at the top of its copy, beside what the release file holds (never part of the tree's digest). */
46
53
  export const MARKER = 'synabun-git.json';
47
54
  export const ADDED_FILES = Object.freeze(['LICENSE.txt', 'SOURCE.txt', MARKER]);
@@ -155,6 +162,11 @@ export function bundledGitRoot(resources, platform = process.platform) {
155
162
  return (platform === 'win32' ? win32 : posix).join(resources, BUNDLED_FOLDER);
156
163
  }
157
164
 
165
+ /** The release file a packaged application carries for `pin`: `<resources>/git-archive/<file>`. */
166
+ export function bundledGitArchive(resources, pin, platform = process.platform) {
167
+ return (platform === 'win32' ? win32 : posix).join(resources, ARCHIVE_FOLDER, pin.file);
168
+ }
169
+
158
170
  /** LICENSE.txt of SynaBun's copy: Git's own license texts of the pinned tag, after a line that says what they cover. */
159
171
  export function licenseText(dir = LICENSES_DIR) {
160
172
  const copying = readFileSync(join(dir, 'COPYING'), 'utf8');
@@ -126,12 +126,12 @@ export function readsIndex(args) {
126
126
  * @param {string[]} args the command and its arguments, the command first (['status', '--porcelain'])
127
127
  * @param {{ monitor?: boolean }} [o] `monitor`: the repository has git's built-in file system monitor switched on
128
128
  */
129
- export function gitReadArgs(args, { monitor = false } = {}) {
129
+ export function gitReadArgs(args, { monitor = false, nullDevice = devNull } = {}) {
130
130
  const [command, ...rest] = Array.isArray(args) ? args : [];
131
131
  const own = [
132
132
  '--no-pager',
133
133
  '-c', `core.fsmonitor=${monitor === true ? 'true' : 'false'}`,
134
- '-c', `core.hooksPath=${devNull}`,
134
+ '-c', `core.hooksPath=${nullDevice}`,
135
135
  '-c', 'log.showSignature=false',
136
136
  ];
137
137
  if (command === undefined) return own;