synabun 2.0.10 → 2.1.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 (517) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +2 -2
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +17 -8
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +1 -1
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +228 -0
  24. package/lib/virustotal.mjs +192 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  42. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  44. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  45. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  46. package/mcp-server/dist/services/neural-interface.js +67 -11
  47. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  48. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  49. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  50. package/mcp-server/dist/services/server-instructions.js +20 -62
  51. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  52. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  53. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  54. package/mcp-server/dist/services/tool-catalog.js +339 -0
  55. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  56. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  60. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  61. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  62. package/mcp-server/dist/services/tool-list.js +334 -0
  63. package/mcp-server/dist/services/tool-list.js.map +1 -0
  64. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  66. package/mcp-server/dist/services/tool-runtime.js +400 -0
  67. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  68. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  70. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  71. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  72. package/mcp-server/dist/tools/agents.d.ts +10 -10
  73. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  74. package/mcp-server/dist/tools/agents.js +22 -13
  75. package/mcp-server/dist/tools/agents.js.map +1 -1
  76. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  77. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  78. package/mcp-server/dist/tools/image-tools.js +1 -1
  79. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  80. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  81. package/mcp-server/dist/tools/skill-tools.js +639 -0
  82. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  83. package/mcp-server/dist/tools/skill.d.ts +7 -0
  84. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  85. package/mcp-server/dist/tools/skill.js +11 -0
  86. package/mcp-server/dist/tools/skill.js.map +1 -0
  87. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  89. package/mcp-server/dist/tools/tool-groups.js +147 -0
  90. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  91. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  93. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  94. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  97. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  98. package/mcp-server/package-lock.json +2 -2
  99. package/mcp-server/package.json +1 -1
  100. package/mcp-server/src/http.ts +75 -12
  101. package/mcp-server/src/index.ts +147 -154
  102. package/mcp-server/src/preload.ts +12 -12
  103. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  104. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  105. package/mcp-server/src/services/neural-interface.ts +79 -20
  106. package/mcp-server/src/services/server-instructions.ts +27 -63
  107. package/mcp-server/src/services/tool-catalog.ts +403 -0
  108. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  109. package/mcp-server/src/services/tool-list.ts +410 -0
  110. package/mcp-server/src/services/tool-runtime.ts +457 -0
  111. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  112. package/mcp-server/src/tools/agents.ts +22 -14
  113. package/mcp-server/src/tools/image-tools.ts +1 -1
  114. package/mcp-server/src/tools/skill-tools.ts +589 -0
  115. package/mcp-server/src/tools/skill.ts +13 -0
  116. package/mcp-server/src/tools/tool-groups.ts +164 -0
  117. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  118. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  119. package/neural-interface/i18n/en.json +3463 -200
  120. package/neural-interface/i18n/pt-BR.json +3411 -151
  121. package/neural-interface/lib/assistant-api.js +127 -28
  122. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  123. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  124. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  125. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  126. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  127. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  128. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  129. package/neural-interface/lib/assistant-catalog.js +6 -7
  130. package/neural-interface/lib/assistant-dispatch.js +871 -156
  131. package/neural-interface/lib/assistant-envelope.js +3 -1
  132. package/neural-interface/lib/assistant-persona.js +80 -11
  133. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  134. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  135. package/neural-interface/lib/assistant-route-gate.js +6 -4
  136. package/neural-interface/lib/assistant-router.js +13 -8
  137. package/neural-interface/lib/assistant-runtime.js +56 -18
  138. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  139. package/neural-interface/lib/assistant-tools.js +51 -0
  140. package/neural-interface/lib/backup-service.js +620 -3
  141. package/neural-interface/lib/backup-zip-reader.js +10 -5
  142. package/neural-interface/lib/backup-zip-writer.js +257 -0
  143. package/neural-interface/lib/browser-execution.js +10 -2
  144. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  145. package/neural-interface/lib/browser-network-guard.js +284 -0
  146. package/neural-interface/lib/browser-policy-roots.js +64 -0
  147. package/neural-interface/lib/browser-tool-policy.js +186 -31
  148. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  149. package/neural-interface/lib/claude-hooks-status.js +103 -0
  150. package/neural-interface/lib/claude-temporary.js +5 -11
  151. package/neural-interface/lib/cli-install.js +18 -138
  152. package/neural-interface/lib/cli-resolver.js +4 -1
  153. package/neural-interface/lib/cli-update-planner.js +32 -31
  154. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  155. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  156. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  157. package/neural-interface/lib/confined-fs.js +30 -3
  158. package/neural-interface/lib/crash-notice-api.js +98 -0
  159. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  160. package/neural-interface/lib/exec-loop-command.js +4 -8
  161. package/neural-interface/lib/http-guards.js +8 -2
  162. package/neural-interface/lib/install-details.js +4 -1
  163. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  164. package/neural-interface/lib/mcp-client-config.js +0 -29
  165. package/neural-interface/lib/mcp-external-servers.js +323 -0
  166. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  167. package/neural-interface/lib/morelogin.js +30 -0
  168. package/neural-interface/lib/native-loop-providers.js +547 -47
  169. package/neural-interface/lib/native-loop-runtime.js +268 -41
  170. package/neural-interface/lib/opencode-config.js +557 -0
  171. package/neural-interface/lib/opencode-db-schema.js +17 -34
  172. package/neural-interface/lib/opencode-history.js +48 -218
  173. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  174. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  175. package/neural-interface/lib/opencode-protocol.js +249 -112
  176. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  177. package/neural-interface/lib/opencode-serve-records.js +39 -21
  178. package/neural-interface/lib/opencode-v2-client.js +48 -153
  179. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  180. package/neural-interface/lib/opencode-worker-data.js +27 -0
  181. package/neural-interface/lib/opencode2-client.js +73 -37
  182. package/neural-interface/lib/opencode2-proxy.js +23 -63
  183. package/neural-interface/lib/opencode2-translate.js +51 -2
  184. package/neural-interface/lib/owner-authority.js +249 -0
  185. package/neural-interface/lib/page-session.js +275 -0
  186. package/neural-interface/lib/peer-identity.js +763 -0
  187. package/neural-interface/lib/request-gate.js +17 -2
  188. package/neural-interface/lib/rulesets/api.js +1 -1
  189. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  190. package/neural-interface/lib/rulesets/installer.js +92 -27
  191. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  192. package/neural-interface/lib/session-cache-builder.js +17 -30
  193. package/neural-interface/lib/session-title-generator.js +58 -36
  194. package/neural-interface/lib/skills/api-v2.js +495 -0
  195. package/neural-interface/lib/skills/bundle.js +418 -0
  196. package/neural-interface/lib/skills/catalog.js +294 -0
  197. package/neural-interface/lib/skills/creation-api.js +191 -0
  198. package/neural-interface/lib/skills/creation-brief.js +247 -0
  199. package/neural-interface/lib/skills/creation-sources.js +267 -0
  200. package/neural-interface/lib/skills/creation.js +843 -0
  201. package/neural-interface/lib/skills/deploy.js +564 -0
  202. package/neural-interface/lib/skills/digest.js +119 -0
  203. package/neural-interface/lib/skills/discover.js +111 -0
  204. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  205. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  206. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  207. package/neural-interface/lib/skills/eval-trace.js +119 -0
  208. package/neural-interface/lib/skills/evals-api.js +212 -0
  209. package/neural-interface/lib/skills/evals.js +1573 -0
  210. package/neural-interface/lib/skills/export.js +200 -0
  211. package/neural-interface/lib/skills/frontmatter.js +64 -0
  212. package/neural-interface/lib/skills/health.js +75 -0
  213. package/neural-interface/lib/skills/json-edit.js +165 -0
  214. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  215. package/neural-interface/lib/skills/known-bridges.js +96 -0
  216. package/neural-interface/lib/skills/library.js +823 -0
  217. package/neural-interface/lib/skills/local-store.js +21 -0
  218. package/neural-interface/lib/skills/migration.js +433 -0
  219. package/neural-interface/lib/skills/operations.js +45 -0
  220. package/neural-interface/lib/skills/ownership.js +427 -0
  221. package/neural-interface/lib/skills/packages.js +88 -0
  222. package/neural-interface/lib/skills/reviews.js +212 -0
  223. package/neural-interface/lib/skills/revision.js +80 -0
  224. package/neural-interface/lib/skills/roots.js +262 -0
  225. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  226. package/neural-interface/lib/skills/scan-assets.js +252 -0
  227. package/neural-interface/lib/skills/scan-context.js +26 -0
  228. package/neural-interface/lib/skills/scan-shell.js +324 -0
  229. package/neural-interface/lib/skills/scan.js +364 -0
  230. package/neural-interface/lib/skills/scanners.js +54 -0
  231. package/neural-interface/lib/skills/service.js +522 -0
  232. package/neural-interface/lib/skills/telemetry.js +113 -0
  233. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  234. package/neural-interface/lib/tool-lists.js +124 -0
  235. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  236. package/neural-interface/lib/worker-confinement.js +1192 -0
  237. package/neural-interface/lib/worker-guard-setting.js +56 -0
  238. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  239. package/neural-interface/lib/worker-relay-ports.js +227 -0
  240. package/neural-interface/lib/worker-resume.js +229 -0
  241. package/neural-interface/lib/worker-sandbox.js +299 -0
  242. package/neural-interface/package-lock.json +18 -12
  243. package/neural-interface/package.json +2 -2
  244. package/neural-interface/public/index.html +3 -1
  245. package/neural-interface/public/index2d.html +19 -11
  246. package/neural-interface/public/onboarding.html +2 -2
  247. package/neural-interface/public/shared/account-menu.js +8 -5
  248. package/neural-interface/public/shared/agent-runtime-options.js +35 -19
  249. package/neural-interface/public/shared/api.js +1527 -1336
  250. package/neural-interface/public/shared/app-frame.js +192 -0
  251. package/neural-interface/public/shared/assistant/asst-agents-dock.js +33 -2
  252. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  253. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  254. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  255. package/neural-interface/public/shared/assistant/asst-brain-picker.js +49 -35
  256. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  257. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  258. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  259. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  260. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  261. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  262. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  263. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  264. package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
  265. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  266. package/neural-interface/public/shared/assistant/asst-render.js +401 -77
  267. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  268. package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
  269. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  270. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  271. package/neural-interface/public/shared/assistant/asst-state.js +11 -15
  272. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  273. package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
  274. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
  275. package/neural-interface/public/shared/automation-context.js +5 -2
  276. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  277. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  278. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  279. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  280. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  281. package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
  282. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  283. package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
  284. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  285. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  286. package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
  287. package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
  288. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  289. package/neural-interface/public/shared/cli-install-view.js +4 -8
  290. package/neural-interface/public/shared/cli-status.js +2 -2
  291. package/neural-interface/public/shared/constants.js +2 -2
  292. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  293. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  294. package/neural-interface/public/shared/context-menu-model.js +366 -0
  295. package/neural-interface/public/shared/context-menu.js +372 -0
  296. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  297. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  298. package/neural-interface/public/shared/desktop-update-view.js +20 -0
  299. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  300. package/neural-interface/public/shared/html-shell.js +9 -11
  301. package/neural-interface/public/shared/i18n.js +2 -0
  302. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  303. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  304. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  305. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  306. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  307. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  308. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  309. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  310. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  311. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  312. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  313. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +47 -252
  314. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  315. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  316. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +6 -21
  317. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  318. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  319. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  320. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  321. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  322. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  323. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  324. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +0 -21
  325. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  326. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +14 -47
  327. package/neural-interface/public/shared/page-session.js +96 -0
  328. package/neural-interface/public/shared/registry.js +5 -3
  329. package/neural-interface/public/shared/retired-2d.js +3 -0
  330. package/neural-interface/public/shared/schedules-view.js +195 -61
  331. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  332. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  333. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  334. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  335. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  336. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  337. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  338. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  339. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  340. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  341. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  342. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  343. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  344. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  345. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  346. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  347. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  348. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  349. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  350. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  351. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  352. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  353. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  354. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  355. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  356. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  357. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  358. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  359. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  360. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  361. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  362. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  363. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  364. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  365. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  366. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  367. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  368. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  369. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  370. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  371. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  372. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  373. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  374. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  375. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  376. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  377. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  378. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  379. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  380. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  381. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  382. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  383. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  384. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  385. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  386. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  387. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  388. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  389. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  390. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  391. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  392. package/neural-interface/public/shared/styles.css +1525 -1779
  393. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  394. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  395. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  396. package/neural-interface/public/shared/tool-list-view.js +112 -0
  397. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  398. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  399. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  400. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  401. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  402. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  403. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  404. package/neural-interface/public/shared/tour/tour.css +144 -0
  405. package/neural-interface/public/shared/ui-automation-studio.js +91 -128
  406. package/neural-interface/public/shared/ui-claude-panel.js +77 -5
  407. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  408. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  409. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  410. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  411. package/neural-interface/public/shared/ui-detail.js +15 -3
  412. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  413. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  414. package/neural-interface/public/shared/ui-menubar.js +8 -18
  415. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  416. package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
  417. package/neural-interface/public/shared/ui-navbar.js +4 -24
  418. package/neural-interface/public/shared/ui-panels.js +96 -65
  419. package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
  420. package/neural-interface/public/shared/ui-sessions.js +5 -3
  421. package/neural-interface/public/shared/ui-settings.js +594 -462
  422. package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
  423. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  424. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  425. package/neural-interface/public/shared/ui-skills.js +33 -2830
  426. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  427. package/neural-interface/public/shared/ui-sync.js +15 -5
  428. package/neural-interface/public/shared/ui-terminal.js +124 -180
  429. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  430. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  431. package/neural-interface/public/shared/ui-trash.js +6 -4
  432. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  433. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  434. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  435. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  436. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  437. package/neural-interface/public/shared/ui-update.js +38 -2
  438. package/neural-interface/public/shared/ui-whiteboard.js +311 -58
  439. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  440. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  441. package/neural-interface/public/shared/view-variant.js +66 -0
  442. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  443. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  444. package/neural-interface/public/shared/worker-end.js +10 -3
  445. package/neural-interface/public/shared/worker-restart.js +12 -0
  446. package/neural-interface/public/sidepanel-session.html +1 -1
  447. package/neural-interface/public/variant/2d/background.js +2 -1
  448. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  449. package/neural-interface/public/variant/2d/graph.js +42 -20
  450. package/neural-interface/public/variant/2d/hulls.js +14 -9
  451. package/neural-interface/public/variant/2d/lasso.js +11 -7
  452. package/neural-interface/public/variant/2d/main.js +32 -1
  453. package/neural-interface/public/variant/2d/minimap.js +11 -13
  454. package/neural-interface/public/variant/3d/main.js +69 -6
  455. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  456. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  457. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  458. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  459. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  460. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  461. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  462. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  463. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  464. package/neural-interface/scripts/build-vendor.mjs +32 -0
  465. package/neural-interface/server.js +3934 -2454
  466. package/neural-interface/templates/rulesets/core.md +1 -1
  467. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  468. package/neural-interface/templates/rulesets/manifest.json +8 -8
  469. package/npm-shrinkwrap.json +18 -12
  470. package/package.json +16 -3
  471. package/postinstall.js +9 -4
  472. package/preuninstall.js +1 -1
  473. package/setup.js +97 -322
  474. package/skills/skill-forge/SKILL.md +177 -0
  475. package/skills/skill-forge/evals/evals.json +54 -0
  476. package/skills/skill-forge/references/package.md +105 -0
  477. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  478. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  479. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  480. package/skills/synabun/SKILL.md +10 -7
  481. package/skills/synabun/modules/audit.md +1 -5
  482. package/skills/synabun/modules/changelog.md +1 -5
  483. package/skills/synabun/modules/idea.md +1 -1
  484. package/skills/synabun/modules/memorize.md +6 -9
  485. package/skills/synabun/modules/schedule.md +4 -4
  486. package/mcp-server/dist/services/profiles.d.ts +0 -120
  487. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  488. package/mcp-server/dist/services/profiles.js +0 -387
  489. package/mcp-server/dist/services/profiles.js.map +0 -1
  490. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  491. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  492. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  493. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  494. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  495. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  496. package/mcp-server/dist/tools/leonardo.js +0 -21
  497. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  498. package/mcp-server/dist/tools/profile.d.ts +0 -17
  499. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  500. package/mcp-server/dist/tools/profile.js +0 -114
  501. package/mcp-server/dist/tools/profile.js.map +0 -1
  502. package/mcp-server/src/services/profiles.ts +0 -428
  503. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  504. package/mcp-server/src/tools/leonardo.ts +0 -30
  505. package/mcp-server/src/tools/profile.ts +0 -122
  506. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  507. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  508. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  509. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  510. package/skills/leonardo/SKILL.md +0 -168
  511. package/skills/leonardo/icon.svg +0 -1
  512. package/skills/leonardo/modules/browser-guide.md +0 -378
  513. package/skills/leonardo/modules/image-prompter.md +0 -283
  514. package/skills/leonardo/modules/model-advisor.md +0 -100
  515. package/skills/leonardo/modules/prompt-library.md +0 -110
  516. package/skills/leonardo/modules/style-guide.md +0 -186
  517. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ /** Shared required scan stage; failures still write auditable reports. */
3
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
4
+ import { basename, resolve } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { applyReviews, credentials, hashFile, reports, VirusTotal } from './virustotal.mjs';
7
+
8
+ export async function scanRelease({ files = [], urls = [], out, prefix = 'virustotal', reviewFile, client = new VirusTotal(), now = Date.now() }) {
9
+ if (!files.length && !urls.length) throw new Error('No scan subjects supplied');
10
+ const items = [];
11
+ for (const file of files) {
12
+ const path = typeof file === 'string' ? file : file.path;
13
+ const expected = typeof file === 'string' ? undefined : file.sha256;
14
+ // Missing/mismatched local bytes are hard errors, never reviewable detections.
15
+ const sha256 = await hashFile(path);
16
+ if (expected && sha256 !== expected) throw new Error(`SHA-256 mismatch: ${basename(path)}`);
17
+ try { items.push(await client.file(path, sha256)); }
18
+ catch (error) { items.push({ kind: 'file', name: basename(path), sha256, status: 'error', analysis_status: 'unavailable', error: error.message }); }
19
+ }
20
+ for (const url of urls) {
21
+ try { items.push(await client.url(url)); }
22
+ catch (error) { items.push({ kind: 'url', name: url, status: 'error', analysis_status: 'unavailable', error: error.message }); }
23
+ }
24
+ const reviews = reviewFile ? JSON.parse(await readFile(reviewFile, 'utf8')) : [];
25
+ const result = reports(applyReviews(items, reviews, now));
26
+ await mkdir(out, { recursive: true });
27
+ await writeFile(resolve(out, `${prefix}.json`), result.json);
28
+ await writeFile(resolve(out, `${prefix}.md`), result.markdown);
29
+ if (!result.report.approved) throw new Error(`VirusTotal gate blocked; inspect ${prefix}.json/.md. Retry failures/inconclusive scans; detections require a hash- and evidence-specific review (docs/release-pipeline.md).`);
30
+ return result;
31
+ }
32
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
33
+ try {
34
+ credentials();
35
+ const files = []; const urls = []; let out, reviewFile, prefix = 'virustotal';
36
+ const args = process.argv.slice(2);
37
+ while (args.length) {
38
+ const arg = args.shift();
39
+ if (arg === '--out') out = args.shift();
40
+ else if (arg === '--prefix') prefix = args.shift();
41
+ else if (arg === '--review') reviewFile = args.shift();
42
+ else if (arg === '--url') urls.push(args.shift());
43
+ else if (arg.startsWith('-')) throw new Error(`Unknown option: ${arg}`);
44
+ else files.push(arg);
45
+ }
46
+ if (!out) throw new Error('Usage: node lib/release-scan.mjs --out DIR [--review FILE] [--url https://synabun.ai/] [FILE ...]');
47
+ if (!['virustotal', 'npm-virustotal'].includes(prefix)) throw new Error('Use prefix virustotal or npm-virustotal');
48
+ await scanRelease({ files, urls, out, prefix, reviewFile });
49
+ } catch (error) { console.error(error.message); process.exitCode = 1; }
50
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * SynaBun — the record of a crash the supervisor recovered from
3
+ *
4
+ * When the server crashes after it was up, its supervisor (lib/server-supervisor.js)
5
+ * starts it again and leaves this record for the server it starts:
6
+ * DATA_HOME/data/server-crash.json. That server shows it to the owner's pages
7
+ * (GET /api/system/crash-notice, neural-interface/lib/crash-notice-api.js) until
8
+ * it is dismissed; a dismissal removes it, so it stays gone in every tab and
9
+ * window, and the next crash writes a new one, with a new id, which shows again.
10
+ *
11
+ * One writer at a time: the supervisor writes the record only while no server
12
+ * runs (between a crash and the next start) and removes it when it ends without
13
+ * a running server after all (the crash-loop guard gave up, the server it
14
+ * started again never came up: the app's stopped page says what happened then);
15
+ * the server removes it, while it runs, when the owner dismisses it. Nothing is
16
+ * written for a stop, a restart, an update or a start that failed.
17
+ *
18
+ * Never throws. `fs` can be handed in (tests).
19
+ */
20
+
21
+ import { randomBytes } from 'node:crypto';
22
+ import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
23
+ import { dirname, join } from 'node:path';
24
+
25
+ export const CRASH_RECORD_NAME = 'server-crash.json';
26
+ export const CRASH_RECORD_VERSION = 1;
27
+
28
+ const NODE_FS = Object.freeze({ mkdirSync, readFileSync, renameSync, rmSync, writeFileSync });
29
+ const ID = /^[0-9a-f]{16}$/;
30
+ const HEX = /^0x[0-9A-F]{8}$/;
31
+ const SIGNAL = /^SIG[A-Z0-9]{1,10}$/;
32
+ const KIND = /^[a-z][a-z-]{0,23}$/;
33
+
34
+ /** Where the record is: beside the supervisor record and the server's logs. */
35
+ export function crashRecordPath(dataHome) {
36
+ return join(dataHome, 'data', CRASH_RECORD_NAME);
37
+ }
38
+
39
+ /** A record, checked field by field (what a page is shown comes from here only); null when it is not one. */
40
+ export function normalizeCrashRecord(raw) {
41
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;
42
+ if (raw.v !== CRASH_RECORD_VERSION || typeof raw.id !== 'string' || !ID.test(raw.id)) return null;
43
+ const at = Number(raw.at);
44
+ if (!Number.isFinite(at) || at <= 0) return null;
45
+ const firstAt = Number(raw.firstAt);
46
+ const exit = raw.exit && typeof raw.exit === 'object' && !Array.isArray(raw.exit) ? raw.exit : {};
47
+ const uptimeMs = Number(raw.uptimeMs);
48
+ return {
49
+ v: CRASH_RECORD_VERSION,
50
+ id: raw.id,
51
+ at,
52
+ firstAt: Number.isFinite(firstAt) && firstAt > 0 && firstAt <= at ? firstAt : at,
53
+ count: Number.isInteger(raw.count) && raw.count > 0 ? Math.min(raw.count, 999) : 1,
54
+ exit: {
55
+ ...(Number.isInteger(exit.code) ? { code: exit.code } : {}),
56
+ ...(typeof exit.hex === 'string' && HEX.test(exit.hex) ? { hex: exit.hex } : {}),
57
+ ...(typeof exit.signal === 'string' && SIGNAL.test(exit.signal) ? { signal: exit.signal } : {}),
58
+ kind: typeof exit.kind === 'string' && KIND.test(exit.kind) ? exit.kind : 'exit',
59
+ },
60
+ ...(raw.uptimeMs !== undefined && raw.uptimeMs !== null && Number.isFinite(uptimeMs) && uptimeMs >= 0 ? { uptimeMs: Math.round(uptimeMs) } : {}),
61
+ };
62
+ }
63
+
64
+ /** The record in the data home, or null. */
65
+ export function readCrashRecord(dataHome, { fs = NODE_FS } = {}) {
66
+ try {
67
+ return normalizeCrashRecord(JSON.parse(fs.readFileSync(crashRecordPath(dataHome), 'utf8')));
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * The supervisor's side: the server crashed after it was up and is being started
75
+ * again. `exit` is lib/server-exit.js describeExit's answer. A record the owner
76
+ * has not dismissed yet is counted on (`count` + 1, its `firstAt` kept); the id
77
+ * is always new. Written whole (a temporary file renamed over it). Returns the
78
+ * record written, or null.
79
+ */
80
+ export function writeCrashRecord(dataHome, { exit = {}, at = Date.now(), uptimeMs = null, pid = process.pid } = {}, {
81
+ fs = NODE_FS, newId = () => randomBytes(8).toString('hex'),
82
+ } = {}) {
83
+ const path = crashRecordPath(dataHome);
84
+ const before = readCrashRecord(dataHome, { fs });
85
+ const record = normalizeCrashRecord({
86
+ v: CRASH_RECORD_VERSION,
87
+ id: newId(),
88
+ at,
89
+ firstAt: before ? before.firstAt : at,
90
+ count: before ? before.count + 1 : 1,
91
+ exit: { code: exit?.code, hex: exit?.hex, signal: exit?.signal, kind: exit?.kind },
92
+ uptimeMs,
93
+ });
94
+ if (!record) return null;
95
+ const temp = `${path}.${pid}.tmp`;
96
+ try {
97
+ fs.mkdirSync(dirname(path), { recursive: true });
98
+ fs.writeFileSync(temp, `${JSON.stringify(record)}\n`);
99
+ fs.renameSync(temp, path);
100
+ return record;
101
+ } catch {
102
+ try { fs.rmSync(temp, { force: true }); } catch {}
103
+ return null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Remove the record: only the one named `id` when an id is given (a page that
109
+ * dismisses an older crash leaves a newer one in place), else whatever is there.
110
+ * Returns true when no record of that id is left.
111
+ */
112
+ export function clearCrashRecord(dataHome, { id = null, fs = NODE_FS } = {}) {
113
+ if (id !== null) {
114
+ const current = readCrashRecord(dataHome, { fs });
115
+ if (!current || current.id !== id) return !current;
116
+ }
117
+ try {
118
+ fs.rmSync(crashRecordPath(dataHome), { force: true });
119
+ return true;
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
@@ -0,0 +1,318 @@
1
+ /**
2
+ * SynaBun — what the end of the server process says, and what its supervisor does
3
+ *
4
+ * setup.js runs the Neural Interface server as a child and decides, when that
5
+ * child ends, whether to start it again (lib/server-supervisor.js). The exit
6
+ * status alone cannot say whether an end was asked for: every way out of
7
+ * server.js passes its `exit` handler, which ends the process by force to skip
8
+ * native teardown (Windows then reports exit code 1, macOS and Linux SIGKILL).
9
+ * So the server says so for a stop that was asked for (a restart has its own
10
+ * marker), twice over: a stop marker in the data home, and a line on its stdout
11
+ * with the token its supervisor gave it, which needs no disk. The status is read
12
+ * for what it can tell: a crash, and of which kind.
13
+ *
14
+ * describeExit({ code, signal }) the status in plain words, for the log, the
15
+ * supervisor record and the app's stopped page
16
+ * serverExitDecision({ ... }) restart / stopped / crashed / failed
17
+ * writeStopMarker / readStopMarker the marker, written whole, read strictly
18
+ * readRestartMarker when the restart was asked for
19
+ * stopAnnouncement / readStopAnnouncement the stop line on stdout
20
+ * watchChildEnd(child, ...) the end of the child, once its output is read
21
+ * createRestartGuard({ ... }) the crash-loop guard: so many starts in a window
22
+ *
23
+ * No imports and no I/O of its own (a writer is handed its file functions). The
24
+ * words are English: the desktop app localizes a status by its `kind`
25
+ * (packaging/desktop/src/server-exit-view.mjs), the page by its `kind` too.
26
+ */
27
+
28
+ /** The sentinel a supervised server exits with to be started again (sysexits EX_TEMPFAIL). */
29
+ export const RESTART_EXIT_CODE = 75;
30
+
31
+ /** The marker server.js leaves in the data home before a stop that was asked for (shutdown, update, signal). */
32
+ export const STOP_MARKER_NAME = 'stop-requested';
33
+
34
+ /** The environment variable that hands a server its supervisor's stop token (one per start, never inherited further). */
35
+ export const STOP_TOKEN_ENV = 'SYNABUN_STOP_TOKEN';
36
+
37
+ /** What a stop reason may be in a marker or a stop line: a short word ("shutdown", "run-update", "sigterm"). */
38
+ export function cleanStopReason(reason) {
39
+ return typeof reason === 'string' ? reason.replace(/[^\w-]/g, '').slice(0, 40) : '';
40
+ }
41
+
42
+ /**
43
+ * Write the stop marker whole: a temporary file beside it, renamed over it, so
44
+ * the supervisor never reads half a marker. `fs` is { writeFileSync,
45
+ * renameSync, rmSync }. Returns { ok: true } when it is in place, else
46
+ * { ok: false, error } (its code: ENOSPC, EROFS, EPERM, …). Never throws.
47
+ */
48
+ export function writeStopMarker(path, { reason, pid, at }, fs) {
49
+ const temp = `${path}.${pid}.tmp`;
50
+ const text = JSON.stringify({ reason: cleanStopReason(reason), pid, at });
51
+ try {
52
+ fs.writeFileSync(temp, text);
53
+ fs.renameSync(temp, path);
54
+ return { ok: true };
55
+ } catch (error) {
56
+ try { fs.rmSync(temp, { force: true }); } catch {}
57
+ return { ok: false, error: String(error?.code || error?.message || error || 'unknown').slice(0, 60) };
58
+ }
59
+ }
60
+
61
+ /** How much earlier than the server's start a marker may say it was written: a clock that moved a little. */
62
+ export const MARKER_CLOCK_SLACK_MS = 60_000;
63
+
64
+ /**
65
+ * The marker as the supervisor takes it: complete JSON left by `pid` (the
66
+ * server it started, never "any server"), written no earlier than `since` (when
67
+ * it started that server) less a minute for a clock that moved. Returns
68
+ * { reason, at } or null: an empty, truncated, foreign or stale file is no stop.
69
+ */
70
+ export function readStopMarker(text, { pid, since = 0 } = {}) {
71
+ if (!Number.isInteger(pid) || pid <= 0) return null;
72
+ let saved;
73
+ try { saved = JSON.parse(String(text ?? '')); } catch { return null; }
74
+ if (!saved || typeof saved !== 'object' || Array.isArray(saved)) return null;
75
+ if (saved.pid !== pid) return null;
76
+ if (!Number.isFinite(saved.at) || saved.at < Number(since || 0) - MARKER_CLOCK_SLACK_MS) return null;
77
+ return { reason: cleanStopReason(saved.reason), at: saved.at };
78
+ }
79
+
80
+ /** When the restart marker says the restart was asked for (server.js writes its Date.now() in it), or null. */
81
+ export function readRestartMarker(text) {
82
+ const value = String(text ?? '').trim();
83
+ if (!/^\d{1,16}$/.test(value)) return null;
84
+ const at = Number(value);
85
+ return Number.isSafeInteger(at) && at > 0 ? at : null;
86
+ }
87
+
88
+ /** The line a server prints when a stop was asked of it: its supervisor reads it on stdout, with no disk involved. */
89
+ export function stopAnnouncement(token, reason) {
90
+ return `[server] Stop requested: ${cleanStopReason(reason) || 'stop'} (supervisor ${token})`;
91
+ }
92
+
93
+ /** The reason of a stop line that carries `token` (the one the supervisor gave this server), or null. */
94
+ export function readStopAnnouncement(line, token) {
95
+ if (typeof token !== 'string' || !/^[0-9a-f]{32}$/.test(token)) return null;
96
+ const found = /^\[server\] Stop requested: ([\w-]{1,40}) \(supervisor ([0-9a-f]{32})\)\s*$/.exec(String(line ?? '').trim());
97
+ return found && found[2] === token ? { reason: found[1] } : null;
98
+ }
99
+
100
+ /** Signals that stop a process on request. Any other signal is a crash or a forced end (systemd's "clean" set). */
101
+ export const CLEAN_SIGNALS = Object.freeze(['SIGTERM', 'SIGINT', 'SIGHUP', 'SIGPIPE']);
102
+
103
+ /** What each kind is called in a sentence: "0xC0000409, native crash". */
104
+ export const EXIT_KIND_WORDS = Object.freeze({
105
+ 'native-crash': 'native crash',
106
+ 'out-of-memory': 'out of memory',
107
+ 'node-fatal': 'Node.js fatal error',
108
+ error: 'stopped by an error',
109
+ killed: 'ended by force',
110
+ interrupted: 'interrupted',
111
+ stopped: 'stopped on request',
112
+ restart: 'restart requested',
113
+ exit: '',
114
+ });
115
+
116
+ // Windows ends a crashed process with an NTSTATUS. These are the ones a Node.js
117
+ // server meets; anything else in the error range is still reported by number.
118
+ const WINDOWS_STATUS = new Map([
119
+ [0xC0000409, ['STATUS_STACK_BUFFER_OVERRUN', 'native-crash', 'a fail-fast inside the process: abort() (an uncaught C++ exception, a failed native assertion) or a failed security check']],
120
+ [0xC0000005, ['STATUS_ACCESS_VIOLATION', 'native-crash', 'an invalid memory access']],
121
+ [0xC00000FD, ['STATUS_STACK_OVERFLOW', 'native-crash', 'a native stack overflow']],
122
+ [0xC0000374, ['STATUS_HEAP_CORRUPTION', 'native-crash', 'corrupted native memory']],
123
+ [0xC000001D, ['STATUS_ILLEGAL_INSTRUCTION', 'native-crash', 'an instruction this processor does not have']],
124
+ [0xC0000096, ['STATUS_PRIVILEGED_INSTRUCTION', 'native-crash', 'a privileged instruction']],
125
+ [0xC0000094, ['STATUS_INTEGER_DIVIDE_BY_ZERO', 'native-crash', 'an integer division by zero']],
126
+ [0xC0000006, ['STATUS_IN_PAGE_ERROR', 'native-crash', 'a page of the program could not be read (a disk or network error)']],
127
+ [0xC0000420, ['STATUS_ASSERTION_FAILURE', 'native-crash', 'a failed native assertion']],
128
+ [0xC0000142, ['STATUS_DLL_INIT_FAILED', 'native-crash', 'a DLL could not initialize']],
129
+ [0x80000003, ['STATUS_BREAKPOINT', 'native-crash', 'a fatal error trap (V8 or libuv stopping the process)']],
130
+ [0xC0000017, ['STATUS_NO_MEMORY', 'out-of-memory', 'Windows had no memory left to give it']],
131
+ [0xC000012D, ['STATUS_COMMITMENT_LIMIT', 'out-of-memory', 'the system commit limit was reached']],
132
+ [0xC000013A, ['STATUS_CONTROL_C_EXIT', 'interrupted', 'Ctrl+C, or the console it ran in was closed']],
133
+ [0x40010004, ['DBG_TERMINATE_PROCESS', 'killed', 'ended by a debugger or by Windows']],
134
+ ]);
135
+
136
+ // Exit codes of a Node.js process.
137
+ const NODE_CODES = new Map([
138
+ [1, ['error', 'an error the server did not handle (printed above), or it was ended by force']],
139
+ [7, ['error', 'its own error handler failed']],
140
+ [134, ['node-fatal', 'Node.js stopped it ("FATAL ERROR" above: out of memory, or an internal check)']],
141
+ [RESTART_EXIT_CODE, ['restart', 'it asked to be started again']],
142
+ ]);
143
+
144
+ const SIGNALS = new Map([
145
+ ['SIGSEGV', ['native-crash', 'a segmentation fault (invalid memory access)']],
146
+ ['SIGBUS', ['native-crash', 'a bus error (invalid memory access)']],
147
+ ['SIGILL', ['native-crash', 'an illegal instruction']],
148
+ ['SIGFPE', ['native-crash', 'an arithmetic error']],
149
+ ['SIGABRT', ['native-crash', 'abort(): an uncaught C++ exception, a failed native assertion or a fatal error']],
150
+ ['SIGTRAP', ['native-crash', 'a fatal error trap']],
151
+ ['SIGSYS', ['native-crash', 'a bad system call']],
152
+ ['SIGKILL', ['killed', 'by force, or by the system when memory ran out']],
153
+ ['SIGTERM', ['stopped', 'it was asked to stop']],
154
+ ['SIGINT', ['stopped', 'it was interrupted']],
155
+ ['SIGHUP', ['stopped', 'its terminal went away']],
156
+ ['SIGPIPE', ['stopped', 'its output was closed']],
157
+ ]);
158
+
159
+ /**
160
+ * `code` as an unsigned 32-bit hex status ("0xC0000409") when it is one: an
161
+ * NTSTATUS-sized value (as Node reports it, or as a negative 32-bit number).
162
+ * Null for ordinary exit codes.
163
+ */
164
+ export function exitStatusHex(code) {
165
+ if (typeof code !== 'number' || !Number.isInteger(code)) return null;
166
+ if (code < 0 && code >= -0x80000000) code >>>= 0;
167
+ if (code < 0x40000000 || code > 0xFFFFFFFF) return null;
168
+ return `0x${code.toString(16).toUpperCase().padStart(8, '0')}`;
169
+ }
170
+
171
+ /**
172
+ * The exit status in plain words. Returns
173
+ * { code, signal, hex, name, kind, detail, label, text }
174
+ * `kind` is one of the keys of EXIT_KIND_WORDS; `label` its words; `text` the
175
+ * short form a person reads ("0xC0000409, native crash", "exit code 1, …",
176
+ * "SIGSEGV, native crash"); `detail` what it means, for the log.
177
+ */
178
+ export function describeExit({ code = null, signal = null } = {}) {
179
+ const numeric = typeof code === 'number' && Number.isInteger(code) ? code : null;
180
+ const sig = typeof signal === 'string' && /^SIG[A-Z0-9]+$/.test(signal) ? signal : null;
181
+ const hex = exitStatusHex(numeric);
182
+ let name = null;
183
+ let kind = 'exit';
184
+ let detail = '';
185
+ if (sig) {
186
+ name = sig;
187
+ const known = SIGNALS.get(sig);
188
+ if (known) [kind, detail] = known;
189
+ else { kind = 'killed'; detail = 'ended by that signal'; }
190
+ } else if (hex) {
191
+ const unsigned = Number.parseInt(hex.slice(2), 16);
192
+ const known = WINDOWS_STATUS.get(unsigned);
193
+ if (known) [name, kind, detail] = known;
194
+ else if (unsigned >= 0xC0000000 && unsigned <= 0xCFFFFFFF) { kind = 'native-crash'; detail = 'a Windows error status'; }
195
+ } else if (numeric !== null) {
196
+ const known = NODE_CODES.get(numeric);
197
+ if (known) [kind, detail] = known;
198
+ else if (numeric === 0) detail = 'a normal exit';
199
+ }
200
+ const label = EXIT_KIND_WORDS[kind] || '';
201
+ const head = sig || hex || (numeric !== null ? `exit code ${numeric}` : 'no exit status');
202
+ return {
203
+ code: numeric,
204
+ signal: sig,
205
+ hex,
206
+ name,
207
+ kind,
208
+ detail,
209
+ label,
210
+ text: label ? `${head}, ${label}` : head,
211
+ };
212
+ }
213
+
214
+ /**
215
+ * One line for the supervisor's log: "exit code 3221226505 = 0xC0000409,
216
+ * native crash (STATUS_STACK_BUFFER_OVERRUN): a fail-fast inside …".
217
+ */
218
+ export function formatExit(described) {
219
+ const d = described && typeof described === 'object' && 'kind' in described ? described : describeExit(described || {});
220
+ const head = d.signal ? `signal ${d.signal}` : d.code !== null ? `exit code ${d.code}${d.hex ? ` = ${d.hex}` : ''}` : 'no exit status';
221
+ const label = d.label ? `, ${d.label}` : '';
222
+ const name = d.name && d.name !== d.signal ? ` (${d.name})` : '';
223
+ const detail = d.detail ? `: ${d.detail}` : '';
224
+ return `${head}${label}${name}${detail}`;
225
+ }
226
+
227
+ /**
228
+ * What the supervisor does when the server it runs has ended:
229
+ * 'restart' it asked to be started again (exit code 75 or the restart marker)
230
+ * 'stopped' it was asked to stop (the stop marker), or stopped cleanly
231
+ * (exit code 0, a clean signal, Windows' Ctrl+C status): the
232
+ * supervisor ends too
233
+ * 'failed' it ended before it was listening: a start that failed, reported
234
+ * as one (starting it again would fail the same way)
235
+ * 'crashed' it ended on its own after it was up: started again, within the
236
+ * supervisor's crash-loop guard
237
+ * Asked for both, the later request wins: `stopAfterRestart` (the stop was
238
+ * asked when, or after, the restart was: the desktop app quitting while a
239
+ * restart shuts down) is a stop; otherwise the restart goes ahead.
240
+ */
241
+ export function serverExitDecision({ code = null, signal = null, restartRequested = false, stopRequested = false, stopAfterRestart = false, listened = false } = {}) {
242
+ if (stopRequested && stopAfterRestart) return 'stopped';
243
+ if (restartRequested || code === RESTART_EXIT_CODE) return 'restart';
244
+ if (stopRequested) return 'stopped';
245
+ if (code === 0) return 'stopped';
246
+ if ((code === null || code === undefined) && CLEAN_SIGNALS.includes(signal)) return 'stopped';
247
+ // Windows' own Ctrl+C, or the console it ran in closing: what SIGINT and SIGHUP are elsewhere.
248
+ if (exitStatusHex(code) === '0xC000013A') return 'stopped';
249
+ if (!listened) return 'failed';
250
+ return 'crashed';
251
+ }
252
+
253
+ /**
254
+ * The fields a supervisor record's error keeps about an exit (lib/start-launcher.js
255
+ * supervisorError): what the desktop app's stopped page shows.
256
+ */
257
+ export function exitRecordFields(described) {
258
+ const d = described && typeof described === 'object' && 'kind' in described ? described : describeExit(described || {});
259
+ return {
260
+ ...(d.code !== null ? { exitCode: d.code } : {}),
261
+ ...(d.signal ? { exitSignal: d.signal } : {}),
262
+ ...(d.hex ? { exitHex: d.hex } : {}),
263
+ exitKind: d.kind,
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Call `onEnd({ code, signal })` once, when `child` has ended and its output
269
+ * has been read to the end ('close'), or `drainMs` after it exited when
270
+ * something it started still holds that output open. Reading to the end
271
+ * first keeps a crash's last words: what a native assertion or an uncaught
272
+ * error printed just before the process died. `cancel()` stops it (the child
273
+ * could not be started, and that was reported already).
274
+ */
275
+ export function watchChildEnd(child, { drainMs = 1500, onEnd, setTimer = setTimeout, clearTimer = clearTimeout } = {}) {
276
+ let ended = null;
277
+ let done = false;
278
+ let drain = null;
279
+ const decide = () => {
280
+ if (done || !ended) return;
281
+ done = true;
282
+ if (drain !== null) clearTimer(drain);
283
+ onEnd?.(ended);
284
+ };
285
+ child.on('exit', (code, signal) => {
286
+ ended = { code, signal };
287
+ drain = setTimer(decide, drainMs);
288
+ });
289
+ child.on('close', (code, signal) => {
290
+ ended ??= { code, signal };
291
+ decide();
292
+ });
293
+ return {
294
+ cancel() {
295
+ done = true;
296
+ if (drain !== null) clearTimer(drain);
297
+ },
298
+ get decided() { return done; },
299
+ };
300
+ }
301
+
302
+ /**
303
+ * The crash-loop guard: `note()` counts one more start of the server and says
304
+ * whether that is more than `limit` within `windowMs` ({ count, exceeded }).
305
+ */
306
+ export function createRestartGuard({ windowMs = 60_000, limit = 5, now = () => Date.now() } = {}) {
307
+ const starts = [];
308
+ return {
309
+ windowMs,
310
+ limit,
311
+ note() {
312
+ const at = now();
313
+ starts.push(at);
314
+ while (starts.length && at - starts[0] > windowMs) starts.shift();
315
+ return { count: starts.length, exceeded: starts.length > limit };
316
+ },
317
+ };
318
+ }