synabun 2.0.10 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (656) hide show
  1. package/CHANGELOG.md +207 -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 +13 -13
  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 +30 -17
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +7 -6
  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 +302 -0
  24. package/lib/virustotal.mjs +203 -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/budget-judgments.d.ts +1 -1
  37. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  38. package/mcp-server/dist/services/budget-judgments.js +5 -3
  39. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  40. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  41. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  42. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  43. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +2 -2
  45. package/mcp-server/dist/services/identity.js +2 -2
  46. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  47. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  48. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  49. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  50. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  51. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  52. package/mcp-server/dist/services/memory-sessions.js +28 -2
  53. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  54. package/mcp-server/dist/services/neural-interface.d.ts +18 -11
  55. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  56. package/mcp-server/dist/services/neural-interface.js +67 -11
  57. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  58. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  59. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  60. package/mcp-server/dist/services/server-instructions.js +22 -63
  61. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  62. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  63. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  64. package/mcp-server/dist/services/tool-catalog.js +339 -0
  65. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  66. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  67. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  68. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  69. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  70. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  71. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  72. package/mcp-server/dist/services/tool-list.js +334 -0
  73. package/mcp-server/dist/services/tool-list.js.map +1 -0
  74. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  75. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  76. package/mcp-server/dist/services/tool-runtime.js +400 -0
  77. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  78. package/mcp-server/dist/services/typesafe-config.js +1 -1
  79. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  80. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  81. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  82. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  83. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  84. package/mcp-server/dist/tools/agents.d.ts +14 -14
  85. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  86. package/mcp-server/dist/tools/agents.js +29 -19
  87. package/mcp-server/dist/tools/agents.js.map +1 -1
  88. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  89. package/mcp-server/dist/tools/choice.d.ts +6 -0
  90. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/choice.js +18 -13
  92. package/mcp-server/dist/tools/choice.js.map +1 -1
  93. package/mcp-server/dist/tools/git-tools.js +2 -2
  94. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  95. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  96. package/mcp-server/dist/tools/image-tools.js +1 -1
  97. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  98. package/mcp-server/dist/tools/morelogin.js +9 -6
  99. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  100. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  101. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  102. package/mcp-server/dist/tools/skill-tools.js +642 -0
  103. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  104. package/mcp-server/dist/tools/skill.d.ts +7 -0
  105. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  106. package/mcp-server/dist/tools/skill.js +11 -0
  107. package/mcp-server/dist/tools/skill.js.map +1 -0
  108. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  109. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  110. package/mcp-server/dist/tools/tool-groups.js +147 -0
  111. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  112. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  113. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  114. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  115. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  116. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  117. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  118. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  119. package/mcp-server/package-lock.json +2 -2
  120. package/mcp-server/package.json +1 -1
  121. package/mcp-server/src/http.ts +75 -12
  122. package/mcp-server/src/index.ts +147 -154
  123. package/mcp-server/src/preload.ts +12 -12
  124. package/mcp-server/src/services/budget-judgments.ts +5 -3
  125. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  126. package/mcp-server/src/services/identity.ts +2 -2
  127. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  128. package/mcp-server/src/services/memory-sessions.ts +24 -3
  129. package/mcp-server/src/services/neural-interface.ts +81 -21
  130. package/mcp-server/src/services/server-instructions.ts +29 -64
  131. package/mcp-server/src/services/tool-catalog.ts +403 -0
  132. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  133. package/mcp-server/src/services/tool-list.ts +410 -0
  134. package/mcp-server/src/services/tool-runtime.ts +457 -0
  135. package/mcp-server/src/services/typesafe-config.ts +1 -1
  136. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  137. package/mcp-server/src/tools/agents.ts +29 -20
  138. package/mcp-server/src/tools/choice.ts +17 -12
  139. package/mcp-server/src/tools/git-tools.ts +2 -2
  140. package/mcp-server/src/tools/image-tools.ts +1 -1
  141. package/mcp-server/src/tools/morelogin.ts +6 -4
  142. package/mcp-server/src/tools/skill-tools.ts +592 -0
  143. package/mcp-server/src/tools/skill.ts +13 -0
  144. package/mcp-server/src/tools/tool-groups.ts +164 -0
  145. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  146. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  147. package/neural-interface/i18n/en.json +3953 -228
  148. package/neural-interface/i18n/pt-BR.json +3901 -179
  149. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  150. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  151. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  152. package/neural-interface/lib/antigravity/bypass.js +100 -0
  153. package/neural-interface/lib/antigravity/catalog.js +273 -0
  154. package/neural-interface/lib/antigravity/home.js +260 -0
  155. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  156. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  157. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  158. package/neural-interface/lib/antigravity/policy.js +74 -0
  159. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  160. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  161. package/neural-interface/lib/antigravity/usage.js +43 -0
  162. package/neural-interface/lib/assistant-api.js +128 -28
  163. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  164. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  165. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  166. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  167. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  168. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  169. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  170. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  171. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  172. package/neural-interface/lib/assistant-budget-split.js +42 -15
  173. package/neural-interface/lib/assistant-catalog.js +81 -15
  174. package/neural-interface/lib/assistant-config.js +6 -3
  175. package/neural-interface/lib/assistant-dispatch.js +1073 -200
  176. package/neural-interface/lib/assistant-envelope.js +156 -2
  177. package/neural-interface/lib/assistant-evidence.js +80 -0
  178. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  179. package/neural-interface/lib/assistant-persona.js +93 -19
  180. package/neural-interface/lib/assistant-plan-permissions.js +25 -9
  181. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  182. package/neural-interface/lib/assistant-route-gate.js +6 -4
  183. package/neural-interface/lib/assistant-router.js +245 -38
  184. package/neural-interface/lib/assistant-runtime.js +214 -30
  185. package/neural-interface/lib/assistant-task-prompt.js +87 -21
  186. package/neural-interface/lib/assistant-tools.js +51 -0
  187. package/neural-interface/lib/assistant-usage.js +29 -0
  188. package/neural-interface/lib/backup-service.js +626 -4
  189. package/neural-interface/lib/backup-zip-reader.js +10 -5
  190. package/neural-interface/lib/backup-zip-writer.js +257 -0
  191. package/neural-interface/lib/browser-execution.js +10 -2
  192. package/neural-interface/lib/browser-guard-cdp.js +84 -0
  193. package/neural-interface/lib/browser-network-guard.js +476 -0
  194. package/neural-interface/lib/browser-policy-roots.js +64 -0
  195. package/neural-interface/lib/browser-tool-policy.js +198 -38
  196. package/neural-interface/lib/claude-agent-bridge.js +11 -2
  197. package/neural-interface/lib/claude-history-cache.js +210 -0
  198. package/neural-interface/lib/claude-hooks-status.js +103 -0
  199. package/neural-interface/lib/claude-model-catalog.js +5 -1
  200. package/neural-interface/lib/claude-temporary.js +5 -11
  201. package/neural-interface/lib/cli-install-api.js +1 -1
  202. package/neural-interface/lib/cli-install.js +163 -154
  203. package/neural-interface/lib/cli-resolver.js +13 -3
  204. package/neural-interface/lib/cli-update-planner.js +52 -41
  205. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  206. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  207. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  208. package/neural-interface/lib/codex-worker-app-server.js +191 -0
  209. package/neural-interface/lib/confined-fs.js +30 -3
  210. package/neural-interface/lib/crash-notice-api.js +98 -0
  211. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  212. package/neural-interface/lib/editor-git-api.js +3 -20
  213. package/neural-interface/lib/exec-loop-command.js +13 -10
  214. package/neural-interface/lib/file-explorer-api.js +74 -40
  215. package/neural-interface/lib/file-explorer-watch.js +9 -3
  216. package/neural-interface/lib/git-bash/install.js +52 -6
  217. package/neural-interface/lib/git-bash/manager.js +59 -17
  218. package/neural-interface/lib/git-bash/pins.js +12 -0
  219. package/neural-interface/lib/git-read.js +2 -2
  220. package/neural-interface/lib/git-status-runner.js +390 -0
  221. package/neural-interface/lib/git-status-watchdog.js +40 -0
  222. package/neural-interface/lib/greeting/budgets.js +1 -0
  223. package/neural-interface/lib/greeting/engine.js +2 -2
  224. package/neural-interface/lib/greeting/sources.js +10 -12
  225. package/neural-interface/lib/greeting/writer.js +1 -1
  226. package/neural-interface/lib/http-guards.js +8 -2
  227. package/neural-interface/lib/install-details.js +4 -1
  228. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  229. package/neural-interface/lib/mcp-client-config.js +35 -33
  230. package/neural-interface/lib/mcp-external-servers.js +332 -0
  231. package/neural-interface/lib/mcp-installer.js +2 -1
  232. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  233. package/neural-interface/lib/memory-session-capture.js +17 -3
  234. package/neural-interface/lib/morelogin.js +177 -25
  235. package/neural-interface/lib/native-loop-providers.js +965 -66
  236. package/neural-interface/lib/native-loop-runtime.js +290 -42
  237. package/neural-interface/lib/opencode-config.js +557 -0
  238. package/neural-interface/lib/opencode-db-schema.js +17 -34
  239. package/neural-interface/lib/opencode-history.js +48 -218
  240. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  241. package/neural-interface/lib/opencode-native-boundary.js +53 -0
  242. package/neural-interface/lib/opencode-protocol.js +249 -112
  243. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  244. package/neural-interface/lib/opencode-serve-records.js +39 -21
  245. package/neural-interface/lib/opencode-v2-client.js +48 -153
  246. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  247. package/neural-interface/lib/opencode-worker-data.js +27 -0
  248. package/neural-interface/lib/opencode2-client.js +73 -37
  249. package/neural-interface/lib/opencode2-proxy.js +23 -63
  250. package/neural-interface/lib/opencode2-translate.js +51 -2
  251. package/neural-interface/lib/owner-authority.js +249 -0
  252. package/neural-interface/lib/page-session.js +275 -0
  253. package/neural-interface/lib/path-confine.js +12 -9
  254. package/neural-interface/lib/peer-identity.js +793 -0
  255. package/neural-interface/lib/pty-host/core.js +28 -1
  256. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  257. package/neural-interface/lib/pty-host/manager.js +12 -1
  258. package/neural-interface/lib/remote-policy.js +1 -1
  259. package/neural-interface/lib/request-gate.js +20 -2
  260. package/neural-interface/lib/rulesets/api.js +7 -6
  261. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  262. package/neural-interface/lib/rulesets/installer.js +180 -36
  263. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  264. package/neural-interface/lib/rulesets/render.js +1 -1
  265. package/neural-interface/lib/session-cache-builder.js +81 -31
  266. package/neural-interface/lib/session-title-generator.js +58 -36
  267. package/neural-interface/lib/skills/api-v2.js +495 -0
  268. package/neural-interface/lib/skills/bundle.js +418 -0
  269. package/neural-interface/lib/skills/catalog.js +298 -0
  270. package/neural-interface/lib/skills/creation-api.js +191 -0
  271. package/neural-interface/lib/skills/creation-brief.js +247 -0
  272. package/neural-interface/lib/skills/creation-sources.js +285 -0
  273. package/neural-interface/lib/skills/creation.js +843 -0
  274. package/neural-interface/lib/skills/deploy.js +569 -0
  275. package/neural-interface/lib/skills/digest.js +119 -0
  276. package/neural-interface/lib/skills/discover.js +111 -0
  277. package/neural-interface/lib/skills/draft-confinement.js +216 -0
  278. package/neural-interface/lib/skills/eval-confinement.js +399 -0
  279. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  280. package/neural-interface/lib/skills/eval-trace.js +135 -0
  281. package/neural-interface/lib/skills/evals-api.js +212 -0
  282. package/neural-interface/lib/skills/evals.js +1573 -0
  283. package/neural-interface/lib/skills/export.js +203 -0
  284. package/neural-interface/lib/skills/frontmatter.js +64 -0
  285. package/neural-interface/lib/skills/health.js +75 -0
  286. package/neural-interface/lib/skills/json-edit.js +165 -0
  287. package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
  288. package/neural-interface/lib/skills/known-bridges.js +96 -0
  289. package/neural-interface/lib/skills/library.js +823 -0
  290. package/neural-interface/lib/skills/local-store.js +21 -0
  291. package/neural-interface/lib/skills/migration.js +433 -0
  292. package/neural-interface/lib/skills/operations.js +45 -0
  293. package/neural-interface/lib/skills/ownership.js +427 -0
  294. package/neural-interface/lib/skills/packages.js +88 -0
  295. package/neural-interface/lib/skills/reviews.js +212 -0
  296. package/neural-interface/lib/skills/revision.js +80 -0
  297. package/neural-interface/lib/skills/roots.js +269 -0
  298. package/neural-interface/lib/skills/runtime-checks.js +358 -0
  299. package/neural-interface/lib/skills/scan-assets.js +252 -0
  300. package/neural-interface/lib/skills/scan-context.js +26 -0
  301. package/neural-interface/lib/skills/scan-shell.js +324 -0
  302. package/neural-interface/lib/skills/scan.js +364 -0
  303. package/neural-interface/lib/skills/scanners.js +54 -0
  304. package/neural-interface/lib/skills/service.js +530 -0
  305. package/neural-interface/lib/skills/telemetry.js +119 -0
  306. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  307. package/neural-interface/lib/synabun-version.js +1 -1
  308. package/neural-interface/lib/system-activity.js +1 -1
  309. package/neural-interface/lib/tool-lists.js +124 -0
  310. package/neural-interface/lib/whatsapp/brain.js +7 -3
  311. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  312. package/neural-interface/lib/whatsapp/cards.js +22 -9
  313. package/neural-interface/lib/whatsapp/config.js +1 -1
  314. package/neural-interface/lib/worker-confinement.js +1268 -0
  315. package/neural-interface/lib/worker-guard-setting.js +56 -0
  316. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  317. package/neural-interface/lib/worker-question.js +74 -0
  318. package/neural-interface/lib/worker-relay-ports.js +227 -0
  319. package/neural-interface/lib/worker-resume.js +238 -0
  320. package/neural-interface/lib/worker-sandbox.js +329 -0
  321. package/neural-interface/package-lock.json +34 -12
  322. package/neural-interface/package.json +3 -2
  323. package/neural-interface/public/index.html +3 -1
  324. package/neural-interface/public/index2d.html +19 -11
  325. package/neural-interface/public/onboarding.html +3 -3
  326. package/neural-interface/public/shared/account-menu.js +8 -5
  327. package/neural-interface/public/shared/agent-runtime-options.js +125 -31
  328. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  329. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  330. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  331. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  332. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  333. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  334. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  335. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  336. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  337. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  338. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  339. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  340. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  341. package/neural-interface/public/shared/api.js +1527 -1336
  342. package/neural-interface/public/shared/app-frame.js +192 -0
  343. package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
  344. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  345. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  346. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  347. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  348. package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
  349. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  350. package/neural-interface/public/shared/assistant/asst-control.js +205 -26
  351. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  352. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  353. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  354. package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
  355. package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
  356. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  357. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  358. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  359. package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  361. package/neural-interface/public/shared/assistant/asst-render.js +445 -82
  362. package/neural-interface/public/shared/assistant/asst-route.js +304 -53
  363. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  364. package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
  365. package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
  366. package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
  367. package/neural-interface/public/shared/assistant/asst-state.js +87 -24
  368. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
  371. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  372. package/neural-interface/public/shared/automation-context.js +5 -2
  373. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  374. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  375. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  376. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  377. package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
  378. package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
  379. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  380. package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
  381. package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
  382. package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
  383. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  384. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  385. package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
  386. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  387. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  388. package/neural-interface/public/shared/cli-install-view.js +11 -12
  389. package/neural-interface/public/shared/cli-status.js +11 -10
  390. package/neural-interface/public/shared/constants.js +5 -4
  391. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  392. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  393. package/neural-interface/public/shared/context-menu-model.js +367 -0
  394. package/neural-interface/public/shared/context-menu.js +372 -0
  395. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  396. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  397. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  398. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  399. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  400. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  401. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  402. package/neural-interface/public/shared/desktop-update-view.js +21 -0
  403. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  404. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  405. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  406. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  407. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  408. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  409. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  410. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  411. package/neural-interface/public/shared/html-shell.js +30 -15
  412. package/neural-interface/public/shared/i18n.js +2 -0
  413. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  414. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  415. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  416. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  417. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  418. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  419. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  420. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  421. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  422. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  423. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  424. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
  425. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  426. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  427. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  428. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
  429. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  430. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  431. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  432. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  433. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  434. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  435. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  436. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
  437. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  438. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
  439. package/neural-interface/public/shared/page-session.js +96 -0
  440. package/neural-interface/public/shared/provider-icons.js +4 -2
  441. package/neural-interface/public/shared/registry.js +5 -3
  442. package/neural-interface/public/shared/retired-2d.js +3 -0
  443. package/neural-interface/public/shared/schedules-view.js +195 -61
  444. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  445. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  446. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  447. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  448. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  449. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  450. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  451. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  452. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  453. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  454. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  455. package/neural-interface/public/shared/skills/ss-creation.js +222 -0
  456. package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
  457. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  458. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  459. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  460. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  461. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  462. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  463. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  464. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  465. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  466. package/neural-interface/public/shared/skills/ss-lint.js +149 -0
  467. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  468. package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
  469. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  470. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  471. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  472. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  473. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  474. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  475. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  476. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
  477. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  478. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  479. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  480. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  481. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  482. package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
  483. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  484. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  485. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  486. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  487. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  488. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  489. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  490. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  491. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
  492. package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
  493. package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
  494. package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
  495. package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
  496. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  497. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  498. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  499. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  500. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  501. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  502. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  503. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  504. package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
  505. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  506. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  507. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  508. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
  509. package/neural-interface/public/shared/state.js +1 -1
  510. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  511. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  512. package/neural-interface/public/shared/styles.css +1822 -1756
  513. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  514. package/neural-interface/public/shared/text-cache.js +29 -0
  515. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  516. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  517. package/neural-interface/public/shared/tool-list-view.js +112 -0
  518. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  519. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  520. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  521. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  522. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  523. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  524. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  525. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  526. package/neural-interface/public/shared/tour/tour.css +144 -0
  527. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  528. package/neural-interface/public/shared/ui-assistant.js +2 -0
  529. package/neural-interface/public/shared/ui-automation-studio.js +111 -140
  530. package/neural-interface/public/shared/ui-claude-panel.js +376 -82
  531. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  532. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  533. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  534. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  535. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  536. package/neural-interface/public/shared/ui-detail.js +15 -3
  537. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  538. package/neural-interface/public/shared/ui-file-links.js +2 -2
  539. package/neural-interface/public/shared/ui-games.js +2 -2
  540. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  541. package/neural-interface/public/shared/ui-judgments.js +1 -1
  542. package/neural-interface/public/shared/ui-keybinds.js +33 -2
  543. package/neural-interface/public/shared/ui-link.js +3 -3
  544. package/neural-interface/public/shared/ui-menubar.js +10 -20
  545. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  546. package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
  547. package/neural-interface/public/shared/ui-navbar.js +49 -24
  548. package/neural-interface/public/shared/ui-notifications.js +1 -0
  549. package/neural-interface/public/shared/ui-panels.js +96 -65
  550. package/neural-interface/public/shared/ui-resume.js +9 -7
  551. package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
  552. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  553. package/neural-interface/public/shared/ui-sessions.js +27 -15
  554. package/neural-interface/public/shared/ui-settings.js +683 -526
  555. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  556. package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
  557. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  558. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  559. package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
  560. package/neural-interface/public/shared/ui-skills.js +33 -2830
  561. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  562. package/neural-interface/public/shared/ui-sync.js +15 -5
  563. package/neural-interface/public/shared/ui-terminal.js +270 -190
  564. package/neural-interface/public/shared/ui-tool-updates.js +6 -4
  565. package/neural-interface/public/shared/ui-tooltip.js +81 -51
  566. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  567. package/neural-interface/public/shared/ui-trash.js +6 -4
  568. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  569. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  570. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  571. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  572. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  573. package/neural-interface/public/shared/ui-update.js +38 -2
  574. package/neural-interface/public/shared/ui-whiteboard.js +312 -59
  575. package/neural-interface/public/shared/ui-worker-end.js +5 -1
  576. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  577. package/neural-interface/public/shared/view-variant.js +66 -0
  578. package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
  579. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  580. package/neural-interface/public/shared/worker-end.js +10 -3
  581. package/neural-interface/public/shared/worker-questions.js +34 -0
  582. package/neural-interface/public/shared/worker-restart.js +12 -0
  583. package/neural-interface/public/sidepanel-session.html +2 -2
  584. package/neural-interface/public/variant/2d/background.js +2 -1
  585. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  586. package/neural-interface/public/variant/2d/graph.js +42 -20
  587. package/neural-interface/public/variant/2d/hulls.js +14 -9
  588. package/neural-interface/public/variant/2d/lasso.js +11 -7
  589. package/neural-interface/public/variant/2d/main.js +32 -1
  590. package/neural-interface/public/variant/2d/minimap.js +11 -13
  591. package/neural-interface/public/variant/3d/main.js +69 -6
  592. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  593. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  594. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  595. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  596. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  597. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  598. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  599. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  600. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  601. package/neural-interface/scripts/build-vendor.mjs +32 -0
  602. package/neural-interface/server.js +4553 -2855
  603. package/neural-interface/templates/rulesets/README.md +1 -1
  604. package/neural-interface/templates/rulesets/core.md +1 -1
  605. package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
  606. package/neural-interface/templates/rulesets/manifest.json +8 -8
  607. package/npm-shrinkwrap.json +34 -12
  608. package/package.json +17 -3
  609. package/postinstall.js +9 -4
  610. package/preuninstall.js +1 -1
  611. package/setup.js +97 -322
  612. package/skills/skill-forge/SKILL.md +177 -0
  613. package/skills/skill-forge/evals/evals.json +54 -0
  614. package/skills/skill-forge/references/package.md +105 -0
  615. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  616. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  617. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  618. package/skills/synabun/SKILL.md +10 -7
  619. package/skills/synabun/modules/audit.md +1 -5
  620. package/skills/synabun/modules/changelog.md +1 -5
  621. package/skills/synabun/modules/idea.md +1 -1
  622. package/skills/synabun/modules/memorize.md +6 -9
  623. package/skills/synabun/modules/schedule.md +6 -6
  624. package/mcp-server/dist/services/profiles.d.ts +0 -120
  625. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  626. package/mcp-server/dist/services/profiles.js +0 -387
  627. package/mcp-server/dist/services/profiles.js.map +0 -1
  628. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  629. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  630. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  631. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  632. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  633. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  634. package/mcp-server/dist/tools/leonardo.js +0 -21
  635. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  636. package/mcp-server/dist/tools/profile.d.ts +0 -17
  637. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  638. package/mcp-server/dist/tools/profile.js +0 -114
  639. package/mcp-server/dist/tools/profile.js.map +0 -1
  640. package/mcp-server/src/services/profiles.ts +0 -428
  641. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  642. package/mcp-server/src/tools/leonardo.ts +0 -30
  643. package/mcp-server/src/tools/profile.ts +0 -122
  644. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  645. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  646. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  647. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  648. package/skills/leonardo/SKILL.md +0 -168
  649. package/skills/leonardo/icon.svg +0 -1
  650. package/skills/leonardo/modules/browser-guide.md +0 -378
  651. package/skills/leonardo/modules/image-prompter.md +0 -283
  652. package/skills/leonardo/modules/model-advisor.md +0 -100
  653. package/skills/leonardo/modules/prompt-library.md +0 -110
  654. package/skills/leonardo/modules/style-guide.md +0 -186
  655. package/skills/leonardo/modules/video-prompter.md +0 -317
  656. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -0,0 +1,176 @@
1
+ // What "the MCP server is built" means, in one place: mcp-server/dist/index.js and every module it imports by a
2
+ // relative path, transitively, are on disk. mcp-server/dist/ is build output that git ignores, yet a checkout can
3
+ // hold a tracked dist/index.js whose newer imports were never committed (they are ignored too): a server started
4
+ // from it fails to load /mcp. postinstall.js and setup.js build when this says no; the Neural Interface's restore,
5
+ // onboarding status and build route, the install details, the release and the packaged app's checks ask it too.
6
+ //
7
+ // The imports are read from the code itself, never from a comment or a string: a light tokenizer skips comments,
8
+ // strings, template literals and regular expressions, so a word "import" in a text cannot ask for a module that is
9
+ // not loaded (a packaged app has no compiler: a false "missing" there would stop it). Only plain fs and path:
10
+ // postinstall.js and setup.js import this on any Node.
11
+
12
+ import { existsSync, readFileSync, statSync } from 'node:fs';
13
+ import { dirname, join, relative, resolve, sep } from 'node:path';
14
+
15
+ const MAX_MODULES = 5000;
16
+ const MAX_MODULE_BYTES = 8 * 1024 * 1024;
17
+ const CODE_EXT = /\.(?:m?js|cjs)$/i;
18
+ // After one of these a `/` starts a regular expression, after anything else it divides.
19
+ const REGEX_AFTER_PUNCT = new Set(['(', ',', '=', ':', '[', '!', '&', '|', '?', '{', '}', ';', '+', '-', '*', '%', '<', '>', '~', '^']);
20
+ const REGEX_AFTER_WORD = new Set(['return', 'typeof', 'instanceof', 'in', 'of', 'new', 'delete', 'void', 'throw', 'case', 'do', 'else', 'yield', 'await']);
21
+
22
+ /**
23
+ * The tokens of a module that matter for its imports: words, punctuation and string literals (their values), with
24
+ * comments, template literal text and regular expressions left out.
25
+ * @returns {{ t: 'word'|'punct'|'string', v: string }[]}
26
+ */
27
+ export function codeTokens(text) {
28
+ const src = String(text ?? '');
29
+ const out = [];
30
+ const frames = []; // one per open `${` of a template literal: the brace depth that closes it
31
+ let depth = 0;
32
+ let i = 0;
33
+ const last = () => out[out.length - 1];
34
+ const regexAllowed = () => {
35
+ const p = last();
36
+ if (!p) return true;
37
+ if (p.t === 'punct') return REGEX_AFTER_PUNCT.has(p.v);
38
+ if (p.t === 'word') return REGEX_AFTER_WORD.has(p.v);
39
+ return false;
40
+ };
41
+ const template = () => {
42
+ // From just after a backtick (or a closing `}` of a `${`): to the closing backtick or the next `${`.
43
+ while (i < src.length) {
44
+ const c = src[i];
45
+ if (c === '\\') { i += 2; continue; }
46
+ if (c === '`') { i += 1; out.push({ t: 'string', v: '' }); return; }
47
+ if (c === '$' && src[i + 1] === '{') { i += 2; frames.push(depth); depth += 1; return; }
48
+ i += 1;
49
+ }
50
+ };
51
+ while (i < src.length) {
52
+ const c = src[i];
53
+ if (c === '/' && src[i + 1] === '/') { const nl = src.indexOf('\n', i); i = nl < 0 ? src.length : nl; continue; }
54
+ if (c === '/' && src[i + 1] === '*') { const end = src.indexOf('*/', i + 2); i = end < 0 ? src.length : end + 2; continue; }
55
+ if (c === '\'' || c === '"') {
56
+ let j = i + 1; let v = '';
57
+ while (j < src.length && src[j] !== c && src[j] !== '\n') { if (src[j] === '\\') { v += src[j + 1] ?? ''; j += 2; continue; } v += src[j]; j += 1; }
58
+ out.push({ t: 'string', v });
59
+ i = j + 1;
60
+ continue;
61
+ }
62
+ if (c === '`') { i += 1; template(); continue; }
63
+ if (c === '/' && regexAllowed()) {
64
+ let j = i + 1; let inClass = false;
65
+ while (j < src.length && src[j] !== '\n') {
66
+ const d = src[j];
67
+ if (d === '\\') { j += 2; continue; }
68
+ if (d === '[') inClass = true;
69
+ else if (d === ']') inClass = false;
70
+ else if (d === '/' && !inClass) break;
71
+ j += 1;
72
+ }
73
+ i = j + 1;
74
+ while (i < src.length && /[a-z]/i.test(src[i])) i += 1;
75
+ out.push({ t: 'punct', v: ')' }); // a value, as far as a following `/` is concerned
76
+ continue;
77
+ }
78
+ if (/[A-Za-z_$]/.test(c)) {
79
+ let j = i + 1;
80
+ while (j < src.length && /[\w$]/.test(src[j])) j += 1;
81
+ out.push({ t: 'word', v: src.slice(i, j) });
82
+ i = j;
83
+ continue;
84
+ }
85
+ if (/\s/.test(c)) { i += 1; continue; }
86
+ if (/[0-9]/.test(c)) { let j = i + 1; while (j < src.length && /[\w.]/.test(src[j])) j += 1; out.push({ t: 'word', v: src.slice(i, j) }); i = j; continue; }
87
+ if (c === '{') depth += 1;
88
+ if (c === '}') {
89
+ if (frames.length && depth - 1 === frames[frames.length - 1]) { depth -= 1; frames.pop(); i += 1; template(); continue; }
90
+ depth = Math.max(0, depth - 1);
91
+ }
92
+ out.push({ t: 'punct', v: c });
93
+ i += 1;
94
+ }
95
+ return out;
96
+ }
97
+
98
+ /** The module specifiers a module loads: static imports, `export … from`, side-effect imports, `import('…')`. */
99
+ export function importSpecifiers(text) {
100
+ const tokens = codeTokens(text);
101
+ const found = [];
102
+ const at = (k) => tokens[k] || { t: 'end', v: '' };
103
+ for (let k = 0; k < tokens.length; k++) {
104
+ const tok = tokens[k];
105
+ if (tok.t !== 'word' || (tok.v !== 'import' && tok.v !== 'export')) continue;
106
+ if (k > 0 && at(k - 1).t === 'punct' && at(k - 1).v === '.') continue; // obj.import
107
+ const next = at(k + 1);
108
+ if (tok.v === 'import') {
109
+ if (next.t === 'punct' && next.v === '.') continue; // import.meta
110
+ if (next.t === 'punct' && next.v === '(') { if (at(k + 2).t === 'string' && at(k + 3).v === ')') found.push(at(k + 2).v); continue; }
111
+ if (next.t === 'string') { found.push(next.v); continue; }
112
+ }
113
+ // import a, { b as c } from '…'; import * as d from '…'; export { e } from '…'; export * from '…'
114
+ let j = k + 1;
115
+ while (j < tokens.length) {
116
+ const x = at(j);
117
+ if (x.t === 'word' && x.v === 'from') break;
118
+ if (x.t === 'word' || (x.t === 'punct' && (x.v === ',' || x.v === '{' || x.v === '}' || x.v === '*'))) { j += 1; continue; }
119
+ j = -1;
120
+ break;
121
+ }
122
+ if (j > k + 1 && at(j).v === 'from' && at(j + 1).t === 'string') {
123
+ if (tok.v === 'export' && !(next.v === '*' || next.v === '{')) continue;
124
+ found.push(at(j + 1).v);
125
+ }
126
+ }
127
+ return found;
128
+ }
129
+
130
+ const posix = (p) => p.split(sep).join('/');
131
+
132
+ /**
133
+ * Every module dist/index.js needs that is not on disk (or cannot be read), as paths from the package root.
134
+ * Modules inside mcp-server/dist are followed; one outside it (the package's lib/) is only looked for.
135
+ * @param {string} packageRoot
136
+ * @param {{ exists?: (p: string) => boolean, readText?: (p: string) => string, entry?: string }} [o]
137
+ * @returns {{ complete: boolean, missing: string[], checked: number }}
138
+ */
139
+ export function mcpDistMissing(packageRoot, { exists = existsSync, readText = null, entry = 'index.js' } = {}) {
140
+ const root = resolve(packageRoot);
141
+ const dist = join(root, 'mcp-server', 'dist');
142
+ const read = readText || ((p) => {
143
+ const st = statSync(p);
144
+ if (!st.isFile()) throw new Error('not a regular file');
145
+ if (st.size > MAX_MODULE_BYTES) return null;
146
+ return readFileSync(p, 'utf8');
147
+ });
148
+ const missing = new Set();
149
+ const seen = new Set();
150
+ const queue = [join(dist, entry)];
151
+ let checked = 0;
152
+ while (queue.length && checked < MAX_MODULES) {
153
+ const file = queue.shift();
154
+ if (seen.has(file)) continue;
155
+ seen.add(file);
156
+ if (!exists(file)) { missing.add(posix(relative(root, file))); continue; }
157
+ const inside = file.startsWith(dist + sep);
158
+ if (!inside || !CODE_EXT.test(file)) continue;
159
+ let text;
160
+ try { text = read(file); } catch { missing.add(posix(relative(root, file))); continue; }
161
+ checked += 1;
162
+ if (text === null) continue; // too large to read here: present, not followed
163
+ for (const spec of importSpecifiers(text)) {
164
+ if (!spec.startsWith('./') && !spec.startsWith('../')) continue;
165
+ const target = resolve(dirname(file), spec.split(/[?#]/)[0]);
166
+ if (!seen.has(target)) queue.push(target);
167
+ }
168
+ }
169
+ const list = [...missing].sort();
170
+ return { complete: list.length === 0, missing: list, checked };
171
+ }
172
+
173
+ /** Whether the MCP server is built: dist/index.js and everything it imports are there. */
174
+ export function mcpDistBuilt(packageRoot, options) {
175
+ return mcpDistMissing(packageRoot, options).complete;
176
+ }
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ /** Shared CI identity, OIDC-only publication and exact-byte handoff contract. */
3
+ import { spawnSync } from 'node:child_process';
4
+ import { createHash } from 'node:crypto';
5
+ import { createReadStream } from 'node:fs';
6
+ import { appendFile, lstat, readFile, readdir, writeFile } from 'node:fs/promises';
7
+ import { join, resolve } from 'node:path';
8
+ import { pathToFileURL } from 'node:url';
9
+ import { hashFile } from './virustotal.mjs';
10
+ import { publishNpm, runNpm, verifyPacked } from './release-publish.mjs';
11
+
12
+ export const REPOSITORY = 'danilokhury/Synabun';
13
+ export const WORKFLOW = '.github/workflows/publish.yml';
14
+ export const PUBLISH_NPM = '11.21.0'; // Verified OIDC-compatible CLI; builder stays 10.9.3.
15
+ export const stableVersion = value => typeof value === 'string' && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value);
16
+ export const exactCommit = value => typeof value === 'string' && /^[a-f0-9]{40}$/.test(value);
17
+ export const requestId = value => typeof value === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(value);
18
+ export const runTitle = ({ version, commit, request }) => `npm ${version} ${commit} ${request}`;
19
+ export const artifactName = (id, attempt) => `npm-publication-${id}-${attempt}`;
20
+ const numberId = value => /^\d+$/.test(String(value)) && Number.isSafeInteger(Number(value)) && Number(value) > 0;
21
+
22
+ export function validateIdentity({ env, event, pkg, head, main }) {
23
+ if (env.GITHUB_REPOSITORY !== REPOSITORY || pkg.name !== 'synabun' || !stableVersion(pkg.version)
24
+ || pkg.repository?.url !== `git+https://github.com/${REPOSITORY}.git`) throw new Error('Unexpected npm package/repository identity');
25
+ if (!exactCommit(head) || head !== main || !numberId(env.GITHUB_RUN_ID) || !numberId(env.GITHUB_RUN_ATTEMPT)) throw new Error('Release commit must be the current main tip; require exact run identity');
26
+ let request = null;
27
+ if (env.GITHUB_EVENT_NAME === 'workflow_dispatch') {
28
+ if (env.GITHUB_REF !== 'refs/heads/main' || env.GITHUB_SHA !== head || event.inputs?.commit !== head
29
+ || event.inputs?.version !== pkg.version || !requestId(event.inputs?.request_id)) throw new Error('Invalid dispatch ref/commit/version/request identity');
30
+ request = event.inputs.request_id;
31
+ } else if (env.GITHUB_EVENT_NAME === 'release') {
32
+ const tag = event.release?.tag_name;
33
+ if (event.action !== 'published' || event.release?.draft !== false || event.release?.prerelease !== false
34
+ || !new RegExp(`^v?\\.?${pkg.version.replaceAll('.', '\\.')}$`).test(tag || '')
35
+ || env.GITHUB_REF !== `refs/tags/${tag}` || env.GITHUB_SHA !== head) throw new Error('Invalid stable release event/tag');
36
+ } else throw new Error('Only main workflow_dispatch or stable release.published can publish');
37
+ if (env.GITHUB_WORKFLOW_REF !== `${REPOSITORY}/${WORKFLOW}@${env.GITHUB_REF}`) throw new Error('Unexpected publisher workflow identity');
38
+ return { repository: REPOSITORY, workflow: WORKFLOW, event: env.GITHUB_EVENT_NAME,
39
+ version: pkg.version, commit: head, request, run_id: Number(env.GITHUB_RUN_ID), run_attempt: Number(env.GITHUB_RUN_ATTEMPT) };
40
+ }
41
+
42
+ export function oidcEnvironment(env, { cli, userconfig, globalconfig, cache }) {
43
+ if (env.RUNNER_ENVIRONMENT !== 'github-hosted' || !env.ACTIONS_ID_TOKEN_REQUEST_URL || !env.ACTIONS_ID_TOKEN_REQUEST_TOKEN) throw new Error('GitHub-hosted OIDC runner and id-token: write are required');
44
+ // Modern npm tries OIDC first. Remove legacy credentials anyway: no silent token fallback.
45
+ const clean = Object.fromEntries(Object.entries(env).filter(([key]) => !/^(NPM|NPM_TOKEN|NODE_AUTH_TOKEN)$/i.test(key) && !/^npm_config_/i.test(key)));
46
+ return { ...clean, npm_execpath: cli, NPM_CONFIG_USERCONFIG: userconfig, NPM_CONFIG_GLOBALCONFIG: globalconfig,
47
+ NPM_CONFIG_CACHE: cache, NPM_CONFIG_REGISTRY: 'https://registry.npmjs.org/', NPM_CONFIG_UPDATE_NOTIFIER: 'false' };
48
+ }
49
+
50
+ export function verifyToolchain(node, npm) {
51
+ if (node.replace(/^v/, '') !== '22.19.0' || npm.trim() !== PUBLISH_NPM) throw new Error(`Publication requires Node 22.19.0 and isolated npm ${PUBLISH_NPM}; builder npm is unchanged`);
52
+ }
53
+
54
+ export async function registryVersion(version, { fetchImpl = fetch, allowMissing = false } = {}) {
55
+ if (!stableVersion(version)) throw new Error('Invalid registry version');
56
+ const response = await fetchImpl(`https://registry.npmjs.org/synabun/${version}`, { signal: AbortSignal.timeout(30000) });
57
+ if (response.status === 404 && allowMissing) return null;
58
+ if (!response.ok) throw new Error(`npm registry lookup failed (${response.status})`);
59
+ const pkg = await response.json();
60
+ if (pkg?.name !== 'synabun' || pkg.version !== version || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(pkg.dist?.integrity || '')) throw new Error('npm registry identity/integrity is unverified');
61
+ return pkg.dist.integrity;
62
+ }
63
+
64
+ export async function sha512(path) {
65
+ const hash = createHash('sha512');
66
+ for await (const bytes of createReadStream(path)) hash.update(bytes);
67
+ return `sha512-${hash.digest('base64')}`;
68
+ }
69
+ export const bundleFiles = version => ['npm-virustotal.json', 'npm-virustotal.md', `synabun-${version}.tgz`];
70
+
71
+ export async function publicationReceipt({ out, identity, registry = registryVersion }) {
72
+ const packed = await verifyPacked({ out });
73
+ if (packed.tarball !== join(out, `synabun-${identity.version}.tgz`)) throw new Error('npm receipt version mismatch');
74
+ const integrity = await sha512(packed.tarball);
75
+ if (await registry(identity.version) !== integrity) throw new Error('npm registry SHA512 differs from scanned tarball');
76
+ const files = {};
77
+ for (const name of bundleFiles(identity.version)) files[name] = { sha256: await hashFile(join(out, name)), size: (await lstat(join(out, name))).size };
78
+ const receipt = { schema: 1, ...identity, integrity, files };
79
+ await writeFile(join(out, 'npm-publication.json'), JSON.stringify(receipt, null, 2) + '\n');
80
+ return receipt;
81
+ }
82
+
83
+ export async function verifyPublication({ out, expected, registry = registryVersion, strictFiles = true }) {
84
+ const receipt = JSON.parse(await readFile(join(out, 'npm-publication.json'), 'utf8'));
85
+ if (receipt.schema !== 1 || receipt.repository !== REPOSITORY || receipt.workflow !== WORKFLOW
86
+ || !stableVersion(receipt.version) || !exactCommit(receipt.commit) || !numberId(receipt.run_id)
87
+ || !numberId(receipt.run_attempt) || !['workflow_dispatch', 'release'].includes(receipt.event)
88
+ || (receipt.event === 'workflow_dispatch' && !requestId(receipt.request))) throw new Error('Unknown or invalid npm publication receipt');
89
+ for (const [key, value] of Object.entries(expected)) if (receipt[key] !== value) throw new Error(`npm publication identity mismatch: ${key}`);
90
+ const names = bundleFiles(expected.version);
91
+ if (JSON.stringify(Object.keys(receipt.files || {}).sort()) !== JSON.stringify([...names].sort())
92
+ || (strictFiles && JSON.stringify((await readdir(out)).sort()) !== JSON.stringify([...names, 'npm-publication.json'].sort()))) throw new Error('Unexpected npm artifact file set');
93
+ for (const name of [...names, 'npm-publication.json']) {
94
+ const info = await lstat(join(out, name));
95
+ if (!info.isFile() || info.isSymbolicLink()) throw new Error('npm artifact must contain regular files only');
96
+ if (name !== 'npm-publication.json' && (info.size !== receipt.files[name].size || await hashFile(join(out, name)) !== receipt.files[name].sha256)) throw new Error(`npm artifact integrity mismatch: ${name}`);
97
+ }
98
+ const packed = await verifyPacked({ out });
99
+ if (packed.tarball !== join(out, `synabun-${expected.version}.tgz`) || await sha512(packed.tarball) !== receipt.integrity
100
+ || await registry(expected.version) !== receipt.integrity) throw new Error('npm publication version/registry integrity mismatch');
101
+ // Read only the packed manifest; never extract arbitrary paths into the checkout.
102
+ const tar = spawnSync('tar', ['-xOf', packed.tarball, 'package/package.json'], { encoding: 'utf8', maxBuffer: 1024 * 1024, timeout: 30000 });
103
+ if (tar.status !== 0) throw new Error('Cannot inspect npm tarball manifest');
104
+ const pkg = JSON.parse(tar.stdout);
105
+ if (pkg.name !== 'synabun' || pkg.version !== expected.version || pkg.repository?.url !== `git+https://github.com/${REPOSITORY}.git`) throw new Error('Packed npm manifest identity mismatch');
106
+ return receipt;
107
+ }
108
+
109
+ function git(...args) {
110
+ const result = spawnSync('git', args, { encoding: 'utf8', timeout: 120000 });
111
+ if (result.status !== 0) throw new Error('Cannot verify current main commit');
112
+ return result.stdout.trim();
113
+ }
114
+ async function ciIdentity() {
115
+ git('fetch', '--no-tags', '--depth=1', 'origin', 'main');
116
+ return validateIdentity({ env: process.env, event: JSON.parse(await readFile(process.env.GITHUB_EVENT_PATH, 'utf8')),
117
+ pkg: JSON.parse(await readFile('package.json', 'utf8')), head: git('rev-parse', 'HEAD'), main: git('rev-parse', 'FETCH_HEAD') });
118
+ }
119
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
120
+ try {
121
+ const stage = process.argv[2], identity = await ciIdentity(), out = join(process.env.RUNNER_TEMP, 'npm-release');
122
+ if (stage === 'identity') {
123
+ const published = await registryVersion(identity.version, { allowMissing: true });
124
+ if (published && identity.event === 'workflow_dispatch') throw new Error('Version already published: recover the original successful run artifacts; never repack an existing version');
125
+ await appendFile(process.env.GITHUB_OUTPUT, `published=${!!published}\n`);
126
+ } else if (stage === 'publish') {
127
+ try { await lstat('.npmrc'); throw new Error('Project .npmrc is forbidden in the publishing checkout'); } catch (error) { if (error.code !== 'ENOENT') throw error; }
128
+ const base = process.env.RUNNER_TEMP;
129
+ const userconfig = join(base, 'npm-oidc-userconfig'), globalconfig = join(base, 'npm-oidc-globalconfig');
130
+ await writeFile(userconfig, ''); await writeFile(globalconfig, '');
131
+ const env = oidcEnvironment(process.env, { cli: join(base, 'npm-oidc-cli/node_modules/npm/bin/npm-cli.js'), userconfig, globalconfig, cache: join(base, 'npm-oidc-cache') });
132
+ const run = (args, options) => runNpm(args, { ...options, env });
133
+ verifyToolchain(process.version, run(['--version'], { capture: true }));
134
+ await publishNpm({ cwd: process.cwd(), out, run, publishPacked: true, provenance: true });
135
+ await publicationReceipt({ out, identity });
136
+ } else throw new Error('Use identity or publish');
137
+ } catch (error) { console.error(error.message); process.exitCode = 1; }
138
+ }
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ /** The only documented npm publication path: check, pack, scan, publish the same tgz. */
3
+ import { spawnSync } from 'node:child_process';
4
+ import { access, chmod, mkdir, mkdtemp, readFile, copyFile, rm } from 'node:fs/promises';
5
+ import { tmpdir } from 'node:os';
6
+ import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
7
+ import { fileURLToPath, pathToFileURL } from 'node:url';
8
+ import { approved, credentials, evidenceDigest, hashFile } from './virustotal.mjs';
9
+ import { scanRelease } from './release-scan.mjs';
10
+
11
+ export function runNpm(args, { cwd, env = process.env, capture = false } = {}) {
12
+ // npm_execpath works with npm.cmd on Windows without shell argument interpolation.
13
+ const npm = env.npm_execpath;
14
+ if (!npm && process.platform === 'win32') throw new Error('On Windows run npm run release:publish so npm_execpath is available');
15
+ const command = npm ? process.execPath : 'npm';
16
+ const result = spawnSync(command, npm ? [npm, ...args] : args, { cwd, env, encoding: 'utf8', stdio: capture ? 'pipe' : 'inherit', timeout: 1800000, maxBuffer: 8 * 1024 * 1024 });
17
+ // Never replay npm's captured output (it may contain authentication diagnostics).
18
+ if (result.error || result.status !== 0) throw new Error(`npm ${args[0]} failed; publication stopped`);
19
+ return result.stdout;
20
+ }
21
+ export async function verifyPacked({ out, now = Date.now() }) {
22
+ const report = JSON.parse(await readFile(join(out, 'npm-virustotal.json'), 'utf8'));
23
+ if (report.schema !== 1 || !report.approved || report.items?.length !== 1 || !approved(report.items)) throw new Error('npm scan report is not approved');
24
+ const item = report.items[0];
25
+ if (item.kind !== 'file' || basename(item.name) !== item.name || !/^synabun-.*\.tgz$/.test(item.name)) throw new Error('Invalid npm scan subject');
26
+ const { evidence_digest, review, ...evidence } = item;
27
+ if (evidenceDigest(evidence) !== evidence_digest) throw new Error('npm report evidence digest mismatch');
28
+ const at = Date.parse(item.analysis_date);
29
+ if (!Number.isFinite(at) || at > now + 300000 || now - at > 7 * 86400000 || item.analysis_status !== 'completed' || item.supported_engines < 10) throw new Error('npm scan report stale, unfinished or inconclusive');
30
+ if (item.status === 'detections') {
31
+ const { applyReviews } = await import('./virustotal.mjs');
32
+ if (!approved(applyReviews([{ ...item, review: null }], [{ ...review, name: item.name, sha256: item.sha256, evidence_digest }], now))) throw new Error('npm detection review required');
33
+ }
34
+ const tarball = join(out, item.name);
35
+ if (await hashFile(tarball) !== item.sha256) throw new Error('npm tarball SHA-256 mismatch; repack and scan');
36
+ return { tarball, sha256: item.sha256 };
37
+ }
38
+ export async function publishNpm({ cwd, out, reviewFile, provenance = false, packOnly = false, publishPacked = false, run = runNpm, scan = scanRelease }) {
39
+ credentials(); // Before installs or registry mutations.
40
+ const rel = relative(resolve(cwd), resolve(out));
41
+ if (!rel || (!isAbsolute(rel) && rel !== '..' && !rel.startsWith('../') && !rel.startsWith('..\\'))) throw new Error('Release output must be outside the checkout to keep reports/tarballs out of npm packages');
42
+ const pkg = JSON.parse(await readFile(join(cwd, 'package.json'), 'utf8'));
43
+ if (pkg.name !== 'synabun' || pkg.scripts?.prepublishOnly?.includes('refuse-publish-from-dev') || !pkg.scripts?.prepublishOnly?.includes('release-publish.mjs guard')) throw new Error('Publish from a fresh prepared Clean clone with npm run release:publish; Dev publication is refused');
44
+ await mkdir(out, { recursive: true });
45
+ if (!publishPacked) {
46
+ run(['run', 'deps:check'], { cwd });
47
+ run(['ci', '--ignore-scripts'], { cwd });
48
+ // Preserve Clean's entire install/build/file/pack chain. npm publish TGZ skips it.
49
+ run(['run', 'prepublishOnly'], { cwd, env: { ...process.env, SYNABUN_RELEASE_PACK: '1' } });
50
+ run(['run', 'prepack'], { cwd });
51
+ for (const file of ['mcp-server/dist/index.js', 'NOTICE', 'THIRD-PARTY-LICENSES.md']) await access(join(cwd, file));
52
+ const staging = await mkdtemp(join(tmpdir(), 'synabun-npm-pack-'));
53
+ try {
54
+ const packed = JSON.parse(run(['pack', '--ignore-scripts', '--json', '--pack-destination', staging], { cwd, capture: true }));
55
+ const name = packed?.[0]?.filename;
56
+ if (packed.length !== 1 || !name || basename(name) !== name || name !== `synabun-${pkg.version}.tgz`) throw new Error('npm pack returned an unexpected tarball');
57
+ const tarball = join(out, name);
58
+ await rm(tarball, { force: true });
59
+ await copyFile(join(staging, name), tarball);
60
+ await chmod(tarball, 0o444);
61
+ await scan({ files: [tarball], out, prefix: 'npm-virustotal', reviewFile });
62
+ } finally { await rm(staging, { recursive: true, force: true }); }
63
+ }
64
+ const packed = await verifyPacked({ out });
65
+ if (basename(packed.tarball) !== `synabun-${pkg.version}.tgz`) throw new Error('npm tarball version mismatch');
66
+ if (!packOnly) {
67
+ run(['publish', packed.tarball, '--ignore-scripts', '--access', 'public', '--tag', 'latest', ...(provenance ? ['--provenance'] : [])], { cwd });
68
+ if (await hashFile(packed.tarball) !== packed.sha256) throw new Error('npm tarball changed during publication; inspect registry immediately');
69
+ }
70
+ return packed;
71
+ }
72
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
73
+ try {
74
+ if (process.argv[2] === 'guard') {
75
+ if (process.env.SYNABUN_RELEASE_PACK !== '1') throw new Error('Direct npm publish is disabled: use npm run release:publish -- --out /absolute/release/folder (packs, scans and publishes the exact tgz).');
76
+ } else {
77
+ const args = process.argv.slice(2); let out, reviewFile, provenance = false, packOnly = false, publishPacked = false;
78
+ while (args.length) {
79
+ const arg = args.shift();
80
+ if (arg === '--out') out = args.shift();
81
+ else if (arg === '--review') reviewFile = args.shift();
82
+ else if (arg === '--provenance') provenance = true;
83
+ else if (arg === '--pack-only') packOnly = true;
84
+ else if (arg === '--publish-packed') publishPacked = true;
85
+ else throw new Error(`Unknown option: ${arg}`);
86
+ }
87
+ if (!out || (packOnly && publishPacked)) throw new Error('Use --out DIR; --pack-only and --publish-packed are mutually exclusive');
88
+ await publishNpm({ cwd: dirname(dirname(fileURLToPath(import.meta.url))), out: resolve(out), reviewFile, provenance, packOnly, publishPacked });
89
+ }
90
+ } catch (error) { console.error(error.message); process.exitCode = 1; }
91
+ }
@@ -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
+ }