@turmind/halo 0.1.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 (296) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/admin-out/404.html +1 -0
  4. package/admin-out/_next/static/IBP_mwBDf-cPwZSZZGpi8/_buildManifest.js +1 -0
  5. package/admin-out/_next/static/IBP_mwBDf-cPwZSZZGpi8/_ssgManifest.js +1 -0
  6. package/admin-out/_next/static/chunks/1d2d5650-810a3ca1ae20cab5.js +18 -0
  7. package/admin-out/_next/static/chunks/200.3b8f8a74f005a134.js +1 -0
  8. package/admin-out/_next/static/chunks/220.3dc41cd6269b88e6.js +1 -0
  9. package/admin-out/_next/static/chunks/298-d6b224b569da4de0.js +1 -0
  10. package/admin-out/_next/static/chunks/304eda7d.b1ee7f523653e027.js +1 -0
  11. package/admin-out/_next/static/chunks/330.3b784cf0dcd69163.js +1 -0
  12. package/admin-out/_next/static/chunks/442.f4302087bddb1ec1.js +1 -0
  13. package/admin-out/_next/static/chunks/443.d1b289781793f8de.js +25 -0
  14. package/admin-out/_next/static/chunks/503-7c6d9935df6e0cea.js +1 -0
  15. package/admin-out/_next/static/chunks/533.5f2d4bfe3d076095.js +1 -0
  16. package/admin-out/_next/static/chunks/579.a2718ddd395a2f3a.js +1 -0
  17. package/admin-out/_next/static/chunks/635-3dd0043ed0520b9d.js +1 -0
  18. package/admin-out/_next/static/chunks/65.859665ba6717967a.js +1 -0
  19. package/admin-out/_next/static/chunks/661.a4fe93f3f2a5e871.js +1 -0
  20. package/admin-out/_next/static/chunks/688.4e014ac6cfc7e152.js +1 -0
  21. package/admin-out/_next/static/chunks/868.0c95dfe283400a00.js +1 -0
  22. package/admin-out/_next/static/chunks/app/_not-found/page-8e35399c8fd5016d.js +1 -0
  23. package/admin-out/_next/static/chunks/app/layout-58f773336e6fce61.js +1 -0
  24. package/admin-out/_next/static/chunks/app/page-cc8043335282d745.js +1 -0
  25. package/admin-out/_next/static/chunks/db534642.09d3c650ff587175.js +1 -0
  26. package/admin-out/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js +1 -0
  27. package/admin-out/_next/static/chunks/framework-c3031ae54a63fe16.js +1 -0
  28. package/admin-out/_next/static/chunks/main-14b48e3c2588354f.js +1 -0
  29. package/admin-out/_next/static/chunks/main-app-483b436873410ddc.js +1 -0
  30. package/admin-out/_next/static/chunks/pages/_app-b2fc6e5f920d1b1a.js +1 -0
  31. package/admin-out/_next/static/chunks/pages/_error-f3696a8f3d67d150.js +1 -0
  32. package/admin-out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  33. package/admin-out/_next/static/chunks/webpack-8bd7ef88a6527feb.js +1 -0
  34. package/admin-out/_next/static/css/659eccb5db697b76.css +1 -0
  35. package/admin-out/_next/static/css/8fce4e1a54dac12a.css +3 -0
  36. package/admin-out/_next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  37. package/admin-out/_next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  38. package/admin-out/_next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  39. package/admin-out/_next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  40. package/admin-out/_next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  41. package/admin-out/_next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  42. package/admin-out/_next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  43. package/admin-out/favicon.ico +0 -0
  44. package/admin-out/icon.svg +18 -0
  45. package/admin-out/index.html +1 -0
  46. package/admin-out/index.txt +23 -0
  47. package/admin-out/monaco/vs/_commonjsHelpers-CT9FvmAN.js +1 -0
  48. package/admin-out/monaco/vs/abap-D-t0cyap.js +1 -0
  49. package/admin-out/monaco/vs/apex-CcIm7xu6.js +1 -0
  50. package/admin-out/monaco/vs/assets/css.worker-HnVq6Ewq.js +93 -0
  51. package/admin-out/monaco/vs/assets/editor.worker-Be8ye1pW.js +26 -0
  52. package/admin-out/monaco/vs/assets/html.worker-B51mlPHg.js +470 -0
  53. package/admin-out/monaco/vs/assets/json.worker-DKiEKt88.js +58 -0
  54. package/admin-out/monaco/vs/assets/ts.worker-CMbG-7ft.js +67731 -0
  55. package/admin-out/monaco/vs/azcli-BA0tQDCg.js +1 -0
  56. package/admin-out/monaco/vs/basic-languages/monaco.contribution.js +1 -0
  57. package/admin-out/monaco/vs/bat-C397hTD6.js +1 -0
  58. package/admin-out/monaco/vs/bicep-DF5aW17k.js +2 -0
  59. package/admin-out/monaco/vs/cameligo-plsz8qhj.js +1 -0
  60. package/admin-out/monaco/vs/clojure-Y2auQMzK.js +1 -0
  61. package/admin-out/monaco/vs/coffee-Bu45yuWE.js +1 -0
  62. package/admin-out/monaco/vs/cpp-CkKPQIni.js +1 -0
  63. package/admin-out/monaco/vs/csharp-CX28MZyh.js +1 -0
  64. package/admin-out/monaco/vs/csp-D8uWnyxW.js +1 -0
  65. package/admin-out/monaco/vs/css-CaeNmE3S.js +3 -0
  66. package/admin-out/monaco/vs/cssMode-CjiAH6dQ.js +1 -0
  67. package/admin-out/monaco/vs/cypher-DVThT8BS.js +1 -0
  68. package/admin-out/monaco/vs/dart-CmGfCvrO.js +1 -0
  69. package/admin-out/monaco/vs/dockerfile-CZqqYdch.js +1 -0
  70. package/admin-out/monaco/vs/ecl-30fUercY.js +1 -0
  71. package/admin-out/monaco/vs/editor/editor.main.css +1 -0
  72. package/admin-out/monaco/vs/editor/editor.main.js +5 -0
  73. package/admin-out/monaco/vs/editor.api-CalNCsUg.js +903 -0
  74. package/admin-out/monaco/vs/elixir-xjPaIfzF.js +1 -0
  75. package/admin-out/monaco/vs/flow9-DqtmStfK.js +1 -0
  76. package/admin-out/monaco/vs/freemarker2-Cz_sV6Md.js +3 -0
  77. package/admin-out/monaco/vs/fsharp-BOMdg4U1.js +1 -0
  78. package/admin-out/monaco/vs/go-D_hbi-Jt.js +1 -0
  79. package/admin-out/monaco/vs/graphql-CKUU4kLG.js +1 -0
  80. package/admin-out/monaco/vs/handlebars-OwglfO-1.js +1 -0
  81. package/admin-out/monaco/vs/hcl-DTaboeZW.js +1 -0
  82. package/admin-out/monaco/vs/html-Pa1xEWsY.js +1 -0
  83. package/admin-out/monaco/vs/htmlMode-Bz67EXwp.js +1 -0
  84. package/admin-out/monaco/vs/ini-CsNwO04R.js +1 -0
  85. package/admin-out/monaco/vs/java-CI4ZMsH9.js +1 -0
  86. package/admin-out/monaco/vs/javascript-PczUCGdz.js +1 -0
  87. package/admin-out/monaco/vs/jsonMode-DULH5oaX.js +7 -0
  88. package/admin-out/monaco/vs/julia-BwzEvaQw.js +1 -0
  89. package/admin-out/monaco/vs/kotlin-IUYPiTV8.js +1 -0
  90. package/admin-out/monaco/vs/language/css/monaco.contribution.js +1 -0
  91. package/admin-out/monaco/vs/language/html/monaco.contribution.js +1 -0
  92. package/admin-out/monaco/vs/language/json/monaco.contribution.js +1 -0
  93. package/admin-out/monaco/vs/language/typescript/monaco.contribution.js +1 -0
  94. package/admin-out/monaco/vs/less-C0eDYdqa.js +2 -0
  95. package/admin-out/monaco/vs/lexon-iON-Kj97.js +1 -0
  96. package/admin-out/monaco/vs/liquid-DqKjdPGy.js +1 -0
  97. package/admin-out/monaco/vs/loader.js +1368 -0
  98. package/admin-out/monaco/vs/lspLanguageFeatures-kM9O9rjY.js +4 -0
  99. package/admin-out/monaco/vs/lua-DtygF91M.js +1 -0
  100. package/admin-out/monaco/vs/m3-CsR4AuFi.js +1 -0
  101. package/admin-out/monaco/vs/markdown-C_rD0bIw.js +1 -0
  102. package/admin-out/monaco/vs/mdx-DEWtB1K5.js +1 -0
  103. package/admin-out/monaco/vs/mips-CiYP61RB.js +1 -0
  104. package/admin-out/monaco/vs/monaco.contribution-D2OdxNBt.js +1 -0
  105. package/admin-out/monaco/vs/monaco.contribution-DO3azKX8.js +1 -0
  106. package/admin-out/monaco/vs/monaco.contribution-EcChJV6a.js +1 -0
  107. package/admin-out/monaco/vs/monaco.contribution-qLAYrEOP.js +1 -0
  108. package/admin-out/monaco/vs/msdax-C38-sJlp.js +1 -0
  109. package/admin-out/monaco/vs/mysql-CdtbpvbG.js +1 -0
  110. package/admin-out/monaco/vs/nls.messages-loader.js +1 -0
  111. package/admin-out/monaco/vs/nls.messages.cs.js.js +17 -0
  112. package/admin-out/monaco/vs/nls.messages.de.js.js +17 -0
  113. package/admin-out/monaco/vs/nls.messages.es.js.js +17 -0
  114. package/admin-out/monaco/vs/nls.messages.fr.js.js +15 -0
  115. package/admin-out/monaco/vs/nls.messages.it.js.js +15 -0
  116. package/admin-out/monaco/vs/nls.messages.ja.js.js +17 -0
  117. package/admin-out/monaco/vs/nls.messages.js.js +10 -0
  118. package/admin-out/monaco/vs/nls.messages.ko.js.js +25 -0
  119. package/admin-out/monaco/vs/nls.messages.pl.js.js +17 -0
  120. package/admin-out/monaco/vs/nls.messages.pt-br.js.js +6 -0
  121. package/admin-out/monaco/vs/nls.messages.ru.js.js +17 -0
  122. package/admin-out/monaco/vs/nls.messages.tr.js.js +15 -0
  123. package/admin-out/monaco/vs/nls.messages.zh-cn.js.js +17 -0
  124. package/admin-out/monaco/vs/nls.messages.zh-tw.js.js +15 -0
  125. package/admin-out/monaco/vs/objective-c-CntZFaHX.js +1 -0
  126. package/admin-out/monaco/vs/pascal-r6kuqfl_.js +1 -0
  127. package/admin-out/monaco/vs/pascaligo-BiXoTmXh.js +1 -0
  128. package/admin-out/monaco/vs/perl-DABw_TcH.js +1 -0
  129. package/admin-out/monaco/vs/pgsql-me_jFXeX.js +1 -0
  130. package/admin-out/monaco/vs/php-D_kh-9LK.js +1 -0
  131. package/admin-out/monaco/vs/pla-VfZjczW0.js +1 -0
  132. package/admin-out/monaco/vs/postiats-BBSzz8Pk.js +1 -0
  133. package/admin-out/monaco/vs/powerquery-Dt-g_2cc.js +1 -0
  134. package/admin-out/monaco/vs/powershell-B-7ap1zc.js +1 -0
  135. package/admin-out/monaco/vs/protobuf-BmtuEB1A.js +2 -0
  136. package/admin-out/monaco/vs/pug-BRpRNeEb.js +1 -0
  137. package/admin-out/monaco/vs/python-Cr0UkIbn.js +1 -0
  138. package/admin-out/monaco/vs/qsharp-BzsFaUU9.js +1 -0
  139. package/admin-out/monaco/vs/r-f8dDdrp4.js +1 -0
  140. package/admin-out/monaco/vs/razor-BYAHOTkz.js +1 -0
  141. package/admin-out/monaco/vs/redis-fvZQY4PI.js +1 -0
  142. package/admin-out/monaco/vs/redshift-45Et0LQi.js +1 -0
  143. package/admin-out/monaco/vs/restructuredtext-C7UUFKFD.js +1 -0
  144. package/admin-out/monaco/vs/ruby-CZO8zYTz.js +1 -0
  145. package/admin-out/monaco/vs/rust-Bfetafyc.js +1 -0
  146. package/admin-out/monaco/vs/sb-3GYllVck.js +1 -0
  147. package/admin-out/monaco/vs/scala-foMgrKo1.js +1 -0
  148. package/admin-out/monaco/vs/scheme-CHdMtr7p.js +1 -0
  149. package/admin-out/monaco/vs/scss-C1cmLt9V.js +3 -0
  150. package/admin-out/monaco/vs/shell-ClXCKCEW.js +1 -0
  151. package/admin-out/monaco/vs/solidity-MZ6ExpPy.js +1 -0
  152. package/admin-out/monaco/vs/sophia-DWkuSsPQ.js +1 -0
  153. package/admin-out/monaco/vs/sparql-AUGFYSyk.js +1 -0
  154. package/admin-out/monaco/vs/sql-32GpJSV2.js +1 -0
  155. package/admin-out/monaco/vs/st-CuDFIVZ_.js +1 -0
  156. package/admin-out/monaco/vs/swift-n-2HociN.js +3 -0
  157. package/admin-out/monaco/vs/systemverilog-Ch4vA8Yt.js +1 -0
  158. package/admin-out/monaco/vs/tcl-D74tq1nH.js +1 -0
  159. package/admin-out/monaco/vs/tsMode-CZz1Umrk.js +11 -0
  160. package/admin-out/monaco/vs/twig-C6taOxMV.js +1 -0
  161. package/admin-out/monaco/vs/typescript-DfOrAzoV.js +1 -0
  162. package/admin-out/monaco/vs/typespec-D-PIh9Xw.js +1 -0
  163. package/admin-out/monaco/vs/vb-Dyb2648j.js +1 -0
  164. package/admin-out/monaco/vs/wgsl-BhLXMOR0.js +298 -0
  165. package/admin-out/monaco/vs/workers-DcJshg-q.js +1 -0
  166. package/admin-out/monaco/vs/xml-CdsdnY8S.js +1 -0
  167. package/admin-out/monaco/vs/yaml-DYGvmE88.js +1 -0
  168. package/bin/halo.js +9 -0
  169. package/bundled-docs/design/agent.md +145 -0
  170. package/bundled-docs/design/architecture.md +270 -0
  171. package/bundled-docs/design/background-dispatch.md +151 -0
  172. package/bundled-docs/design/command.md +58 -0
  173. package/bundled-docs/design/cron.md +88 -0
  174. package/bundled-docs/design/evolution.md +383 -0
  175. package/bundled-docs/design/express-self.md +145 -0
  176. package/bundled-docs/design/feishu.md +224 -0
  177. package/bundled-docs/design/halo-city.md +112 -0
  178. package/bundled-docs/design/prompt-system.md +411 -0
  179. package/bundled-docs/design/session.md +321 -0
  180. package/bundled-docs/design/slack.md +212 -0
  181. package/bundled-docs/design/storage.md +578 -0
  182. package/bundled-docs/design/telegram.md +161 -0
  183. package/bundled-docs/design/web.md +222 -0
  184. package/bundled-docs/design/wechat.md +146 -0
  185. package/bundled-docs/design/ws.md +144 -0
  186. package/bundled-docs/dev/acp-adapter.md +337 -0
  187. package/bundled-docs/dev/add-channel.md +376 -0
  188. package/bundled-docs/dev/add-model-provider.md +386 -0
  189. package/bundled-docs/dev/api.md +686 -0
  190. package/bundled-docs/dev/deploy.md +198 -0
  191. package/bundled-docs/dev/desktop-packaging.md +327 -0
  192. package/bundled-docs/dev/env.md +158 -0
  193. package/bundled-docs/dev/logging.md +85 -0
  194. package/bundled-docs/dev/previews.md +225 -0
  195. package/bundled-docs/dev/tools.md +331 -0
  196. package/bundled-docs/guide/README.md +29 -0
  197. package/bundled-docs/guide/agents.md +264 -0
  198. package/bundled-docs/guide/channels/README.md +62 -0
  199. package/bundled-docs/guide/channels/acp.md +150 -0
  200. package/bundled-docs/guide/channels/feishu.md +154 -0
  201. package/bundled-docs/guide/channels/slack.md +156 -0
  202. package/bundled-docs/guide/channels/telegram.md +112 -0
  203. package/bundled-docs/guide/channels/web.md +149 -0
  204. package/bundled-docs/guide/channels/wechat.md +110 -0
  205. package/bundled-docs/guide/chat.md +112 -0
  206. package/bundled-docs/guide/cli.md +197 -0
  207. package/bundled-docs/guide/getting-started.md +93 -0
  208. package/bundled-docs/guide/secrets-and-credentials.md +161 -0
  209. package/bundled-docs/guide/sessions.md +104 -0
  210. package/bundled-docs/guide/skills.md +302 -0
  211. package/bundled-docs/guide/testing-agents-and-skills.md +136 -0
  212. package/bundled-docs/guide/workspace.md +131 -0
  213. package/bundled-docs/plans/cron-tasks.md +362 -0
  214. package/bundled-docs/plans/design-suggestions.md +203 -0
  215. package/bundled-docs/plans/issue-session-deep-nesting.md +208 -0
  216. package/bundled-docs/plans/launch-plan.md +111 -0
  217. package/bundled-docs/plans/md-and-agent-refactor.md +197 -0
  218. package/bundled-docs/plans/remote-mac-worker.md +196 -0
  219. package/bundled-docs/plans/roadmap.md +40 -0
  220. package/bundled-docs/plans/self-evolution.md +1379 -0
  221. package/bundled-docs/plans/todo.md +32 -0
  222. package/bundled-docs/plans/weixin-channel.md +255 -0
  223. package/bundled-docs/requirements/agent.md +92 -0
  224. package/bundled-docs/requirements/chat.md +81 -0
  225. package/bundled-docs/requirements/command.md +130 -0
  226. package/bundled-docs/requirements/editor.md +82 -0
  227. package/bundled-docs/requirements/explorer.md +89 -0
  228. package/bundled-docs/requirements/overview.md +62 -0
  229. package/bundled-docs/requirements/session.md +73 -0
  230. package/bundled-docs/requirements/settings.md +224 -0
  231. package/bundled-docs/requirements/skill.md +125 -0
  232. package/bundled-docs/requirements/source-control.md +82 -0
  233. package/bundled-docs/requirements/terminal.md +118 -0
  234. package/bundled-docs/requirements/workspace.md +80 -0
  235. package/bundled-docs/test/docs-seeding.md +101 -0
  236. package/bundled-docs/test/session.md +374 -0
  237. package/dist/index.js +26750 -0
  238. package/package.json +67 -0
  239. package/templates/INSTRUCTIONS.md +25 -0
  240. package/templates/agents/__apply_agent__/AGENT.md +229 -0
  241. package/templates/agents/__apply_agent__/agent.yaml +30 -0
  242. package/templates/agents/__evo_agent__/AGENT.md +323 -0
  243. package/templates/agents/__evo_agent__/agent.yaml +29 -0
  244. package/templates/agents/__score__/AGENT.md +177 -0
  245. package/templates/agents/__score__/agent.yaml +29 -0
  246. package/templates/agents/deep-executor/AGENT.md +60 -0
  247. package/templates/agents/deep-executor/agent.yaml +25 -0
  248. package/templates/agents/default/AGENT.md +38 -0
  249. package/templates/agents/default/agent.yaml +51 -0
  250. package/templates/agents/executor/AGENT.md +38 -0
  251. package/templates/agents/executor/agent.yaml +25 -0
  252. package/templates/aliases.yaml +26 -0
  253. package/templates/canvas/self.html +660 -0
  254. package/templates/config.yaml +69 -0
  255. package/templates/models/anthropic.yaml +47 -0
  256. package/templates/models/aws-bedrock-claude-invoke.yaml +156 -0
  257. package/templates/models/aws-bedrock-mantle.yaml +89 -0
  258. package/templates/models/deepseek.yaml +62 -0
  259. package/templates/models/doubao.yaml +137 -0
  260. package/templates/models/hunyuan.yaml +56 -0
  261. package/templates/models/kimi.yaml +41 -0
  262. package/templates/models/mimo-token-plan-china.yaml +61 -0
  263. package/templates/models/minimax.yaml +80 -0
  264. package/templates/models/openai.yaml +51 -0
  265. package/templates/models/qwen.yaml +78 -0
  266. package/templates/optional-skills/tavily-web-search/SKILL.md +91 -0
  267. package/templates/optional-skills/tavily-web-search/config.yaml +10 -0
  268. package/templates/prompts/all/TOOL_GUIDELINES.md +35 -0
  269. package/templates/prompts/all/TOOL_SHELL.md +4 -0
  270. package/templates/prompts/all/TOOL_SHELL.windows.md +7 -0
  271. package/templates/prompts/bootstrap/BOOTSTRAP.md +26 -0
  272. package/templates/prompts/root/WORKSPACE_MEMORY.md +11 -0
  273. package/templates/schema.sql +30 -0
  274. package/templates/skills/acp/SKILL.md +236 -0
  275. package/templates/skills/acp/templates/SKILL.md.tmpl +74 -0
  276. package/templates/skills/acp/templates/ask.py +176 -0
  277. package/templates/skills/acp/templates/config.yaml.tmpl +33 -0
  278. package/templates/skills/agent/SKILL.md +128 -0
  279. package/templates/skills/aws-knowledge/SKILL.md +82 -0
  280. package/templates/skills/aws-knowledge/scripts/aws_kb.py +61 -0
  281. package/templates/skills/cron/SKILL.md +217 -0
  282. package/templates/skills/cron/manage-cron.py +434 -0
  283. package/templates/skills/halo/SKILL.md +98 -0
  284. package/templates/skills/nova-web-search/SKILL.md +65 -0
  285. package/templates/skills/self/SKILL.md +139 -0
  286. package/templates/skills/send-file/SKILL.md +28 -0
  287. package/templates/skills/send-file/feishu.md +16 -0
  288. package/templates/skills/send-file/slack.md +7 -0
  289. package/templates/skills/send-file/telegram.md +9 -0
  290. package/templates/skills/send-file/web.md +6 -0
  291. package/templates/skills/send-file/wechat.md +8 -0
  292. package/templates/skills/skill/SKILL.md +131 -0
  293. package/templates/skills/workspace/SKILL.md +28 -0
  294. package/templates/skills/workspace/setup-tidy.md +265 -0
  295. package/templates/skills/workspace/share.md +187 -0
  296. package/templates/skills/workspace/stage.py +487 -0
@@ -0,0 +1,198 @@
1
+ # Deployment
2
+
3
+ ## Architecture overview
4
+
5
+ Halo only needs **one Node process** (Hono on port 9527 by default). API, WebSocket, and static frontend live in the same process.
6
+
7
+ ```
8
+ Browser ──────────────▶ Hono (:9527)
9
+ ├── /api/* → API routes
10
+ ├── /ws → WebSocket (chat + terminal)
11
+ └── /* → Static files (packages/admin/out/)
12
+ ```
13
+
14
+ Next.js is build-time only (`next build` → static export to `out/`) — no Next.js process at runtime.
15
+
16
+ Nginx is **not required**. Use it only for domain routing, SSL termination, or when the port is shared with other services.
17
+
18
+ ## Prerequisites
19
+
20
+ - Node.js ≥ 22
21
+ - pnpm ≥ 9
22
+ - AWS credentials configured (`~/.aws/credentials` or env vars) with Bedrock access
23
+
24
+ ## 1. Install dependencies and build
25
+
26
+ ```bash
27
+ cd /path/to/halo
28
+ pnpm install
29
+
30
+ pnpm --filter @turmind/halo-core build
31
+ pnpm --filter @turmind/halo-server build
32
+ pnpm --filter @turmind/halo-admin build # next build + copy-monaco; never a bare next build (Monaco would 404)
33
+ ```
34
+
35
+ ## 2. Create the runtime directory
36
+
37
+ ```bash
38
+ mkdir -p /path/to/halo/data
39
+ ```
40
+
41
+ SQLite databases are created automatically under `data/` on first start.
42
+
43
+ ## 3. Run `halo setup`
44
+
45
+ Seeds `~/.halo/global/` with templates (agents, skills, prompts, models, docs), creates `secrets/config.yaml`, and walks you through password / port / model API keys / optional skills.
46
+
47
+ ```bash
48
+ halo setup # interactive — picks up arrow-key UI on TTYs
49
+ ```
50
+
51
+ Re-run any time to change the password, refresh model keys, or toggle optional skills. Built-in agent / skill files are force-overwritten on every run; user-created agents and skills are left alone.
52
+
53
+ For Docker / CI builds where stdin isn't a TTY, see the **Docker** section below.
54
+
55
+ ## 4. Environment variables (optional)
56
+
57
+ Full list in [env.md](env.md). The most common one is `HALO_PASSWORD`, which acts as a plaintext password and bypasses the scrypt hash stored by `halo setup`. Use it when an external secret store (k8s / systemd / Docker secrets) already protects the value:
58
+
59
+ ```bash
60
+ echo 'export HALO_PASSWORD=your_password_here' >> ~/.bashrc
61
+ source ~/.bashrc
62
+ ```
63
+
64
+ When `HALO_PASSWORD` is set, the password chosen via `halo setup` is ignored at runtime.
65
+
66
+ ## 5. Start the server
67
+
68
+ > **单实例锁**:server 启动时会写 `~/.halo/global/server.lock`(Linux 用 flock,macOS/Windows 回退到 pid 探测),退出时自动清理。如果 lock 里记录的进程还活着,新 server 会拒绝启动并打印 `kill <pid>` 提示。原因是 WeChat 长轮询循环跟 HTTP server 解耦,多个进程并存会导致同一条微信消息被 fan out 到多个 session。陈旧 lock(进程已不在)会被自动识别并清除。要手动重启先 kill 旧的:`kill $(cat ~/.halo/global/server.lock)`。
69
+
70
+ ### Option A: quick start
71
+
72
+ ```bash
73
+ cd /path/to/halo/packages/server
74
+ HALO_PASSWORD=your_password nohup node dist/index.js > /tmp/server.log 2>&1 &
75
+ ```
76
+
77
+ ### Option B: systemd
78
+
79
+ ```bash
80
+ sudo tee /etc/systemd/system/halo.service <<'EOF'
81
+ [Unit]
82
+ Description=Halo Server
83
+ After=network.target
84
+
85
+ [Service]
86
+ Type=simple
87
+ User=YOUR_USER
88
+ WorkingDirectory=/path/to/halo/packages/server
89
+ ExecStart=/path/to/node dist/index.js
90
+ Restart=on-failure
91
+ RestartSec=5
92
+ Environment=NODE_ENV=production
93
+ Environment=HALO_PASSWORD=your_password
94
+
95
+ [Install]
96
+ WantedBy=multi-user.target
97
+ EOF
98
+
99
+ sudo systemctl daemon-reload
100
+ sudo systemctl enable --now halo
101
+ ```
102
+
103
+ ## 6. Verify
104
+
105
+ ```bash
106
+ curl http://localhost:9527/api/health
107
+ # expect: {"status":"ok", ...}
108
+
109
+ curl -s -o /dev/null -w "%{http_code}" http://localhost:9527
110
+ # expect: 200
111
+ ```
112
+
113
+ Browser: http://localhost:9527
114
+
115
+ ## 7. Redeploy after code changes
116
+
117
+ ```bash
118
+ cd /path/to/halo
119
+
120
+ cd packages/admin && npx next build --no-lint && node scripts/copy-monaco.mjs && cd ../..
121
+ pnpm --filter @turmind/halo-server build
122
+
123
+ # 用 lock 文件里的 pid 确保旧进程彻底退出(避免孤儿进程继续长轮询)
124
+ kill $(cat ~/.halo/global/server.lock) 2>/dev/null
125
+ sleep 2
126
+ cd packages/server && HALO_PASSWORD=your_password nohup node dist/index.js > /tmp/server.log 2>&1 &
127
+ ```
128
+
129
+ ## 8. Install via npm (recommended)
130
+
131
+ The whole monorepo is bundled and published to npm as a single package:
132
+
133
+ ```bash
134
+ npm install -g @turmind/halo
135
+ ```
136
+
137
+ This installs the `halo` binary on `$PATH`. Subcommands available:
138
+
139
+ | Command | Purpose |
140
+ |---|---|
141
+ | `halo setup` | Interactive password / port / model keys / optional skills setup |
142
+ | `halo setup --non-interactive` (alias `-y`) | Skip every prompt — seed templates only, supply password via `HALO_PASSWORD` env. Use in Dockerfiles / CI. |
143
+ | `halo upgrade` | Bump the npm install in place. Compares the bundled version against `npm view @turmind/halo version`; no-op if already latest, otherwise runs `npm install -g @turmind/halo@latest` and prints a server-restart hint. On EACCES, suggests retrying with `sudo`. |
144
+ | `halo server start` | Launch HTTP/WS server (foreground). Add `-d` for daemon. |
145
+ | `halo server stop` / `restart` / `status` / `logs` | Server lifecycle |
146
+ | `halo tui` | Interactive TUI client |
147
+ | `halo cli "<prompt>"` | One-shot prompt → reply, exit |
148
+ | `halo agents` / `halo sessions` | List agents / sessions |
149
+
150
+ ### Upgrade flow
151
+
152
+ 1. `halo upgrade` — bumps the on-disk npm package
153
+ 2. `halo server restart` — server's startup check sees `~/.halo/global/.template-version` is behind the new bundled `TEMPLATE_VERSION`, runs `ensureHaloHome` automatically, then starts. Refreshes `docs/`, built-in agents, built-in skills, system prompts, and the model registry. User-owned files (USER.md, custom agents/skills, INSTRUCTIONS.md overrides) are left alone. See `init.ts` for the per-category overwrite policy.
154
+
155
+ The published package contains a single bundled JS entry (~620 KB), all built-in templates (agents / skills / prompts / models), bundled platform docs, and the admin Web UI static export. Total install footprint ≈ 120 MB after npm dedupes shared deps.
156
+
157
+ ### Non-interactive (Docker / CI) details
158
+
159
+ `halo setup --non-interactive` (alias `-y`) skips every prompt. It only:
160
+
161
+ - Seeds / refreshes `~/.halo/global/` from the bundled templates (per the per-category overwrite policy in `init.ts`)
162
+ - Generates `server.jwt_secret` if missing
163
+ - **Does not** set a password — supply one via `HALO_PASSWORD` env
164
+
165
+ Minimal Dockerfile:
166
+
167
+ ```dockerfile
168
+ FROM node:22-slim
169
+ RUN apt-get update && apt-get install -y --no-install-recommends bubblewrap && rm -rf /var/lib/apt/lists/*
170
+ RUN npm install -g @turmind/halo
171
+ ENV HALO_PASSWORD=changeme
172
+ ENV HALO_PORT=9527
173
+ EXPOSE 9527
174
+ CMD halo setup -y && halo server start
175
+ ```
176
+
177
+ ## 9. Optional: Nginx reverse proxy
178
+
179
+ ```nginx
180
+ server {
181
+ listen 80;
182
+ server_name your-domain.com;
183
+
184
+ client_max_body_size 50m;
185
+
186
+ location / {
187
+ proxy_pass http://127.0.0.1:9527;
188
+ proxy_http_version 1.1;
189
+ proxy_set_header Host $host;
190
+ proxy_set_header X-Real-IP $remote_addr;
191
+ proxy_set_header Upgrade $http_upgrade;
192
+ proxy_set_header Connection "upgrade";
193
+ proxy_buffering off;
194
+ proxy_read_timeout 86400s;
195
+ proxy_send_timeout 86400s;
196
+ }
197
+ }
198
+ ```
@@ -0,0 +1,327 @@
1
+ # Desktop Packaging (Electron / DMG)
2
+
3
+ How to build the macOS desktop app (`Halo-0.1.5-arm64.dmg`). The desktop
4
+ build is a thin Electron shell (`packages/desktop`) that bundles a self-
5
+ contained server runtime + a private `node` binary + the admin static export,
6
+ then spawns the server as a child process and points a `BrowserWindow` at it.
7
+
8
+ See [design](../design/) for the server itself; this doc is only about the
9
+ packaging pipeline.
10
+
11
+ > **Before packaging, check for `desktop-packaging.local.md` in this directory**
12
+ > (gitignored, per-host). If present it carries operator/machine-specific setup
13
+ > not captured here — e.g. building on a Linux host (wine/32-bit, workspace
14
+ > build order, `corepack enable`) and artifact-distribution gotchas. Read it
15
+ > first; absence just means none were recorded on this machine.
16
+
17
+ ## Architecture
18
+
19
+ ```
20
+ Halo.app
21
+ ├── Electron main (packages/desktop/src/main.cjs)
22
+ │ • picks a free port, seeds ~/.halo, spawns the server, waits /api/health
23
+ │ • on quit: SIGTERM the server, SIGKILL fallback after 1.5s
24
+ └── resources/ (electron-builder extraResources)
25
+ ├── server-runtime/ ← pnpm-deployed @turmind/halo-server tree (dist + node_modules + templates), node-linker=hoisted (flat, no .pnpm symlink farm)
26
+ ├── admin-out/ ← Next.js static export
27
+ └── node ← private node v22 binary (server child runs on this, NOT Electron's node)
28
+ ```
29
+
30
+ Why a private `node`: the server's native addons (`better-sqlite3`,
31
+ `node-pty`) are prebuilt for node 22. Electron's embedded node is a different
32
+ ABI, so the server must run on a real node 22 — hence we download and bundle
33
+ one.
34
+
35
+ ## One-shot build
36
+
37
+ ```bash
38
+ cd packages/halo # repo root
39
+ # 1. build the upstream artifacts the stage script consumes, IN ORDER.
40
+ # - core MUST build before server: server imports @turmind/halo-core
41
+ # (e.g. GitManager in routes/git.ts). A stale/missing core/dist makes
42
+ # server's tsc fail with "GitManager has no method X" / type errors.
43
+ # - admin MUST use the full `pnpm build` (next build + copy-monaco.mjs),
44
+ # NOT a bare `next build` — the latter skips Monaco and stage-runtime.mjs
45
+ # now FATAL-aborts on the missing admin-out/monaco/vs/loader.js.
46
+ pnpm --filter @turmind/halo-core build
47
+ pnpm --filter @turmind/halo-server build
48
+ pnpm --filter @turmind/halo-admin build
49
+
50
+ # 2. stage + package (arm64)
51
+ cd packages/desktop
52
+ CI=true pnpm dist:arm64
53
+ ```
54
+
55
+ Output: `packages/desktop/dist/Halo-0.1.5-arm64.dmg` (~143 MB).
56
+
57
+ `pnpm dist:arm64` runs three steps (see `package.json` scripts):
58
+ `gen-icon` → `prepack-dmg` (`scripts/stage-runtime.mjs`) → `electron-builder
59
+ --mac --arm64`.
60
+
61
+ Other targets: `pnpm dist:x64` (Intel mac), `pnpm dist:win` (Windows nsis,
62
+ cross-staged from mac).
63
+
64
+ ## Faster rebuilds
65
+
66
+ The staging pipeline is split so a code-only change doesn't redo the heavy
67
+ dependency work. Three levers, in order of how often they help:
68
+
69
+ 1. **Auto-fast (automatic).** A full `pnpm dist:arm64` now fingerprints the
70
+ dependency inputs (`packages/server/package.json`, `core/package.json`, the
71
+ cli bundle's `dist-pub/package.json`, `pnpm-lock.yaml`, + target/node
72
+ version) and records it in `resources/.stage-meta`. On the next run, if a
73
+ prior full stage exists for the same target **and the fingerprint is
74
+ unchanged**, staging auto-takes the fast path: it re-syncs only the compiled
75
+ outputs (server `dist`, admin `out`, cli bundle) and reuses the existing
76
+ `node_modules` + bundled `node`. Measured: full stage ~29 s → fast ~1.5 s.
77
+ So after the first build, "I only changed TS" rebuilds skip the install
78
+ entirely with no flag to remember.
79
+ - Force a clean re-stage (e.g. you suspect a corrupt tree): `HALO_STAGE_FULL=1 pnpm dist:arm64`.
80
+ - Any dependency edit changes the fingerprint → next build does a real stage automatically.
81
+
82
+ 2. **Explicit fast (`--fast`).** `pnpm dist:arm64:fast` / `prepack-dmg:fast`
83
+ force the fast path and hard-fail if no prior full stage exists. Same effect
84
+ as auto-fast but explicit; auto-fast makes this rarely necessary.
85
+
86
+ 3. **`npmRebuild: false`** (electron-builder.yml). The desktop package has no
87
+ native deps of its own — `better-sqlite3` / `node-pty` / `@parcel/watcher`
88
+ all live in the staged `server-runtime` / `cli-runtime` trees, fixed up for
89
+ the target *by `stage-runtime.mjs`* (not by electron-builder). Skipping
90
+ `@electron/rebuild` removes dead time every pack. Note the staging fixups are
91
+ what actually make these correct per-target — see the cross-stage gotchas.
92
+
93
+ **What's left (the ~70 s floor):** electron-builder itself — asar packing, dmg
94
+ generation, and Gatekeeper scanning the ~350 MB of `extraResources`. Neither
95
+ fast-staging nor `npmRebuild:false` touches that; it runs on every build.
96
+ A further win would be de-duplicating `cli-runtime` (~137 MB) against
97
+ `server-runtime` (~92 MB) — they share most deps (lark sdk, drizzle,
98
+ better-sqlite3, protobufjs) — but that needs the two trees to physically share
99
+ files without breaking cli's standalone require paths; deferred.
100
+
101
+ ## Why `CI=true` is mandatory
102
+
103
+ `stage-runtime.mjs` runs `pnpm deploy --prod`, which under the hood does a
104
+ `pnpm install`. In a non-TTY shell (which is how these builds run) pnpm aborts
105
+ with `[ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY]` unless `CI=true` is set.
106
+
107
+ **Side effect (now auto-handled)**: `pnpm deploy --prod` prunes the *root
108
+ workspace*'s `node_modules` to prod-only as a side effect — the devDep symlinks
109
+ (`typescript`, `electron-builder`, `sharp`, `png-to-ico`) vanish, which used to
110
+ break the next `gen-icon` / `tsc` / build with `command not found` /
111
+ `ERR_MODULE_NOT_FOUND`. `stage-runtime.mjs` now runs `pnpm install` right after
112
+ the deploy to restore the full workspace, so this no longer bites. If you ever
113
+ hit a half-pruned tree manually, recover with:
114
+
115
+ ```bash
116
+ CI=true pnpm install --config.verifyDepsBeforeRun=false
117
+ ```
118
+
119
+ Still good practice: build server + admin **before** `pnpm dist:arm64`.
120
+
121
+ ## Build steps in detail (`stage-runtime.mjs`)
122
+
123
+ 0. **Stamp the build version** → write `<package.json version>-<short git sha>`
124
+ (`-dirty` suffix if the tree has uncommitted changes) to
125
+ `resources/halo-version`, on every path (full / fast / auto-fast — the sha
126
+ changes per commit even when deps don't). Shipped via `extraResources`;
127
+ `main.cjs` reads it and passes it as `HALO_VERSION` to the spawned server, so
128
+ `GET /api/health` reports it and the admin settings sidebar shows `Halo
129
+ v<version>`. Without it the server falls back to `'dev'`. Same
130
+ `<version>-<sha>` scheme as the CLI bundle (`halo --version`), so all three
131
+ agree. Dev (`electron .`) computes it live from git instead.
132
+ 1. **`pnpm deploy --legacy --prod --config.node-linker=hoisted`** → flattens
133
+ `@turmind/halo-server` (+ the `@turmind/halo-core` workspace dep) into
134
+ `resources/server-runtime/node_modules` as a flat, real-directory tree (no
135
+ `.pnpm` symlink farm), so the app is self-contained. `hoisted` on **every**
136
+ target — see the dangling-self-symlink gotcha for why macOS can't use the
137
+ default isolated layout.
138
+ 2. **Copy `templates/`** — `pnpm deploy` doesn't pick these up; copied
139
+ explicitly. This is what carries the built-in skills (incl. `send-file`),
140
+ agents, prompts, models into the dmg.
141
+ 3. **Cross-arch native fixup** — when target arch/platform ≠ host, re-fetch
142
+ `better-sqlite3` prebuilds via `prebuild-install`.
143
+ 4. **Trim bloat** — drop non-target `node-pty` prebuilds, `better-sqlite3`'s
144
+ C source, and the server's `src/`/`tests/`.
145
+ 5. **Copy `admin-out/`** — the Next.js static export.
146
+ 6. **Download + stage node** — fetch `node-v22.11.0-<platform>-<arch>` from
147
+ nodejs.org, extract the `node` binary into `resources/`.
148
+ 7. **codesign (ad-hoc)** — `codesign --force --sign -` on the node binary for
149
+ Gatekeeper. **We deliberately do NOT `strip`** — see gotcha below.
150
+
151
+ ## Gotchas (learned the hard way)
152
+
153
+ - **`pnpm deploy` (default isolated linker) leaves a dangling self-symlink that
154
+ escapes the .app — use `node-linker=hoisted` on EVERY target.** The default
155
+ isolated layout creates `server-runtime/node_modules/.pnpm/node_modules/@turmind/halo-server
156
+ -> ../../../../../../../server`, a relative climb whose depth only resolves
157
+ *inside the monorepo* (back to `packages/server`). Once copied into
158
+ `Halo.app/Contents/Resources/`, the same relative path points outside the
159
+ bundle (`Halo.app/server`) and dangles. Symptom: a colleague who installs the
160
+ dmg hits `No such file: /Applications/Halo.app/Contents/Resources/server-runtime/node_modules/.pnpm/node_modules/@turmind/halo-server`
161
+ — even though the build machine runs fine, because there the symlink target
162
+ *does* exist. The build also carried ~1100 symlinks (the whole `.pnpm` farm),
163
+ slowing Gatekeeper's first-launch scan. Windows already used `hoisted` (mac-
164
+ created Unix symlinks don't survive the copy into the .exe); we now use it on
165
+ **all** targets — a flat, real-directory `node_modules` has neither the
166
+ escaping self-symlink nor the farm. Verify: `find Halo.app -type l | wc -l`
167
+ should be ~36 (Electron framework links only), and
168
+ `find Halo.app -path '*@turmind/halo-server'` should be empty.
169
+ *Note this is separate from the "is damaged" Gatekeeper prompt — that's the
170
+ ad-hoc signature + download quarantine (`identity: null` in the yml), fixed
171
+ per-machine with `sudo xattr -dr com.apple.quarantine /Applications/Halo.app`,
172
+ or properly with a Developer ID signature + notarization.*
173
+
174
+ - **Never `strip` the bundled node binary.** Recent macOS `strip` (Xcode 26
175
+ toolchain) trims the *dynamic* symbol table, not just debug symbols. The
176
+ result: `dlopen()` of native addons (`better-sqlite3`, `node-pty`) crashes
177
+ with SIGSEGV (server exits `code=null` / 139) before any JS runs. The dmg
178
+ looks fine but won't boot. Keep the full ~114 MB binary; the ~30 MB saved
179
+ isn't worth a dead app. (`stage-runtime.mjs` has the strip step removed.)
180
+
181
+ - **Desktop port is sequential from 9527 (NOT random), and that's load-bearing
182
+ for persistence.** `main.cjs` `findFreePort()` tries 9527 first, then scans
183
+ upward for the first free one, and passes it to the server child. It used to
184
+ use `listen(0)` (an OS-random ephemeral port) — but the window loads
185
+ `http://127.0.0.1:PORT`, and **localStorage is partitioned by origin**, so a
186
+ changing port gave a brand-new origin every launch and silently wiped *all*
187
+ persisted state (last folder, open session, expanded tree, editor tabs). A
188
+ stable preferred port keeps the origin stable so localStorage survives
189
+ restarts, while still stepping aside if 9527 is genuinely taken. (The
190
+ user-facing `HALO_PORT` still only matters for CLI / web deployments — the
191
+ server reads it from its own env.)
192
+
193
+ - **macOS GUI PATH is minimal — `spawn('halo')` can't find the launcher.**
194
+ An app launched from Finder/Dock inherits launchd's bare PATH
195
+ (`/usr/bin:/bin:/usr/sbin:/sbin`), which excludes `/usr/local/bin` where the
196
+ "Install 'halo' Command" menu drops the launcher. The server child inherits
197
+ this PATH, so the evo wrapper / cron runner's `spawn('halo', …)`
198
+ (`resolveHaloCli()` returns bare `halo`) hit `spawn halo ENOENT` even
199
+ though the launcher exists. `startServer()` in `main.cjs` therefore prepends
200
+ the standard CLI dirs (`/usr/local/bin`, `/opt/homebrew/bin`, + sbin
201
+ variants — the same set `tools/sandbox.js` assumes) to the child's
202
+ `env.PATH`, **guarded by `process.platform === 'darwin'`** (Windows PATH uses
203
+ `;` and these dirs don't exist there). Note this still requires the user to
204
+ have run the menu action that installs `/usr/local/bin/halo`; a fresh
205
+ install that never did has no `halo` on any PATH.
206
+
207
+ - **Server child lifecycle.** On `before-quit` the main process sends SIGTERM,
208
+ then force-SIGKILLs after 1.5 s. This is the backstop for a wedged server
209
+ whose main thread is stuck in a syscall and never delivers SIGTERM. Without
210
+ it, a zombie server keeps holding the port / `server.lock`. (The file watcher
211
+ is `@parcel/watcher` — VS Code's engine — using native recursive FSEvents/
212
+ inotify with an ignore list; it replaced chokidar, whose recursive mode
213
+ overflowed the macOS FSEvents queue on large repos and could deadlock on
214
+ close. See `ws/file-watcher.ts`.)
215
+
216
+ - **Stale `server.lock`.** If a prior server was `kill -9`'d, its PID is left
217
+ in `~/.halo/global/server.lock`. The server now probes whether that PID is
218
+ alive on startup and self-heals (removes the lock + continues) instead of
219
+ exiting 1. See `acquireSingleInstanceLock` in `packages/server/src/index.ts`.
220
+
221
+ - **Build admin with `pnpm --filter @turmind/halo-admin build`, NOT a bare
222
+ `next build` — Monaco won't ship otherwise.** The editor self-hosts Monaco
223
+ (no CDN at runtime): `scripts/copy-monaco.mjs` stages `monaco-editor/min/vs`
224
+ into `admin/out/monaco/vs`, and the admin `build` script chains it
225
+ (`next build && copy-monaco.mjs`). Both downstream consumers (cli
226
+ `build-bundle.mjs`, desktop `stage-runtime.mjs`) copy `admin/out` wholesale,
227
+ so a missing `out/monaco/` propagates into every artifact. Symptom: editor
228
+ opening *any* file throws `Uncaught SyntaxError: Unexpected token '<'` at
229
+ `loader.js` — the server returned the 404 HTML page because Monaco's loader
230
+ isn't there. This is exactly how a monaco-less 0.1.7 shipped: packaging ran a
231
+ bare `npx next build --no-lint` (skipping copy-monaco), and cli's backstop
232
+ copy silently failed because it resolved `monaco-editor` from cli's
233
+ node_modules (it's *admin's* devDependency). Now hard-gated: both
234
+ `build-bundle.mjs` and `stage-runtime.mjs` `process.exit(1)` if
235
+ `admin-out/monaco/vs/loader.js` is absent, and every doc's build command ends
236
+ with `&& node scripts/copy-monaco.mjs`. If you must run `next build`
237
+ directly, append the copy step yourself.
238
+
239
+ - **Changing anything under `templates/` requires bumping `TEMPLATE_VERSION`
240
+ (`packages/server/src/init.ts`) — otherwise the change never reaches existing
241
+ installs.** Server startup only re-runs `ensureHaloHome` (the template
242
+ reseed) when the on-disk `~/.halo/global/.template-version` is *strictly less
243
+ than* the compiled `TEMPLATE_VERSION` (`index.ts` startup block). Equal → the
244
+ whole reseed is skipped, so a template edit with no version bump is invisible
245
+ to every machine that already has the prior version stamped. This bit the
246
+ `team`-delegation rewrite: `default/AGENT.md` gained the "team whitelist"
247
+ paragraph but `TEMPLATE_VERSION` stayed put, so upgraded users' `default`
248
+ agent kept reading the old AGENT.md and didn't know what `team` was. It's an
249
+ integer, not a hash — it must increase monotonically (a git sha would compare
250
+ nonsensically and can decrease). Fresh `halo setup` always reseeds regardless,
251
+ which is why it's easy to miss in dev. Rule: touch `templates/` → `+1` the
252
+ version in the same commit.
253
+
254
+ - **Crash diagnostics.** Server stdout/stderr is tee'd to
255
+ `~/.halo/logs/desktop.log`; the last 30 lines also surface in the crash
256
+ dialog. Check that file first when a packaged build won't start.
257
+
258
+ - **No synchronous `window.confirm` / `window.prompt`.** Electron's renderer
259
+ can't block on a native dialog, so the preload (`packages/desktop/src/preload.cjs`)
260
+ shims both: the sync `window.confirm` shows an overlay but always returns
261
+ `false`, and `window.prompt` returns `null`. Admin code that needs the real
262
+ answer **must** use the async helpers in `packages/admin/src/shared/utils.ts`
263
+ — `confirmAction()` (→ `window.haloConfirm`) and `promptInput()` (→
264
+ `window.haloPrompt`) — and `await` them. Calling `window.confirm()` directly
265
+ silently no-ops in the desktop app (the symptom: clicking OK on a delete
266
+ confirm does nothing, because `if (!confirm(...)) return` always returns). In
267
+ a plain browser the helpers fall back to the native sync dialogs.
268
+
269
+ - **Cross-staging needs a full restage — auto-fast skips the native fixup.**
270
+ After editing `stage-runtime.mjs`, or any time you cross-stage a target whose
271
+ `node_modules` differs from what's on disk, run with `HALO_STAGE_FULL=1`. The
272
+ fast path only re-syncs compiled outputs and **reuses the existing
273
+ `node_modules`**, so it never re-runs the per-target native fixups
274
+ (`@parcel/watcher` swap, `better-sqlite3` prebuild-install). Symptom of
275
+ forgetting: the win/x64 dmg still carries the host's arm64 binaries.
276
+
277
+ - **`@parcel/watcher` binary doesn't cross-stage via `npm install`.** Its
278
+ per-platform binary package (`@parcel/watcher-win32-x64` etc.) declares
279
+ `os`/`cpu`, so `npm install` on a mac host **silently skips it** — leaving
280
+ only the host's `watcher-darwin-arm64` (dragged in by `pnpm deploy`), and the
281
+ packaged app crashes with *"no prebuild or local build of @parcel/watcher
282
+ found"*. `stage-runtime.mjs` `installParcelWatcherBinary()` fetches the right
283
+ one with `npm pack` (no os/cpu gate) and extracts it into every `@parcel`
284
+ scope it finds under `node_modules` (`findParcelScopesWithWatcher` walks for
285
+ them — one under the flat `hoisted` layout, several if a tree is ever staged
286
+ the default isolated way), removing the host's. Verify after
287
+ a build: `find …/win-unpacked -path '*watcher-*' -name '*.node' | xargs file`
288
+ should show only PE32 (win) / the target's Mach-O — no foreign arch.
289
+ (`better-sqlite3` and `node-pty` don't hit this — they use `prebuild-install`
290
+ and bundled multi-platform prebuilds respectively.)
291
+
292
+ - **`ENOTEMPTY` / `directory not empty` mid-stage or in electron-builder.** A
293
+ prior stage's `resources/{cli,server}-runtime/node_modules` (or
294
+ `dist/win-unpacked`) can fail to delete cleanly on macOS (open handles, deep
295
+ nesting). Hard-clean before a full restage:
296
+ `rm -rf resources/{cli-runtime,server-runtime,admin-out,node} dist/{win-unpacked,mac,mac-arm64}`
297
+ then rebuild.
298
+
299
+ - **A `--x64` / `--win` pack also emits a stray `Halo-0.1.5-arm64.dmg` — it's
300
+ poisoned, discard it.** electron-builder honours the yml's `arm64` mac target
301
+ even when you pass `--x64`, so it re-packages the *current* (x64/win-staged)
302
+ `resources/` as an arm64 dmg. That dmg's native binaries are the wrong arch.
303
+ The real per-target artifacts are `Halo-0.1.5.dmg` (x64) and `Halo Setup
304
+ 0.1.5.exe` (win); only trust the arm64 dmg from an actual `dist:arm64` run.
305
+
306
+ ## Verifying a build
307
+
308
+ ```bash
309
+ # staged skills include send-file (and the other 6 built-ins)
310
+ ls packages/desktop/resources/server-runtime/templates/skills/
311
+
312
+ # compiled init carries the current template version + send-file
313
+ grep -o "TEMPLATE_VERSION = [0-9]*" packages/desktop/resources/server-runtime/dist/init.js
314
+
315
+ # build version stamp is clean (<version>-<sha>, no -dirty) — surfaces as
316
+ # "Halo v<version>" in the settings sidebar via GET /api/health
317
+ cat packages/desktop/dist/win-unpacked/resources/halo-version # or resources/halo-version pre-pack
318
+
319
+ # the dmg exists
320
+ ls -lh packages/desktop/dist/*.dmg
321
+ ```
322
+
323
+ Then mount the dmg, drag to /Applications, launch. First run shows the dark
324
+ setup window (password prompt); subsequent runs go straight to the workspace.
325
+ On upgrade, the server reseeds `~/.halo/global/skills/` from the bundled
326
+ templates because `TEMPLATE_VERSION` bumped — that's how a newly-added built-in
327
+ skill like `send-file` reaches existing users without manual steps.