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
@@ -12,6 +12,11 @@
12
12
  // the time it happened), and its own probe of the server. Nothing is guessed:
13
13
  // no percentages, and no step is named before the launch reported it.
14
14
  //
15
+ // A restart the user asked for is shown by the same bridge (`restart()`): the
16
+ // screen is drawn in the click, and what it says comes from the same three
17
+ // things, plus the server's own word that it is shutting down. The supervisor
18
+ // that brings the server back reports on the same beacon (`reason: 'restart'`).
19
+ //
15
20
  // offline.html is shown when nothing can be fetched, so the server pastes this
16
21
  // file into it (lib/offline-page.js drops the `export` keywords). Keep every
17
22
  // export a plain `export function` / `export const` at the start of a line,
@@ -37,6 +42,7 @@ export const START_LIMITS = {
37
42
  lostAfterMs: 3000, // a launch that was reporting went silent: stop repeating what it said
38
43
  idleBeaconChecks: 2, // on load, look for a start already under way (a reload in the middle of one)
39
44
  reloadDelayMs: 150, // the "it is up" frame before the page reloads
45
+ restartGraceMs: 15000, // a restart was asked and the server neither went away nor said it is going: stop waiting
40
46
  beaconTimeoutMs: 1200,
41
47
  };
42
48
 
@@ -152,7 +158,7 @@ export function startBeaconTarget(loc) {
152
158
  }
153
159
 
154
160
  const START_LAUNCH_STATES = ['starting', 'started', 'failed', 'gave-up'];
155
- const START_LAUNCH_STEPS = ['received', 'locked', 'terminal', 'spawned'];
161
+ const START_LAUNCH_STEPS = ['received', 'locked', 'terminal', 'spawned', 'exited'];
156
162
  const START_SUPERVISOR_PHASES = ['checking', 'dependencies', 'browser', 'build', 'snapshot', 'server', 'listening', 'failed'];
157
163
 
158
164
  function startCleanError(error) {
@@ -217,6 +223,8 @@ export function readStartBeacon(data, { port, askedAt = null, following = null,
217
223
  const newest = Math.max(last.at, supervisor ? supervisor.at : 0, startedAt);
218
224
  return {
219
225
  id,
226
+ // 'restart': a supervisor bringing its server back, not a launcher starting one.
227
+ reason: data.reason === 'restart' ? 'restart' : 'start',
220
228
  state: data.state,
221
229
  startedAt,
222
230
  step: last.id,
@@ -251,7 +259,10 @@ export function fetchStartBeacon(target, { fetchImpl, timeoutMs = START_LIMITS.b
251
259
  * idle → launching → starting | waiting → stalled | failed, or online at any
252
260
  * point. The page keeps probing in every state, so a server started by hand is
253
261
  * noticed just the same.
254
- * probe() → Promise<boolean> is the server answering?
262
+ * probe() → Promise<boolean | { up, boot, stopping }> is the server
263
+ * answering? `boot` names the server that answered and
264
+ * `stopping` is its own word that it is shutting down for a
265
+ * restart; a server that says neither is just `true`.
255
266
  * launch() → hand START_LINK to the OS
256
267
  * beacon() → Promise<object|null> the launch's raw report (optional)
257
268
  * port → the server's port (what a beacon must be about)
@@ -267,6 +278,13 @@ export function fetchStartBeacon(target, { fetchImpl, timeoutMs = START_LIMITS.b
267
278
  * 'stalled' stallAfterMs with no word, or the launcher gave up waiting
268
279
  * 'failed' the launcher reports that the start failed, and why
269
280
  * 'online' the server answers this page
281
+ *
282
+ * A restart (`restart()`) adds two:
283
+ * 'restarting' the server was asked to restart, or said it is restarting;
284
+ * it has not come back and no supervisor reports on it yet
285
+ * 'refused' the restart did not happen and the server still answers
286
+ * While one is under way `snapshot().restart` says what is known of it, and
287
+ * 'online' means a server other than the one that was asked answers.
270
288
  */
271
289
  export function createStartBridge({
272
290
  probe,
@@ -292,9 +310,13 @@ export function createStartBridge({
292
310
  let launchSeenAt = 0;
293
311
  let idleChecks = 0;
294
312
  let shown = '';
313
+ // The restart under way, on this page's clock: when it was asked for (or
314
+ // this page was told), when the server said it is restarting, when it first
315
+ // did not answer, and the server that is being replaced.
316
+ let restart = null;
295
317
  const past = [];
296
318
 
297
- const busy = () => state === 'launching' || state === 'starting' || state === 'waiting';
319
+ const busy = () => state === 'launching' || state === 'starting' || state === 'waiting' || state === 'restarting';
298
320
 
299
321
  function snapshot() {
300
322
  return {
@@ -304,15 +326,24 @@ export function createStartBridge({
304
326
  since,
305
327
  elapsedMs: since === null ? 0 : Math.max(0, now() - since),
306
328
  launch: launchInfo,
329
+ restart: restart ? {
330
+ asked: restart.asked,
331
+ askedAt: restart.askedAt,
332
+ acceptedAt: restart.acceptedAt,
333
+ downAt: restart.downAt,
334
+ refused: restart.refused,
335
+ } : null,
307
336
  };
308
337
  }
309
338
 
310
339
  function update() {
311
340
  const snap = snapshot();
312
341
  const l = snap.launch;
342
+ const r = snap.restart;
313
343
  const key = [
314
344
  snap.state, snap.clicks, busy() ? Math.floor(snap.elapsedMs / 1000) : '',
315
345
  l ? [l.id, l.state, l.step, l.supervisor?.phase, l.supervisor?.detail?.step, l.supervisor?.detail?.files, l.quietMs >= lim.quietAfterMs, l.error?.message].join('|') : '',
346
+ r ? [r.acceptedAt !== null, r.downAt !== null, r.refused?.code].join('|') : '',
316
347
  ].join('/');
317
348
  if (key === shown) return;
318
349
  shown = key;
@@ -326,14 +357,65 @@ export function createStartBridge({
326
357
  }
327
358
 
328
359
  function schedule(delay) {
329
- if (stopped || state === 'online') return;
360
+ if (stopped || state === 'online' || state === 'refused') return;
330
361
  if (timer) clearTimer(timer);
331
362
  timer = setTimer(tick, delay ?? (busy() ? lim.pollMs : lim.idlePollMs));
332
363
  }
333
364
 
365
+ /** One probe answer as { up, boot, stopping }; a bare `true` is a server that says nothing about itself. */
366
+ function readProbe(value) {
367
+ if (value === true) return { up: true, boot: '', stopping: false };
368
+ if (value && typeof value === 'object' && value.up === true) {
369
+ return { up: true, boot: typeof value.boot === 'string' ? value.boot : '', stopping: value.stopping === true };
370
+ }
371
+ return { up: false, boot: '', stopping: false };
372
+ }
373
+
374
+ /** The restart did not happen and the server is still there: say why and stop. */
375
+ function refuse(why) {
376
+ restart.refused = why;
377
+ set('refused');
378
+ }
379
+
380
+ /**
381
+ * During a restart an answer is not yet the server being back: the one that
382
+ * was asked keeps answering while it shuts down. True only when the server
383
+ * that answers is another one. A server that names itself (`boot`) settles
384
+ * it; one that does not (an older server) is believed once it was seen gone.
385
+ */
386
+ function judgeRestart(seen) {
387
+ const r = restart;
388
+ if (!seen.up) {
389
+ if (r.downAt === null) r.downAt = now();
390
+ return false;
391
+ }
392
+ if (seen.stopping) {
393
+ // Its own word: it is the one being replaced, and it is on its way out.
394
+ if (r.acceptedAt === null) r.acceptedAt = now();
395
+ if (!r.boot && seen.boot) r.boot = seen.boot;
396
+ r.downAt = null; // an answer it missed was not it stopping
397
+ return false;
398
+ }
399
+ const named = !!r.boot && !!seen.boot;
400
+ if (named && seen.boot !== r.boot) return true;
401
+ if (!named && r.downAt !== null) return true;
402
+ // The same server (or one that cannot be told apart), not shutting down.
403
+ if (named) r.downAt = null;
404
+ if (r.pending) return false; // the request is still on its way
405
+ if (r.rejected) { refuse(r.rejected); return false; }
406
+ if (r.unanswered) { refuse({ code: 'unreachable', status: 0, message: '' }); return false; }
407
+ if (r.acceptedAt !== null && now() - r.acceptedAt >= lim.restartGraceMs) {
408
+ if (named) { refuse({ code: 'still-running', status: 0, message: '' }); return false; }
409
+ return true; // it tells nothing about itself: load it again and see
410
+ }
411
+ return false;
412
+ }
413
+
334
414
  /** What the launch says, when there is one to listen to right now. */
335
415
  async function listen() {
336
416
  if (!beacon) return;
417
+ // A restart: nobody is starting anything while the old server still answers.
418
+ if (restart && restart.downAt === null) return;
337
419
  // Idle: only just after load, for a start that was already under way.
338
420
  if (state === 'idle' && idleChecks >= lim.idleBeaconChecks) return;
339
421
  if (state === 'idle') idleChecks++;
@@ -357,7 +439,8 @@ export function createStartBridge({
357
439
  // reading how. What it said last is no longer known to be true.
358
440
  if (state === 'starting' && now() - launchSeenAt >= lim.lostAfterMs) {
359
441
  launchInfo = null;
360
- if (askedAt === null) { since = null; set('idle'); } // a start this page never asked for: back to watching
442
+ if (restart) set('restarting'); // back to what this page sees for itself
443
+ else if (askedAt === null) { since = null; set('idle'); } // a start this page never asked for: back to watching
361
444
  else set(now() - askedAt >= lim.hintAfterMs ? 'waiting' : 'launching');
362
445
  }
363
446
  }
@@ -366,17 +449,22 @@ export function createStartBridge({
366
449
  timer = null;
367
450
  if (stopped || probing) return;
368
451
  probing = true;
369
- let up = false;
370
- try { up = (await probe()) === true; } catch {}
371
- if (!up && !stopped) await listen();
452
+ let seen = readProbe(false);
453
+ try { seen = readProbe(await probe()); } catch {}
454
+ let up = seen.up && !seen.stopping; // a server on its way out is not one to connect to
455
+ if (restart && !stopped && state !== 'refused') up = judgeRestart(seen);
456
+ if (!up && !stopped && state !== 'refused') await listen();
372
457
  probing = false;
373
458
  if (stopped) return;
374
459
  if (up) { set('online'); update(); return; }
460
+ if (state === 'refused') { update(); return; }
375
461
  if (state === 'launching' || state === 'waiting') {
376
462
  const waited = now() - askedAt;
377
463
  if (waited >= lim.stallAfterMs) set('stalled');
378
464
  else if (waited >= lim.hintAfterMs) set('waiting');
379
465
  }
466
+ // Gone, and nobody reports bringing it back: the button and the command return.
467
+ if (state === 'restarting' && restart.downAt !== null && now() - restart.downAt >= lim.stallAfterMs) set('stalled');
380
468
  update();
381
469
  schedule();
382
470
  }
@@ -388,13 +476,14 @@ export function createStartBridge({
388
476
  watch() { schedule(0); },
389
477
  /** The click. Ignored while a start is already under way. */
390
478
  start() {
391
- if (stopped || state === 'online' || busy()) return false;
479
+ if (stopped || state === 'online' || state === 'refused' || busy()) return false;
392
480
  clicks++;
393
481
  askedAt = now();
394
482
  since = askedAt;
395
483
  // What the last try reported is over: a launcher still saying so is not this one.
396
484
  if (launchInfo && !past.includes(launchInfo.id)) past.push(launchInfo.id);
397
485
  launchInfo = null;
486
+ restart = null; // a restart that did not bring it back is over: this is a start
398
487
  state = 'idle'; // a second try after 'stalled' announces 'launching' again
399
488
  set('launching');
400
489
  update();
@@ -402,6 +491,67 @@ export function createStartBridge({
402
491
  schedule();
403
492
  return true;
404
493
  },
494
+ /**
495
+ * A restart of the server. Call it from the click itself: the first
496
+ * snapshot is handed to onUpdate before anything is asked of the network.
497
+ * request() → Promise<{ ok, boot, status, message }> the asking (this
498
+ * page's own click); leave it out when the page is only told
499
+ * announced → the server itself said it is restarting (`boot`: which one)
500
+ * A request that fails counts as refused only once the server is seen to
501
+ * be still there. Returns false when a restart is already being shown; an
502
+ * announcement then still counts as the server's word.
503
+ */
504
+ restart({ request = null, announced = false, boot = '' } = {}) {
505
+ if (stopped || state === 'online') return false;
506
+ if (restart) {
507
+ if (announced && !restart.refused) {
508
+ if (restart.acceptedAt === null) restart.acceptedAt = now();
509
+ if (!restart.boot && boot) restart.boot = String(boot);
510
+ update();
511
+ }
512
+ return false;
513
+ }
514
+ if (typeof request !== 'function' && !announced) return false;
515
+ askedAt = now();
516
+ since = askedAt;
517
+ if (launchInfo && !past.includes(launchInfo.id)) past.push(launchInfo.id);
518
+ launchInfo = null;
519
+ const mine = restart = {
520
+ asked: typeof request === 'function',
521
+ askedAt,
522
+ acceptedAt: announced ? askedAt : null,
523
+ downAt: null,
524
+ boot: announced && boot ? String(boot) : '',
525
+ pending: typeof request === 'function',
526
+ rejected: null,
527
+ unanswered: false,
528
+ refused: null,
529
+ };
530
+ state = 'idle';
531
+ set('restarting');
532
+ update();
533
+ if (mine.asked) {
534
+ Promise.resolve().then(() => request()).then((answer) => {
535
+ if (stopped || restart !== mine) return;
536
+ mine.pending = false;
537
+ if (answer && answer.ok === true) {
538
+ if (mine.acceptedAt === null) mine.acceptedAt = now();
539
+ if (answer.boot) mine.boot = String(answer.boot);
540
+ } else {
541
+ mine.rejected = { code: 'refused', status: Number(answer?.status) || 0, message: String(answer?.message || '').slice(0, 200) };
542
+ }
543
+ update();
544
+ schedule(0); // look at once: a refusal is only one when the server still answers
545
+ }, () => {
546
+ if (stopped || restart !== mine) return;
547
+ mine.pending = false;
548
+ mine.unanswered = true;
549
+ schedule(0);
550
+ });
551
+ }
552
+ schedule();
553
+ return true;
554
+ },
405
555
  stop() {
406
556
  stopped = true;
407
557
  if (timer) clearTimer(timer);
@@ -452,6 +602,22 @@ export const START_COPY = {
452
602
  headingStarting: 'Starting the server',
453
603
  headingReady: 'Server is up',
454
604
  headingFailed: 'The server did not start',
605
+ // A restart: each sentence is said only once the page holds what it says.
606
+ restartAsked: 'Asked the server to restart.',
607
+ restartStopping: 'The server accepted the restart and is shutting down.',
608
+ restartSlowStop: 'Still shutting down after {seconds} s.',
609
+ restartDown: 'The server stopped answering. Waiting for it to come back.',
610
+ restartRespawning: 'The supervisor is starting the server again.',
611
+ restartStalled: 'The server has not come back. Start it with the button, or run the command below.',
612
+ restartRefused: 'The server did not accept the restart: {reason}',
613
+ restartUnreachable: 'The server did not answer the restart request.',
614
+ restartStillRunning: 'The server accepted the restart but did not shut down.',
615
+ trailAccepted: 'Restart accepted',
616
+ trailNoAnswer: 'Server stopped answering',
617
+ trailStopped: 'Server process exited',
618
+ restartElapsedLabel: 'Time since the restart began',
619
+ headingRestarting: 'Restarting the server',
620
+ headingRestartFailed: 'The server did not restart',
455
621
  };
456
622
 
457
623
  /** `{name}` placeholders, like the app's t(). */
@@ -474,12 +640,15 @@ function startStepSeconds(ms) {
474
640
 
475
641
  /**
476
642
  * Everything a surface renders for one snapshot, as data:
643
+ * reason 'start' | 'restart' what is under way: a start, or a restart of a server that was up
477
644
  * mascot 'asleep' | 'waking' | 'working' | 'ready' | 'failed' | 'stalled'
478
645
  * busy the button is disabled (a start is under way)
479
- * button 'start' | 'starting' | 'retry'
646
+ * button 'start' | 'starting' | 'retry' | 'none' (a restart: nothing to press) | 'back' (it was refused)
647
+ * heading key | null the title when it is not the surface's own for this mascot
480
648
  * headline { key, params } | null the status sentence (the live region)
481
649
  * hint { key, params } | null a second, quieter sentence
482
650
  * elapsed '12 s' | '' the page's clock since the click
651
+ * elapsedLabel key what that clock counts from
483
652
  * trail [{ key, time }] what was reported, with when
484
653
  * error { message, log } | null
485
654
  * A headline names a step only when the snapshot holds the report of it.
@@ -488,38 +657,57 @@ export function startStatusView(snap, { platform = 'linux', limits = START_LIMIT
488
657
  const state = snap?.state || 'idle';
489
658
  const launchOf = snap?.launch || null;
490
659
  const sup = launchOf?.supervisor || null;
660
+ // A restart this page asked for or was told of; or one it only reads the report of.
661
+ const restart = snap?.restart || null;
662
+ const restartReport = launchOf?.reason === 'restart';
491
663
  const view = {
492
664
  state,
665
+ reason: restart || restartReport ? 'restart' : 'start',
493
666
  mascot: 'asleep',
494
- busy: state === 'launching' || state === 'starting' || state === 'waiting',
667
+ busy: state === 'launching' || state === 'starting' || state === 'waiting' || state === 'restarting',
495
668
  button: 'start',
669
+ heading: null,
496
670
  headline: null,
497
671
  hint: null,
498
672
  elapsed: '',
673
+ elapsedLabel: restart || restartReport ? 'restartElapsedLabel' : 'elapsedLabel',
499
674
  trail: [],
500
675
  error: null,
501
676
  };
502
677
  if (view.busy) {
503
- view.button = 'starting';
678
+ view.button = restart ? 'none' : 'starting';
504
679
  view.elapsed = formatStartElapsed(snap.elapsedMs);
680
+ if (view.reason === 'restart') view.heading = 'headingRestarting';
505
681
  }
506
682
 
507
683
  // The reported steps, each at the time it was reported for, counted from the
508
684
  // click (or from the launch's own start when another page asked for it).
509
- if (launchOf) {
685
+ const firstPhase = (phase) => (sup ? sup.phases.find(p => p.phase === phase)?.at || (sup.phase === phase ? sup.at : 0) : 0);
686
+ if (restart) {
687
+ // A restart this page follows from its beginning: what the server said to
688
+ // it, what its own probe saw, then what the supervisor reports.
689
+ const add = (key, at) => { if (at !== null && at !== undefined) view.trail.push({ key, time: startStepSeconds(at - restart.askedAt) }); };
690
+ add('trailAccepted', restart.acceptedAt);
691
+ const exited = launchOf ? launchOf.steps.find(step => step.id === 'exited')?.at : undefined;
692
+ if (exited !== undefined) add('trailStopped', exited);
693
+ else add('trailNoAnswer', restart.downAt);
694
+ add('trailServer', firstPhase('server') || null);
695
+ add('trailListening', firstPhase('listening') || null);
696
+ } else if (launchOf) {
510
697
  const asked = snap.askedAt !== null && snap.askedAt !== undefined && launchOf.startedAt >= snap.askedAt - 2000;
511
698
  const zero = asked ? snap.askedAt : launchOf.startedAt;
512
699
  const add = (key, at) => { if (at) view.trail.push({ key, time: startStepSeconds(at - zero) }); };
513
- add('trailLauncher', launchOf.startedAt);
700
+ if (!restartReport) add('trailLauncher', launchOf.startedAt); // a restart has no launcher
514
701
  for (const step of launchOf.steps) {
515
702
  if (step.id === 'terminal') add('trailTerminal', step.at);
516
703
  if (step.id === 'spawned') add('trailProcess', step.at);
704
+ if (step.id === 'exited') add('trailStopped', step.at);
517
705
  }
518
706
  if (sup) {
519
- add('trailSupervisor', sup.startedAt);
520
- const first = (phase) => sup.phases.find(p => p.phase === phase)?.at || (sup.phase === phase ? sup.at : 0);
521
- add('trailServer', first('server'));
522
- add('trailListening', first('listening'));
707
+ // The supervisor of a restart was usually there long before it: listed only when it began with it.
708
+ if (!restartReport || sup.startedAt >= launchOf.startedAt - 2000) add('trailSupervisor', sup.startedAt);
709
+ add('trailServer', firstPhase('server'));
710
+ add('trailListening', firstPhase('listening'));
523
711
  }
524
712
  }
525
713
 
@@ -540,7 +728,34 @@ export function startStatusView(snap, { platform = 'linux', limits = START_LIMIT
540
728
  }
541
729
  if (state === 'stalled') {
542
730
  view.mascot = 'stalled';
543
- view.headline = launchOf?.state === 'gave-up' ? { key: 'gaveUp', params: {} } : { key: 'stalled', params: {} };
731
+ view.headline = launchOf?.state === 'gave-up' ? { key: 'gaveUp', params: {} }
732
+ : restart ? { key: 'restartStalled', params: {} } : { key: 'stalled', params: {} };
733
+ return view;
734
+ }
735
+ if (state === 'refused') {
736
+ // The server is still there: nothing to start, only the way back to the app.
737
+ const why = restart?.refused || {};
738
+ view.mascot = 'failed';
739
+ view.button = 'back';
740
+ view.heading = 'headingRestartFailed';
741
+ view.headline = why.code === 'unreachable' ? { key: 'restartUnreachable', params: {} }
742
+ : why.code === 'still-running' ? { key: 'restartStillRunning', params: {} }
743
+ : { key: 'restartRefused', params: { reason: why.message || `HTTP ${why.status || 0}` } };
744
+ return view;
745
+ }
746
+ if (state === 'restarting' && restart) {
747
+ if (restart.downAt !== null) {
748
+ view.mascot = 'working';
749
+ view.headline = { key: 'restartDown', params: {} };
750
+ } else if (restart.acceptedAt !== null) {
751
+ view.mascot = 'working';
752
+ view.headline = { key: 'restartStopping', params: {} };
753
+ const stoppingMs = snap.elapsedMs - (restart.acceptedAt - restart.askedAt);
754
+ if (stoppingMs >= limits.quietAfterMs) view.hint = { key: 'restartSlowStop', params: { seconds: Math.floor(stoppingMs / 1000) } };
755
+ } else {
756
+ view.mascot = 'waking';
757
+ view.headline = { key: 'restartAsked', params: {} };
758
+ }
544
759
  return view;
545
760
  }
546
761
  if (state === 'failed') {
@@ -568,6 +783,10 @@ export function startStatusView(snap, { platform = 'linux', limits = START_LIMIT
568
783
  // Slow on purpose (an install, a download, a backup): silence there is expected.
569
784
  const slow = ['dependencies', 'browser', 'build', 'snapshot'].includes(sup.phase);
570
785
  if (isQuiet && !slow) view.hint = { key: 'quiet', params: { seconds: quiet } };
786
+ } else if (restartReport) {
787
+ // The supervisor opened this report to bring the server back; it has named no step yet.
788
+ view.headline = { key: 'restartRespawning', params: {} };
789
+ if (isQuiet) view.hint = { key: 'quiet', params: { seconds: quiet } };
571
790
  } else if (launchOf.step === 'terminal') {
572
791
  view.headline = isQuiet ? { key: 'terminalQuiet', params: { seconds: quiet } } : { key: 'terminalOpened', params: {} };
573
792
  } else if (launchOf.step === 'spawned') {
@@ -33,6 +33,27 @@ const FAT_BLOB_KEYS = new Set([
33
33
  // as a string and still needs JSON.parse for the server payload.
34
34
  const STR_SENTINEL = Symbol('storage:str');
35
35
 
36
+ // Showcase mode: a page opened with `?showcase` (any value but "0" or "false")
37
+ // hydrates as usual but never writes back, so what a demo walk-through opens,
38
+ // moves or types leaves the user's saved UI state as it was. Reads work from
39
+ // the in-memory cache. (blob-store.js keeps its own writes.)
40
+ function _readShowcaseMode() {
41
+ try {
42
+ const params = new URLSearchParams(globalThis.window?.location?.search || '');
43
+ if (!params.has('showcase')) return false;
44
+ const value = String(params.get('showcase')).trim().toLowerCase();
45
+ return value !== '0' && value !== 'false';
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+ const SHOWCASE_MODE = _readShowcaseMode();
51
+
52
+ /** True when this page load is a showcase (`?showcase`): nothing is written back. */
53
+ export function isShowcaseMode() {
54
+ return SHOWCASE_MODE;
55
+ }
56
+
36
57
  // ── Hydrate from server ──────────────────────────────
37
58
 
38
59
  async function _hydrate() {
@@ -55,8 +76,8 @@ async function _hydrate() {
55
76
  // First run or server down: import from localStorage
56
77
  console.info('[storage] Migrating localStorage to server...');
57
78
  _migrateFromLocalStorage();
58
- // Build dirty set from everything we imported
59
- for (const [key, val] of Object.entries(_cache)) {
79
+ // Build dirty set from everything we imported (a showcase only reads it)
80
+ if (!SHOWCASE_MODE) for (const [key, val] of Object.entries(_cache)) {
60
81
  try { _dirty[key] = JSON.parse(val); } catch { _dirty[key] = val; }
61
82
  }
62
83
  if (Object.keys(_dirty).length > 0) {
@@ -74,6 +95,10 @@ else await _hydrate();
74
95
  // ── Drop-in API (mirrors localStorage) ───────────────
75
96
 
76
97
  export const storage = (hostedSidepanelId && sidepanelHost()?.storageFor(hostedSidepanelId)) || {
98
+ // False in showcase mode: values stay in this page. Code that sends a value to the server
99
+ // by itself (an unload beacon) asks this first.
100
+ persists: !SHOWCASE_MODE,
101
+
77
102
  getItem(key) {
78
103
  return _cache[key] ?? null;
79
104
  },
@@ -105,11 +130,13 @@ export const storage = (hostedSidepanelId && sidepanelHost()?.storageFor(hostedS
105
130
  // ── Debounced flush to server ────────────────────────
106
131
 
107
132
  function _scheduleDebouncedFlush() {
133
+ if (SHOWCASE_MODE) { _dirty = {}; return; }
108
134
  if (_flushTimer) clearTimeout(_flushTimer);
109
135
  _flushTimer = setTimeout(_flushToServer, DEBOUNCE_MS);
110
136
  }
111
137
 
112
138
  async function _flushToServer() {
139
+ if (SHOWCASE_MODE) { _dirty = {}; _flushTimer = null; return; }
113
140
  const payload = {};
114
141
  for (const [k, v] of Object.entries(_dirty)) {
115
142
  if (v === STR_SENTINEL) {
@@ -142,7 +169,7 @@ async function _flushToServer() {
142
169
 
143
170
  // ── Flush on page unload (sendBeacon for reliability) ──
144
171
 
145
- window.addEventListener('beforeunload', () => {
172
+ if (!SHOWCASE_MODE) window.addEventListener('beforeunload', () => {
146
173
  if (_flushTimer) {
147
174
  clearTimeout(_flushTimer);
148
175
  _flushTimer = null;