synabun 1.3.0 → 2.0.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 (1163) hide show
  1. package/CHANGELOG.md +205 -662
  2. package/LICENSE-COMMERCIAL.md +33 -0
  3. package/NOTICE +17 -0
  4. package/README.md +175 -853
  5. package/THIRD-PARTY-LICENSES.md +136 -0
  6. package/benchmarks/browser-live-results.json +79 -0
  7. package/benchmarks/browser-v2-results.json +258 -0
  8. package/benchmarks/fixtures/browser/README.md +57 -0
  9. package/benchmarks/fixtures/browser/page-state.json +132 -0
  10. package/benchmarks/fixtures/browser/social.json +92 -0
  11. package/benchmarks/fixtures/browser/target.json +319 -0
  12. package/benchmarks/fixtures/desktop/README.md +58 -0
  13. package/benchmarks/fixtures/desktop/abstention.json +59 -0
  14. package/benchmarks/fixtures/desktop/adversarial.json +22 -0
  15. package/benchmarks/fixtures/desktop/forbidden.json +93 -0
  16. package/benchmarks/fixtures/desktop/generate.mjs +1023 -0
  17. package/benchmarks/fixtures/desktop/target.json +136 -0
  18. package/bin/synabun.js +30 -0
  19. package/docs/whatsapp.md +325 -0
  20. package/hooks/claude-code/post-plan.mjs +613 -391
  21. package/hooks/claude-code/post-remember.mjs +246 -122
  22. package/hooks/claude-code/pre-compact.mjs +171 -110
  23. package/hooks/claude-code/pre-task.mjs +133 -0
  24. package/hooks/claude-code/pre-websearch.mjs +15 -4
  25. package/hooks/claude-code/prompt-origin.mjs +94 -0
  26. package/hooks/claude-code/prompt-submit.mjs +1244 -861
  27. package/hooks/claude-code/session-start.mjs +114 -156
  28. package/hooks/claude-code/shared.mjs +1228 -368
  29. package/hooks/claude-code/state.mjs +167 -0
  30. package/hooks/claude-code/stop.mjs +515 -140
  31. package/hooks/claude-code/subagent-start.mjs +154 -0
  32. package/hooks/claude-code/subagent-stop.mjs +102 -0
  33. package/hooks/claude-code/task-gate.mjs +150 -0
  34. package/launcher.mjs +40 -0
  35. package/lib/claude-hooks.js +137 -0
  36. package/lib/claude-usage-audit.js +141 -0
  37. package/lib/client-config-repair.js +145 -0
  38. package/lib/data-home-migration.js +502 -0
  39. package/lib/desktop-pwa.js +206 -0
  40. package/lib/external-tools.js +221 -0
  41. package/lib/first-launch-protection.js +245 -0
  42. package/lib/packaged-runtime.js +110 -0
  43. package/lib/paths.d.ts +21 -0
  44. package/lib/paths.js +103 -0
  45. package/lib/start-launcher.js +1309 -0
  46. package/mcp-server/dist/config.d.ts +22 -0
  47. package/mcp-server/dist/config.d.ts.map +1 -0
  48. package/mcp-server/dist/config.js +109 -0
  49. package/mcp-server/dist/config.js.map +1 -0
  50. package/mcp-server/dist/http.d.ts +30 -0
  51. package/mcp-server/dist/http.d.ts.map +1 -0
  52. package/mcp-server/dist/http.js +276 -0
  53. package/mcp-server/dist/http.js.map +1 -0
  54. package/mcp-server/dist/index.d.ts +48 -0
  55. package/mcp-server/dist/index.d.ts.map +1 -0
  56. package/mcp-server/dist/index.js +538 -0
  57. package/mcp-server/dist/index.js.map +1 -0
  58. package/mcp-server/dist/preload-http.d.ts +3 -0
  59. package/mcp-server/dist/preload-http.d.ts.map +1 -0
  60. package/mcp-server/dist/preload-http.js +28 -0
  61. package/mcp-server/dist/preload-http.js.map +1 -0
  62. package/mcp-server/dist/preload.d.ts +3 -0
  63. package/mcp-server/dist/preload.d.ts.map +1 -0
  64. package/mcp-server/dist/preload.js +32 -0
  65. package/mcp-server/dist/preload.js.map +1 -0
  66. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts +19 -0
  67. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts.map +1 -0
  68. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js +403 -0
  69. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js.map +1 -0
  70. package/mcp-server/dist/scripts/setup-embeddings.d.ts +3 -0
  71. package/mcp-server/dist/scripts/setup-embeddings.d.ts.map +1 -0
  72. package/mcp-server/dist/scripts/setup-embeddings.js +15 -0
  73. package/mcp-server/dist/scripts/setup-embeddings.js.map +1 -0
  74. package/mcp-server/dist/services/assistant-judgments.d.ts +79 -0
  75. package/mcp-server/dist/services/assistant-judgments.d.ts.map +1 -0
  76. package/mcp-server/dist/services/assistant-judgments.js +128 -0
  77. package/mcp-server/dist/services/assistant-judgments.js.map +1 -0
  78. package/mcp-server/dist/services/browser-assist-gate.d.ts +314 -0
  79. package/mcp-server/dist/services/browser-assist-gate.d.ts.map +1 -0
  80. package/mcp-server/dist/services/browser-assist-gate.js +344 -0
  81. package/mcp-server/dist/services/browser-assist-gate.js.map +1 -0
  82. package/mcp-server/dist/services/browser-assist.d.ts +104 -0
  83. package/mcp-server/dist/services/browser-assist.d.ts.map +1 -0
  84. package/mcp-server/dist/services/browser-assist.js +282 -0
  85. package/mcp-server/dist/services/browser-assist.js.map +1 -0
  86. package/mcp-server/dist/services/browser-bench.d.ts +772 -0
  87. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -0
  88. package/mcp-server/dist/services/browser-bench.js +424 -0
  89. package/mcp-server/dist/services/browser-bench.js.map +1 -0
  90. package/mcp-server/dist/services/browser-judgments.d.ts +196 -0
  91. package/mcp-server/dist/services/browser-judgments.d.ts.map +1 -0
  92. package/mcp-server/dist/services/browser-judgments.js +363 -0
  93. package/mcp-server/dist/services/browser-judgments.js.map +1 -0
  94. package/mcp-server/dist/services/browser-risk.d.ts +166 -0
  95. package/mcp-server/dist/services/browser-risk.d.ts.map +1 -0
  96. package/mcp-server/dist/services/browser-risk.js +458 -0
  97. package/mcp-server/dist/services/browser-risk.js.map +1 -0
  98. package/mcp-server/dist/services/categories.d.ts +61 -0
  99. package/mcp-server/dist/services/categories.d.ts.map +1 -0
  100. package/mcp-server/dist/services/categories.js +377 -0
  101. package/mcp-server/dist/services/categories.js.map +1 -0
  102. package/mcp-server/dist/services/codex-config-heal.d.ts +49 -0
  103. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -0
  104. package/mcp-server/dist/services/codex-config-heal.js +346 -0
  105. package/mcp-server/dist/services/codex-config-heal.js.map +1 -0
  106. package/mcp-server/dist/services/desktop-assist-gate.d.ts +198 -0
  107. package/mcp-server/dist/services/desktop-assist-gate.d.ts.map +1 -0
  108. package/mcp-server/dist/services/desktop-assist-gate.js +202 -0
  109. package/mcp-server/dist/services/desktop-assist-gate.js.map +1 -0
  110. package/mcp-server/dist/services/desktop-assist.d.ts +104 -0
  111. package/mcp-server/dist/services/desktop-assist.d.ts.map +1 -0
  112. package/mcp-server/dist/services/desktop-assist.js +266 -0
  113. package/mcp-server/dist/services/desktop-assist.js.map +1 -0
  114. package/mcp-server/dist/services/desktop-bench.d.ts +553 -0
  115. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -0
  116. package/mcp-server/dist/services/desktop-bench.js +311 -0
  117. package/mcp-server/dist/services/desktop-bench.js.map +1 -0
  118. package/mcp-server/dist/services/desktop-client.d.ts +23 -0
  119. package/mcp-server/dist/services/desktop-client.d.ts.map +1 -0
  120. package/mcp-server/dist/services/desktop-client.js +79 -0
  121. package/mcp-server/dist/services/desktop-client.js.map +1 -0
  122. package/mcp-server/dist/services/desktop-judgments.d.ts +125 -0
  123. package/mcp-server/dist/services/desktop-judgments.d.ts.map +1 -0
  124. package/mcp-server/dist/services/desktop-judgments.js +177 -0
  125. package/mcp-server/dist/services/desktop-judgments.js.map +1 -0
  126. package/mcp-server/dist/services/desktop-risk.d.ts +283 -0
  127. package/mcp-server/dist/services/desktop-risk.d.ts.map +1 -0
  128. package/mcp-server/dist/services/desktop-risk.js +605 -0
  129. package/mcp-server/dist/services/desktop-risk.js.map +1 -0
  130. package/mcp-server/dist/services/discord.d.ts +112 -0
  131. package/mcp-server/dist/services/discord.d.ts.map +1 -0
  132. package/mcp-server/dist/services/discord.js +477 -0
  133. package/mcp-server/dist/services/discord.js.map +1 -0
  134. package/mcp-server/dist/services/edit-stale.d.ts +81 -0
  135. package/mcp-server/dist/services/edit-stale.d.ts.map +1 -0
  136. package/mcp-server/dist/services/edit-stale.js +358 -0
  137. package/mcp-server/dist/services/edit-stale.js.map +1 -0
  138. package/mcp-server/dist/services/embedding-service.d.ts +16 -0
  139. package/mcp-server/dist/services/embedding-service.d.ts.map +1 -0
  140. package/mcp-server/dist/services/embedding-service.js +126 -0
  141. package/mcp-server/dist/services/embedding-service.js.map +1 -0
  142. package/mcp-server/dist/services/embedding-worker.d.ts +2 -0
  143. package/mcp-server/dist/services/embedding-worker.d.ts.map +1 -0
  144. package/mcp-server/dist/services/embedding-worker.js +52 -0
  145. package/mcp-server/dist/services/embedding-worker.js.map +1 -0
  146. package/mcp-server/dist/services/fb-regions.d.ts +71 -0
  147. package/mcp-server/dist/services/fb-regions.d.ts.map +1 -0
  148. package/mcp-server/dist/services/fb-regions.js +211 -0
  149. package/mcp-server/dist/services/fb-regions.js.map +1 -0
  150. package/mcp-server/dist/services/file-checksums.d.ts +22 -0
  151. package/mcp-server/dist/services/file-checksums.d.ts.map +1 -0
  152. package/mcp-server/dist/services/file-checksums.js +55 -0
  153. package/mcp-server/dist/services/file-checksums.js.map +1 -0
  154. package/mcp-server/dist/services/file-excerpt.d.ts +24 -0
  155. package/mcp-server/dist/services/file-excerpt.d.ts.map +1 -0
  156. package/mcp-server/dist/services/file-excerpt.js +65 -0
  157. package/mcp-server/dist/services/file-excerpt.js.map +1 -0
  158. package/mcp-server/dist/services/identity.d.ts +124 -0
  159. package/mcp-server/dist/services/identity.d.ts.map +1 -0
  160. package/mcp-server/dist/services/identity.js +241 -0
  161. package/mcp-server/dist/services/identity.js.map +1 -0
  162. package/mcp-server/dist/services/local-embeddings.d.ts +2 -0
  163. package/mcp-server/dist/services/local-embeddings.d.ts.map +1 -0
  164. package/mcp-server/dist/services/local-embeddings.js +3 -0
  165. package/mcp-server/dist/services/local-embeddings.js.map +1 -0
  166. package/mcp-server/dist/services/memory-client.d.ts +4 -0
  167. package/mcp-server/dist/services/memory-client.d.ts.map +1 -0
  168. package/mcp-server/dist/services/memory-client.js +27 -0
  169. package/mcp-server/dist/services/memory-client.js.map +1 -0
  170. package/mcp-server/dist/services/memory-judge-backfill.d.ts +135 -0
  171. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -0
  172. package/mcp-server/dist/services/memory-judge-backfill.js +510 -0
  173. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -0
  174. package/mcp-server/dist/services/memory-judgments.d.ts +362 -0
  175. package/mcp-server/dist/services/memory-judgments.d.ts.map +1 -0
  176. package/mcp-server/dist/services/memory-judgments.js +674 -0
  177. package/mcp-server/dist/services/memory-judgments.js.map +1 -0
  178. package/mcp-server/dist/services/memory-maintenance.d.ts +108 -0
  179. package/mcp-server/dist/services/memory-maintenance.d.ts.map +1 -0
  180. package/mcp-server/dist/services/memory-maintenance.js +403 -0
  181. package/mcp-server/dist/services/memory-maintenance.js.map +1 -0
  182. package/mcp-server/dist/services/memory-retrieval.d.ts +66 -0
  183. package/mcp-server/dist/services/memory-retrieval.d.ts.map +1 -0
  184. package/mcp-server/dist/services/memory-retrieval.js +412 -0
  185. package/mcp-server/dist/services/memory-retrieval.js.map +1 -0
  186. package/mcp-server/dist/services/memory-schema.d.ts +16 -0
  187. package/mcp-server/dist/services/memory-schema.d.ts.map +1 -0
  188. package/mcp-server/dist/services/memory-schema.js +282 -0
  189. package/mcp-server/dist/services/memory-schema.js.map +1 -0
  190. package/mcp-server/dist/services/memory-sessions.d.ts +24 -0
  191. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -0
  192. package/mcp-server/dist/services/memory-sessions.js +110 -0
  193. package/mcp-server/dist/services/memory-sessions.js.map +1 -0
  194. package/mcp-server/dist/services/memory-triage.d.ts +60 -0
  195. package/mcp-server/dist/services/memory-triage.d.ts.map +1 -0
  196. package/mcp-server/dist/services/memory-triage.js +189 -0
  197. package/mcp-server/dist/services/memory-triage.js.map +1 -0
  198. package/mcp-server/dist/services/memory-verification.d.ts +24 -0
  199. package/mcp-server/dist/services/memory-verification.d.ts.map +1 -0
  200. package/mcp-server/dist/services/memory-verification.js +92 -0
  201. package/mcp-server/dist/services/memory-verification.js.map +1 -0
  202. package/mcp-server/dist/services/memory-writer.d.ts +26 -0
  203. package/mcp-server/dist/services/memory-writer.d.ts.map +1 -0
  204. package/mcp-server/dist/services/memory-writer.js +126 -0
  205. package/mcp-server/dist/services/memory-writer.js.map +1 -0
  206. package/mcp-server/dist/services/neural-interface.d.ts +439 -0
  207. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -0
  208. package/mcp-server/dist/services/neural-interface.js +1135 -0
  209. package/mcp-server/dist/services/neural-interface.js.map +1 -0
  210. package/mcp-server/dist/services/profiles.d.ts +120 -0
  211. package/mcp-server/dist/services/profiles.d.ts.map +1 -0
  212. package/mcp-server/dist/services/profiles.js +387 -0
  213. package/mcp-server/dist/services/profiles.js.map +1 -0
  214. package/mcp-server/dist/services/secret-gate.d.ts +29 -0
  215. package/mcp-server/dist/services/secret-gate.d.ts.map +1 -0
  216. package/mcp-server/dist/services/secret-gate.js +43 -0
  217. package/mcp-server/dist/services/secret-gate.js.map +1 -0
  218. package/mcp-server/dist/services/server-instructions.d.ts +10 -0
  219. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -0
  220. package/mcp-server/dist/services/server-instructions.js +101 -0
  221. package/mcp-server/dist/services/server-instructions.js.map +1 -0
  222. package/mcp-server/dist/services/sqlite.d.ts +235 -0
  223. package/mcp-server/dist/services/sqlite.d.ts.map +1 -0
  224. package/mcp-server/dist/services/sqlite.js +1185 -0
  225. package/mcp-server/dist/services/sqlite.js.map +1 -0
  226. package/mcp-server/dist/services/typesafe-bench.d.ts +99 -0
  227. package/mcp-server/dist/services/typesafe-bench.d.ts.map +1 -0
  228. package/mcp-server/dist/services/typesafe-bench.js +218 -0
  229. package/mcp-server/dist/services/typesafe-bench.js.map +1 -0
  230. package/mcp-server/dist/services/typesafe-config.d.ts +342 -0
  231. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -0
  232. package/mcp-server/dist/services/typesafe-config.js +746 -0
  233. package/mcp-server/dist/services/typesafe-config.js.map +1 -0
  234. package/mcp-server/dist/services/typesafe.d.ts +213 -0
  235. package/mcp-server/dist/services/typesafe.d.ts.map +1 -0
  236. package/mcp-server/dist/services/typesafe.js +330 -0
  237. package/mcp-server/dist/services/typesafe.js.map +1 -0
  238. package/mcp-server/dist/services/vector-cache.d.ts +68 -0
  239. package/mcp-server/dist/services/vector-cache.d.ts.map +1 -0
  240. package/mcp-server/dist/services/vector-cache.js +271 -0
  241. package/mcp-server/dist/services/vector-cache.js.map +1 -0
  242. package/mcp-server/dist/services/whiteboard-geometry.d.ts +305 -0
  243. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -0
  244. package/mcp-server/dist/services/whiteboard-geometry.js +666 -0
  245. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -0
  246. package/mcp-server/dist/tools/agents.d.ts +212 -0
  247. package/mcp-server/dist/tools/agents.d.ts.map +1 -0
  248. package/mcp-server/dist/tools/agents.js +407 -0
  249. package/mcp-server/dist/tools/agents.js.map +1 -0
  250. package/mcp-server/dist/tools/bluesky.d.ts +39 -0
  251. package/mcp-server/dist/tools/bluesky.d.ts.map +1 -0
  252. package/mcp-server/dist/tools/bluesky.js +1144 -0
  253. package/mcp-server/dist/tools/bluesky.js.map +1 -0
  254. package/mcp-server/dist/tools/browser-advanced.d.ts +58 -0
  255. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -0
  256. package/mcp-server/dist/tools/browser-advanced.js +106 -0
  257. package/mcp-server/dist/tools/browser-advanced.js.map +1 -0
  258. package/mcp-server/dist/tools/browser-batch.d.ts +99 -0
  259. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -0
  260. package/mcp-server/dist/tools/browser-batch.js +187 -0
  261. package/mcp-server/dist/tools/browser-batch.js.map +1 -0
  262. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +21 -0
  263. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -0
  264. package/mcp-server/dist/tools/browser-cheatsheet.js +144 -0
  265. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -0
  266. package/mcp-server/dist/tools/browser-interact.d.ts +239 -0
  267. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -0
  268. package/mcp-server/dist/tools/browser-interact.js +300 -0
  269. package/mcp-server/dist/tools/browser-interact.js.map +1 -0
  270. package/mcp-server/dist/tools/browser-navigate.d.ts +90 -0
  271. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -0
  272. package/mcp-server/dist/tools/browser-navigate.js +142 -0
  273. package/mcp-server/dist/tools/browser-navigate.js.map +1 -0
  274. package/mcp-server/dist/tools/browser-observe.d.ts +561 -0
  275. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -0
  276. package/mcp-server/dist/tools/browser-observe.js +2009 -0
  277. package/mcp-server/dist/tools/browser-observe.js.map +1 -0
  278. package/mcp-server/dist/tools/browser.d.ts +34 -0
  279. package/mcp-server/dist/tools/browser.d.ts.map +1 -0
  280. package/mcp-server/dist/tools/browser.js +112 -0
  281. package/mcp-server/dist/tools/browser.js.map +1 -0
  282. package/mcp-server/dist/tools/card-tools.d.ts +82 -0
  283. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -0
  284. package/mcp-server/dist/tools/card-tools.js +137 -0
  285. package/mcp-server/dist/tools/card-tools.js.map +1 -0
  286. package/mcp-server/dist/tools/card.d.ts +8 -0
  287. package/mcp-server/dist/tools/card.d.ts.map +1 -0
  288. package/mcp-server/dist/tools/card.js +16 -0
  289. package/mcp-server/dist/tools/card.js.map +1 -0
  290. package/mcp-server/dist/tools/category.d.ts +32 -0
  291. package/mcp-server/dist/tools/category.d.ts.map +1 -0
  292. package/mcp-server/dist/tools/category.js +290 -0
  293. package/mcp-server/dist/tools/category.js.map +1 -0
  294. package/mcp-server/dist/tools/choice.d.ts +160 -0
  295. package/mcp-server/dist/tools/choice.d.ts.map +1 -0
  296. package/mcp-server/dist/tools/choice.js +80 -0
  297. package/mcp-server/dist/tools/choice.js.map +1 -0
  298. package/mcp-server/dist/tools/computer.d.ts +71 -0
  299. package/mcp-server/dist/tools/computer.d.ts.map +1 -0
  300. package/mcp-server/dist/tools/computer.js +174 -0
  301. package/mcp-server/dist/tools/computer.js.map +1 -0
  302. package/mcp-server/dist/tools/discord-channel.d.ts +44 -0
  303. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -0
  304. package/mcp-server/dist/tools/discord-channel.js +236 -0
  305. package/mcp-server/dist/tools/discord-channel.js.map +1 -0
  306. package/mcp-server/dist/tools/discord-guild.d.ts +20 -0
  307. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -0
  308. package/mcp-server/dist/tools/discord-guild.js +138 -0
  309. package/mcp-server/dist/tools/discord-guild.js.map +1 -0
  310. package/mcp-server/dist/tools/discord-member.d.ts +26 -0
  311. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -0
  312. package/mcp-server/dist/tools/discord-member.js +122 -0
  313. package/mcp-server/dist/tools/discord-member.js.map +1 -0
  314. package/mcp-server/dist/tools/discord-message.d.ts +36 -0
  315. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -0
  316. package/mcp-server/dist/tools/discord-message.js +184 -0
  317. package/mcp-server/dist/tools/discord-message.js.map +1 -0
  318. package/mcp-server/dist/tools/discord-onboarding.d.ts +32 -0
  319. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -0
  320. package/mcp-server/dist/tools/discord-onboarding.js +179 -0
  321. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -0
  322. package/mcp-server/dist/tools/discord-role.d.ts +34 -0
  323. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -0
  324. package/mcp-server/dist/tools/discord-role.js +178 -0
  325. package/mcp-server/dist/tools/discord-role.js.map +1 -0
  326. package/mcp-server/dist/tools/discord-thread.d.ts +28 -0
  327. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -0
  328. package/mcp-server/dist/tools/discord-thread.js +88 -0
  329. package/mcp-server/dist/tools/discord-thread.js.map +1 -0
  330. package/mcp-server/dist/tools/discord-webhook.d.ts +32 -0
  331. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -0
  332. package/mcp-server/dist/tools/discord-webhook.js +154 -0
  333. package/mcp-server/dist/tools/discord-webhook.js.map +1 -0
  334. package/mcp-server/dist/tools/discord.d.ts +7 -0
  335. package/mcp-server/dist/tools/discord.d.ts.map +1 -0
  336. package/mcp-server/dist/tools/discord.js +25 -0
  337. package/mcp-server/dist/tools/discord.js.map +1 -0
  338. package/mcp-server/dist/tools/fb-groups.d.ts +98 -0
  339. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -0
  340. package/mcp-server/dist/tools/fb-groups.js +369 -0
  341. package/mcp-server/dist/tools/fb-groups.js.map +1 -0
  342. package/mcp-server/dist/tools/forget.d.ts +14 -0
  343. package/mcp-server/dist/tools/forget.d.ts.map +1 -0
  344. package/mcp-server/dist/tools/forget.js +26 -0
  345. package/mcp-server/dist/tools/forget.js.map +1 -0
  346. package/mcp-server/dist/tools/git-tools.d.ts +24 -0
  347. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -0
  348. package/mcp-server/dist/tools/git-tools.js +107 -0
  349. package/mcp-server/dist/tools/git-tools.js.map +1 -0
  350. package/mcp-server/dist/tools/git.d.ts +7 -0
  351. package/mcp-server/dist/tools/git.d.ts.map +1 -0
  352. package/mcp-server/dist/tools/git.js +11 -0
  353. package/mcp-server/dist/tools/git.js.map +1 -0
  354. package/mcp-server/dist/tools/gsc/experience.d.ts +135 -0
  355. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -0
  356. package/mcp-server/dist/tools/gsc/experience.js +351 -0
  357. package/mcp-server/dist/tools/gsc/experience.js.map +1 -0
  358. package/mcp-server/dist/tools/gsc/helpers.d.ts +78 -0
  359. package/mcp-server/dist/tools/gsc/helpers.d.ts.map +1 -0
  360. package/mcp-server/dist/tools/gsc/helpers.js +258 -0
  361. package/mcp-server/dist/tools/gsc/helpers.js.map +1 -0
  362. package/mcp-server/dist/tools/gsc/indexing.d.ts +121 -0
  363. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -0
  364. package/mcp-server/dist/tools/gsc/indexing.js +357 -0
  365. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -0
  366. package/mcp-server/dist/tools/gsc/inspect.d.ts +78 -0
  367. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -0
  368. package/mcp-server/dist/tools/gsc/inspect.js +321 -0
  369. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -0
  370. package/mcp-server/dist/tools/gsc/navigate.d.ts +44 -0
  371. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -0
  372. package/mcp-server/dist/tools/gsc/navigate.js +197 -0
  373. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -0
  374. package/mcp-server/dist/tools/gsc/performance.d.ts +84 -0
  375. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -0
  376. package/mcp-server/dist/tools/gsc/performance.js +274 -0
  377. package/mcp-server/dist/tools/gsc/performance.js.map +1 -0
  378. package/mcp-server/dist/tools/gsc/settings.d.ts +153 -0
  379. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -0
  380. package/mcp-server/dist/tools/gsc/settings.js +425 -0
  381. package/mcp-server/dist/tools/gsc/settings.js.map +1 -0
  382. package/mcp-server/dist/tools/gsc.d.ts +7 -0
  383. package/mcp-server/dist/tools/gsc.d.ts.map +1 -0
  384. package/mcp-server/dist/tools/gsc.js +51 -0
  385. package/mcp-server/dist/tools/gsc.js.map +1 -0
  386. package/mcp-server/dist/tools/image-tools.d.ts +24 -0
  387. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -0
  388. package/mcp-server/dist/tools/image-tools.js +80 -0
  389. package/mcp-server/dist/tools/image-tools.js.map +1 -0
  390. package/mcp-server/dist/tools/image.d.ts +10 -0
  391. package/mcp-server/dist/tools/image.d.ts.map +1 -0
  392. package/mcp-server/dist/tools/image.js +14 -0
  393. package/mcp-server/dist/tools/image.js.map +1 -0
  394. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +97 -0
  395. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -0
  396. package/mcp-server/dist/tools/leonardo-browser-tools.js +228 -0
  397. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -0
  398. package/mcp-server/dist/tools/leonardo.d.ts +13 -0
  399. package/mcp-server/dist/tools/leonardo.d.ts.map +1 -0
  400. package/mcp-server/dist/tools/leonardo.js +21 -0
  401. package/mcp-server/dist/tools/leonardo.js.map +1 -0
  402. package/mcp-server/dist/tools/loop.d.ts +30 -0
  403. package/mcp-server/dist/tools/loop.d.ts.map +1 -0
  404. package/mcp-server/dist/tools/loop.js +401 -0
  405. package/mcp-server/dist/tools/loop.js.map +1 -0
  406. package/mcp-server/dist/tools/memories.d.ts +56 -0
  407. package/mcp-server/dist/tools/memories.d.ts.map +1 -0
  408. package/mcp-server/dist/tools/memories.js +171 -0
  409. package/mcp-server/dist/tools/memories.js.map +1 -0
  410. package/mcp-server/dist/tools/morelogin.d.ts +24 -0
  411. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -0
  412. package/mcp-server/dist/tools/morelogin.js +79 -0
  413. package/mcp-server/dist/tools/morelogin.js.map +1 -0
  414. package/mcp-server/dist/tools/profile.d.ts +17 -0
  415. package/mcp-server/dist/tools/profile.d.ts.map +1 -0
  416. package/mcp-server/dist/tools/profile.js +114 -0
  417. package/mcp-server/dist/tools/profile.js.map +1 -0
  418. package/mcp-server/dist/tools/recall.d.ts +71 -0
  419. package/mcp-server/dist/tools/recall.d.ts.map +1 -0
  420. package/mcp-server/dist/tools/recall.js +147 -0
  421. package/mcp-server/dist/tools/recall.js.map +1 -0
  422. package/mcp-server/dist/tools/reflect.d.ts +56 -0
  423. package/mcp-server/dist/tools/reflect.d.ts.map +1 -0
  424. package/mcp-server/dist/tools/reflect.js +157 -0
  425. package/mcp-server/dist/tools/reflect.js.map +1 -0
  426. package/mcp-server/dist/tools/remember.d.ts +62 -0
  427. package/mcp-server/dist/tools/remember.d.ts.map +1 -0
  428. package/mcp-server/dist/tools/remember.js +201 -0
  429. package/mcp-server/dist/tools/remember.js.map +1 -0
  430. package/mcp-server/dist/tools/response.d.ts +23 -0
  431. package/mcp-server/dist/tools/response.d.ts.map +1 -0
  432. package/mcp-server/dist/tools/response.js +24 -0
  433. package/mcp-server/dist/tools/response.js.map +1 -0
  434. package/mcp-server/dist/tools/restore.d.ts +14 -0
  435. package/mcp-server/dist/tools/restore.d.ts.map +1 -0
  436. package/mcp-server/dist/tools/restore.js +23 -0
  437. package/mcp-server/dist/tools/restore.js.map +1 -0
  438. package/mcp-server/dist/tools/style-guide-tools.d.ts +51 -0
  439. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -0
  440. package/mcp-server/dist/tools/style-guide-tools.js +231 -0
  441. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -0
  442. package/mcp-server/dist/tools/style-guide.d.ts +8 -0
  443. package/mcp-server/dist/tools/style-guide.d.ts.map +1 -0
  444. package/mcp-server/dist/tools/style-guide.js +12 -0
  445. package/mcp-server/dist/tools/style-guide.js.map +1 -0
  446. package/mcp-server/dist/tools/sync.d.ts +20 -0
  447. package/mcp-server/dist/tools/sync.d.ts.map +1 -0
  448. package/mcp-server/dist/tools/sync.js +156 -0
  449. package/mcp-server/dist/tools/sync.js.map +1 -0
  450. package/mcp-server/dist/tools/tictactoe-tools.d.ts +18 -0
  451. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -0
  452. package/mcp-server/dist/tools/tictactoe-tools.js +78 -0
  453. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -0
  454. package/mcp-server/dist/tools/tictactoe.d.ts +7 -0
  455. package/mcp-server/dist/tools/tictactoe.d.ts.map +1 -0
  456. package/mcp-server/dist/tools/tictactoe.js +11 -0
  457. package/mcp-server/dist/tools/tictactoe.js.map +1 -0
  458. package/mcp-server/dist/tools/utils.d.ts +19 -0
  459. package/mcp-server/dist/tools/utils.d.ts.map +1 -0
  460. package/mcp-server/dist/tools/utils.js +54 -0
  461. package/mcp-server/dist/tools/utils.js.map +1 -0
  462. package/mcp-server/dist/tools/whiteboard-format.d.ts +88 -0
  463. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -0
  464. package/mcp-server/dist/tools/whiteboard-format.js +337 -0
  465. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -0
  466. package/mcp-server/dist/tools/whiteboard-tools.d.ts +326 -0
  467. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -0
  468. package/mcp-server/dist/tools/whiteboard-tools.js +222 -0
  469. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -0
  470. package/mcp-server/dist/tools/whiteboard.d.ts +8 -0
  471. package/mcp-server/dist/tools/whiteboard.d.ts.map +1 -0
  472. package/mcp-server/dist/tools/whiteboard.js +16 -0
  473. package/mcp-server/dist/tools/whiteboard.js.map +1 -0
  474. package/mcp-server/dist/tools/youtube/analytics.d.ts +46 -0
  475. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -0
  476. package/mcp-server/dist/tools/youtube/analytics.js +83 -0
  477. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -0
  478. package/mcp-server/dist/tools/youtube/dedup.d.ts +35 -0
  479. package/mcp-server/dist/tools/youtube/dedup.d.ts.map +1 -0
  480. package/mcp-server/dist/tools/youtube/dedup.js +56 -0
  481. package/mcp-server/dist/tools/youtube/dedup.js.map +1 -0
  482. package/mcp-server/dist/tools/youtube/download.d.ts +31 -0
  483. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -0
  484. package/mcp-server/dist/tools/youtube/download.js +30 -0
  485. package/mcp-server/dist/tools/youtube/download.js.map +1 -0
  486. package/mcp-server/dist/tools/youtube/helpers.d.ts +66 -0
  487. package/mcp-server/dist/tools/youtube/helpers.d.ts.map +1 -0
  488. package/mcp-server/dist/tools/youtube/helpers.js +143 -0
  489. package/mcp-server/dist/tools/youtube/helpers.js.map +1 -0
  490. package/mcp-server/dist/tools/youtube/media.d.ts +43 -0
  491. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -0
  492. package/mcp-server/dist/tools/youtube/media.js +97 -0
  493. package/mcp-server/dist/tools/youtube/media.js.map +1 -0
  494. package/mcp-server/dist/tools/youtube/metadata.d.ts +33 -0
  495. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -0
  496. package/mcp-server/dist/tools/youtube/metadata.js +106 -0
  497. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -0
  498. package/mcp-server/dist/tools/youtube/navigate.d.ts +30 -0
  499. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -0
  500. package/mcp-server/dist/tools/youtube/navigate.js +71 -0
  501. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -0
  502. package/mcp-server/dist/tools/youtube/playlist.d.ts +29 -0
  503. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -0
  504. package/mcp-server/dist/tools/youtube/playlist.js +128 -0
  505. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -0
  506. package/mcp-server/dist/tools/youtube/schedule.d.ts +31 -0
  507. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -0
  508. package/mcp-server/dist/tools/youtube/schedule.js +147 -0
  509. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -0
  510. package/mcp-server/dist/tools/youtube/source.d.ts +36 -0
  511. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -0
  512. package/mcp-server/dist/tools/youtube/source.js +85 -0
  513. package/mcp-server/dist/tools/youtube/source.js.map +1 -0
  514. package/mcp-server/dist/tools/youtube/upload.d.ts +53 -0
  515. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -0
  516. package/mcp-server/dist/tools/youtube/upload.js +255 -0
  517. package/mcp-server/dist/tools/youtube/upload.js.map +1 -0
  518. package/mcp-server/dist/tools/youtube.d.ts +12 -0
  519. package/mcp-server/dist/tools/youtube.d.ts.map +1 -0
  520. package/mcp-server/dist/tools/youtube.js +38 -0
  521. package/mcp-server/dist/tools/youtube.js.map +1 -0
  522. package/mcp-server/dist/tui.d.ts +3 -0
  523. package/mcp-server/dist/tui.d.ts.map +1 -0
  524. package/mcp-server/dist/tui.js +806 -0
  525. package/mcp-server/dist/tui.js.map +1 -0
  526. package/mcp-server/dist/types.d.ts +77 -0
  527. package/mcp-server/dist/types.d.ts.map +1 -0
  528. package/mcp-server/dist/types.js +2 -0
  529. package/mcp-server/dist/types.js.map +1 -0
  530. package/mcp-server/package-lock.json +4798 -0
  531. package/mcp-server/package.json +55 -38
  532. package/mcp-server/run.mjs +12 -2
  533. package/mcp-server/src/config.ts +48 -3
  534. package/mcp-server/src/http.ts +296 -93
  535. package/mcp-server/src/index.ts +546 -203
  536. package/mcp-server/src/preload-http.ts +19 -18
  537. package/mcp-server/src/preload.ts +19 -18
  538. package/mcp-server/src/scripts/setup-embeddings.ts +9 -0
  539. package/mcp-server/src/services/assistant-judgments.ts +157 -0
  540. package/mcp-server/src/services/browser-assist-gate.ts +383 -0
  541. package/mcp-server/src/services/browser-assist.ts +267 -0
  542. package/mcp-server/src/services/browser-bench.ts +477 -0
  543. package/mcp-server/src/services/browser-judgments.ts +414 -0
  544. package/mcp-server/src/services/browser-risk.ts +484 -0
  545. package/mcp-server/src/services/categories.ts +36 -19
  546. package/mcp-server/src/services/codex-config-heal.ts +403 -0
  547. package/mcp-server/src/services/desktop-assist-gate.ts +251 -0
  548. package/mcp-server/src/services/desktop-assist.ts +308 -0
  549. package/mcp-server/src/services/desktop-bench.ts +372 -0
  550. package/mcp-server/src/services/desktop-client.ts +76 -0
  551. package/mcp-server/src/services/desktop-judgments.ts +218 -0
  552. package/mcp-server/src/services/desktop-risk.ts +743 -0
  553. package/mcp-server/src/services/edit-stale.ts +299 -0
  554. package/mcp-server/src/services/embedding-service.ts +108 -0
  555. package/mcp-server/src/services/embedding-worker.ts +46 -0
  556. package/mcp-server/src/services/fb-regions.ts +221 -0
  557. package/mcp-server/src/services/file-checksums.ts +26 -9
  558. package/mcp-server/src/services/file-excerpt.ts +59 -0
  559. package/mcp-server/src/services/identity.ts +291 -0
  560. package/mcp-server/src/services/local-embeddings.ts +2 -77
  561. package/mcp-server/src/services/memory-client.ts +23 -0
  562. package/mcp-server/src/services/memory-judge-backfill.ts +462 -0
  563. package/mcp-server/src/services/memory-judgments.ts +901 -0
  564. package/mcp-server/src/services/memory-maintenance.ts +360 -0
  565. package/mcp-server/src/services/memory-retrieval.ts +349 -0
  566. package/mcp-server/src/services/memory-schema.ts +280 -0
  567. package/mcp-server/src/services/memory-sessions.ts +87 -0
  568. package/mcp-server/src/services/memory-triage.ts +182 -0
  569. package/mcp-server/src/services/memory-verification.ts +68 -0
  570. package/mcp-server/src/services/memory-writer.ts +125 -0
  571. package/mcp-server/src/services/neural-interface.ts +1217 -73
  572. package/mcp-server/src/services/profiles.ts +428 -0
  573. package/mcp-server/src/services/secret-gate.ts +52 -0
  574. package/mcp-server/src/services/server-instructions.ts +122 -0
  575. package/mcp-server/src/services/sqlite.ts +1456 -823
  576. package/mcp-server/src/services/typesafe-bench.ts +247 -0
  577. package/mcp-server/src/services/typesafe-config.ts +790 -0
  578. package/mcp-server/src/services/typesafe.ts +390 -0
  579. package/mcp-server/src/services/vector-cache.ts +255 -0
  580. package/mcp-server/src/services/whiteboard-geometry.ts +711 -0
  581. package/mcp-server/src/tools/agents.ts +539 -0
  582. package/mcp-server/src/tools/bluesky.ts +1186 -0
  583. package/mcp-server/src/tools/browser-advanced.ts +15 -6
  584. package/mcp-server/src/tools/browser-batch.ts +178 -0
  585. package/mcp-server/src/tools/browser-cheatsheet.ts +155 -0
  586. package/mcp-server/src/tools/browser-interact.ts +362 -255
  587. package/mcp-server/src/tools/browser-navigate.ts +164 -77
  588. package/mcp-server/src/tools/browser-observe.ts +2222 -1330
  589. package/mcp-server/src/tools/browser.ts +181 -107
  590. package/mcp-server/src/tools/card.ts +7 -5
  591. package/mcp-server/src/tools/choice.ts +103 -0
  592. package/mcp-server/src/tools/computer.ts +190 -0
  593. package/mcp-server/src/tools/discord.ts +10 -23
  594. package/mcp-server/src/tools/fb-groups.ts +387 -0
  595. package/mcp-server/src/tools/forget.ts +1 -1
  596. package/mcp-server/src/tools/git.ts +3 -1
  597. package/mcp-server/src/tools/gsc/experience.ts +440 -0
  598. package/mcp-server/src/tools/gsc/helpers.ts +288 -0
  599. package/mcp-server/src/tools/gsc/indexing.ts +495 -0
  600. package/mcp-server/src/tools/gsc/inspect.ts +391 -0
  601. package/mcp-server/src/tools/gsc/navigate.ts +236 -0
  602. package/mcp-server/src/tools/gsc/performance.ts +352 -0
  603. package/mcp-server/src/tools/gsc/settings.ts +564 -0
  604. package/mcp-server/src/tools/gsc.ts +95 -0
  605. package/mcp-server/src/tools/image.ts +3 -1
  606. package/mcp-server/src/tools/leonardo-browser-tools.ts +47 -37
  607. package/mcp-server/src/tools/leonardo.ts +7 -5
  608. package/mcp-server/src/tools/loop.ts +118 -25
  609. package/mcp-server/src/tools/memories.ts +65 -5
  610. package/mcp-server/src/tools/morelogin.ts +81 -0
  611. package/mcp-server/src/tools/profile.ts +122 -0
  612. package/mcp-server/src/tools/recall.ts +105 -261
  613. package/mcp-server/src/tools/reflect.ts +171 -145
  614. package/mcp-server/src/tools/remember.ts +223 -115
  615. package/mcp-server/src/tools/style-guide-tools.ts +250 -0
  616. package/mcp-server/src/tools/style-guide.ts +16 -0
  617. package/mcp-server/src/tools/sync.ts +66 -9
  618. package/mcp-server/src/tools/tictactoe.ts +3 -1
  619. package/mcp-server/src/tools/utils.ts +22 -0
  620. package/mcp-server/src/tools/whiteboard-format.ts +394 -0
  621. package/mcp-server/src/tools/whiteboard-tools.ts +191 -183
  622. package/mcp-server/src/tools/whiteboard.ts +7 -5
  623. package/mcp-server/src/tools/youtube/analytics.ts +106 -0
  624. package/mcp-server/src/tools/youtube/dedup.ts +57 -0
  625. package/mcp-server/src/tools/youtube/download.ts +41 -0
  626. package/mcp-server/src/tools/youtube/helpers.ts +183 -0
  627. package/mcp-server/src/tools/youtube/media.ts +109 -0
  628. package/mcp-server/src/tools/youtube/metadata.ts +112 -0
  629. package/mcp-server/src/tools/youtube/navigate.ts +81 -0
  630. package/mcp-server/src/tools/youtube/playlist.ts +134 -0
  631. package/mcp-server/src/tools/youtube/schedule.ts +158 -0
  632. package/mcp-server/src/tools/youtube/source.ts +101 -0
  633. package/mcp-server/src/tools/youtube/upload.ts +296 -0
  634. package/mcp-server/src/tools/youtube.ts +47 -0
  635. package/mcp-server/src/tui.ts +8 -9
  636. package/neural-interface/games/TicTacToe/1.svg +1 -0
  637. package/neural-interface/games/TicTacToe/2.svg +1 -0
  638. package/neural-interface/games/TicTacToe/3.svg +1 -0
  639. package/neural-interface/games/TicTacToe/4.svg +1 -0
  640. package/neural-interface/games/TicTacToe/5.svg +1 -0
  641. package/neural-interface/games/TicTacToe/6.svg +1 -0
  642. package/neural-interface/games/TicTacToe/7.svg +1 -0
  643. package/neural-interface/games/TicTacToe/8.svg +1 -0
  644. package/neural-interface/games/TicTacToe/9.svg +1 -0
  645. package/neural-interface/games/TicTacToe/Board.svg +1 -0
  646. package/neural-interface/games/TicTacToe/Circle.svg +1 -0
  647. package/neural-interface/games/TicTacToe/Cross.svg +1 -0
  648. package/neural-interface/i18n/en.json +7022 -0
  649. package/neural-interface/i18n/pt-BR.json +7026 -0
  650. package/neural-interface/lib/assistant-api.js +1005 -0
  651. package/neural-interface/lib/assistant-attachments.js +121 -0
  652. package/neural-interface/lib/assistant-brains/claude.js +207 -0
  653. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +43 -0
  654. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +56 -0
  655. package/neural-interface/lib/assistant-brains/codex.js +213 -0
  656. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +47 -0
  657. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +62 -0
  658. package/neural-interface/lib/assistant-brains/opencode.js +320 -0
  659. package/neural-interface/lib/assistant-budget.js +318 -0
  660. package/neural-interface/lib/assistant-catalog.js +731 -0
  661. package/neural-interface/lib/assistant-clarify.js +664 -0
  662. package/neural-interface/lib/assistant-config.js +232 -0
  663. package/neural-interface/lib/assistant-dispatch.js +2744 -0
  664. package/neural-interface/lib/assistant-envelope.js +582 -0
  665. package/neural-interface/lib/assistant-evidence.js +236 -0
  666. package/neural-interface/lib/assistant-hidden-models.js +272 -0
  667. package/neural-interface/lib/assistant-jev.js +117 -0
  668. package/neural-interface/lib/assistant-judgments.js +111 -0
  669. package/neural-interface/lib/assistant-media.js +335 -0
  670. package/neural-interface/lib/assistant-memory.js +398 -0
  671. package/neural-interface/lib/assistant-persona.js +444 -0
  672. package/neural-interface/lib/assistant-plan-permissions.js +448 -0
  673. package/neural-interface/lib/assistant-playbooks/design.md +87 -0
  674. package/neural-interface/lib/assistant-playbooks.js +72 -0
  675. package/neural-interface/lib/assistant-pricing.js +159 -0
  676. package/neural-interface/lib/assistant-route-gate-codex.js +163 -0
  677. package/neural-interface/lib/assistant-route-gate.js +212 -0
  678. package/neural-interface/lib/assistant-router.js +1324 -0
  679. package/neural-interface/lib/assistant-runtime.js +3337 -0
  680. package/neural-interface/lib/assistant-task-prompt.js +472 -0
  681. package/neural-interface/lib/assistant-usage.js +1688 -0
  682. package/neural-interface/lib/augmented-path.js +70 -0
  683. package/neural-interface/lib/automation-storage-migration.js +54 -0
  684. package/neural-interface/lib/backup-service.js +851 -0
  685. package/neural-interface/lib/backup-zip-reader.js +262 -0
  686. package/neural-interface/lib/browser-assist-api.js +41 -0
  687. package/neural-interface/lib/browser-console.js +176 -0
  688. package/neural-interface/lib/browser-execution.js +241 -0
  689. package/neural-interface/lib/browser-extraction.js +191 -0
  690. package/neural-interface/lib/browser-language.js +83 -0
  691. package/neural-interface/lib/browser-screenshot.js +399 -0
  692. package/neural-interface/lib/browser-semantic-context.js +639 -0
  693. package/neural-interface/lib/browser-snapshots.js +192 -0
  694. package/neural-interface/lib/browser-tab-recovery.js +119 -0
  695. package/neural-interface/lib/browser-targets.js +70 -0
  696. package/neural-interface/lib/browser-tool-policy.js +926 -0
  697. package/neural-interface/lib/claude-accounts.js +230 -0
  698. package/neural-interface/lib/claude-agent-bridge.js +3203 -0
  699. package/neural-interface/lib/claude-bypass-policy.js +99 -0
  700. package/neural-interface/lib/claude-executable.js +241 -0
  701. package/neural-interface/lib/claude-history.js +429 -0
  702. package/neural-interface/lib/claude-model-catalog.js +444 -0
  703. package/neural-interface/lib/claude-panel-requests.js +165 -0
  704. package/neural-interface/lib/claude-panel-session.js +272 -0
  705. package/neural-interface/lib/claude-permission-rules.js +153 -0
  706. package/neural-interface/lib/claude-session-cleanup.js +71 -0
  707. package/neural-interface/lib/claude-session-list.js +82 -0
  708. package/neural-interface/lib/claude-session-ops.js +140 -0
  709. package/neural-interface/lib/claude-settings-view.js +51 -0
  710. package/neural-interface/lib/claude-skin-connect.js +79 -0
  711. package/neural-interface/lib/claude-stall-policy.js +7 -0
  712. package/neural-interface/lib/claude-temporary.js +435 -0
  713. package/neural-interface/lib/cli-update-planner.js +312 -0
  714. package/neural-interface/lib/codex-capabilities.d.ts +41 -0
  715. package/neural-interface/lib/codex-capabilities.js +148 -0
  716. package/neural-interface/lib/codex-collaboration-mode.js +17 -0
  717. package/neural-interface/lib/codex-compat-rpc.js +241 -0
  718. package/neural-interface/lib/codex-mcp-approvals.js +56 -0
  719. package/neural-interface/lib/codex-mcp-apps.js +47 -0
  720. package/neural-interface/lib/codex-model-context.js +202 -0
  721. package/neural-interface/lib/codex-plan-store.js +71 -0
  722. package/neural-interface/lib/codex-request-journal.js +242 -0
  723. package/neural-interface/lib/codex-runtime-path.js +213 -0
  724. package/neural-interface/lib/codex-session-catalog.js +231 -0
  725. package/neural-interface/lib/codex-session-settings.js +46 -0
  726. package/neural-interface/lib/codex-sidepanel-permissions.js +110 -0
  727. package/neural-interface/lib/codex-thread-history.js +165 -0
  728. package/neural-interface/lib/codex-user-verification.js +34 -0
  729. package/neural-interface/lib/codex-writer-lifecycle.js +152 -0
  730. package/neural-interface/lib/confined-fs.js +256 -0
  731. package/neural-interface/lib/db.js +922 -0
  732. package/neural-interface/lib/design-md.js +9 -0
  733. package/neural-interface/lib/desktop/api.js +92 -0
  734. package/neural-interface/lib/desktop/audit.js +62 -0
  735. package/neural-interface/lib/desktop/build.js +423 -0
  736. package/neural-interface/lib/desktop/config.js +606 -0
  737. package/neural-interface/lib/desktop/coords.js +76 -0
  738. package/neural-interface/lib/desktop/fake-helper.js +1192 -0
  739. package/neural-interface/lib/desktop/frames.js +77 -0
  740. package/neural-interface/lib/desktop/grants.js +84 -0
  741. package/neural-interface/lib/desktop/guards.js +170 -0
  742. package/neural-interface/lib/desktop/helper/SynabunDesktop.swift +3056 -0
  743. package/neural-interface/lib/desktop/lease.js +51 -0
  744. package/neural-interface/lib/desktop/manager.js +545 -0
  745. package/neural-interface/lib/desktop/press-contexts.js +103 -0
  746. package/neural-interface/lib/desktop/protocol.js +495 -0
  747. package/neural-interface/lib/desktop/service.js +1202 -0
  748. package/neural-interface/lib/effort-levels.js +148 -0
  749. package/neural-interface/lib/event-loop-monitor.js +67 -0
  750. package/neural-interface/lib/exec-loop-command.js +209 -0
  751. package/neural-interface/lib/folder-picker.ps1 +77 -0
  752. package/neural-interface/lib/http-guards.js +287 -0
  753. package/neural-interface/lib/jsonl-lines.js +94 -0
  754. package/neural-interface/lib/loop-logger.js +127 -0
  755. package/neural-interface/lib/mcp-client-config.js +133 -0
  756. package/neural-interface/lib/mcp-installer.js +487 -0
  757. package/neural-interface/lib/mcp-profile-refresh-coordinator.js +78 -0
  758. package/neural-interface/lib/mcp-profile-servers.js +120 -0
  759. package/neural-interface/lib/memo-cache.js +84 -0
  760. package/neural-interface/lib/memory-api.js +758 -0
  761. package/neural-interface/lib/memory-map-api.js +1150 -0
  762. package/neural-interface/lib/memory-map-layout.js +872 -0
  763. package/neural-interface/lib/memory-map-worker.js +165 -0
  764. package/neural-interface/lib/memory-session-capture.js +81 -0
  765. package/neural-interface/lib/memory-timers.js +62 -0
  766. package/neural-interface/lib/morelogin.js +301 -0
  767. package/neural-interface/lib/native-binary-runtime.js +382 -0
  768. package/neural-interface/lib/native-loop-goal.js +33 -0
  769. package/neural-interface/lib/native-loop-providers.js +1002 -0
  770. package/neural-interface/lib/native-loop-runtime.js +747 -0
  771. package/neural-interface/lib/offline-page.js +54 -0
  772. package/neural-interface/lib/opencode-hidden-models.js +113 -0
  773. package/neural-interface/lib/opencode-history.js +253 -0
  774. package/neural-interface/lib/opencode-runtime-config.js +58 -0
  775. package/neural-interface/lib/opencode-v2-client.js +486 -0
  776. package/neural-interface/lib/opencode-v2-ws-requests.js +955 -0
  777. package/neural-interface/lib/path-confine.js +213 -0
  778. package/neural-interface/lib/plugin-installer.js +264 -0
  779. package/neural-interface/lib/project-storage.js +766 -0
  780. package/neural-interface/lib/pty-host/core.js +603 -0
  781. package/neural-interface/lib/pty-host/flow.js +43 -0
  782. package/neural-interface/lib/pty-host/host.js +47 -0
  783. package/neural-interface/lib/pty-host/manager.js +360 -0
  784. package/neural-interface/lib/pty-host/protocol.js +81 -0
  785. package/neural-interface/lib/pty-host/snapshot.js +133 -0
  786. package/neural-interface/lib/remote-policy.js +582 -0
  787. package/neural-interface/lib/rulesets/api.js +175 -0
  788. package/neural-interface/lib/rulesets/fs-safe.js +245 -0
  789. package/neural-interface/lib/rulesets/installer.js +1453 -0
  790. package/neural-interface/lib/rulesets/json-edit.js +186 -0
  791. package/neural-interface/lib/rulesets/lock.js +170 -0
  792. package/neural-interface/lib/rulesets/managed-text.js +314 -0
  793. package/neural-interface/lib/rulesets/normalise.js +24 -0
  794. package/neural-interface/lib/rulesets/render.js +84 -0
  795. package/neural-interface/lib/schedule-launch-intent.js +40 -0
  796. package/neural-interface/lib/session-cache-builder.js +524 -0
  797. package/neural-interface/lib/session-chunker.js +344 -0
  798. package/neural-interface/lib/session-indexer.js +327 -0
  799. package/neural-interface/lib/session-title-generator.js +232 -0
  800. package/neural-interface/lib/skill-prompt-bridge.js +11 -0
  801. package/neural-interface/lib/style-guide/api.js +375 -0
  802. package/neural-interface/lib/style-guide/color.js +352 -0
  803. package/neural-interface/lib/style-guide/export-css.js +99 -0
  804. package/neural-interface/lib/style-guide/export-dtcg.js +209 -0
  805. package/neural-interface/lib/style-guide/export-tailwind.js +102 -0
  806. package/neural-interface/lib/style-guide/fonts.json +227 -0
  807. package/neural-interface/lib/style-guide/import.js +1268 -0
  808. package/neural-interface/lib/style-guide/pointers.js +103 -0
  809. package/neural-interface/lib/style-guide/presets.js +232 -0
  810. package/neural-interface/lib/style-guide/render-design-md.js +319 -0
  811. package/neural-interface/lib/style-guide/schema.js +848 -0
  812. package/neural-interface/lib/style-guide/security.js +43 -0
  813. package/neural-interface/lib/style-guide/store.js +491 -0
  814. package/neural-interface/lib/style-guide/summary.js +106 -0
  815. package/neural-interface/lib/style-guide/tokens.js +170 -0
  816. package/neural-interface/lib/synabun-update-plan.js +324 -0
  817. package/neural-interface/lib/synabun-version.js +179 -0
  818. package/neural-interface/lib/system-restore.js +99 -0
  819. package/neural-interface/lib/terminal-env.js +86 -0
  820. package/neural-interface/lib/ui-blob-store.js +106 -0
  821. package/neural-interface/lib/vector-cache.js +2 -0
  822. package/neural-interface/lib/virtual-socket.js +60 -0
  823. package/neural-interface/lib/whatsapp/api.js +146 -0
  824. package/neural-interface/lib/whatsapp/auth-store.js +389 -0
  825. package/neural-interface/lib/whatsapp/baileys-adapter.js +457 -0
  826. package/neural-interface/lib/whatsapp/brain.js +128 -0
  827. package/neural-interface/lib/whatsapp/bridge.js +1499 -0
  828. package/neural-interface/lib/whatsapp/cards.js +519 -0
  829. package/neural-interface/lib/whatsapp/commands.js +92 -0
  830. package/neural-interface/lib/whatsapp/config.js +291 -0
  831. package/neural-interface/lib/whatsapp/connector/entry.mjs +25 -0
  832. package/neural-interface/lib/whatsapp/connector/manifest.json +6 -0
  833. package/neural-interface/lib/whatsapp/connector/npm-shrinkwrap.json +717 -0
  834. package/neural-interface/lib/whatsapp/connector/package.json +8 -0
  835. package/neural-interface/lib/whatsapp/fake-baileys.js +444 -0
  836. package/neural-interface/lib/whatsapp/format.js +552 -0
  837. package/neural-interface/lib/whatsapp/host-core.js +1225 -0
  838. package/neural-interface/lib/whatsapp/host.js +119 -0
  839. package/neural-interface/lib/whatsapp/identity.js +288 -0
  840. package/neural-interface/lib/whatsapp/inbound.js +149 -0
  841. package/neural-interface/lib/whatsapp/installer.js +562 -0
  842. package/neural-interface/lib/whatsapp/manager.js +638 -0
  843. package/neural-interface/lib/whatsapp/paths.js +81 -0
  844. package/neural-interface/lib/whatsapp/protocol.js +327 -0
  845. package/neural-interface/lib/whatsapp/qr-svg.js +57 -0
  846. package/neural-interface/lib/whatsapp/redact.js +130 -0
  847. package/neural-interface/lib/whatsapp/service.js +1305 -0
  848. package/neural-interface/lib/whatsapp/vendor/qrcodegen.js +848 -0
  849. package/neural-interface/package-lock.json +4431 -0
  850. package/neural-interface/package.json +58 -27
  851. package/neural-interface/public/apple-touch-icon.png +0 -0
  852. package/neural-interface/public/category-logos/synabun.png +0 -0
  853. package/neural-interface/public/claude.svg +1 -0
  854. package/neural-interface/public/favicon-16x16.png +0 -0
  855. package/neural-interface/public/favicon-32x32.png +0 -0
  856. package/neural-interface/public/icon-192.png +0 -0
  857. package/neural-interface/public/icon-512.png +0 -0
  858. package/neural-interface/public/index.html +32 -182
  859. package/neural-interface/public/index2d.html +2 -2
  860. package/neural-interface/public/install-app.html +31 -0
  861. package/neural-interface/public/logo-synabun.png +0 -0
  862. package/neural-interface/public/logoHD.png +0 -0
  863. package/neural-interface/public/manifest.json +2 -0
  864. package/neural-interface/public/mcp.svg +4 -0
  865. package/neural-interface/public/offline.html +670 -299
  866. package/neural-interface/public/onboarding.html +804 -161
  867. package/neural-interface/public/opencode-logo-dark-square.svg +1 -0
  868. package/neural-interface/public/shared/agent-runtime-options.js +596 -0
  869. package/neural-interface/public/shared/api.js +339 -28
  870. package/neural-interface/public/shared/assistant/asst-accounts.js +318 -0
  871. package/neural-interface/public/shared/assistant/asst-agents-dock.js +296 -0
  872. package/neural-interface/public/shared/assistant/asst-api.js +270 -0
  873. package/neural-interface/public/shared/assistant/asst-brain-picker.js +744 -0
  874. package/neural-interface/public/shared/assistant/asst-budget.js +333 -0
  875. package/neural-interface/public/shared/assistant/asst-composer.js +656 -0
  876. package/neural-interface/public/shared/assistant/asst-computer.js +803 -0
  877. package/neural-interface/public/shared/assistant/asst-control.js +1018 -0
  878. package/neural-interface/public/shared/assistant/asst-hosts.js +262 -0
  879. package/neural-interface/public/shared/assistant/asst-mascot.js +313 -0
  880. package/neural-interface/public/shared/assistant/asst-menu.js +375 -0
  881. package/neural-interface/public/shared/assistant/asst-panel.js +1845 -0
  882. package/neural-interface/public/shared/assistant/asst-rack-styles.js +586 -0
  883. package/neural-interface/public/shared/assistant/asst-render.js +3305 -0
  884. package/neural-interface/public/shared/assistant/asst-route.js +1653 -0
  885. package/neural-interface/public/shared/assistant/asst-roving.js +75 -0
  886. package/neural-interface/public/shared/assistant/asst-sanitize.js +117 -0
  887. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +488 -0
  888. package/neural-interface/public/shared/assistant/asst-sidepanel.js +675 -0
  889. package/neural-interface/public/shared/assistant/asst-slash.js +258 -0
  890. package/neural-interface/public/shared/assistant/asst-state.js +727 -0
  891. package/neural-interface/public/shared/assistant/asst-styles.js +1686 -0
  892. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +1017 -0
  893. package/neural-interface/public/shared/assistant/asst-usage.js +444 -0
  894. package/neural-interface/public/shared/assistant/asst-ws.js +175 -0
  895. package/neural-interface/public/shared/automation-context.js +75 -0
  896. package/neural-interface/public/shared/blob-store.js +99 -0
  897. package/neural-interface/public/shared/cdx/cdx-capabilities.js +118 -0
  898. package/neural-interface/public/shared/cdx/cdx-cards.js +37 -0
  899. package/neural-interface/public/shared/cdx/cdx-commands.js +171 -0
  900. package/neural-interface/public/shared/cdx/cdx-context-menu.js +402 -0
  901. package/neural-interface/public/shared/cdx/cdx-context-model.js +220 -0
  902. package/neural-interface/public/shared/cdx/cdx-features.js +408 -0
  903. package/neural-interface/public/shared/cdx/cdx-icons.js +89 -0
  904. package/neural-interface/public/shared/cdx/cdx-mcp-app-host.js +414 -0
  905. package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.html +6 -0
  906. package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.js +56 -0
  907. package/neural-interface/public/shared/cdx/cdx-mcp-app.js +178 -0
  908. package/neural-interface/public/shared/cdx/cdx-model-catalog.js +120 -0
  909. package/neural-interface/public/shared/cdx/cdx-output-model.js +261 -0
  910. package/neural-interface/public/shared/cdx/cdx-output.js +382 -0
  911. package/neural-interface/public/shared/cdx/cdx-panel.js +1372 -0
  912. package/neural-interface/public/shared/cdx/cdx-plan.js +266 -0
  913. package/neural-interface/public/shared/cdx/cdx-protocol.js +682 -0
  914. package/neural-interface/public/shared/cdx/cdx-render.js +3381 -0
  915. package/neural-interface/public/shared/cdx/cdx-requests.js +1136 -0
  916. package/neural-interface/public/shared/cdx/cdx-styles.js +3251 -0
  917. package/neural-interface/public/shared/cdx/cdx-tabs.js +7992 -0
  918. package/neural-interface/public/shared/cdx/cdx-ws.js +363 -0
  919. package/neural-interface/public/shared/cli-status.js +169 -0
  920. package/neural-interface/public/shared/command-runner-model.js +30 -0
  921. package/neural-interface/public/shared/constants.js +22 -5
  922. package/neural-interface/public/shared/cp/cp-agents.js +186 -0
  923. package/neural-interface/public/shared/cp/cp-bash.js +252 -0
  924. package/neural-interface/public/shared/cp/cp-compose.js +159 -0
  925. package/neural-interface/public/shared/cp/cp-context-menu.js +309 -0
  926. package/neural-interface/public/shared/cp/cp-context-model.js +157 -0
  927. package/neural-interface/public/shared/cp/cp-context-window.js +80 -0
  928. package/neural-interface/public/shared/cp/cp-ctx.js +11 -0
  929. package/neural-interface/public/shared/cp/cp-diff.js +255 -0
  930. package/neural-interface/public/shared/cp/cp-event-rows.js +637 -0
  931. package/neural-interface/public/shared/cp/cp-events.js +846 -0
  932. package/neural-interface/public/shared/cp/cp-markdown.js +104 -0
  933. package/neural-interface/public/shared/cp/cp-permission-model.js +905 -0
  934. package/neural-interface/public/shared/cp/cp-permissions.js +626 -0
  935. package/neural-interface/public/shared/cp/cp-rehydrate.js +113 -0
  936. package/neural-interface/public/shared/cp/cp-restore.js +224 -0
  937. package/neural-interface/public/shared/cp/cp-session-model.js +338 -0
  938. package/neural-interface/public/shared/cp/cp-session-settings.js +254 -0
  939. package/neural-interface/public/shared/cp/cp-sessions.js +227 -0
  940. package/neural-interface/public/shared/cp/cp-statusline.js +153 -0
  941. package/neural-interface/public/shared/cp/cp-styles.js +349 -0
  942. package/neural-interface/public/shared/cp/cp-tasks-model.js +282 -0
  943. package/neural-interface/public/shared/cp/cp-tasks.js +107 -0
  944. package/neural-interface/public/shared/cp/cp-temporary-view.js +56 -0
  945. package/neural-interface/public/shared/cp/cp-temporary.js +231 -0
  946. package/neural-interface/public/shared/cp/cp-tool-cards.js +326 -0
  947. package/neural-interface/public/shared/cp/cp-tool-results.js +618 -0
  948. package/neural-interface/public/shared/cp/cp-usage-model.js +124 -0
  949. package/neural-interface/public/shared/graph-activity.js +68 -0
  950. package/neural-interface/public/shared/html-shell.js +116 -17
  951. package/neural-interface/public/shared/i18n.js +132 -34
  952. package/neural-interface/public/shared/node-positions-store.js +44 -0
  953. package/neural-interface/public/shared/ocp-hidden-models.js +116 -0
  954. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +355 -0
  955. package/neural-interface/public/shared/ocp-v2/ocp-v2-autoaccept-button.js +35 -0
  956. package/neural-interface/public/shared/ocp-v2/ocp-v2-automation-ownership.js +72 -0
  957. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +280 -0
  958. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +67 -0
  959. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +179 -0
  960. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes.js +164 -0
  961. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +725 -0
  962. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +75 -0
  963. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +1204 -0
  964. package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm-logic.js +117 -0
  965. package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm.js +59 -0
  966. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-gauge.js +252 -0
  967. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +478 -0
  968. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +179 -0
  969. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +233 -0
  970. package/neural-interface/public/shared/ocp-v2/ocp-v2-icons.js +30 -0
  971. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +1065 -0
  972. package/neural-interface/public/shared/ocp-v2/ocp-v2-mention-hints.js +182 -0
  973. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +655 -0
  974. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +2636 -0
  975. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +601 -0
  976. package/neural-interface/public/shared/ocp-v2/ocp-v2-profile-inheritance.js +78 -0
  977. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +649 -0
  978. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +35 -0
  979. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +357 -0
  980. package/neural-interface/public/shared/ocp-v2/ocp-v2-render-logic.js +189 -0
  981. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1869 -0
  982. package/neural-interface/public/shared/ocp-v2/ocp-v2-select.js +42 -0
  983. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +117 -0
  984. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +1714 -0
  985. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +468 -0
  986. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-widgets.js +63 -0
  987. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +366 -0
  988. package/neural-interface/public/shared/ocp-v2/ocp-v2-settings-link.js +19 -0
  989. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +343 -0
  990. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +746 -0
  991. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +347 -0
  992. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +338 -0
  993. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +3543 -0
  994. package/neural-interface/public/shared/ocp-v2/ocp-v2-subagents-logic.js +482 -0
  995. package/neural-interface/public/shared/ocp-v2/ocp-v2-tools-logic.js +351 -0
  996. package/neural-interface/public/shared/ocp-v2/ocp-v2-tools.js +168 -0
  997. package/neural-interface/public/shared/ocp-v2/ocp-v2-variantpicker.js +315 -0
  998. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +545 -0
  999. package/neural-interface/public/shared/provider-icons.js +30 -0
  1000. package/neural-interface/public/shared/pwa-install-page.js +34 -0
  1001. package/neural-interface/public/shared/pwa-install.css +12 -0
  1002. package/neural-interface/public/shared/pwa-install.js +28 -0
  1003. package/neural-interface/public/shared/session-title.js +100 -0
  1004. package/neural-interface/public/shared/settings/settings-ia.js +389 -0
  1005. package/neural-interface/public/shared/settings/settings-kit.js +234 -0
  1006. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  1007. package/neural-interface/public/shared/settings/settings-search.js +463 -0
  1008. package/neural-interface/public/shared/settings/settings-shell.js +570 -0
  1009. package/neural-interface/public/shared/start-bridge.js +583 -0
  1010. package/neural-interface/public/shared/state.js +5 -0
  1011. package/neural-interface/public/shared/storage.js +50 -7
  1012. package/neural-interface/public/shared/styleguide/sg-agents.js +42 -0
  1013. package/neural-interface/public/shared/styleguide/sg-brand.js +4 -0
  1014. package/neural-interface/public/shared/styleguide/sg-color-client.js +388 -0
  1015. package/neural-interface/public/shared/styleguide/sg-colors.js +151 -0
  1016. package/neural-interface/public/shared/styleguide/sg-components.js +35 -0
  1017. package/neural-interface/public/shared/styleguide/sg-history.js +41 -0
  1018. package/neural-interface/public/shared/styleguide/sg-icons-logo.js +38 -0
  1019. package/neural-interface/public/shared/styleguide/sg-imagery.js +4 -0
  1020. package/neural-interface/public/shared/styleguide/sg-import-export.js +48 -0
  1021. package/neural-interface/public/shared/styleguide/sg-kit.js +284 -0
  1022. package/neural-interface/public/shared/styleguide/sg-labels.js +318 -0
  1023. package/neural-interface/public/shared/styleguide/sg-layout.js +4 -0
  1024. package/neural-interface/public/shared/styleguide/sg-motion.js +5 -0
  1025. package/neural-interface/public/shared/styleguide/sg-panel.js +237 -0
  1026. package/neural-interface/public/shared/styleguide/sg-preview.js +138 -0
  1027. package/neural-interface/public/shared/styleguide/sg-rules.js +9 -0
  1028. package/neural-interface/public/shared/styleguide/sg-shape.js +24 -0
  1029. package/neural-interface/public/shared/styleguide/sg-state.js +132 -0
  1030. package/neural-interface/public/shared/styleguide/sg-typography.js +125 -0
  1031. package/neural-interface/public/shared/styles.css +8614 -1477
  1032. package/neural-interface/public/shared/synabun-mascot.js +1625 -0
  1033. package/neural-interface/public/shared/synabun-ticker.js +376 -0
  1034. package/neural-interface/public/shared/term-ready.js +307 -0
  1035. package/neural-interface/public/shared/term-renderer.js +343 -0
  1036. package/neural-interface/public/shared/ui-assistant-panel.js +10 -0
  1037. package/neural-interface/public/shared/ui-assistant.js +77 -0
  1038. package/neural-interface/public/shared/ui-automation-studio.js +5105 -3573
  1039. package/neural-interface/public/shared/ui-claude-panel.js +11685 -6333
  1040. package/neural-interface/public/shared/ui-codex-panel.js +12 -0
  1041. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  1042. package/neural-interface/public/shared/ui-cost-widget.js +55 -34
  1043. package/neural-interface/public/shared/ui-detail.js +77 -14
  1044. package/neural-interface/public/shared/ui-explorer.js +360 -181
  1045. package/neural-interface/public/shared/ui-file-explorer.js +485 -107
  1046. package/neural-interface/public/shared/ui-help.js +2 -2
  1047. package/neural-interface/public/shared/ui-image-gallery.js +45 -21
  1048. package/neural-interface/public/shared/ui-judgments.js +1346 -0
  1049. package/neural-interface/public/shared/ui-keybinds.js +3 -2
  1050. package/neural-interface/public/shared/ui-link.js +105 -51
  1051. package/neural-interface/public/shared/ui-loading.js +222 -11
  1052. package/neural-interface/public/shared/ui-memory-maintenance.js +111 -0
  1053. package/neural-interface/public/shared/ui-menubar.js +100 -31
  1054. package/neural-interface/public/shared/ui-native-loop-router-state.js +94 -0
  1055. package/neural-interface/public/shared/ui-native-loop-router.js +493 -0
  1056. package/neural-interface/public/shared/ui-native-window-id.js +7 -0
  1057. package/neural-interface/public/shared/ui-navbar.js +357 -222
  1058. package/neural-interface/public/shared/ui-notifications.js +58 -12
  1059. package/neural-interface/public/shared/ui-opencode-panel-v2.js +17 -0
  1060. package/neural-interface/public/shared/ui-panels.js +15 -7
  1061. package/neural-interface/public/shared/ui-resume.js +823 -653
  1062. package/neural-interface/public/shared/ui-schedule-queue.js +172 -0
  1063. package/neural-interface/public/shared/ui-schedules-studio.js +1882 -0
  1064. package/neural-interface/public/shared/ui-scroll-follow.js +194 -0
  1065. package/neural-interface/public/shared/ui-sessions.js +789 -34
  1066. package/neural-interface/public/shared/ui-settings.js +5774 -2194
  1067. package/neural-interface/public/shared/ui-sidepanel-layout.js +109 -0
  1068. package/neural-interface/public/shared/ui-sidepanel-runtime.js +19 -0
  1069. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +40 -0
  1070. package/neural-interface/public/shared/ui-sidepanel-sessions.js +221 -0
  1071. package/neural-interface/public/shared/ui-sidepanel-state.js +131 -0
  1072. package/neural-interface/public/shared/ui-sidepanel-tray.js +547 -0
  1073. package/neural-interface/public/shared/ui-sidepanel-windows.js +387 -0
  1074. package/neural-interface/public/shared/ui-skills.js +18 -32
  1075. package/neural-interface/public/shared/ui-styleguide.js +35 -0
  1076. package/neural-interface/public/shared/ui-sync.js +87 -0
  1077. package/neural-interface/public/shared/ui-terminal.js +7863 -6385
  1078. package/neural-interface/public/shared/ui-tool-updates.js +292 -0
  1079. package/neural-interface/public/shared/ui-tooltip.js +156 -15
  1080. package/neural-interface/public/shared/ui-tutorial-draw.js +119 -683
  1081. package/neural-interface/public/shared/ui-tutorial-layout.js +135 -0
  1082. package/neural-interface/public/shared/ui-tutorial-steps.js +59 -2209
  1083. package/neural-interface/public/shared/ui-tutorial.css +102 -0
  1084. package/neural-interface/public/shared/ui-tutorial.js +557 -1076
  1085. package/neural-interface/public/shared/ui-update.js +420 -68
  1086. package/neural-interface/public/shared/ui-whatsapp.js +941 -0
  1087. package/neural-interface/public/shared/ui-whiteboard.js +1076 -233
  1088. package/neural-interface/public/shared/ui-workspaces.js +2 -1
  1089. package/neural-interface/public/shared/utils.js +58 -0
  1090. package/neural-interface/public/shared/vterm-buffer.js +15 -5
  1091. package/neural-interface/public/shared/whatsapp/wa-phone.js +65 -0
  1092. package/neural-interface/public/shared/whatsapp/wa-view.js +411 -0
  1093. package/neural-interface/public/sidepanel-session.html +18 -0
  1094. package/neural-interface/public/sw.js +10 -2
  1095. package/neural-interface/public/synabun.png +0 -0
  1096. package/neural-interface/public/variant/2d/graph.js +1707 -1635
  1097. package/neural-interface/public/variant/2d/lasso.js +2 -1
  1098. package/neural-interface/public/variant/2d/layout.js +595 -594
  1099. package/neural-interface/public/variant/2d/main.js +557 -521
  1100. package/neural-interface/public/variant/2d/settings-gfx.js +18 -24
  1101. package/neural-interface/public/variant/3d/main.js +517 -500
  1102. package/neural-interface/public/variant/3d/map-controls.js +144 -0
  1103. package/neural-interface/public/variant/3d/map-data.js +251 -0
  1104. package/neural-interface/public/variant/3d/map-labels.js +241 -0
  1105. package/neural-interface/public/variant/3d/map-pick.js +90 -0
  1106. package/neural-interface/public/variant/3d/map-renderer.js +521 -0
  1107. package/neural-interface/public/variant/3d/map-shaders.js +158 -0
  1108. package/neural-interface/public/variant/3d/map-view.js +618 -0
  1109. package/neural-interface/public/vendor/three/manifest.json +3 -0
  1110. package/neural-interface/public/vendor/three/three-0.169.0.min.js +3844 -0
  1111. package/neural-interface/public/vendor/xterm/manifest.json +3 -0
  1112. package/neural-interface/public/vendor/xterm/xterm-6.0.0.css +285 -0
  1113. package/neural-interface/public/vendor/xterm/xterm-6.0.0.min.js +192 -0
  1114. package/neural-interface/scripts/build-vendor.mjs +64 -0
  1115. package/neural-interface/scripts/codex-protocol-fixture.mjs +83 -0
  1116. package/neural-interface/scripts/rebuild-pty.js +39 -22
  1117. package/neural-interface/scripts/vendor-three-entry.js +24 -0
  1118. package/neural-interface/scripts/vendor-xterm-entry.js +8 -0
  1119. package/neural-interface/server.js +33983 -15000
  1120. package/neural-interface/skins/default/skin.css +90 -0
  1121. package/neural-interface/skins/default/skin.json +8 -0
  1122. package/neural-interface/templates/rulesets/README.md +9 -0
  1123. package/neural-interface/templates/rulesets/claude.md +3 -0
  1124. package/neural-interface/templates/rulesets/codex.md +2 -0
  1125. package/neural-interface/templates/rulesets/coexistence.md +6 -0
  1126. package/neural-interface/templates/rulesets/core.md +26 -0
  1127. package/neural-interface/templates/rulesets/cursor.md +2 -0
  1128. package/neural-interface/templates/rulesets/gemini.md +2 -0
  1129. package/neural-interface/templates/rulesets/legacy-hashes.json +227 -0
  1130. package/neural-interface/templates/rulesets/manifest.json +12 -0
  1131. package/neural-interface/templates/rulesets/opencode.md +2 -0
  1132. package/package.json +131 -53
  1133. package/postinstall.js +55 -26
  1134. package/preuninstall.js +187 -0
  1135. package/scripts/restore-data-home-from-backup.mjs +239 -0
  1136. package/setup.js +872 -260
  1137. package/skills/leonardo/SKILL.md +19 -10
  1138. package/skills/leonardo/modules/image-prompter.md +18 -12
  1139. package/skills/leonardo/modules/video-prompter.md +19 -13
  1140. package/skills/synabun/SKILL.md +209 -185
  1141. package/skills/synabun/icon.png +0 -0
  1142. package/skills/synabun/modules/audit.md +380 -374
  1143. package/skills/synabun/modules/changelog.md +15 -6
  1144. package/skills/synabun/modules/idea.md +148 -144
  1145. package/skills/synabun/modules/memorize.md +13 -4
  1146. package/skills/synabun/modules/schedule.md +198 -0
  1147. package/skills/synabun/synabunicon.png +0 -0
  1148. package/updater.cmd +31 -0
  1149. package/updater.mjs +382 -0
  1150. package/updater.sh +29 -0
  1151. package/hooks/claude-code/test-user-learning.mjs +0 -531
  1152. package/neural-interface/public/claude-chat.css +0 -878
  1153. package/neural-interface/public/claude-chat.html +0 -92
  1154. package/neural-interface/public/claude-chat.js +0 -1168
  1155. package/neural-interface/public/index-legacy.html +0 -14677
  1156. package/neural-interface/public/index2d-legacy.html +0 -8193
  1157. package/neural-interface/public/shared/html-term-renderer.js +0 -1022
  1158. package/neural-interface/public/variant/3d/background.js +0 -884
  1159. package/neural-interface/public/variant/3d/camera.js +0 -661
  1160. package/neural-interface/public/variant/3d/gfx.js +0 -116
  1161. package/neural-interface/public/variant/3d/graph.js +0 -2399
  1162. package/neural-interface/public/variant/3d/settings-gfx.js +0 -228
  1163. package/neural-interface/templates/CLAUDE-template.md +0 -211
@@ -0,0 +1,1309 @@
1
+ /**
2
+ * SynaBun — the Start Server bridge
3
+ *
4
+ * The offline page cannot ask a server that is down to start itself, so it
5
+ * hands the operating system a synabun://start link. This module owns both
6
+ * ends of that hand-off:
7
+ *
8
+ * registration — what each OS needs to route synabun:// to launcher.mjs
9
+ * (HKCU registry on Windows, ~/.synabun/SynaBun.app on macOS,
10
+ * a .desktop entry on Linux). Written by postinstall.js, by
11
+ * setup.js on every start and by the server at boot, so npm
12
+ * and GitHub installs, new or old, end up the same.
13
+ * launch — what launcher.mjs does when the OS runs it: nothing if the
14
+ * server already answers or a start is under way, otherwise
15
+ * the normal supervisor (setup.js), once.
16
+ *
17
+ * progress — a launch says what it has actually seen happen (itself
18
+ * running, the Terminal asked to open, the supervisor's own
19
+ * phases, the server answering) on a loopback beacon the
20
+ * offline page reads, because that page has no server to ask.
21
+ *
22
+ * Nothing here stays running: the OS starts the launcher per click and it
23
+ * exits once the server answers.
24
+ */
25
+
26
+ import { execFileSync, spawn, spawnSync } from 'node:child_process';
27
+ import {
28
+ chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync,
29
+ } from 'node:fs';
30
+ import { createServer } from 'node:http';
31
+ import { createRequire } from 'node:module';
32
+ import { connect } from 'node:net';
33
+ import { homedir, tmpdir } from 'node:os';
34
+ import { basename, dirname, join, posix, win32 } from 'node:path';
35
+ import { randomBytes } from 'node:crypto';
36
+ import { getDataHome, getPlatformDataHome, pathIsInside } from './paths.js';
37
+ import { readPackagedRuntime } from './packaged-runtime.js';
38
+
39
+ export const START_PROTOCOL = 'synabun';
40
+ /** 1 was the handler that ran setup.js directly (no lock, no health check). */
41
+ export const LAUNCHER_REVISION = 2;
42
+ export const DEFAULT_PORT = 3344;
43
+
44
+ const BUNDLE_ID = 'ai.synabun.launcher';
45
+ const LSREGISTER = '/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister';
46
+ const WIN_KEY = 'HKCU\\Software\\Classes\\synabun';
47
+
48
+ /** A start that has not answered after this long no longer blocks a new one. */
49
+ export const LOCK_TTL_MS = 10 * 60 * 1000;
50
+ export const SUPERVISOR_TTL_MS = 15 * 60 * 1000;
51
+ const HEALTH_WAIT_MS = 5 * 60 * 1000;
52
+ /** How often a waiting launch looks: at the port, and at what the supervisor says it is doing. */
53
+ const WAIT_POLL_MS = 200;
54
+
55
+ // ═══════════════════════════════════════════
56
+ // Quoting
57
+ // ═══════════════════════════════════════════
58
+
59
+ /** One POSIX shell word. */
60
+ export function shQuote(value) {
61
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
62
+ }
63
+
64
+ /** One argument of a Windows command line (CommandLineToArgvW rules). */
65
+ export function winQuote(value) {
66
+ let out = '"';
67
+ let slashes = 0;
68
+ for (const ch of String(value)) {
69
+ if (ch === '\\') { slashes++; continue; }
70
+ if (ch === '"') { out += '\\'.repeat(slashes * 2 + 1) + '"'; slashes = 0; continue; }
71
+ out += '\\'.repeat(slashes) + ch;
72
+ slashes = 0;
73
+ }
74
+ return `${out}${'\\'.repeat(slashes * 2)}"`;
75
+ }
76
+
77
+ /**
78
+ * One argument of a .desktop Exec line: quoted per the Exec rules (" ` $ \
79
+ * escaped), then escaped again as a desktop-entry string, with % doubled so
80
+ * it is not read as a field code.
81
+ */
82
+ export function desktopExecArg(value) {
83
+ const quoted = `"${String(value).replace(/(["`$\\])/g, '\\$1')}"`;
84
+ return quoted.replace(/\\/g, '\\\\').replace(/%/g, '%%');
85
+ }
86
+
87
+ function xmlEscape(value) {
88
+ return String(value).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
89
+ }
90
+
91
+ // ═══════════════════════════════════════════
92
+ // Configuration the launcher must agree with the server on
93
+ // ═══════════════════════════════════════════
94
+
95
+ export function normalizePort(value) {
96
+ const n = Number(value);
97
+ return Number.isInteger(n) && n >= 1 && n <= 65535 ? n : null;
98
+ }
99
+
100
+ function readDotEnvValue(dataHome, key) {
101
+ try {
102
+ for (const line of readFileSync(join(dataHome, '.env'), 'utf8').split(/\r?\n/)) {
103
+ const trimmed = line.trim();
104
+ if (!trimmed.startsWith(`${key}=`)) continue;
105
+ return trimmed.slice(key.length + 1).trim().replace(/^(['"])(.*)\1$/, '$2');
106
+ }
107
+ } catch {}
108
+ return null;
109
+ }
110
+
111
+ /** The port the server listens on: the environment, then DATA_HOME/.env, then 3344 — as server.js reads it. */
112
+ export function resolvePort({ explicit = null, env = process.env, dataHome } = {}) {
113
+ return normalizePort(explicit)
114
+ || normalizePort(env.NEURAL_PORT)
115
+ || (dataHome ? normalizePort(readDotEnvValue(dataHome, 'NEURAL_PORT')) : null)
116
+ || DEFAULT_PORT;
117
+ }
118
+
119
+ /**
120
+ * What a registration has to pin because the OS starts the launcher with a
121
+ * bare environment: a data home that is not the platform default, and a port
122
+ * the launcher would not arrive at as 3344 by itself.
123
+ */
124
+ export function launcherOverrides({ env = process.env, home = homedir(), platform = process.platform } = {}) {
125
+ const dataHome = getDataHome({ env, home, os: platform });
126
+ const standard = getPlatformDataHome({ env, home, os: platform });
127
+ // Read the port the way the launcher will, so setup.js (bare environment)
128
+ // and the server (DATA_HOME/.env loaded) write the same registration.
129
+ const port = resolvePort({ env, dataHome });
130
+ // Where a handler with no --port lands: DATA_HOME/.env, then 3344. A server
131
+ // run with NEURAL_PORT=3344 over a .env that names another port has to pin
132
+ // its 3344, or the next click starts (and waits for) the other one.
133
+ const unpinned = resolvePort({ env: {}, dataHome });
134
+ return {
135
+ dataHome: dataHome !== standard ? dataHome : null,
136
+ port: port !== DEFAULT_PORT || unpinned !== DEFAULT_PORT ? port : null,
137
+ };
138
+ }
139
+
140
+ // ═══════════════════════════════════════════
141
+ // Registration
142
+ // ═══════════════════════════════════════════
143
+
144
+ export function launcherLocations({ platform = process.platform, home = homedir() } = {}) {
145
+ if (platform === 'win32') return { kind: 'registry', key: WIN_KEY };
146
+ if (platform === 'darwin') {
147
+ const app = posix.join(home, '.synabun', 'SynaBun.app');
148
+ return {
149
+ kind: 'app',
150
+ app,
151
+ plist: posix.join(app, 'Contents', 'Info.plist'),
152
+ executable: posix.join(app, 'Contents', 'MacOS', 'synabun-launcher'),
153
+ terminalScript: posix.join(app, 'Contents', 'Resources', 'SynaBun Server.command'),
154
+ };
155
+ }
156
+ return { kind: 'desktop', desktopFile: posix.join(home, '.local', 'share', 'applications', 'synabun.desktop') };
157
+ }
158
+
159
+ function launcherArgs({ dataHome, port, terminalScript }) {
160
+ const args = ['--via=protocol'];
161
+ if (dataHome) args.push('--data-home', dataHome);
162
+ if (port) args.push('--port', String(port));
163
+ if (terminalScript) args.push('--terminal-script', terminalScript);
164
+ return args;
165
+ }
166
+
167
+ /**
168
+ * Everything a registration consists of, as data: files to write, registry
169
+ * values to set and the commands that tell the OS about them. No side effects.
170
+ */
171
+ export function buildLauncherPlan({
172
+ platform = process.platform,
173
+ nodePath = process.execPath,
174
+ packageRoot,
175
+ home = homedir(),
176
+ dataHome = null,
177
+ port = null,
178
+ entry = null,
179
+ } = {}) {
180
+ if (!packageRoot) throw new Error('packageRoot is required');
181
+ const where = launcherLocations({ platform, home });
182
+ const plan = { platform, revision: LAUNCHER_REVISION, where, files: [], registry: [], refresh: [] };
183
+ // A packaged application is reached through its entry executable, which
184
+ // brings its own Node: `<entry> launcher ...` and `<entry> start`.
185
+ const handler = (args, launcherPath) => (entry ? [entry, 'launcher', ...args] : [nodePath, launcherPath, ...args]);
186
+
187
+ if (platform === 'win32') {
188
+ const launcher = win32.join(packageRoot, 'launcher.mjs');
189
+ const command = [...handler(launcherArgs({ dataHome, port }), launcher), '--'].map(winQuote).join(' ') + ' "%1"';
190
+ plan.registry.push(
191
+ { key: WIN_KEY, name: null, value: 'URL:SynaBun Protocol' },
192
+ { key: WIN_KEY, name: 'URL Protocol', value: '' },
193
+ { key: `${WIN_KEY}\\shell\\open\\command`, name: null, value: command },
194
+ );
195
+ return plan;
196
+ }
197
+
198
+ const launcher = posix.join(packageRoot, 'launcher.mjs');
199
+ const setup = posix.join(packageRoot, 'setup.js');
200
+
201
+ if (platform === 'darwin') {
202
+ const args = launcherArgs({ dataHome, port, terminalScript: where.terminalScript });
203
+ plan.files.push({
204
+ path: where.plist,
205
+ mode: 0o644,
206
+ content: [
207
+ '<?xml version="1.0" encoding="UTF-8"?>',
208
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
209
+ '<plist version="1.0">',
210
+ '<dict>',
211
+ ` <key>CFBundleIdentifier</key><string>${BUNDLE_ID}</string>`,
212
+ ' <key>CFBundleName</key><string>SynaBun</string>',
213
+ ' <key>CFBundleExecutable</key><string>synabun-launcher</string>',
214
+ ' <key>CFBundlePackageType</key><string>APPL</string>',
215
+ ` <key>CFBundleVersion</key><string>${LAUNCHER_REVISION}.0</string>`,
216
+ // No Dock icon: the launcher is gone again within seconds.
217
+ ' <key>LSUIElement</key><true/>',
218
+ ' <key>CFBundleURLTypes</key>',
219
+ ' <array><dict>',
220
+ ' <key>CFBundleURLName</key><string>SynaBun Protocol</string>',
221
+ ` <key>CFBundleURLSchemes</key><array><string>${xmlEscape(START_PROTOCOL)}</string></array>`,
222
+ ' </dict></array>',
223
+ '</dict>',
224
+ '</plist>',
225
+ '',
226
+ ].join('\n'),
227
+ }, {
228
+ // macOS delivers the link as an Apple Event, which a script never sees:
229
+ // being launched at all is the request to start.
230
+ path: where.executable,
231
+ mode: 0o755,
232
+ content: [
233
+ '#!/bin/sh',
234
+ `# SynaBun start launcher (revision ${LAUNCHER_REVISION}). macOS runs this for ${START_PROTOCOL}:// links.`,
235
+ '# Generated by SynaBun and rewritten when the install moves. Do not edit.',
236
+ ...(entry ? [
237
+ `ENTRY=${shQuote(entry)}`,
238
+ '[ -x "$ENTRY" ] || exit 127',
239
+ `exec "$ENTRY" ${['launcher', ...args].map(shQuote).join(' ')} >/dev/null 2>&1`,
240
+ ] : [
241
+ `NODE=${shQuote(nodePath)}`,
242
+ 'if [ ! -x "$NODE" ]; then',
243
+ ' NODE="$(PATH="$PATH:/opt/homebrew/bin:/usr/local/bin" command -v node 2>/dev/null)"',
244
+ 'fi',
245
+ '[ -n "$NODE" ] || exit 127',
246
+ `exec "$NODE" ${[launcher, ...args].map(shQuote).join(' ')} >/dev/null 2>&1`,
247
+ ]),
248
+ '',
249
+ ].join('\n'),
250
+ }, {
251
+ // Terminal runs this in the user's login shell: the same environment and
252
+ // the same privacy grants as a server they started by hand.
253
+ path: where.terminalScript,
254
+ mode: 0o755,
255
+ content: [
256
+ '#!/bin/sh',
257
+ '# Runs the SynaBun server in this window (opened by the Start Server button).',
258
+ '# Generated by SynaBun and rewritten when the install moves. Do not edit.',
259
+ `printf '\\033]0;SynaBun Server\\007'`,
260
+ ...(entry ? [] : [
261
+ `NODE=${shQuote(nodePath)}`,
262
+ '[ -x "$NODE" ] || NODE=node',
263
+ ]),
264
+ ...(dataHome ? [`export SYNABUN_DATA_HOME=${shQuote(dataHome)}`] : []),
265
+ ...(port ? [`export NEURAL_PORT=${shQuote(String(port))}`] : []),
266
+ 'export SYNABUN_OPEN_BROWSER=0',
267
+ `cd ${shQuote(packageRoot)} || exit 1`,
268
+ entry
269
+ ? `exec ${shQuote(entry)} start`
270
+ : `exec "$NODE" --disable-warning=ExperimentalWarning ${shQuote(setup)}`,
271
+ '',
272
+ ].join('\n'),
273
+ });
274
+ plan.refresh.push({ file: LSREGISTER, args: ['-f', where.app] });
275
+ return plan;
276
+ }
277
+
278
+ const exec = [...handler(launcherArgs({ dataHome, port }), launcher), '--'].map(desktopExecArg).join(' ');
279
+ plan.files.push({
280
+ path: where.desktopFile,
281
+ mode: 0o644,
282
+ content: [
283
+ '[Desktop Entry]',
284
+ 'Type=Application',
285
+ 'Name=SynaBun',
286
+ `Comment=Starts the SynaBun server (${START_PROTOCOL}:// links)`,
287
+ `Exec=${exec} %u`,
288
+ `MimeType=x-scheme-handler/${START_PROTOCOL};`,
289
+ 'NoDisplay=true',
290
+ // The launcher starts the server in the background and exits: no window to keep.
291
+ 'Terminal=false',
292
+ `X-SynaBun-Launcher=${LAUNCHER_REVISION}`,
293
+ '',
294
+ ].join('\n'),
295
+ });
296
+ plan.refresh.push(
297
+ { file: 'xdg-mime', args: ['default', basename(where.desktopFile), `x-scheme-handler/${START_PROTOCOL}`] },
298
+ { file: 'update-desktop-database', args: [dirname(where.desktopFile)] },
299
+ );
300
+ return plan;
301
+ }
302
+
303
+ function regExe(env = process.env) {
304
+ const root = env.SystemRoot || env.SYSTEMROOT || env.windir;
305
+ return root ? win32.join(root, 'System32', 'reg.exe') : 'reg';
306
+ }
307
+
308
+ function defaultRun(file, args, { timeout = 15000 } = {}) {
309
+ return execFileSync(file, args, { encoding: 'utf8', timeout, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
310
+ }
311
+
312
+ /** The current value of one registry entry, or null when it is not there. */
313
+ function readRegistryValue(entry, { run = defaultRun, env = process.env } = {}) {
314
+ try {
315
+ const out = run(regExe(env), ['query', entry.key, ...(entry.name === null ? ['/ve'] : ['/v', entry.name])]);
316
+ for (const line of String(out).split(/\r?\n/)) {
317
+ const at = line.indexOf('REG_SZ');
318
+ if (at < 0) continue;
319
+ return line.slice(at + 'REG_SZ'.length).trim();
320
+ }
321
+ return null;
322
+ } catch {
323
+ return null;
324
+ }
325
+ }
326
+
327
+ function fileMatches(file) {
328
+ try {
329
+ if (readFileSync(file.path, 'utf8') !== file.content) return false;
330
+ // A launcher that lost its executable bit is as good as missing.
331
+ return !(file.mode & 0o111) || (statSync(file.path).mode & 0o111) !== 0;
332
+ } catch {
333
+ return false;
334
+ }
335
+ }
336
+
337
+ /** 'registered' | 'stale' | 'missing', without changing anything. */
338
+ export function inspectLauncher(plan, { run = defaultRun, env = process.env } = {}) {
339
+ let present = 0;
340
+ let current = 0;
341
+ for (const file of plan.files) {
342
+ if (existsSync(file.path)) present++;
343
+ if (fileMatches(file)) current++;
344
+ }
345
+ for (const entry of plan.registry) {
346
+ const value = readRegistryValue(entry, { run, env });
347
+ if (value !== null) present++;
348
+ if (value === entry.value) current++;
349
+ }
350
+ const total = plan.files.length + plan.registry.length;
351
+ if (current === total) return 'registered';
352
+ return present ? 'stale' : 'missing';
353
+ }
354
+
355
+ /**
356
+ * Bring the OS in line with a plan. Writes only what differs, so calling it on
357
+ * every start costs a few file reads. Returns { ok, changed, state, errors }.
358
+ */
359
+ export function applyLauncherPlan(plan, { run = defaultRun, env = process.env } = {}) {
360
+ const errors = [];
361
+ let changed = 0;
362
+
363
+ for (const file of plan.files) {
364
+ if (fileMatches(file)) continue;
365
+ try {
366
+ mkdirSync(dirname(file.path), { recursive: true });
367
+ const temp = `${file.path}.${process.pid}.tmp`;
368
+ writeFileSync(temp, file.content, { encoding: 'utf8', mode: file.mode });
369
+ chmodSync(temp, file.mode);
370
+ renameSync(temp, file.path);
371
+ changed++;
372
+ } catch (error) {
373
+ errors.push(`${file.path}: ${error.message}`);
374
+ }
375
+ }
376
+
377
+ for (const entry of plan.registry) {
378
+ if (readRegistryValue(entry, { run, env }) === entry.value) continue;
379
+ try {
380
+ run(regExe(env), ['add', entry.key, ...(entry.name === null ? ['/ve'] : ['/v', entry.name]), '/t', 'REG_SZ', '/d', entry.value, '/f']);
381
+ changed++;
382
+ } catch (error) {
383
+ errors.push(`${entry.key}: ${error.message}`);
384
+ }
385
+ }
386
+
387
+ // The OS caches its handler table: tell it only when something moved. These
388
+ // are best effort (a minimal Linux has neither tool and still resolves the
389
+ // entry through mimeinfo on most desktops).
390
+ if (changed) {
391
+ for (const command of plan.refresh) {
392
+ try { run(command.file, command.args); } catch {}
393
+ }
394
+ }
395
+
396
+ const state = errors.length ? inspectLauncher(plan, { run, env }) : 'registered';
397
+ return { ok: errors.length === 0, changed, state, errors };
398
+ }
399
+
400
+ /**
401
+ * Whether this process may touch the user's registration. A test run or a
402
+ * throwaway data home must never repoint the real handler at itself.
403
+ */
404
+ export function launcherRegistrationAllowed({ env = process.env, dataHome = null } = {}) {
405
+ if (env.SYNABUN_LAUNCHER_REGISTER === '0') return false;
406
+ if (env.NODE_TEST_CONTEXT || env.VITEST) return false;
407
+ if (dataHome) {
408
+ try { if (pathIsInside(dataHome, tmpdir())) return false; } catch {}
409
+ }
410
+ return true;
411
+ }
412
+
413
+ /** The entry executable of a packaged application, or null for an npm or Git install. */
414
+ function packagedEntry({ packageRoot, env }) {
415
+ return packageRoot ? readPackagedRuntime({ packageRoot, env })?.entry ?? null : null;
416
+ }
417
+
418
+ /** Register (or repair) the synabun:// handler for this install. Never throws. */
419
+ export function registerStartLauncher({
420
+ platform = process.platform,
421
+ nodePath = process.execPath,
422
+ packageRoot,
423
+ home = homedir(),
424
+ env = process.env,
425
+ run = defaultRun,
426
+ } = {}) {
427
+ try {
428
+ const overrides = launcherOverrides({ env, home, platform });
429
+ if (!launcherRegistrationAllowed({ env, dataHome: overrides.dataHome })) {
430
+ return { ok: true, changed: 0, state: 'skipped', errors: [] };
431
+ }
432
+ const plan = buildLauncherPlan({ platform, nodePath, packageRoot, home, entry: packagedEntry({ packageRoot, env }), ...overrides });
433
+ return applyLauncherPlan(plan, { run, env });
434
+ } catch (error) {
435
+ return { ok: false, changed: 0, state: 'missing', errors: [error.message] };
436
+ }
437
+ }
438
+
439
+ /** Read-only: is this install's handler in place? */
440
+ export function startLauncherState({
441
+ platform = process.platform,
442
+ nodePath = process.execPath,
443
+ packageRoot,
444
+ home = homedir(),
445
+ env = process.env,
446
+ run = defaultRun,
447
+ } = {}) {
448
+ try {
449
+ const plan = buildLauncherPlan({ platform, nodePath, packageRoot, home, entry: packagedEntry({ packageRoot, env }), ...launcherOverrides({ env, home, platform }) });
450
+ return inspectLauncher(plan, { run, env });
451
+ } catch {
452
+ return 'missing';
453
+ }
454
+ }
455
+
456
+ // ═══════════════════════════════════════════
457
+ // Launch
458
+ // ═══════════════════════════════════════════
459
+
460
+ /**
461
+ * Read the launcher's arguments. Only `start` is an action: a link that asks
462
+ * for anything else is ignored, and nothing from the link is ever passed on.
463
+ */
464
+ export function parseLauncherArgs(argv = []) {
465
+ const out = { action: 'start', via: 'manual', dataHome: null, port: null, terminalScript: null, link: null };
466
+ let afterDashes = false;
467
+ for (let i = 0; i < argv.length; i++) {
468
+ const arg = String(argv[i] ?? '');
469
+ if (new RegExp(`^${START_PROTOCOL}:`, 'i').test(arg)) {
470
+ out.link = arg;
471
+ out.action = parseStartLink(arg);
472
+ continue;
473
+ }
474
+ if (afterDashes) continue; // whatever else follows `--` came from the link
475
+ if (arg === '--') { afterDashes = true; continue; }
476
+ if (arg === '--via=protocol') out.via = 'protocol';
477
+ else if (arg === '--data-home') out.dataHome = argv[++i] || null;
478
+ else if (arg === '--port') out.port = normalizePort(argv[++i]);
479
+ else if (arg === '--terminal-script') out.terminalScript = argv[++i] || null;
480
+ }
481
+ return out;
482
+ }
483
+
484
+ /** 'start' for synabun://start (any query), otherwise 'ignore'. */
485
+ export function parseStartLink(link) {
486
+ const match = new RegExp(`^${START_PROTOCOL}:(?://)?([a-z-]*)/?(?:[?#].*)?$`, 'i').exec(String(link || '').trim());
487
+ return match && match[1].toLowerCase() === 'start' ? 'start' : 'ignore';
488
+ }
489
+
490
+ export function pidAlive(pid) {
491
+ if (!Number.isInteger(pid) || pid <= 0) return false;
492
+ try { process.kill(pid, 0); return true; } catch (error) { return error?.code === 'EPERM'; }
493
+ }
494
+
495
+ function readJson(path) {
496
+ try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
497
+ }
498
+
499
+ /** What a click should do, given what is already there. */
500
+ export function decideLaunch({ healthy, lock = null, supervisor = null, now = Date.now(), alive = pidAlive } = {}) {
501
+ if (healthy) return 'already-running';
502
+ if (lock && alive(lock.pid) && now - Number(lock.startedAt || 0) < LOCK_TTL_MS) return 'already-starting';
503
+ // A supervisor only lives while its server does. One that has been alive
504
+ // this long without a server answering is a reused pid, not a start; one
505
+ // that wrote down its own end is over, whoever has its pid now.
506
+ if (supervisor && !supervisor.endedAt && alive(supervisor.pid) && now - Number(supervisor.startedAt || 0) < SUPERVISOR_TTL_MS) return 'already-starting';
507
+ return 'start';
508
+ }
509
+
510
+ export function launcherStatePaths(dataHome) {
511
+ const dir = join(dataHome, 'data');
512
+ const lock = join(dir, 'start-launcher.lock');
513
+ return {
514
+ dir,
515
+ lock,
516
+ mutex: launchMutexPath(lock),
517
+ supervisor: join(dir, 'supervisor.json'),
518
+ log: join(dir, 'launcher.log'),
519
+ };
520
+ }
521
+
522
+ /**
523
+ * The mutex that goes with a lock file: DATA_HOME/runtime/start-launcher.mutex
524
+ * for DATA_HOME/data/start-launcher.lock. Derived from the lock's own path, so
525
+ * everyone after one lock is behind one mutex, and kept out of data/: an
526
+ * operating-system lock is not data, and a backup that copied it or a restore
527
+ * that replaced it would split it in two.
528
+ */
529
+ export function launchMutexPath(lockPath) {
530
+ return join(dirname(dirname(lockPath)), 'runtime', `${basename(lockPath, '.lock')}.mutex`);
531
+ }
532
+
533
+ /** What a supervisor says it is doing. Each one is written when it begins, never ahead of it. */
534
+ export const SUPERVISOR_PHASES = Object.freeze([
535
+ 'checking', // looking at the install: nothing slow has begun
536
+ 'dependencies', // npm install is running (a first start, or the first after an update)
537
+ 'browser', // downloading the automation browser
538
+ 'build', // compiling the MCP server
539
+ 'snapshot', // backing the data home up before the first start of a new version
540
+ 'server', // the server process was spawned
541
+ 'listening', // the server printed its address
542
+ 'failed', // it ended without a server; `error` says why
543
+ ]);
544
+
545
+ function supervisorError(error) {
546
+ const exitCode = Number(error?.exitCode);
547
+ return {
548
+ code: String(error?.code || 'SETUP_FAILED').slice(0, 40),
549
+ message: String(error?.message || error || 'the start failed').slice(0, 300),
550
+ ...(Number.isInteger(exitCode) ? { exitCode } : {}),
551
+ };
552
+ }
553
+
554
+ function writeRecord(path, record, pid) {
555
+ const text = JSON.stringify(record) + '\n';
556
+ const temp = `${path}.${pid}.tmp`;
557
+ try {
558
+ // Replaced whole: a launcher reading it mid-start never sees half a record.
559
+ writeFileSync(temp, text, 'utf8');
560
+ renameSync(temp, path);
561
+ } catch {
562
+ try { writeFileSync(path, text, 'utf8'); } catch {}
563
+ try { rmSync(temp, { force: true }); } catch {}
564
+ }
565
+ }
566
+
567
+ /**
568
+ * Called by setup.js: say that a supervisor owns the server from now on, and
569
+ * what it is doing. A second click reads the record and starts nothing; the
570
+ * launcher that started this supervisor passes its phases on to the page.
571
+ * Returns { phase(name, detail), fail(error), release() }: `release` (on
572
+ * exit) removes the record unless it says why the start failed.
573
+ */
574
+ export function openSupervisorRecord(dataHome, { pid = process.pid, now = () => Date.now() } = {}) {
575
+ const { dir, supervisor } = launcherStatePaths(dataHome);
576
+ const startedAt = now();
577
+ const record = { pid, startedAt, phase: 'checking', phaseAt: startedAt };
578
+ let ended = false;
579
+ const write = () => {
580
+ try { mkdirSync(dir, { recursive: true }); } catch {}
581
+ writeRecord(supervisor, record, pid);
582
+ };
583
+ write();
584
+ return {
585
+ phase(name, detail = null) {
586
+ if (ended || !SUPERVISOR_PHASES.includes(name) || name === 'failed') return;
587
+ record.phase = name;
588
+ record.phaseAt = now();
589
+ if (detail && typeof detail === 'object') record.detail = detail; else delete record.detail;
590
+ write();
591
+ },
592
+ fail(error) {
593
+ if (ended) return;
594
+ ended = true;
595
+ record.phase = 'failed';
596
+ record.phaseAt = record.endedAt = now();
597
+ record.error = supervisorError(error);
598
+ delete record.detail;
599
+ write();
600
+ },
601
+ release() {
602
+ // A record of why it failed stays for the launcher and the next click to
603
+ // read; `endedAt` keeps it from ever counting as a start under way.
604
+ if (ended) return;
605
+ try { if (readJson(supervisor)?.pid === pid) rmSync(supervisor, { force: true }); } catch {}
606
+ },
607
+ };
608
+ }
609
+
610
+ /**
611
+ * A start that ended before it could open its record (two data homes that
612
+ * disagree, say). Written only into a data directory that is already there:
613
+ * a failed start creates nothing.
614
+ */
615
+ export function noteSupervisorFailure(dataHome, error, { pid = process.pid, now = Date.now() } = {}) {
616
+ const { dir, supervisor } = launcherStatePaths(dataHome);
617
+ if (!existsSync(dir)) return false;
618
+ writeRecord(supervisor, { pid, startedAt: now, phase: 'failed', phaseAt: now, endedAt: now, error: supervisorError(error) }, pid);
619
+ return true;
620
+ }
621
+
622
+ const MUTEX_TIMEOUT_MS = 2000;
623
+ const SQLITE_BUSY = 5;
624
+
625
+ /**
626
+ * node:sqlite, loaded on first use: postinstall.js and setup.js import this
627
+ * module on any Node, and only a launch needs it. The server runs with
628
+ * --disable-warning=ExperimentalWarning; the OS starts the launcher without
629
+ * it, so the one line node prints for this module is kept out of its console.
630
+ */
631
+ function loadSqlite() {
632
+ const emitWarning = process.emitWarning;
633
+ process.emitWarning = function (warning, ...rest) {
634
+ if (/SQLite is an experimental feature/i.test(String(warning?.message ?? warning))) return undefined;
635
+ return emitWarning.call(this, warning, ...rest);
636
+ };
637
+ try { return createRequire(import.meta.url)('node:sqlite'); } finally { process.emitWarning = emitWarning; }
638
+ }
639
+
640
+ /**
641
+ * Run `fn` as the only process inside, among everyone who uses `path`. The
642
+ * mutex is a write transaction (BEGIN IMMEDIATE) on an empty SQLite database,
643
+ * like the rules installer's lock (neural-interface/lib/rulesets/lock.js):
644
+ * the operating system holds it and gives it back when the process ends,
645
+ * however it ends. Nothing is ever written to the file, there is no stale
646
+ * state to clean up, and so nothing here has to judge or remove someone
647
+ * else's claim. Returns { ran, value }; `ran` is false when another process
648
+ * stayed inside for `timeoutMs`. Throws when the mutex cannot be used at all.
649
+ */
650
+ function withLaunchMutex(path, fn, { timeoutMs = MUTEX_TIMEOUT_MS } = {}) {
651
+ const { DatabaseSync } = loadSqlite();
652
+ mkdirSync(dirname(path), { recursive: true });
653
+ // Which file the path leads to, by stat alone (never a second descriptor on it).
654
+ const identity = () => {
655
+ try { const info = statSync(path, { bigint: true }); return `${info.dev}:${info.ino}`; } catch { return null; }
656
+ };
657
+ for (let attempt = 0; attempt < 2; attempt++) {
658
+ const before = identity();
659
+ const db = new DatabaseSync(path);
660
+ try {
661
+ db.exec(`PRAGMA busy_timeout=${Math.max(0, Math.floor(Number(timeoutMs) || 0))}`);
662
+ const opened = identity();
663
+ try {
664
+ db.exec('BEGIN IMMEDIATE');
665
+ } catch (error) {
666
+ const busy = (Number(error?.errcode) & 0xff) === SQLITE_BUSY || /database is locked/i.test(String(error?.errstr || error?.message || ''));
667
+ if (busy) return { ran: false };
668
+ throw error;
669
+ }
670
+ try {
671
+ // Deleted or replaced while it was being taken: this transaction is on
672
+ // a file nobody else looks at. Give it back and take the one there now.
673
+ if (opened === null || (before !== null && before !== opened) || identity() !== opened) continue;
674
+ return { ran: true, value: fn() };
675
+ } finally {
676
+ try { db.exec('ROLLBACK'); } catch {}
677
+ }
678
+ } finally {
679
+ try { db.close(); } catch {}
680
+ }
681
+ }
682
+ throw new Error(`its mutex (${path}) was replaced while it was being taken`);
683
+ }
684
+
685
+ /**
686
+ * Take the launch lock, or return null when a live launch holds it. Reading
687
+ * the record, judging it dead and replacing it all happen inside the launch
688
+ * mutex, and so does the release: two launchers that found the same dead lock
689
+ * cannot remove each other's fresh one, and a late release cannot remove a
690
+ * lock taken after it. Throws (code LAUNCH_LOCK_UNAVAILABLE) when the lock
691
+ * cannot be taken at all; the caller then starts nothing.
692
+ */
693
+ export function acquireLaunchLock(lockPath, {
694
+ pid = process.pid, now = Date.now(), alive = pidAlive, port = null, mutexTimeoutMs = MUTEX_TIMEOUT_MS,
695
+ } = {}) {
696
+ const mutexPath = launchMutexPath(lockPath);
697
+ const id = randomBytes(8).toString('hex');
698
+ const record = JSON.stringify({ pid, startedAt: now, port, id }) + '\n';
699
+ let taken;
700
+ try {
701
+ taken = withLaunchMutex(mutexPath, () => {
702
+ const held = readJson(lockPath);
703
+ if (held && alive(held.pid) && now - Number(held.startedAt || 0) < LOCK_TTL_MS) return false;
704
+ // There and unreadable, a moment old: nobody else is writing it now (this
705
+ // is the mutex), so it is what a launcher that died mid-write left, or
706
+ // not ours at all. It gets a few seconds before it counts as debris.
707
+ if (!held) {
708
+ try { if (Date.now() - statSync(lockPath).mtimeMs < 5000) return false; } catch {}
709
+ }
710
+ // Written in place: a reader outside the mutex (decideLaunch) may catch
711
+ // it half written, reads nothing, and then waits here like everyone else.
712
+ mkdirSync(dirname(lockPath), { recursive: true });
713
+ writeFileSync(lockPath, record, 'utf8');
714
+ return true;
715
+ }, { timeoutMs: mutexTimeoutMs });
716
+ } catch (cause) {
717
+ throw Object.assign(new Error(`the launch lock could not be taken (${lockPath}): ${cause?.message || cause}`), { code: 'LAUNCH_LOCK_UNAVAILABLE', cause });
718
+ }
719
+ // Someone else inside the mutex for that long is a launch at work, too.
720
+ if (!taken.ran || !taken.value) return null;
721
+ return {
722
+ release() {
723
+ try {
724
+ withLaunchMutex(mutexPath, () => {
725
+ if (readJson(lockPath)?.id === id) rmSync(lockPath, { force: true });
726
+ }, { timeoutMs: mutexTimeoutMs });
727
+ } catch {}
728
+ },
729
+ };
730
+ }
731
+
732
+ /** True when something accepts a TCP connection on the port. */
733
+ export function portAccepts(port, { timeoutMs = 400, host = '127.0.0.1', connectImpl = connect } = {}) {
734
+ return new Promise((done) => {
735
+ let settled = false;
736
+ let socket = null;
737
+ const finish = (value) => {
738
+ if (settled) return;
739
+ settled = true;
740
+ try { socket?.destroy(); } catch {}
741
+ done(value);
742
+ };
743
+ try {
744
+ socket = connectImpl({ port, host });
745
+ socket.setTimeout?.(timeoutMs, () => finish(false));
746
+ socket.once('connect', () => finish(true));
747
+ socket.once('error', () => finish(false));
748
+ } catch {
749
+ finish(false);
750
+ }
751
+ });
752
+ }
753
+
754
+ /**
755
+ * True when a server holds the port: something answers HTTP there (a starting
756
+ * server answers before it is ready), or, when no answer came, something still
757
+ * accepts connections. A server too busy to answer in time is a server all the
758
+ * same, and a second one could not take its port.
759
+ */
760
+ export async function probeServer(port, { timeoutMs = 1500, fetchImpl = globalThis.fetch, accepts = portAccepts } = {}) {
761
+ try {
762
+ const response = await fetchImpl(`http://127.0.0.1:${port}/api/health`, {
763
+ signal: AbortSignal.timeout(timeoutMs),
764
+ headers: { accept: 'application/json' },
765
+ });
766
+ try { await response.body?.cancel(); } catch {}
767
+ return true;
768
+ } catch {
769
+ try { return (await accepts(port)) === true; } catch { return false; }
770
+ }
771
+ }
772
+
773
+ // ═══════════════════════════════════════════
774
+ // Progress: the start beacon
775
+ // ═══════════════════════════════════════════
776
+
777
+ export const BEACON_VERSION = 1;
778
+ export const BEACON_PATH = '/synabun-start-status';
779
+ /** How long a launch that failed keeps saying so, for a page that has not read it yet. */
780
+ export const BEACON_LINGER_MS = 20 * 1000;
781
+
782
+ /**
783
+ * Where a launch reports while the server is down: the server's port + 10000
784
+ * on the loopback address (3344 → 13344), − 10000 when that is past the last
785
+ * port. The page derives it the same way (`startBeaconPort` in
786
+ * neural-interface/public/shared/start-bridge.js): the two must agree.
787
+ */
788
+ export function startBeaconPort(port) {
789
+ const p = normalizePort(port) || DEFAULT_PORT;
790
+ return p + 10000 <= 65535 ? p + 10000 : p - 10000;
791
+ }
792
+
793
+ const DETAIL_NUMBERS = ['files', 'bytes', 'skippedFiles', 'port', 'pid', 'exitCode'];
794
+ const DETAIL_STRINGS = ['name', 'step', 'version'];
795
+
796
+ /** What of a supervisor record goes out on the beacon: known fields, bounded, nothing else from the file. */
797
+ export function publicSupervisorState(record) {
798
+ if (!record || typeof record !== 'object' || !SUPERVISOR_PHASES.includes(record.phase)) return null;
799
+ const out = {
800
+ phase: record.phase,
801
+ at: Number(record.phaseAt) || Number(record.startedAt) || 0,
802
+ startedAt: Number(record.startedAt) || 0,
803
+ };
804
+ if (record.detail && typeof record.detail === 'object') {
805
+ const detail = {};
806
+ for (const key of DETAIL_NUMBERS) if (Number.isFinite(Number(record.detail[key])) && record.detail[key] !== null) detail[key] = Number(record.detail[key]);
807
+ for (const key of DETAIL_STRINGS) if (typeof record.detail[key] === 'string') detail[key] = record.detail[key].slice(0, 80);
808
+ if (Object.keys(detail).length) out.detail = detail;
809
+ }
810
+ if (record.phase === 'failed') out.error = supervisorError(record.error);
811
+ return out;
812
+ }
813
+
814
+ /**
815
+ * The beacon of one launch: a loopback-only HTTP listener that answers one
816
+ * read-only question, "what has this launch seen happen so far?".
817
+ *
818
+ * step(id) — something the launcher itself did or saw, with its time
819
+ * supervisor(rec) — the supervisor's own record, as it stands
820
+ * finish(state, e) — 'started' | 'failed' | 'gave-up'
821
+ *
822
+ * It answers only its own host name (a page elsewhere cannot reach it by
823
+ * rebinding a name), gives the answer only to a page of this server's own
824
+ * loopback origin, and changes nothing. A port it cannot have is not an
825
+ * error: the launch goes on unreported and the page says less.
826
+ */
827
+ export function createStartBeacon({
828
+ port,
829
+ startedAt = Date.now(),
830
+ now = () => Date.now(),
831
+ launchId = randomBytes(8).toString('hex'),
832
+ logPath = null,
833
+ createServerImpl = createServer,
834
+ } = {}) {
835
+ const beaconPort = startBeaconPort(port);
836
+ const hosts = new Set([`127.0.0.1:${beaconPort}`, `localhost:${beaconPort}`]);
837
+ const origins = new Set([`http://localhost:${port}`, `http://127.0.0.1:${port}`, `http://[::1]:${port}`]);
838
+ const status = { state: 'starting', steps: [{ id: 'received', at: startedAt }], supervisor: null, error: null };
839
+ let server = null;
840
+ let listening = false;
841
+ let closed = false;
842
+ let retryTimer = null;
843
+ let finalReads = 0;
844
+ let onFinalRead = null;
845
+ // Each phase the supervisor was seen in, with the time it gave for it. One
846
+ // that came and went between two looks is not here: this is what was seen.
847
+ const phases = [];
848
+
849
+ const snapshot = () => ({
850
+ synabun: 'start-beacon',
851
+ v: BEACON_VERSION,
852
+ port,
853
+ launchId,
854
+ startedAt,
855
+ now: now(),
856
+ state: status.state,
857
+ steps: status.steps,
858
+ supervisor: status.supervisor,
859
+ error: status.error,
860
+ log: logPath,
861
+ });
862
+
863
+ function handle(req, res) {
864
+ const deny = (code) => { res.writeHead(code, { 'Content-Length': '0', 'Cache-Control': 'no-store' }); res.end(); };
865
+ try {
866
+ if (!hosts.has(String(req.headers.host || '').toLowerCase())) return deny(403);
867
+ const origin = req.headers.origin;
868
+ if (origin !== undefined && !origins.has(origin)) return deny(403);
869
+ if (String(req.url || '').split('?')[0] !== BEACON_PATH) return deny(404);
870
+ const cors = origin ? { 'Access-Control-Allow-Origin': origin, Vary: 'Origin' } : {};
871
+ if (req.method === 'OPTIONS') {
872
+ res.writeHead(204, {
873
+ ...cors,
874
+ 'Access-Control-Allow-Methods': 'GET',
875
+ 'Access-Control-Allow-Private-Network': 'true',
876
+ 'Access-Control-Max-Age': '600',
877
+ 'Content-Length': '0',
878
+ });
879
+ return res.end();
880
+ }
881
+ if (req.method !== 'GET') return deny(405);
882
+ const body = JSON.stringify(snapshot());
883
+ res.writeHead(200, {
884
+ ...cors,
885
+ 'Content-Type': 'application/json; charset=utf-8',
886
+ 'Content-Length': String(Buffer.byteLength(body)),
887
+ 'Cache-Control': 'no-store',
888
+ 'X-Content-Type-Options': 'nosniff',
889
+ });
890
+ res.end(body);
891
+ if (status.state !== 'starting' && origin) {
892
+ finalReads++;
893
+ onFinalRead?.();
894
+ }
895
+ } catch {
896
+ try { deny(500); } catch {}
897
+ }
898
+ }
899
+
900
+ /** Try to take the port; `retryMs` keeps trying while an earlier launch still holds it. Resolves true | false. */
901
+ function listen({ retryMs = 0, retryEveryMs = 400 } = {}) {
902
+ const deadline = now() + retryMs;
903
+ return new Promise((done) => {
904
+ const attempt = () => {
905
+ retryTimer = null;
906
+ if (closed) return done(false);
907
+ let candidate;
908
+ try {
909
+ candidate = createServerImpl(handle);
910
+ } catch {
911
+ return done(false);
912
+ }
913
+ let bound = false;
914
+ // Kept for the listener's whole life: an error with nobody listening for
915
+ // it would end the launcher, and the beacon is never worth that.
916
+ candidate.on('error', () => {
917
+ if (bound) return;
918
+ try { candidate.close(); } catch {}
919
+ if (closed || now() >= deadline) return done(false);
920
+ retryTimer = setTimeout(attempt, retryEveryMs);
921
+ retryTimer.unref?.();
922
+ });
923
+ candidate.once('listening', () => {
924
+ bound = true;
925
+ if (closed) { try { candidate.close(); } catch {} return done(false); }
926
+ server = candidate;
927
+ listening = true;
928
+ // A slow or silent client cannot hold the launcher open.
929
+ server.headersTimeout = 5000;
930
+ server.requestTimeout = 5000;
931
+ server.keepAliveTimeout = 1000;
932
+ done(true);
933
+ });
934
+ candidate.unref?.();
935
+ candidate.listen({ port: beaconPort, host: '127.0.0.1', exclusive: true });
936
+ };
937
+ attempt();
938
+ });
939
+ }
940
+
941
+ return {
942
+ port: beaconPort,
943
+ launchId,
944
+ get listening() { return listening; },
945
+ get state() { return status.state; },
946
+ snapshot,
947
+ listen,
948
+ step(id, extra = {}) {
949
+ if (status.state !== 'starting' || status.steps.some(s => s.id === id)) return;
950
+ status.steps.push({ id: String(id), at: now(), ...extra });
951
+ },
952
+ supervisor(record) {
953
+ if (status.state !== 'starting') return;
954
+ const next = publicSupervisorState(record);
955
+ if (!next) return;
956
+ if (phases[phases.length - 1]?.phase !== next.phase) phases.push({ phase: next.phase, at: next.at });
957
+ status.supervisor = { ...next, phases: phases.slice(-12) };
958
+ },
959
+ finish(state, error = null) {
960
+ if (status.state !== 'starting') return;
961
+ status.state = state;
962
+ status.error = error ? supervisorError(error) : null;
963
+ status.steps.push({ id: state, at: now() });
964
+ },
965
+ /**
966
+ * Keep answering until a page has read how it ended (and a moment more,
967
+ * for a second tab), `ms` at the most. A beacon nobody can reach is over at once.
968
+ */
969
+ linger(ms, { afterReadMs = 1500 } = {}) {
970
+ if (!listening || closed || !(ms > 0)) return Promise.resolve();
971
+ return new Promise((done) => {
972
+ let timer = setTimeout(done, ms);
973
+ const soon = () => {
974
+ onFinalRead = null;
975
+ clearTimeout(timer);
976
+ timer = setTimeout(done, afterReadMs);
977
+ };
978
+ if (finalReads > 0) soon(); else onFinalRead = soon;
979
+ });
980
+ },
981
+ close() {
982
+ closed = true;
983
+ listening = false;
984
+ if (retryTimer) clearTimeout(retryTimer);
985
+ retryTimer = null;
986
+ onFinalRead = null;
987
+ try { server?.close(); } catch {}
988
+ try { server?.closeAllConnections?.(); } catch {}
989
+ server = null;
990
+ },
991
+ };
992
+ }
993
+
994
+ const LOGIN_ENV_SHELLS = new Set(['zsh', 'bash', 'sh', 'dash', 'ksh', 'mksh', 'yash', 'fish']);
995
+ /** What a launch decides for itself. A profile may export its own; the launch's values stand. */
996
+ const LAUNCH_SETTINGS = ['SYNABUN_DATA_HOME', 'NEURAL_PORT', 'SYNABUN_OPEN_BROWSER'];
997
+
998
+ /**
999
+ * The environment a terminal would give the server. A desktop starts the
1000
+ * launcher with a bare one (no nvm, no Homebrew, no exported keys), so ask the
1001
+ * user's own shell, bounded, and fall back to a PATH that at least finds node.
1002
+ * The shell's answer goes over `env`, except for the launch's own settings:
1003
+ * the server has to listen where the launcher probes and keep its state where
1004
+ * the launcher's lock is.
1005
+ */
1006
+ export function resolveLaunchEnv({
1007
+ env = process.env,
1008
+ nodePath = process.execPath,
1009
+ platform = process.platform,
1010
+ home = homedir(),
1011
+ timeoutMs = 5000,
1012
+ runShell = spawnSync,
1013
+ } = {}) {
1014
+ // Windows hands a protocol handler the user's full environment already.
1015
+ if (platform === 'win32') return { ...env };
1016
+ const nodeDir = dirname(nodePath);
1017
+ // node first, so npm / npx (and CLIs installed next to it) are the same install.
1018
+ const withNode = (base) => {
1019
+ const parts = String(base.PATH || '').split(':').filter(Boolean);
1020
+ const extra = ['/usr/local/bin', '/opt/homebrew/bin', posix.join(home, '.local', 'bin'), '/usr/bin', '/bin'];
1021
+ const path = [nodeDir, ...parts, ...extra].filter((p, i, all) => all.indexOf(p) === i).join(':');
1022
+ return { ...base, PATH: path };
1023
+ };
1024
+
1025
+ const shell = env.SHELL || '';
1026
+ if (!LOGIN_ENV_SHELLS.has(basename(shell))) return withNode(env);
1027
+ try {
1028
+ const marker = `__SYNABUN_ENV_${randomBytes(8).toString('hex')}__`;
1029
+ const script = `process.stdout.write(${JSON.stringify(marker)}+JSON.stringify(process.env)+${JSON.stringify(marker)})`;
1030
+ const result = runShell(shell, ['-ilc', `${shQuote(nodePath)} -e ${shQuote(script)}`], {
1031
+ encoding: 'utf8',
1032
+ timeout: timeoutMs,
1033
+ stdio: ['ignore', 'pipe', 'ignore'],
1034
+ env: { ...env, HOME: env.HOME || home },
1035
+ });
1036
+ const out = String(result?.stdout || '');
1037
+ const start = out.indexOf(marker);
1038
+ const end = out.lastIndexOf(marker);
1039
+ if (start < 0 || end <= start) return withNode(env);
1040
+ const resolved = JSON.parse(out.slice(start + marker.length, end));
1041
+ if (!resolved || typeof resolved !== 'object') return withNode(env);
1042
+ const decided = Object.fromEntries(LAUNCH_SETTINGS.filter(key => env[key]).map(key => [key, env[key]]));
1043
+ return withNode({ ...env, ...resolved, ...decided });
1044
+ } catch {
1045
+ return withNode(env);
1046
+ }
1047
+ }
1048
+
1049
+ function appendLog(logPath, line) {
1050
+ try {
1051
+ mkdirSync(dirname(logPath), { recursive: true });
1052
+ try { if (statSync(logPath).size > 1024 * 1024) renameSync(logPath, `${logPath}.1`); } catch {}
1053
+ writeFileSync(logPath, `${new Date().toISOString()} ${line}\n`, { encoding: 'utf8', flag: 'a' });
1054
+ } catch {}
1055
+ }
1056
+
1057
+ export function noteDesktopLaunch(dataHome, line) {
1058
+ appendLog(launcherStatePaths(dataHome).log, `desktop: ${line}`);
1059
+ }
1060
+
1061
+ async function waitForServer({ port, probe, sleep, now, timeoutMs, pollMs = WAIT_POLL_MS, stillGoing = () => true }) {
1062
+ const deadline = now() + timeoutMs;
1063
+ while (now() < deadline) {
1064
+ if (await probe(port)) return true;
1065
+ if (!stillGoing()) return false;
1066
+ await sleep(pollMs);
1067
+ }
1068
+ return false;
1069
+ }
1070
+
1071
+ /**
1072
+ * Run one launch. Returns { outcome, code, port }:
1073
+ * 'ignored' — the link asked for something other than start
1074
+ * 'already-running' — a server answers
1075
+ * 'already-starting' — another launch (or a supervisor) is on it
1076
+ * 'started' — the server answered after this launch
1077
+ * 'spawned' — started, but it had not answered when the wait ended
1078
+ * 'failed' — it could not be started
1079
+ *
1080
+ * `beacon: true` (launcher.mjs) reports the launch on its beacon while it
1081
+ * lasts, and `lingerMs` keeps a failure readable there for a moment. Every
1082
+ * step is also written to DATA_HOME/data/launcher.log with how long it took.
1083
+ */
1084
+ export async function runLauncher({
1085
+ argv = [],
1086
+ env = process.env,
1087
+ platform = process.platform,
1088
+ packageRoot,
1089
+ nodePath = process.execPath,
1090
+ home = homedir(),
1091
+ probe = probeServer,
1092
+ spawnImpl = spawn,
1093
+ runShell = spawnSync,
1094
+ sleep = (ms) => new Promise(r => setTimeout(r, ms)),
1095
+ now = () => Date.now(),
1096
+ waitMs = HEALTH_WAIT_MS,
1097
+ pollMs = WAIT_POLL_MS,
1098
+ beacon = false,
1099
+ lingerMs = 0,
1100
+ alive = pidAlive,
1101
+ background = false,
1102
+ } = {}) {
1103
+ const args = parseLauncherArgs(argv);
1104
+ if (args.action !== 'start') return { outcome: 'ignored', code: 0, port: null };
1105
+
1106
+ const launchedAt = now();
1107
+ const since = () => `${Math.max(0, now() - launchedAt)} ms`;
1108
+ const launchEnv = { ...env };
1109
+ if (args.dataHome) launchEnv.SYNABUN_DATA_HOME = args.dataHome;
1110
+ const dataHome = getDataHome({ env: launchEnv, home, os: platform });
1111
+ const port = resolvePort({ explicit: args.port, env: launchEnv, dataHome });
1112
+ // From here on the environment says what was decided, and resolveLaunchEnv
1113
+ // keeps it over anything a profile exports: the port probed below, the data
1114
+ // home the lock is in and the server that gets started are the same ones.
1115
+ Object.assign(launchEnv, { SYNABUN_DATA_HOME: dataHome, NEURAL_PORT: String(port), SYNABUN_OPEN_BROWSER: '0' });
1116
+ const paths = launcherStatePaths(dataHome);
1117
+ const log = (line) => appendLog(paths.log, `[${process.pid}] ${line}`);
1118
+ const makeBeacon = () => {
1119
+ if (!beacon) return null;
1120
+ try {
1121
+ const options = { port, startedAt: launchedAt, now, logPath: paths.log };
1122
+ return typeof beacon === 'function' ? beacon(options) : createStartBeacon(options);
1123
+ } catch {
1124
+ return null;
1125
+ }
1126
+ };
1127
+
1128
+ const healthy = await probe(port);
1129
+ const decision = decideLaunch({ healthy, lock: readJson(paths.lock), supervisor: readJson(paths.supervisor), now: now(), alive });
1130
+ if (decision !== 'start') {
1131
+ log(`${decision} (port ${port}, via ${args.via})`);
1132
+ // A start somebody else began (by hand in a terminal, say) has nobody
1133
+ // reporting it when no launcher is with it: this click does, for as long
1134
+ // as that start lasts. Windows keeps a console open for a launcher that
1135
+ // stays, so there it stands down at once.
1136
+ if (decision === 'already-starting' && (platform !== 'win32' || background)) {
1137
+ const standIn = makeBeacon();
1138
+ if (standIn && await standIn.listen()) {
1139
+ const supervisorOf = () => {
1140
+ const record = readJson(paths.supervisor);
1141
+ if (record) standIn.supervisor(record);
1142
+ return record;
1143
+ };
1144
+ const up = await waitForServer({
1145
+ port, probe, sleep, now, timeoutMs: waitMs, pollMs,
1146
+ stillGoing: () => {
1147
+ const record = supervisorOf();
1148
+ const lock = readJson(paths.lock);
1149
+ return !!(record && !record.endedAt && alive(record.pid)) || !!(lock && alive(lock.pid));
1150
+ },
1151
+ });
1152
+ const record = readJson(paths.supervisor);
1153
+ if (up) standIn.finish('started');
1154
+ else if (record?.phase === 'failed') standIn.finish('failed', record.error);
1155
+ else standIn.finish('gave-up');
1156
+ if (!up) await standIn.linger(lingerMs);
1157
+ }
1158
+ standIn?.close();
1159
+ }
1160
+ return { outcome: decision, code: 0, port };
1161
+ }
1162
+
1163
+ const report = makeBeacon();
1164
+ // An earlier launch that failed may still be saying so: take over when it lets go.
1165
+ report?.listen({ retryMs: 5000 });
1166
+ const fail = (error, line = error.message) => {
1167
+ log(`failed: ${line}`);
1168
+ report?.finish('failed', error);
1169
+ return { outcome: 'failed', code: 1, port };
1170
+ };
1171
+ const conclude = async (result) => {
1172
+ if (report && result.outcome !== 'started') await report.linger(lingerMs);
1173
+ report?.close();
1174
+ return result;
1175
+ };
1176
+
1177
+ let lock;
1178
+ try {
1179
+ lock = acquireLaunchLock(paths.lock, { now: now(), port });
1180
+ } catch (error) {
1181
+ return conclude(fail({ code: 'LOCK_UNAVAILABLE', message: error.message }));
1182
+ }
1183
+ if (!lock) {
1184
+ log(`already-starting (lost the lock, port ${port})`);
1185
+ report?.close();
1186
+ return { outcome: 'already-starting', code: 0, port };
1187
+ }
1188
+ log(`starting (port ${port}, via ${args.via})`);
1189
+ report?.step('locked');
1190
+
1191
+ // What the supervisor of this launch says about itself. One of an earlier
1192
+ // launch (a failure left for the next click to read) is not ours.
1193
+ let supervisor = null;
1194
+ const readSupervisor = () => {
1195
+ const record = readJson(paths.supervisor);
1196
+ if (!record || Number(record.startedAt) < launchedAt - 2000) return;
1197
+ if (!supervisor) log(`the supervisor is running after ${since()}`);
1198
+ if (record.phase && record.phase !== supervisor?.phase) log(`supervisor: ${record.phase}${record.phase === 'failed' ? ` (${record.error?.message || 'no reason given'})` : ''} at ${since()}`);
1199
+ supervisor = record;
1200
+ report?.supervisor(record);
1201
+ };
1202
+ const supervisorEnded = () => !!supervisor && (!!supervisor.endedAt || !alive(supervisor.pid));
1203
+ const supervisorFailure = (fallback) => (supervisor?.phase === 'failed' && supervisor.error
1204
+ ? { ...supervisor.error }
1205
+ : fallback);
1206
+ const answered = () => {
1207
+ log(`started: answering after ${since()}`);
1208
+ report?.finish('started');
1209
+ return { outcome: 'started', code: 0, port };
1210
+ };
1211
+ const gaveUp = () => {
1212
+ log(`not answering yet (gave up waiting after ${since()})`);
1213
+ report?.finish('gave-up', { code: 'NO_ANSWER', message: `no answer after ${Math.round(waitMs / 1000)} s` });
1214
+ return { outcome: 'spawned', code: 0, port };
1215
+ };
1216
+
1217
+ const setup = join(packageRoot, 'setup.js');
1218
+ const nodeArgs = ['--disable-warning=ExperimentalWarning', setup];
1219
+ let result;
1220
+ try {
1221
+ result = await (async () => {
1222
+ if (!existsSync(setup)) return fail({ code: 'SETUP_MISSING', message: `${setup} is missing` });
1223
+
1224
+ // ── Windows: the OS opened a console for this process; the server lives in it ──
1225
+ if (platform === 'win32' && !background) {
1226
+ log(`starting in this console (port ${port})`);
1227
+ const child = spawnImpl(nodePath, nodeArgs, {
1228
+ cwd: packageRoot,
1229
+ stdio: 'inherit',
1230
+ env: resolveLaunchEnv({ env: launchEnv, nodePath, platform, home, runShell }),
1231
+ });
1232
+ report?.step('spawned');
1233
+ let exited = null;
1234
+ const done = new Promise((res) => {
1235
+ child.once('error', (error) => { exited = { code: 1, error }; res(); });
1236
+ child.once('exit', (code) => { exited = { code: code ?? 1 }; res(); });
1237
+ });
1238
+ const up = await waitForServer({ port, probe, sleep, now, timeoutMs: waitMs, pollMs, stillGoing: () => { readSupervisor(); return !exited; } });
1239
+ lock.release();
1240
+ if (up) {
1241
+ answered();
1242
+ report?.close(); // the console stays for as long as the server does; the beacon does not
1243
+ } else {
1244
+ readSupervisor();
1245
+ fail(supervisorFailure({
1246
+ code: exited ? 'SUPERVISOR_EXITED' : 'NO_ANSWER',
1247
+ message: exited ? `the supervisor exited (${exited.error?.message || `code ${exited.code}`})` : `no answer after ${Math.round(waitMs / 1000)} s`,
1248
+ exitCode: exited?.code,
1249
+ }), `not answering${exited ? ` (exit ${exited.code})` : ''}`);
1250
+ // Still running, just silent: the console stays, the beacon says so and goes.
1251
+ if (!exited && report) { await report.linger(lingerMs); report.close(); }
1252
+ }
1253
+ await done; // keep the console (and the supervisor in it) until the server stops
1254
+ return { outcome: up ? 'started' : 'failed', code: exited?.code ?? 0, port };
1255
+ }
1256
+
1257
+ // ── macOS: a Terminal window, exactly like a server started by hand ──
1258
+ if (!background && platform === 'darwin' && args.terminalScript && existsSync(args.terminalScript)) {
1259
+ const asked = now();
1260
+ const opened = runShell('/usr/bin/open', ['-a', 'Terminal', args.terminalScript], { timeout: 15000, stdio: 'ignore' });
1261
+ if (!opened?.error && opened?.status === 0) {
1262
+ log(`opened Terminal (port ${port}) in ${Math.max(0, now() - asked)} ms`);
1263
+ report?.step('terminal');
1264
+ const up = await waitForServer({ port, probe, sleep, now, timeoutMs: waitMs, pollMs, stillGoing: () => { readSupervisor(); return !supervisorEnded(); } });
1265
+ if (up) return answered();
1266
+ // The supervisor this launch saw start is gone and nothing answers:
1267
+ // it is over, and holding the lock would only block the next click.
1268
+ if (supervisorEnded()) {
1269
+ return fail(supervisorFailure({ code: 'SUPERVISOR_EXITED', message: 'the server stopped before it answered (the Terminal window says why)' }));
1270
+ }
1271
+ return gaveUp();
1272
+ }
1273
+ log(`Terminal did not open (${opened?.error?.message || `status ${opened?.status}`}); starting in the background`);
1274
+ }
1275
+
1276
+ // ── Linux (and the macOS fallback): detached, with the login shell's environment ──
1277
+ let out = 'ignore';
1278
+ try { out = openSync(paths.log, 'a'); } catch {}
1279
+ const child = spawnImpl(nodePath, nodeArgs, {
1280
+ cwd: packageRoot,
1281
+ detached: true,
1282
+ windowsHide: background,
1283
+ stdio: ['ignore', out, out],
1284
+ // Packaged re-entry and isolated tests can supply an already resolved
1285
+ // environment. Do not read a login profile or add host CLI paths twice.
1286
+ env: background && launchEnv.SYNABUN_LAUNCH_ENV === 'resolved'
1287
+ ? launchEnv : resolveLaunchEnv({ env: launchEnv, nodePath, platform, home, runShell }),
1288
+ });
1289
+ let exited = null;
1290
+ child.once('error', (error) => { exited = { code: 1, error }; });
1291
+ child.once('exit', (code) => { exited = { code: code ?? 1 }; });
1292
+ child.unref();
1293
+ if (typeof out === 'number') { try { closeSync(out); } catch {} }
1294
+ log(`started in the background (pid ${child.pid}, port ${port}) after ${since()}`);
1295
+ report?.step('spawned');
1296
+ const up = await waitForServer({ port, probe, sleep, now, timeoutMs: waitMs, pollMs, stillGoing: () => { readSupervisor(); return !exited; } });
1297
+ if (up) return answered();
1298
+ if (exited) {
1299
+ readSupervisor();
1300
+ const reason = `the supervisor exited (${exited.error?.message || `code ${exited.code}`})`;
1301
+ return fail(supervisorFailure({ code: 'SUPERVISOR_EXITED', message: reason, exitCode: exited.code }), reason);
1302
+ }
1303
+ return gaveUp();
1304
+ })();
1305
+ } finally {
1306
+ lock.release();
1307
+ }
1308
+ return conclude(result);
1309
+ }