synabun 2.0.5 → 2.0.9

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 (527) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +2 -1
  3. package/THIRD-PARTY-LICENSES.md +13 -0
  4. package/docs/whatsapp.md +151 -16
  5. package/hooks/claude-code/post-plan.mjs +6 -30
  6. package/hooks/claude-code/session-start.mjs +1 -4
  7. package/hooks/claude-code/shared.mjs +112 -75
  8. package/lib/child-path.js +303 -0
  9. package/lib/claude-hooks.js +2 -1
  10. package/lib/data-home-migration.js +2 -0
  11. package/lib/desktop-app.js +265 -0
  12. package/lib/external-tools.js +7 -3
  13. package/lib/log-redact.js +102 -0
  14. package/lib/package-processes.js +670 -0
  15. package/lib/packaged-runtime.js +138 -11
  16. package/lib/runtime-dependencies.js +225 -0
  17. package/mcp-server/dist/http.d.ts +3 -1
  18. package/mcp-server/dist/http.d.ts.map +1 -1
  19. package/mcp-server/dist/http.js +15 -4
  20. package/mcp-server/dist/http.js.map +1 -1
  21. package/mcp-server/dist/index.d.ts.map +1 -1
  22. package/mcp-server/dist/index.js +9 -6
  23. package/mcp-server/dist/index.js.map +1 -1
  24. package/mcp-server/dist/services/browser-bench.d.ts +1 -1
  25. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
  26. package/mcp-server/dist/services/browser-bench.js +1 -1
  27. package/mcp-server/dist/services/browser-bench.js.map +1 -1
  28. package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
  29. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
  30. package/mcp-server/dist/services/budget-judgments.js +153 -0
  31. package/mcp-server/dist/services/budget-judgments.js.map +1 -0
  32. package/mcp-server/dist/services/categories.d.ts +35 -2
  33. package/mcp-server/dist/services/categories.d.ts.map +1 -1
  34. package/mcp-server/dist/services/categories.js +77 -33
  35. package/mcp-server/dist/services/categories.js.map +1 -1
  36. package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +41 -4
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
  41. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
  42. package/mcp-server/dist/services/desktop-bench.js +1 -1
  43. package/mcp-server/dist/services/desktop-bench.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +11 -0
  45. package/mcp-server/dist/services/identity.d.ts.map +1 -1
  46. package/mcp-server/dist/services/identity.js +24 -0
  47. package/mcp-server/dist/services/identity.js.map +1 -1
  48. package/mcp-server/dist/services/neural-interface.d.ts +13 -0
  49. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  50. package/mcp-server/dist/services/neural-interface.js +34 -2
  51. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  52. package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
  53. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
  54. package/mcp-server/dist/services/typesafe-config.js +4 -1
  55. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  56. package/mcp-server/dist/tools/agents.d.ts +11 -8
  57. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  58. package/mcp-server/dist/tools/agents.js +35 -14
  59. package/mcp-server/dist/tools/agents.js.map +1 -1
  60. package/mcp-server/dist/tools/bluesky.js +1 -1
  61. package/mcp-server/dist/tools/bluesky.js.map +1 -1
  62. package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
  63. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
  64. package/mcp-server/dist/tools/browser-advanced.js +1 -1
  65. package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
  66. package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
  67. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
  68. package/mcp-server/dist/tools/browser-batch.js +1 -1
  69. package/mcp-server/dist/tools/browser-batch.js.map +1 -1
  70. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
  71. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
  72. package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
  73. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
  74. package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
  75. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
  76. package/mcp-server/dist/tools/browser-interact.js +1 -1
  77. package/mcp-server/dist/tools/browser-interact.js.map +1 -1
  78. package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
  79. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
  80. package/mcp-server/dist/tools/browser-navigate.js +1 -1
  81. package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
  82. package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
  83. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
  84. package/mcp-server/dist/tools/browser-observe.js +1 -1
  85. package/mcp-server/dist/tools/browser-observe.js.map +1 -1
  86. package/mcp-server/dist/tools/card-tools.d.ts +1 -1
  87. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
  88. package/mcp-server/dist/tools/card-tools.js +1 -1
  89. package/mcp-server/dist/tools/card-tools.js.map +1 -1
  90. package/mcp-server/dist/tools/category.d.ts +1 -1
  91. package/mcp-server/dist/tools/category.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/category.js +5 -1
  93. package/mcp-server/dist/tools/category.js.map +1 -1
  94. package/mcp-server/dist/tools/choice.d.ts +1 -1
  95. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/choice.js +1 -1
  97. package/mcp-server/dist/tools/choice.js.map +1 -1
  98. package/mcp-server/dist/tools/computer.d.ts +1 -1
  99. package/mcp-server/dist/tools/computer.d.ts.map +1 -1
  100. package/mcp-server/dist/tools/computer.js +1 -1
  101. package/mcp-server/dist/tools/computer.js.map +1 -1
  102. package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
  103. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
  104. package/mcp-server/dist/tools/discord-channel.js +1 -1
  105. package/mcp-server/dist/tools/discord-channel.js.map +1 -1
  106. package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
  107. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
  108. package/mcp-server/dist/tools/discord-guild.js +1 -1
  109. package/mcp-server/dist/tools/discord-guild.js.map +1 -1
  110. package/mcp-server/dist/tools/discord-member.d.ts +1 -1
  111. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
  112. package/mcp-server/dist/tools/discord-member.js +1 -1
  113. package/mcp-server/dist/tools/discord-member.js.map +1 -1
  114. package/mcp-server/dist/tools/discord-message.d.ts +1 -1
  115. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
  116. package/mcp-server/dist/tools/discord-message.js +1 -1
  117. package/mcp-server/dist/tools/discord-message.js.map +1 -1
  118. package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
  119. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
  120. package/mcp-server/dist/tools/discord-onboarding.js +1 -1
  121. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
  122. package/mcp-server/dist/tools/discord-role.d.ts +1 -1
  123. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
  124. package/mcp-server/dist/tools/discord-role.js +1 -1
  125. package/mcp-server/dist/tools/discord-role.js.map +1 -1
  126. package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
  127. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
  128. package/mcp-server/dist/tools/discord-thread.js +1 -1
  129. package/mcp-server/dist/tools/discord-thread.js.map +1 -1
  130. package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
  131. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
  132. package/mcp-server/dist/tools/discord-webhook.js +1 -1
  133. package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
  134. package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
  135. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
  136. package/mcp-server/dist/tools/fb-groups.js +1 -1
  137. package/mcp-server/dist/tools/fb-groups.js.map +1 -1
  138. package/mcp-server/dist/tools/forget.d.ts +1 -1
  139. package/mcp-server/dist/tools/forget.d.ts.map +1 -1
  140. package/mcp-server/dist/tools/forget.js +1 -1
  141. package/mcp-server/dist/tools/forget.js.map +1 -1
  142. package/mcp-server/dist/tools/git-tools.d.ts +1 -1
  143. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
  144. package/mcp-server/dist/tools/git-tools.js +1 -1
  145. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  146. package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
  147. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
  148. package/mcp-server/dist/tools/gsc/experience.js +1 -1
  149. package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
  150. package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
  151. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
  152. package/mcp-server/dist/tools/gsc/indexing.js +1 -1
  153. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
  154. package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
  155. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
  156. package/mcp-server/dist/tools/gsc/inspect.js +1 -1
  157. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
  158. package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
  159. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
  160. package/mcp-server/dist/tools/gsc/navigate.js +1 -1
  161. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
  162. package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
  163. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
  164. package/mcp-server/dist/tools/gsc/performance.js +1 -1
  165. package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
  166. package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
  167. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
  168. package/mcp-server/dist/tools/gsc/settings.js +1 -1
  169. package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
  170. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  171. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
  172. package/mcp-server/dist/tools/image-tools.js +1 -1
  173. package/mcp-server/dist/tools/image-tools.js.map +1 -1
  174. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
  175. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
  176. package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
  177. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
  178. package/mcp-server/dist/tools/loop.d.ts +1 -1
  179. package/mcp-server/dist/tools/loop.d.ts.map +1 -1
  180. package/mcp-server/dist/tools/loop.js +1 -1
  181. package/mcp-server/dist/tools/loop.js.map +1 -1
  182. package/mcp-server/dist/tools/memories.d.ts +1 -1
  183. package/mcp-server/dist/tools/memories.d.ts.map +1 -1
  184. package/mcp-server/dist/tools/memories.js +1 -1
  185. package/mcp-server/dist/tools/memories.js.map +1 -1
  186. package/mcp-server/dist/tools/morelogin.d.ts +1 -1
  187. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  188. package/mcp-server/dist/tools/morelogin.js +1 -1
  189. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  190. package/mcp-server/dist/tools/profile.d.ts +1 -1
  191. package/mcp-server/dist/tools/profile.d.ts.map +1 -1
  192. package/mcp-server/dist/tools/profile.js +1 -1
  193. package/mcp-server/dist/tools/profile.js.map +1 -1
  194. package/mcp-server/dist/tools/recall.d.ts +1 -1
  195. package/mcp-server/dist/tools/recall.d.ts.map +1 -1
  196. package/mcp-server/dist/tools/recall.js +1 -1
  197. package/mcp-server/dist/tools/recall.js.map +1 -1
  198. package/mcp-server/dist/tools/reflect.d.ts +1 -1
  199. package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
  200. package/mcp-server/dist/tools/reflect.js +1 -1
  201. package/mcp-server/dist/tools/reflect.js.map +1 -1
  202. package/mcp-server/dist/tools/remember.d.ts +1 -1
  203. package/mcp-server/dist/tools/remember.d.ts.map +1 -1
  204. package/mcp-server/dist/tools/remember.js +13 -11
  205. package/mcp-server/dist/tools/remember.js.map +1 -1
  206. package/mcp-server/dist/tools/restore.d.ts +1 -1
  207. package/mcp-server/dist/tools/restore.d.ts.map +1 -1
  208. package/mcp-server/dist/tools/restore.js +1 -1
  209. package/mcp-server/dist/tools/restore.js.map +1 -1
  210. package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
  211. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
  212. package/mcp-server/dist/tools/style-guide-tools.js +1 -1
  213. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
  214. package/mcp-server/dist/tools/sync.d.ts +1 -1
  215. package/mcp-server/dist/tools/sync.d.ts.map +1 -1
  216. package/mcp-server/dist/tools/sync.js +1 -1
  217. package/mcp-server/dist/tools/sync.js.map +1 -1
  218. package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
  219. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
  220. package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
  221. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
  222. package/mcp-server/dist/tools/utils.d.ts +1 -1
  223. package/mcp-server/dist/tools/utils.d.ts.map +1 -1
  224. package/mcp-server/dist/tools/utils.js +1 -1
  225. package/mcp-server/dist/tools/utils.js.map +1 -1
  226. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
  227. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
  228. package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
  229. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
  230. package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
  231. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  232. package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
  233. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  234. package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
  235. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
  236. package/mcp-server/dist/tools/youtube/analytics.js +1 -1
  237. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
  238. package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
  239. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
  240. package/mcp-server/dist/tools/youtube/download.js +1 -1
  241. package/mcp-server/dist/tools/youtube/download.js.map +1 -1
  242. package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
  243. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
  244. package/mcp-server/dist/tools/youtube/media.js +1 -1
  245. package/mcp-server/dist/tools/youtube/media.js.map +1 -1
  246. package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
  247. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
  248. package/mcp-server/dist/tools/youtube/metadata.js +1 -1
  249. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
  250. package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
  251. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
  252. package/mcp-server/dist/tools/youtube/navigate.js +1 -1
  253. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
  254. package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
  255. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
  256. package/mcp-server/dist/tools/youtube/playlist.js +1 -1
  257. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
  258. package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
  259. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
  260. package/mcp-server/dist/tools/youtube/schedule.js +1 -1
  261. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
  262. package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
  263. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
  264. package/mcp-server/dist/tools/youtube/source.js +1 -1
  265. package/mcp-server/dist/tools/youtube/source.js.map +1 -1
  266. package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
  267. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
  268. package/mcp-server/dist/tools/youtube/upload.js +4 -9
  269. package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
  270. package/mcp-server/dist/tui.js +18 -5
  271. package/mcp-server/dist/tui.js.map +1 -1
  272. package/mcp-server/dist/version.d.ts +5 -0
  273. package/mcp-server/dist/version.d.ts.map +1 -0
  274. package/mcp-server/dist/version.js +21 -0
  275. package/mcp-server/dist/version.js.map +1 -0
  276. package/mcp-server/package-lock.json +3 -3
  277. package/mcp-server/package.json +3 -3
  278. package/mcp-server/run.mjs +15 -9
  279. package/mcp-server/src/http.ts +18 -3
  280. package/mcp-server/src/index.ts +9 -6
  281. package/mcp-server/src/services/browser-bench.ts +1 -1
  282. package/mcp-server/src/services/budget-judgments.ts +183 -0
  283. package/mcp-server/src/services/categories.ts +77 -32
  284. package/mcp-server/src/services/codex-config-heal.ts +35 -4
  285. package/mcp-server/src/services/desktop-bench.ts +1 -1
  286. package/mcp-server/src/services/identity.ts +27 -0
  287. package/mcp-server/src/services/neural-interface.ts +34 -2
  288. package/mcp-server/src/services/typesafe-config.ts +4 -1
  289. package/mcp-server/src/tools/agents.ts +34 -15
  290. package/mcp-server/src/tools/bluesky.ts +1 -1
  291. package/mcp-server/src/tools/browser-advanced.ts +1 -1
  292. package/mcp-server/src/tools/browser-batch.ts +1 -1
  293. package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
  294. package/mcp-server/src/tools/browser-interact.ts +1 -1
  295. package/mcp-server/src/tools/browser-navigate.ts +1 -1
  296. package/mcp-server/src/tools/browser-observe.ts +1 -1
  297. package/mcp-server/src/tools/card-tools.ts +1 -1
  298. package/mcp-server/src/tools/category.ts +6 -1
  299. package/mcp-server/src/tools/choice.ts +1 -1
  300. package/mcp-server/src/tools/computer.ts +1 -1
  301. package/mcp-server/src/tools/discord-channel.ts +1 -1
  302. package/mcp-server/src/tools/discord-guild.ts +1 -1
  303. package/mcp-server/src/tools/discord-member.ts +1 -1
  304. package/mcp-server/src/tools/discord-message.ts +1 -1
  305. package/mcp-server/src/tools/discord-onboarding.ts +1 -1
  306. package/mcp-server/src/tools/discord-role.ts +1 -1
  307. package/mcp-server/src/tools/discord-thread.ts +1 -1
  308. package/mcp-server/src/tools/discord-webhook.ts +1 -1
  309. package/mcp-server/src/tools/fb-groups.ts +1 -1
  310. package/mcp-server/src/tools/forget.ts +1 -1
  311. package/mcp-server/src/tools/git-tools.ts +1 -1
  312. package/mcp-server/src/tools/gsc/experience.ts +1 -1
  313. package/mcp-server/src/tools/gsc/indexing.ts +1 -1
  314. package/mcp-server/src/tools/gsc/inspect.ts +1 -1
  315. package/mcp-server/src/tools/gsc/navigate.ts +1 -1
  316. package/mcp-server/src/tools/gsc/performance.ts +1 -1
  317. package/mcp-server/src/tools/gsc/settings.ts +1 -1
  318. package/mcp-server/src/tools/image-tools.ts +1 -1
  319. package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
  320. package/mcp-server/src/tools/loop.ts +1 -1
  321. package/mcp-server/src/tools/memories.ts +1 -1
  322. package/mcp-server/src/tools/morelogin.ts +1 -1
  323. package/mcp-server/src/tools/profile.ts +1 -1
  324. package/mcp-server/src/tools/recall.ts +1 -1
  325. package/mcp-server/src/tools/reflect.ts +1 -1
  326. package/mcp-server/src/tools/remember.ts +13 -12
  327. package/mcp-server/src/tools/restore.ts +1 -1
  328. package/mcp-server/src/tools/style-guide-tools.ts +1 -1
  329. package/mcp-server/src/tools/sync.ts +1 -1
  330. package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
  331. package/mcp-server/src/tools/utils.ts +1 -1
  332. package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
  333. package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
  334. package/mcp-server/src/tools/youtube/analytics.ts +1 -1
  335. package/mcp-server/src/tools/youtube/download.ts +1 -1
  336. package/mcp-server/src/tools/youtube/media.ts +1 -1
  337. package/mcp-server/src/tools/youtube/metadata.ts +1 -1
  338. package/mcp-server/src/tools/youtube/navigate.ts +1 -1
  339. package/mcp-server/src/tools/youtube/playlist.ts +1 -1
  340. package/mcp-server/src/tools/youtube/schedule.ts +1 -1
  341. package/mcp-server/src/tools/youtube/source.ts +1 -1
  342. package/mcp-server/src/tools/youtube/upload.ts +4 -12
  343. package/mcp-server/src/tui.ts +24 -10
  344. package/mcp-server/src/version.ts +22 -0
  345. package/neural-interface/i18n/en.json +645 -57
  346. package/neural-interface/i18n/pt-BR.json +644 -56
  347. package/neural-interface/lib/account-usage.js +163 -0
  348. package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
  349. package/neural-interface/lib/agent-sandbox-setting.js +57 -0
  350. package/neural-interface/lib/assistant-api.js +274 -52
  351. package/neural-interface/lib/assistant-brains/claude.js +15 -2
  352. package/neural-interface/lib/assistant-brains/codex.js +3 -0
  353. package/neural-interface/lib/assistant-brains/opencode.js +17 -4
  354. package/neural-interface/lib/assistant-budget-planner.js +390 -0
  355. package/neural-interface/lib/assistant-budget-split.js +408 -0
  356. package/neural-interface/lib/assistant-budget.js +94 -95
  357. package/neural-interface/lib/assistant-config.js +62 -14
  358. package/neural-interface/lib/assistant-dispatch.js +432 -125
  359. package/neural-interface/lib/assistant-jev.js +26 -3
  360. package/neural-interface/lib/assistant-persona.js +68 -11
  361. package/neural-interface/lib/assistant-plan-permissions.js +8 -1
  362. package/neural-interface/lib/assistant-route-accounts.js +14 -0
  363. package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
  364. package/neural-interface/lib/assistant-route-gate.js +19 -3
  365. package/neural-interface/lib/assistant-router.js +373 -42
  366. package/neural-interface/lib/assistant-runtime.js +312 -48
  367. package/neural-interface/lib/assistant-user-writer.js +4 -2
  368. package/neural-interface/lib/augmented-path.js +317 -53
  369. package/neural-interface/lib/claude-accounts.js +342 -31
  370. package/neural-interface/lib/claude-agent-bridge.js +90 -3
  371. package/neural-interface/lib/claude-executable.js +6 -1
  372. package/neural-interface/lib/claude-model-catalog.js +3 -3
  373. package/neural-interface/lib/claude-panel-auth.js +221 -0
  374. package/neural-interface/lib/claude-profile-api-key.js +62 -0
  375. package/neural-interface/lib/cli-install-api.js +137 -0
  376. package/neural-interface/lib/cli-install.js +797 -0
  377. package/neural-interface/lib/cli-path.js +130 -0
  378. package/neural-interface/lib/cli-resolver.js +744 -0
  379. package/neural-interface/lib/cli-update-planner.js +143 -7
  380. package/neural-interface/lib/codex-account-auth.js +180 -0
  381. package/neural-interface/lib/codex-accounts.js +502 -0
  382. package/neural-interface/lib/codex-capabilities.js +2 -1
  383. package/neural-interface/lib/codex-compat-rpc.js +8 -2
  384. package/neural-interface/lib/codex-mcp-approvals.js +5 -1
  385. package/neural-interface/lib/codex-panel-auth.js +62 -0
  386. package/neural-interface/lib/codex-session-settings.js +1 -1
  387. package/neural-interface/lib/codex-settings-schema.js +122 -0
  388. package/neural-interface/lib/desktop/config.js +3 -0
  389. package/neural-interface/lib/exec-loop-command.js +3 -1
  390. package/neural-interface/lib/git-bash/bzip2.js +339 -0
  391. package/neural-interface/lib/git-bash/claude-settings.js +73 -0
  392. package/neural-interface/lib/git-bash/extract-worker.js +34 -0
  393. package/neural-interface/lib/git-bash/install.js +529 -0
  394. package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
  395. package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
  396. package/neural-interface/lib/git-bash/manager.js +268 -0
  397. package/neural-interface/lib/git-bash/pins.js +206 -0
  398. package/neural-interface/lib/git-bash/pins.json +34 -0
  399. package/neural-interface/lib/git-bash/resolver.js +194 -0
  400. package/neural-interface/lib/git-read.js +34 -2
  401. package/neural-interface/lib/install-details.js +163 -0
  402. package/neural-interface/lib/mcp-installer.js +14 -2
  403. package/neural-interface/lib/native-loop-providers.js +22 -4
  404. package/neural-interface/lib/native-loop-runtime.js +9 -4
  405. package/neural-interface/lib/os-path.js +319 -0
  406. package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
  407. package/neural-interface/lib/provider-panel-terminal-auth.js +96 -0
  408. package/neural-interface/lib/pty-host/core.js +2 -1
  409. package/neural-interface/lib/pty-host/manager.js +27 -2
  410. package/neural-interface/lib/remote-policy.js +20 -11
  411. package/neural-interface/lib/session-indexer.js +3 -1
  412. package/neural-interface/lib/session-title-generator.js +6 -3
  413. package/neural-interface/lib/system-about.js +340 -0
  414. package/neural-interface/lib/system-activity.js +66 -0
  415. package/neural-interface/lib/terminal-env.js +11 -3
  416. package/neural-interface/lib/terminal-path.js +55 -0
  417. package/neural-interface/lib/virtual-socket.js +12 -1
  418. package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
  419. package/neural-interface/lib/whatsapp/bridge.js +78 -34
  420. package/neural-interface/lib/whatsapp/commands.js +13 -14
  421. package/neural-interface/lib/whatsapp/config.js +134 -20
  422. package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
  423. package/neural-interface/lib/whatsapp/host-core.js +168 -35
  424. package/neural-interface/lib/whatsapp/inbound.js +5 -3
  425. package/neural-interface/lib/whatsapp/local-file.js +524 -0
  426. package/neural-interface/lib/whatsapp/manager.js +92 -32
  427. package/neural-interface/lib/whatsapp/paths.js +11 -1
  428. package/neural-interface/lib/whatsapp/protocol.js +65 -5
  429. package/neural-interface/lib/whatsapp/redact.js +63 -0
  430. package/neural-interface/lib/whatsapp/service.js +343 -91
  431. package/neural-interface/lib/whatsapp/transcription.js +463 -0
  432. package/neural-interface/package-lock.json +2 -2
  433. package/neural-interface/package.json +1 -1
  434. package/neural-interface/public/index.html +2 -0
  435. package/neural-interface/public/index2d.html +2 -0
  436. package/neural-interface/public/offline.html +16 -1
  437. package/neural-interface/public/onboarding.html +96 -2
  438. package/neural-interface/public/shared/account-i18n.js +7 -0
  439. package/neural-interface/public/shared/account-menu.js +542 -0
  440. package/neural-interface/public/shared/account-usage.js +79 -0
  441. package/neural-interface/public/shared/account-view.js +336 -0
  442. package/neural-interface/public/shared/agent-runtime-options.js +28 -8
  443. package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
  444. package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
  445. package/neural-interface/public/shared/assistant/asst-api.js +39 -2
  446. package/neural-interface/public/shared/assistant/asst-brain-picker.js +288 -32
  447. package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
  448. package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
  449. package/neural-interface/public/shared/assistant/asst-menu.js +219 -80
  450. package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
  451. package/neural-interface/public/shared/assistant/asst-panel.js +87 -16
  452. package/neural-interface/public/shared/assistant/asst-render.js +38 -7
  453. package/neural-interface/public/shared/assistant/asst-route.js +72 -14
  454. package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
  455. package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
  456. package/neural-interface/public/shared/assistant/asst-state.js +1 -1
  457. package/neural-interface/public/shared/assistant/asst-styles.js +160 -29
  458. package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
  459. package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
  460. package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
  461. package/neural-interface/public/shared/cdx/cdx-panel.js +49 -108
  462. package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
  463. package/neural-interface/public/shared/cdx/cdx-styles.js +172 -111
  464. package/neural-interface/public/shared/cdx/cdx-tabs.js +264 -664
  465. package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
  466. package/neural-interface/public/shared/claude-account-view.js +3 -0
  467. package/neural-interface/public/shared/cli-install-view.js +392 -0
  468. package/neural-interface/public/shared/cli-status.js +282 -22
  469. package/neural-interface/public/shared/cp/cp-events.js +2 -1
  470. package/neural-interface/public/shared/cp/cp-styles.js +46 -0
  471. package/neural-interface/public/shared/desktop-bridge.js +39 -0
  472. package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
  473. package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
  474. package/neural-interface/public/shared/desktop-titlebar.js +153 -0
  475. package/neural-interface/public/shared/desktop-update-view.js +462 -0
  476. package/neural-interface/public/shared/editor/editor-panel.js +1 -1
  477. package/neural-interface/public/shared/explorer/fx-main.js +1 -1
  478. package/neural-interface/public/shared/git-bash-view.js +145 -0
  479. package/neural-interface/public/shared/html-shell.js +1 -0
  480. package/neural-interface/public/shared/i18n.js +19 -1
  481. package/neural-interface/public/shared/install-details-view.js +86 -0
  482. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -1
  483. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +36 -1
  484. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
  485. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +169 -2
  486. package/neural-interface/public/shared/pwa-install-page.js +3 -1
  487. package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
  488. package/neural-interface/public/shared/settings/settings-about.js +254 -0
  489. package/neural-interface/public/shared/settings/settings-ia.js +15 -0
  490. package/neural-interface/public/shared/showcase-privacy.js +490 -0
  491. package/neural-interface/public/shared/storage.js +30 -3
  492. package/neural-interface/public/shared/styles.css +281 -32
  493. package/neural-interface/public/shared/synabun-mascot.js +48 -2
  494. package/neural-interface/public/shared/ui-claude-panel.js +262 -217
  495. package/neural-interface/public/shared/ui-cli-banner.js +355 -0
  496. package/neural-interface/public/shared/ui-explorer.js +2 -0
  497. package/neural-interface/public/shared/ui-git-bash.js +124 -0
  498. package/neural-interface/public/shared/ui-judgments.js +10 -1
  499. package/neural-interface/public/shared/ui-loading.js +30 -6
  500. package/neural-interface/public/shared/ui-menubar.js +13 -3
  501. package/neural-interface/public/shared/ui-navbar.js +3 -1
  502. package/neural-interface/public/shared/ui-provider-auth.js +104 -0
  503. package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
  504. package/neural-interface/public/shared/ui-sessions.js +9 -3
  505. package/neural-interface/public/shared/ui-settings.js +55 -3
  506. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
  507. package/neural-interface/public/shared/ui-sync.js +8 -0
  508. package/neural-interface/public/shared/ui-terminal.js +17 -0
  509. package/neural-interface/public/shared/ui-tool-updates.js +5 -0
  510. package/neural-interface/public/shared/ui-update.js +598 -7
  511. package/neural-interface/public/shared/ui-whatsapp.js +21 -6
  512. package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
  513. package/neural-interface/public/shared/worker-end.js +2 -0
  514. package/neural-interface/public/sw.js +1 -1
  515. package/neural-interface/public/variant/2d/main.js +10 -1
  516. package/neural-interface/public/variant/3d/main.js +10 -1
  517. package/neural-interface/scripts/rebuild-pty.js +14 -2
  518. package/neural-interface/server.js +1384 -679
  519. package/npm-shrinkwrap.json +4424 -0
  520. package/package.json +57 -2
  521. package/postinstall.js +61 -47
  522. package/preuninstall.js +8 -35
  523. package/setup.js +80 -47
  524. package/skills/synabun/modules/idea.md +1 -1
  525. package/skills/synabun/modules/schedule.md +1 -1
  526. package/updater.cmd +2 -2
  527. package/updater.mjs +97 -3
@@ -0,0 +1,490 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Showcase privacy
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Loaded only in showcase mode (`?showcase`, storage.js isShowcaseMode) by the
6
+ // main entry points, for product screenshots: the feature stays sharp, the
7
+ // user's own content is blurred (a CSS filter; no text is removed or rewritten).
8
+ //
9
+ // - Wholesale, by selector: the surfaces that always hold the user's content
10
+ // (memory text, session titles, transcripts, terminal output, Resume results,
11
+ // account names, toast text). Pure CSS, so a node is blurred the moment it
12
+ // is drawn.
13
+ // - By what it says: any element whose own visible text, value or placeholder
14
+ // matches a private pattern (emails, home paths, ids, keys, phone numbers,
15
+ // the names of the user's other projects, their OS user name...). A
16
+ // debounced MutationObserver looks only at added or changed nodes.
17
+ // - Same-origin frames (detached side-panel sessions) get the same treatment.
18
+ //
19
+ // Canvas text cannot be blurred: the 3D map's labels (#map-labels) and the 2D
20
+ // map (#canvas-main) are drawn on canvases; blur("#map-labels") blurs all of
21
+ // them at once.
22
+ //
23
+ // window.__synabunShowcase = { scan, blur, unblur, stats }.
24
+
25
+ const BLUR_CLASS = 'sb-showcase-blur';
26
+ const MANUAL_CLASS = 'sb-showcase-manual';
27
+ const CLEAR_ATTR = 'data-sb-showcase-clear';
28
+ const STYLE_ID = 'sb-showcase-style';
29
+ const BLUR = 'blur(7px)';
30
+ const DEBOUNCE_MS = 120;
31
+ // Private text split over a few spans (a path in pieces): a parent holding at
32
+ // most this much text is read as one, up to this many levels up.
33
+ const SPAN_MAX = 160;
34
+ const SPAN_LEVELS = 3;
35
+
36
+ /** Surfaces that always hold the user's own content (class names from the shared UI modules). */
37
+ export const WHOLESALE_SELECTORS = [
38
+ // Memories: detail card, map hover title, Memory Explorer rows, bookmarks, trash
39
+ '.detail-card [data-role="content"]', '.detail-card .md-textarea', '.detail-card .tag-text',
40
+ '.detail-card [data-role="files-content"]', '.detail-card [data-role="meta-content"] .detail-meta-row > span:last-child',
41
+ '#tooltip-preview', '.explorer-memory .explorer-label', '.dropdown-item-title',
42
+ '.tw-item-preview', '.tw-detail-body', '.tw-detail-tags', '.tw-detail-file',
43
+ // Session pills, windows and tabs, with their titles
44
+ '.term-minimized-pill-label', '.sp-session-title', '.asp-title',
45
+ '.cp-session-label', '.cp-sess-prompt', '.cp-sess-meta',
46
+ '#cxp-session-label', '.cxp-sess-prompt', '.cxp-sess-meta',
47
+ '.ocpv2-session-label', '.ocpv2-session-item[data-tooltip] .ocpv2-session-item-label', '.ocpv2-session-item-meta', '.ocpv2-session-share-url',
48
+ '.term-tab-label', '.term-float-tab-title', '.peek-tab-label', '.sm-card-project', '.sm-card-val',
49
+ // Claude Code side panel transcript
50
+ '.msg-user .msg-bubble', '.msg-assistant .msg-body', '.msg-thinking-content', '.tool-detail', '.tool-section', '.tool-result-content',
51
+ '.cp-bash-cmd', '.cp-bash-desc', '.cp-bash-out', '.cp-queue-text', '.cp-todo-txt', '.cp-task-title',
52
+ // Codex side panel transcript
53
+ '.cxp-msg-user-text', '.cxp-msg-body', '.cxp-reasoning-body', '.cxp-think-detail',
54
+ '.cxp-card-subtitle', '.cxp-card-body', '.cxp-fold-body', '.cxp-queue-item-text',
55
+ // OpenCode side panel transcript
56
+ '.ocpv2-part-text', '.ocpv2-reasoning-body', '.ocpv2-tc-summary', '.ocpv2-tc-meta', '.ocpv2-tc-body',
57
+ '.ocpv2-tool-input', '.ocpv2-tool-output', '.ocpv2-sb-summary',
58
+ // Assistant transcript, run and worker cards
59
+ '.asst-msg-text', '.asst-md', '.asst-thinking-content', '.asst-run-title', '.asst-run-summary', '.asst-run-escalate-reason',
60
+ '.asst-tool-detail', '.asst-tool-body', '.asst-tool-section', '.asst-tl-said', '.asst-tl-subject', '.asst-bubble-text',
61
+ '.asst-station-sub', '.asst-station-log', '.asst-auto-title', '.asst-auto-summary', '.asst-auto-detail',
62
+ // Composers, once they hold text (an empty one keeps its placeholder sharp)
63
+ '#cp-input:not(:placeholder-shown)', '#cxp-input:not(:placeholder-shown)', '.ocpv2-compose-input:not(:placeholder-shown)',
64
+ '.asst-input:not(:placeholder-shown)',
65
+ // Terminal output (the WebGL canvas and the DOM renderer alike) and the terminal's browser tab
66
+ '.term-viewport .xterm-screen', '.term-float-tab-body .xterm', '.browser-canvas', '.browser-url-input', '.browser-title-label',
67
+ // Resume results
68
+ '.resume-session-prompt', '.resume-session-snippet', '.resume-continue-prompt', '.resume-project-name',
69
+ '.resume-project-badge', '.resume-branch',
70
+ // Account emails and names
71
+ '.acct-menu-trigger-name', '.acct-menu-item-name', '.acct-menu-item-id', '.acct-menu-avatar', '.acct-menu-note', '.acct-menu-v',
72
+ '.asst-account-label', '.asst-account-sub', '.asst-account-cred', '.as-launch-account-name',
73
+ '.asst-acct-name', '.asst-acct-line', '.asst-acct-avatar', '.asst-acct-value-text',
74
+ '.invite-key-box', '.invite-url-text', '.invite-key-status',
75
+ // Toast text
76
+ '.notif-toast-msg', '.term-toast', '#fe-toast', '.as-toast', '.ss-toast', '.cc-toast', '.asp-toast',
77
+ // Workspace names, generated image names
78
+ '.ws-row-name', '.ws-row-meta', '#ws-active-name', '.ig-card-name', '.ig-lb-name',
79
+ ];
80
+ const WHOLESALE = WHOLESALE_SELECTORS.join(',');
81
+
82
+ // The shared tooltip element shows a memory or session title for some rows and
83
+ // a control's name for the rest: it is blurred when what it describes is private.
84
+ const TOOLTIP_TEXT = '.ui-tooltip-text';
85
+
86
+ // ── Patterns ──────────────────────────────────────────────────────
87
+
88
+ const PHONE_PATTERNS = [
89
+ /(?:^|[^\w+])\+\d{1,3}[\s.-]?\(?\d{1,4}\)?(?:[\s.-]?\d{2,5}){2,4}\b/, // +55 11 9####-####, +1 415 555 0100
90
+ /\(\d{2,3}\)\s?\d{4,5}[\s.-]\d{4}\b/, // (11) 91234-5678
91
+ /\b\d{2}\s9\d{4}[\s-]\d{4}\b/, // 11 91234-5678
92
+ /\b\d{3}[\s.-]\d{3}[\s.-]\d{4}\b/, // 415-555-0100
93
+ ];
94
+
95
+ /**
96
+ * Patterns that name a checkout's own private work stay out of the published code: a checkout
97
+ * may list them in showcase-private-patterns.json beside this file, as [reason, source, flags,
98
+ * ownOnly] rows. A release has none, and the page's one request for it answers 404.
99
+ */
100
+ export function patternsFromRows(rows) {
101
+ return (Array.isArray(rows) ? rows : []).flatMap((row) => {
102
+ if (!Array.isArray(row) || typeof row[0] !== 'string' || typeof row[1] !== 'string') return [];
103
+ try { return [[row[0], new RegExp(row[1], typeof row[2] === 'string' ? row[2] : ''), row[3] === true]]; } catch { return []; }
104
+ });
105
+ }
106
+ const LOCAL_PATTERNS = typeof window === 'undefined' ? [] : await fetch(new URL('./showcase-private-patterns.json', import.meta.url))
107
+ .then((res) => (res.ok ? res.json() : []))
108
+ .then(patternsFromRows, () => []);
109
+
110
+ /**
111
+ * What the matcher looks for, besides the names it is given: [reason, test, ownOnly].
112
+ * ownOnly tests are skipped on the joined text of a few spans, where labels and numbers
113
+ * that sit side by side would read as one long key or a phone number.
114
+ */
115
+ export const PRIVATE_PATTERNS = [
116
+ ['email', /[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}/],
117
+ ['home-path', /(?:^|[^\w.-])\/(?:Users|home)\/[^/\s]+|\b[A-Za-z]:\\Users\\[^\\\s]+/],
118
+ ['uuid', /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i],
119
+ ['secret', /\b(?:sk|pk|rk)[-_][A-Za-z0-9_-]{12,}|\bgh[pousr]_[A-Za-z0-9]{16,}|\bgithub_pat_[A-Za-z0-9_]{20,}|\bxox[a-z]-[A-Za-z0-9-]{8,}|\bAIza[0-9A-Za-z_-]{20,}/],
120
+ ['secret', (text) => (text.match(/[A-Za-z0-9+=]{24,}/g) || []).some((run) => (run.match(/\d/g) || []).length >= 3 && (run.match(/[A-Za-z]/g) || []).length >= 3), true],
121
+ ['phone', (text) => PHONE_PATTERNS.some((re) => re.test(text)), true],
122
+ ...LOCAL_PATTERNS,
123
+ ];
124
+
125
+ const SELF_PROJECT = 'synabun';
126
+ const GENERIC_USER_NAMES = new Set(['user', 'users', 'admin', 'root', 'shared', 'home', 'guest', 'test', 'public']);
127
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
128
+ const squash = (s) => String(s || '').toLowerCase().replace(/[^a-z0-9]/g, '');
129
+
130
+ /** A path's last folder name ('/a/b/' → 'b', 'C:\\x\\y' → 'y'). */
131
+ function baseName(path) {
132
+ const parts = String(path || '').split(/[\\/]+/).filter(Boolean);
133
+ return parts.length ? parts[parts.length - 1] : '';
134
+ }
135
+
136
+ /**
137
+ * The names a registered-project list gives away: every project's label and folder
138
+ * name except SynaBun's own, and the OS user name read from the home folder in the paths.
139
+ * @param {Array<{path?: string, label?: string}>} projects GET /api/projects → projects
140
+ */
141
+ export function namesFromProjects(projects = []) {
142
+ const projectNames = new Set();
143
+ const userNames = new Set();
144
+ for (const p of Array.isArray(projects) ? projects : []) {
145
+ const path = String(p?.path || '');
146
+ const label = String(p?.label || '');
147
+ for (const name of [/[\\/]/.test(label) ? baseName(label) : label, baseName(path)]) {
148
+ const key = squash(name);
149
+ if (key.length >= 4 && key !== SELF_PROJECT) projectNames.add(name.trim());
150
+ }
151
+ const home = path.match(/^\/(?:Users|home)\/([^/]+)/) || path.match(/^[A-Za-z]:\\Users\\([^\\]+)/i);
152
+ if (home && home[1].length >= 3 && !GENERIC_USER_NAMES.has(home[1].toLowerCase())) userNames.add(home[1]);
153
+ }
154
+ return { projectNames: [...projectNames], userNames: [...userNames] };
155
+ }
156
+
157
+ /**
158
+ * A name as a pattern: its words (camelCase humps, '-', '_', '.', spaces) may be joined by
159
+ * '-', '_' or '.' or nothing ('SynabunWeb' finds "synabun-web"), never inside a longer word.
160
+ * A plain one-word name ('Downloads') must match its case, so ordinary prose stays clear.
161
+ */
162
+ function namePattern(name) {
163
+ const words = String(name).trim().split(/[\s_.-]+|(?<=[a-z0-9])(?=[A-Z])/).filter(Boolean);
164
+ if (!words.length) return null;
165
+ const plainWord = words.length === 1 && !/[A-Z].*[A-Z]/.test(name.slice(1));
166
+ const body = words.map(escapeRe).join('[_.-]?');
167
+ return new RegExp(`(?<![A-Za-z0-9])${body}(?![A-Za-z0-9])`, plainWord ? '' : 'i');
168
+ }
169
+
170
+ /**
171
+ * The private-text test. Returns the reason ('email', 'project-name'...) or null.
172
+ * @param {{projectNames?: string[], userNames?: string[], extraPatterns?: Array}} names extraPatterns: rows of patternsFromRows (the page's own are already in PRIVATE_PATTERNS)
173
+ */
174
+ export function createPrivacyMatcher({ projectNames = [], userNames = [], extraPatterns = [] } = {}) {
175
+ const named = [
176
+ ...projectNames.filter((n) => squash(n) !== SELF_PROJECT).map((n) => ['project-name', namePattern(n)]),
177
+ ...userNames.map((n) => ['user-name', new RegExp(`(?<![A-Za-z0-9])${escapeRe(n)}(?![A-Za-z0-9])`, 'i')]),
178
+ ].filter(([, re]) => re);
179
+ const checks = [...PRIVATE_PATTERNS, ...extraPatterns, ...named];
180
+ /** @param {{ joined?: boolean }} [options] joined: the text of several spans run together */
181
+ return function privateReason(text, { joined = false } = {}) {
182
+ const value = String(text ?? '');
183
+ if (value.trim().length < 3) return null;
184
+ for (const [reason, test, ownOnly] of checks) {
185
+ if (joined && ownOnly) continue;
186
+ if (typeof test === 'function' ? test(value) : test.test(value)) return reason;
187
+ }
188
+ return null;
189
+ };
190
+ }
191
+
192
+ // ── What an element shows ─────────────────────────────────────────
193
+
194
+ const SKIP_TAGS = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE', 'HTML', 'HEAD', 'BODY', 'IFRAME', 'CANVAS']);
195
+ const TEXTLESS_INPUTS = new Set(['hidden', 'checkbox', 'radio', 'range', 'color', 'file', 'image', 'submit', 'reset', 'button']);
196
+
197
+ /** The text of an element's own text nodes (not its children's). */
198
+ export function ownText(el) {
199
+ let text = '';
200
+ for (const node of el?.childNodes || []) if (node.nodeType === 3) text += node.data;
201
+ return text;
202
+ }
203
+
204
+ /** What a form field shows: its value (never a password's) and placeholder; a select's chosen option. */
205
+ export function fieldTexts(el) {
206
+ const tag = el?.tagName;
207
+ if (tag === 'SELECT') return [...(el.selectedOptions || [])].map((o) => o.textContent || '');
208
+ if (tag !== 'INPUT' && tag !== 'TEXTAREA') return [];
209
+ const type = String(el.type || 'text').toLowerCase();
210
+ if (tag === 'INPUT' && TEXTLESS_INPUTS.has(type)) return [];
211
+ return [type === 'password' ? '' : String(el.value || ''), String(el.placeholder || '')];
212
+ }
213
+
214
+ /** Why an element's own text, value or placeholder is private, or null. */
215
+ export function elementReason(el, matcher) {
216
+ if (!el || el.nodeType !== 1 || SKIP_TAGS.has(el.tagName)) return null;
217
+ return matcher([ownText(el), ...fieldTexts(el)].filter(Boolean).join('\n'));
218
+ }
219
+
220
+ // ── The page side ─────────────────────────────────────────────────
221
+
222
+ function styleText() {
223
+ // One rule per selector: a selector one engine rejects drops only its own rule.
224
+ return [...WHOLESALE_SELECTORS, `.${BLUR_CLASS}`, `.${MANUAL_CLASS}`]
225
+ .map((sel) => `${sel} { filter: ${BLUR} !important; }`).join('\n');
226
+ }
227
+
228
+ /**
229
+ * Turn showcase privacy on for this window (and its same-origin frames). Idempotent.
230
+ * @param {{ win?: Window, fetchProjects?: () => Promise<Array<{path:string,label:string}>> }} [options]
231
+ */
232
+ export function installShowcasePrivacy({ win = globalThis.window, fetchProjects } = {}) {
233
+ if (win.__synabunShowcase) return win.__synabunShowcase;
234
+ const docs = new Set();
235
+ let matcher = createPrivacyMatcher();
236
+ let namesLoaded = false;
237
+ let nameCount = 0;
238
+ let scans = 0;
239
+ let lastScanMs = 0;
240
+
241
+ const isCleared = (el) => el.hasAttribute(CLEAR_ATTR);
242
+ const setBlur = (el, on) => { if (el.classList.contains(BLUR_CLASS) !== on) el.classList.toggle(BLUR_CLASS, on); };
243
+
244
+ function hoverIsPrivate(doc, state) {
245
+ const hovered = state.lastHover;
246
+ if (!hovered || !hovered.isConnected) return false;
247
+ try {
248
+ if (hovered.closest(WHOLESALE) || hovered.querySelector(WHOLESALE)) return true;
249
+ } catch { /* a selector this engine rejects */ }
250
+ return !!matcher(hovered.getAttribute('data-tooltip') || '');
251
+ }
252
+
253
+ /** Is this element private by what it shows (or, for the tooltip, by what it describes)? */
254
+ function isPrivate(el, doc, state) {
255
+ if (isCleared(el)) return false;
256
+ if (elementReason(el, matcher)) return true;
257
+ return el.matches?.(TOOLTIP_TEXT) && hoverIsPrivate(doc, state);
258
+ }
259
+
260
+ // A small element's whole text (null for a container): a parent with many children is a list, not a split label.
261
+ function smallText(node, cache) {
262
+ if (cache?.has(node)) return cache.get(node);
263
+ let text = null;
264
+ if (node.childElementCount <= 12 && !SKIP_TAGS.has(node.tagName)) {
265
+ text = node.textContent || '';
266
+ if (text.length > SPAN_MAX) text = null;
267
+ }
268
+ cache?.set(node, text);
269
+ return text;
270
+ }
271
+
272
+ // Private text split over spans: the nearest small ancestor whose whole text is private.
273
+ function spanningAncestor(el, cache) {
274
+ let node = el?.parentElement;
275
+ for (let level = 0; node && level < SPAN_LEVELS; level++, node = node.parentElement) {
276
+ if (isCleared(node)) return null;
277
+ const text = smallText(node, cache);
278
+ if (text === null) return null;
279
+ if (matcher(text, { joined: true })) return node;
280
+ }
281
+ return null;
282
+ }
283
+
284
+ /** Still private, for an element that carries the blur class: by its own text or as a span hit. */
285
+ function stillPrivate(el, doc, state) {
286
+ if (isPrivate(el, doc, state)) return true;
287
+ const text = isCleared(el) ? null : smallText(el);
288
+ return text !== null && !!matcher(text, { joined: true });
289
+ }
290
+
291
+ /** The elements that should carry the blur class: root alone, or root and everything under it. */
292
+ function collect(root, doc, state, into, { walk = true } = {}) {
293
+ const cache = new Map();
294
+ const consider = (el) => {
295
+ if (!el || el.nodeType !== 1 || isCleared(el)) return;
296
+ if (isPrivate(el, doc, state)) { into.add(el); return; }
297
+ const up = ownText(el).trim() ? spanningAncestor(el, cache) : null;
298
+ if (up) into.add(up);
299
+ };
300
+ if (root.nodeType === 3) { consider(root.parentElement); return into; }
301
+ if (root.nodeType !== 1 && root.nodeType !== 9) return into;
302
+ const start = root.nodeType === 9 ? root.body : root;
303
+ if (!start) return into;
304
+ if (start.nodeType === 1) consider(start);
305
+ if (!walk) return into;
306
+ const walker = doc.createTreeWalker(start, 1 /* SHOW_ELEMENT */);
307
+ for (let el = walker.nextNode(); el; el = walker.nextNode()) consider(el);
308
+ return into;
309
+ }
310
+
311
+ function attachFrames(root) {
312
+ const frames = root.nodeType === 1 && root.tagName === 'IFRAME' ? [root] : [...(root.querySelectorAll?.('iframe') || [])];
313
+ for (const frame of frames) {
314
+ if (frame.__sbShowcase) continue;
315
+ frame.__sbShowcase = true;
316
+ const tryAttach = () => {
317
+ try {
318
+ const doc = frame.contentDocument;
319
+ if (doc && doc.readyState !== 'uninitialized' && doc.location?.href !== 'about:blank') attachDocument(doc);
320
+ } catch { /* another origin: nothing to reach */ }
321
+ };
322
+ frame.addEventListener('load', tryAttach);
323
+ tryAttach();
324
+ }
325
+ }
326
+
327
+ function attachDocument(doc) {
328
+ if (!doc || doc.__sbShowcaseState) return;
329
+ // pending: added nodes (walked); touched: elements whose own content changed (checked alone).
330
+ const state = { doc, pending: new Set(), touched: new Set(), timer: null, lastHover: null, observer: null };
331
+ doc.__sbShowcaseState = state;
332
+ docs.add(doc);
333
+ const style = doc.createElement('style');
334
+ style.id = STYLE_ID;
335
+ style.textContent = styleText();
336
+ (doc.head || doc.documentElement).appendChild(style);
337
+
338
+ const flush = () => {
339
+ state.timer = null;
340
+ const added = [...state.pending];
341
+ const touched = [...state.touched];
342
+ state.pending.clear();
343
+ state.touched.clear();
344
+ const hits = new Set();
345
+ const seen = new Set();
346
+ const note = (node) => {
347
+ // What lost its private text loses the blur: the node and the small ancestors a span hit may sit on.
348
+ let el = node.nodeType === 1 ? node : node.parentElement;
349
+ for (let level = 0; el && level <= SPAN_LEVELS; level++, el = el.parentElement) seen.add(el);
350
+ };
351
+ for (const node of added) {
352
+ if (!node.isConnected) continue;
353
+ collect(node, doc, state, hits);
354
+ note(node);
355
+ if (node.nodeType === 1) {
356
+ attachFrames(node);
357
+ for (const marked of node.querySelectorAll(`.${BLUR_CLASS}`)) seen.add(marked);
358
+ }
359
+ }
360
+ for (const node of touched) {
361
+ if (!node.isConnected) continue;
362
+ collect(node, doc, state, hits, { walk: false });
363
+ note(node);
364
+ }
365
+ for (const el of seen) if (el.classList?.contains(BLUR_CLASS) && !hits.has(el) && !stillPrivate(el, doc, state)) setBlur(el, false);
366
+ for (const el of hits) setBlur(el, true);
367
+ };
368
+ const schedule = () => { if (!state.timer) state.timer = doc.defaultView.setTimeout(flush, DEBOUNCE_MS); };
369
+ const queue = (node) => { if (node) { state.pending.add(node); schedule(); } };
370
+ const touch = (node) => { if (node) { state.touched.add(node); schedule(); } };
371
+
372
+ state.observer = new doc.defaultView.MutationObserver((records) => {
373
+ for (const r of records) {
374
+ if (r.type === 'childList') {
375
+ for (const n of r.addedNodes) queue(n);
376
+ if (r.removedNodes.length) touch(r.target);
377
+ } else {
378
+ touch(r.target);
379
+ }
380
+ }
381
+ });
382
+ state.observer.observe(doc.documentElement, {
383
+ childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['value', 'placeholder'],
384
+ });
385
+ const onField = (e) => { if (e.target?.nodeType === 1) touch(e.target); };
386
+ doc.addEventListener('input', onField, true);
387
+ doc.addEventListener('change', onField, true);
388
+ doc.addEventListener('mouseover', (e) => {
389
+ state.lastHover = e.target?.closest?.('[data-tooltip]') || null;
390
+ }, true);
391
+
392
+ scanDocument(doc);
393
+ attachFrames(doc);
394
+ }
395
+
396
+ function liveDocs() {
397
+ for (const doc of docs) if (!doc.defaultView || !doc.documentElement?.isConnected) docs.delete(doc);
398
+ return [...docs];
399
+ }
400
+
401
+ function scanDocument(doc) {
402
+ const state = doc.__sbShowcaseState;
403
+ const hits = collect(doc, doc, state, new Set());
404
+ for (const el of doc.querySelectorAll(`.${BLUR_CLASS}`)) if (!hits.has(el)) setBlur(el, false);
405
+ for (const el of hits) setBlur(el, true);
406
+ }
407
+
408
+ function blurredIn(doc) {
409
+ const out = new Set();
410
+ for (const sel of [...WHOLESALE_SELECTORS, `.${BLUR_CLASS}`, `.${MANUAL_CLASS}`]) {
411
+ try { for (const el of doc.querySelectorAll(sel)) if (!isCleared(el)) out.add(el); } catch { /* rejected selector */ }
412
+ }
413
+ return out;
414
+ }
415
+
416
+ function each(selector, fn) {
417
+ if (typeof selector !== 'string' || !selector.trim()) throw new Error('showcase: a CSS selector is needed');
418
+ let count = 0;
419
+ for (const doc of liveDocs()) {
420
+ let found;
421
+ try { found = doc.querySelectorAll(selector); } catch { throw new Error(`showcase: invalid selector ${selector}`); }
422
+ for (const el of found) { fn(el); count++; }
423
+ }
424
+ return count;
425
+ }
426
+
427
+ const api = Object.freeze({
428
+ /** Scan every page and frame again from scratch; returns how many elements are blurred. */
429
+ scan() {
430
+ const started = Date.now();
431
+ for (const doc of liveDocs()) attachFrames(doc);
432
+ let total = 0;
433
+ for (const doc of liveDocs()) { scanDocument(doc); total += blurredIn(doc).size; }
434
+ scans++;
435
+ lastScanMs = Date.now() - started;
436
+ return total;
437
+ },
438
+ /** Blur what the selector matches (also undoes an unblur); returns how many. */
439
+ blur(selector) {
440
+ return each(selector, (el) => {
441
+ el.removeAttribute(CLEAR_ATTR);
442
+ el.style.removeProperty('filter');
443
+ el.classList.add(MANUAL_CLASS);
444
+ });
445
+ },
446
+ /** Keep what the selector matches sharp, whatever the rules say; returns how many. */
447
+ unblur(selector) {
448
+ return each(selector, (el) => {
449
+ el.classList.remove(BLUR_CLASS, MANUAL_CLASS);
450
+ el.setAttribute(CLEAR_ATTR, '');
451
+ el.style.setProperty('filter', 'none', 'important');
452
+ });
453
+ },
454
+ /** Counts only: never the private text or names themselves. */
455
+ stats() {
456
+ const all = liveDocs();
457
+ const count = (sel) => all.reduce((n, doc) => n + doc.querySelectorAll(sel).length, 0);
458
+ return {
459
+ documents: all.length,
460
+ blurred: all.reduce((n, doc) => n + blurredIn(doc).size, 0),
461
+ matched: count(`.${BLUR_CLASS}`),
462
+ manual: count(`.${MANUAL_CLASS}`),
463
+ cleared: count(`[${CLEAR_ATTR}]`),
464
+ namesLoaded,
465
+ names: nameCount,
466
+ scans,
467
+ lastScanMs,
468
+ };
469
+ },
470
+ });
471
+ win.__synabunShowcase = api;
472
+
473
+ attachDocument(win.document);
474
+
475
+ // The registered projects name the user's other work and, by their paths, the OS user.
476
+ const load = fetchProjects || (async () => {
477
+ const res = await win.fetch('/api/projects');
478
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
479
+ return (await res.json())?.projects || [];
480
+ });
481
+ Promise.resolve().then(load).then((projects) => {
482
+ const names = namesFromProjects(projects);
483
+ matcher = createPrivacyMatcher(names);
484
+ nameCount = names.projectNames.length + names.userNames.length;
485
+ namesLoaded = true;
486
+ api.scan();
487
+ }).catch((err) => console.warn('[showcase] project names unavailable, pattern blur only:', err?.message || err));
488
+
489
+ return api;
490
+ }
@@ -33,6 +33,27 @@ const FAT_BLOB_KEYS = new Set([
33
33
  // as a string and still needs JSON.parse for the server payload.
34
34
  const STR_SENTINEL = Symbol('storage:str');
35
35
 
36
+ // Showcase mode: a page opened with `?showcase` (any value but "0" or "false")
37
+ // hydrates as usual but never writes back, so what a demo walk-through opens,
38
+ // moves or types leaves the user's saved UI state as it was. Reads work from
39
+ // the in-memory cache. (blob-store.js keeps its own writes.)
40
+ function _readShowcaseMode() {
41
+ try {
42
+ const params = new URLSearchParams(globalThis.window?.location?.search || '');
43
+ if (!params.has('showcase')) return false;
44
+ const value = String(params.get('showcase')).trim().toLowerCase();
45
+ return value !== '0' && value !== 'false';
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+ const SHOWCASE_MODE = _readShowcaseMode();
51
+
52
+ /** True when this page load is a showcase (`?showcase`): nothing is written back. */
53
+ export function isShowcaseMode() {
54
+ return SHOWCASE_MODE;
55
+ }
56
+
36
57
  // ── Hydrate from server ──────────────────────────────
37
58
 
38
59
  async function _hydrate() {
@@ -55,8 +76,8 @@ async function _hydrate() {
55
76
  // First run or server down: import from localStorage
56
77
  console.info('[storage] Migrating localStorage to server...');
57
78
  _migrateFromLocalStorage();
58
- // Build dirty set from everything we imported
59
- for (const [key, val] of Object.entries(_cache)) {
79
+ // Build dirty set from everything we imported (a showcase only reads it)
80
+ if (!SHOWCASE_MODE) for (const [key, val] of Object.entries(_cache)) {
60
81
  try { _dirty[key] = JSON.parse(val); } catch { _dirty[key] = val; }
61
82
  }
62
83
  if (Object.keys(_dirty).length > 0) {
@@ -74,6 +95,10 @@ else await _hydrate();
74
95
  // ── Drop-in API (mirrors localStorage) ───────────────
75
96
 
76
97
  export const storage = (hostedSidepanelId && sidepanelHost()?.storageFor(hostedSidepanelId)) || {
98
+ // False in showcase mode: values stay in this page. Code that sends a value to the server
99
+ // by itself (an unload beacon) asks this first.
100
+ persists: !SHOWCASE_MODE,
101
+
77
102
  getItem(key) {
78
103
  return _cache[key] ?? null;
79
104
  },
@@ -105,11 +130,13 @@ export const storage = (hostedSidepanelId && sidepanelHost()?.storageFor(hostedS
105
130
  // ── Debounced flush to server ────────────────────────
106
131
 
107
132
  function _scheduleDebouncedFlush() {
133
+ if (SHOWCASE_MODE) { _dirty = {}; return; }
108
134
  if (_flushTimer) clearTimeout(_flushTimer);
109
135
  _flushTimer = setTimeout(_flushToServer, DEBOUNCE_MS);
110
136
  }
111
137
 
112
138
  async function _flushToServer() {
139
+ if (SHOWCASE_MODE) { _dirty = {}; _flushTimer = null; return; }
113
140
  const payload = {};
114
141
  for (const [k, v] of Object.entries(_dirty)) {
115
142
  if (v === STR_SENTINEL) {
@@ -142,7 +169,7 @@ async function _flushToServer() {
142
169
 
143
170
  // ── Flush on page unload (sendBeacon for reliability) ──
144
171
 
145
- window.addEventListener('beforeunload', () => {
172
+ if (!SHOWCASE_MODE) window.addEventListener('beforeunload', () => {
146
173
  if (_flushTimer) {
147
174
  clearTimeout(_flushTimer);
148
175
  _flushTimer = null;