bazilion 0.12.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +795 -240
  3. package/dist/cli.js.map +1 -1
  4. package/dist/daemon.js +4555 -1280
  5. package/dist/daemon.js.map +1 -1
  6. package/dist/migrations/0001_init.sql +21 -0
  7. package/dist/web/client/assets/AgentTabs-BcmmwYJc.js +1 -0
  8. package/dist/web/client/assets/ConfigPage-BCLp-FUC.js +1 -0
  9. package/dist/web/client/assets/ConfirmDialog-CyL9A20V.js +1 -0
  10. package/dist/web/client/assets/FieldRow-D6REbYUa.js +1 -0
  11. package/dist/web/client/assets/Page-NV96O47A.js +1 -0
  12. package/dist/web/client/assets/ProductionTeamPolicyEditor-DBKnOezm.js +7 -0
  13. package/dist/web/client/assets/{ProfileCommunicationEditor-C43HvUe-.js → ProfileCommunicationEditor-4RK2laqO.js} +1 -1
  14. package/dist/web/client/assets/SectionTabs-4kCQXuFY.js +1 -0
  15. package/dist/web/client/assets/TeamTabs-CdtNEqt4.js +1 -0
  16. package/dist/web/client/assets/{TemplatesTabs-6Jd_xQj7.js → TemplatesTabs-CZyss2Y0.js} +1 -1
  17. package/dist/web/client/assets/UnsavedChangesGuard-CEoK-ZcN.js +1 -0
  18. package/dist/web/client/assets/_id-4ZS-w_Aj.js +1 -0
  19. package/dist/web/client/assets/{_id-AOAT0H_5.js → _id-DIqtdQij.js} +1 -1
  20. package/dist/web/client/assets/_id-DbcC2E7F.js +1 -0
  21. package/dist/web/client/assets/_id-f_r5M0p3.js +1 -0
  22. package/dist/web/client/assets/activity-CB9Ufth-.js +1 -0
  23. package/dist/web/client/assets/agents-D-JGXVg9.js +1 -0
  24. package/dist/web/client/assets/agents-DS2XafoA.js +1 -0
  25. package/dist/web/client/assets/approvals-Cke_mrJv.js +1 -0
  26. package/dist/web/client/assets/attention-PYwRM2wC.js +1 -0
  27. package/dist/web/client/assets/chevron-right-BG4OeFRQ.js +1 -0
  28. package/dist/web/client/assets/config-DF9Gqmfa.js +3 -0
  29. package/dist/web/client/assets/dm-sans-latin-400-normal-BwCSEQnW.woff +0 -0
  30. package/dist/web/client/assets/dm-sans-latin-400-normal-CW0RaeGs.woff2 +0 -0
  31. package/dist/web/client/assets/dm-sans-latin-500-normal-B9HHJjqV.woff2 +0 -0
  32. package/dist/web/client/assets/dm-sans-latin-500-normal-Dr3UlScf.woff +0 -0
  33. package/dist/web/client/assets/dm-sans-latin-600-normal-Aqo67rzb.woff2 +0 -0
  34. package/dist/web/client/assets/dm-sans-latin-600-normal-BmdmIIQ2.woff +0 -0
  35. package/dist/web/client/assets/dm-sans-latin-700-normal-CUSSCpQX.woff +0 -0
  36. package/dist/web/client/assets/dm-sans-latin-700-normal-DvUfVpUG.woff2 +0 -0
  37. package/dist/web/client/assets/dm-serif-display-latin-400-normal-C5_t9oOD.woff2 +0 -0
  38. package/dist/web/client/assets/dm-serif-display-latin-400-normal-_kVFYEpx.woff +0 -0
  39. package/dist/web/client/assets/{inbox-Bn1GbUsJ.js → inbox-DC_HdOfI.js} +1 -1
  40. package/dist/web/client/assets/index-Cofc7zhl.css +1 -0
  41. package/dist/web/client/assets/index-Cp6UHWr2.js +52 -0
  42. package/dist/web/client/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  43. package/dist/web/client/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  44. package/dist/web/client/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  45. package/dist/web/client/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  46. package/dist/web/client/assets/learning-BfrQ4YZY.js +1 -0
  47. package/dist/web/client/assets/login-D4n3AcBR.js +1 -0
  48. package/dist/web/client/assets/mcp-CnjQc_JJ.js +1 -0
  49. package/dist/web/client/assets/members-wxsVTJdW.js +1 -0
  50. package/dist/web/client/assets/memory-Du60O3pE.js +1 -0
  51. package/dist/web/client/assets/{policy-DaijITy7.js → policy-BOfnkJsL.js} +1 -1
  52. package/dist/web/client/assets/{preload-helper-mNuBjNXU.js → preload-helper-bztXdkDj.js} +1 -1
  53. package/dist/web/client/assets/routes-B9CuhzUW.js +1 -0
  54. package/dist/web/client/assets/services-DyvqJqM2.js +1 -0
  55. package/dist/web/client/assets/skills-DQo7ioW7.js +1 -0
  56. package/dist/web/client/assets/styles-B5vcgXln.css +2 -0
  57. package/dist/web/client/assets/{teams-D92R9Pb7.js → teams-DPggy97H.js} +1 -1
  58. package/dist/web/client/assets/teams-DdawcfNR.js +1 -0
  59. package/dist/web/client/assets/telegram-DcTeDpP5.js +1 -0
  60. package/dist/web/client/assets/tokens-CIEKwZXc.js +1 -0
  61. package/dist/web/client/assets/triggers-Bc2RrUPm.js +1 -0
  62. package/dist/web/client/assets/utils-B6KiDbIe.js +1 -0
  63. package/dist/web/client/assets/welcome-DlhNXUIa.js +1 -0
  64. package/dist/web/client/assets/wire-constants-BzdHSc6A.js +74 -0
  65. package/dist/web/client/assets/x-DAXGIe_Q.js +1 -0
  66. package/dist/web/server/assets/{AgentTabs-aXdsiiMp.js → AgentTabs-Dq8hDM2Q.js} +9 -2
  67. package/dist/web/server/assets/{ChatPane-Ba5wTsVk.js → ChatPane-CgSe7tsy.js} +81 -43
  68. package/dist/web/server/assets/{ConfigPage-BKlh5vOU.js → ConfigPage-CiQ-gxmJ.js} +3 -2
  69. package/dist/web/server/assets/ConfirmDialog-C-MPYYSU.js +99 -0
  70. package/dist/web/server/assets/FieldRow-DCOF07kl.js +160 -0
  71. package/dist/web/server/assets/{Page-LENulSXY.js → Page-Bz-ydB8j.js} +4 -10
  72. package/dist/web/server/assets/{ProductionTeamPolicyEditor-Y4mzFFvR.js → ProductionTeamPolicyEditor-DQSdP7Yx.js} +140 -123
  73. package/dist/web/server/assets/{SectionTabs-BJTPr5nU.js → SectionTabs-B2Yw81XN.js} +2 -2
  74. package/dist/web/server/assets/{Sidebar-DsRVCszF.js → Sidebar-DKyk7oIo.js} +563 -283
  75. package/dist/web/server/assets/{TeamTabs-zZzbP1t_.js → TeamTabs-Co3n2exj.js} +2 -6
  76. package/dist/web/server/assets/{TemplatesTabs-CcRxZhJe.js → TemplatesTabs-B0fA361R.js} +1 -1
  77. package/dist/web/server/assets/UnsavedChangesGuard-CiStXyBI.js +40 -0
  78. package/dist/web/server/assets/{_id-Dj1yZ3zJ.js → _id-BAA9KJBU.js} +4 -4
  79. package/dist/web/server/assets/{_id-LrvXU4Nr.js → _id-BM5m2FLT.js} +4 -4
  80. package/dist/web/server/assets/{_id-CwyGjJeP.js → _id-BYKpT2Tu.js} +4 -5
  81. package/dist/web/server/assets/{_id-BHYLL8M-.js → _id-BeKhPKw3.js} +350 -185
  82. package/dist/web/server/assets/{_id-C3HY4lWg.js → _id-BqWTB8Wl.js} +188 -86
  83. package/dist/web/server/assets/{_id-DinTp_7X.js → _id-BwvCtzlD.js} +3 -3
  84. package/dist/web/server/assets/{_id-D27T85mU.js → _id-DYU6uIBx.js} +4 -5
  85. package/dist/web/server/assets/{_id-IzadFZV9.js → _id-DvlDkk04.js} +4 -4
  86. package/dist/web/server/assets/{_id-BhOB8Umi.js → _id-DxCnEFpm.js} +4 -4
  87. package/dist/web/server/assets/{_id-tB47Vsfp.js → _id-HMy7jM3P.js} +3 -3
  88. package/dist/web/server/assets/{_id-D3Ap7zZA.js → _id-ndxFHx0t.js} +75 -71
  89. package/dist/web/server/assets/{_id-FcHdSocl.js → _id-sNu5aXZt.js} +5 -4
  90. package/dist/web/server/assets/{_tanstack-start-manifest_v-BGDBdnUs.js → _tanstack-start-manifest_v-C-OiOZeM.js} +118 -94
  91. package/dist/web/server/assets/{activity-DohitgwW.js → activity-9VP6QDTH.js} +3 -3
  92. package/dist/web/server/assets/{activity-BCI4mfNS.js → activity-BWv1goK6.js} +3 -3
  93. package/dist/web/server/assets/{activity-D9SMqBhy.js → activity-LjpblbDL.js} +3 -3
  94. package/dist/web/server/assets/{agents-C9xlWK0Z.js → agents-BDFGrU3o.js} +4 -4
  95. package/dist/web/server/assets/{agents-BY74v7fM.js → agents-BJAv919x.js} +3 -3
  96. package/dist/web/server/assets/{agents-BvttypqB.js → agents-CJk4pxKu.js} +3 -3
  97. package/dist/web/server/assets/{agents-CgZMHj80.js → agents-CYw1xHoY.js} +138 -16
  98. package/dist/web/server/assets/{agents-DZuFEj0D.js → agents-DfNKc_Q7.js} +52 -8
  99. package/dist/web/server/assets/{agents-BPKOshR5.js → agents-DjyQk94u.js} +4 -4
  100. package/dist/web/server/assets/{approvals-CWcFHlor.js → approvals-7JOoBchn.js} +3 -3
  101. package/dist/web/server/assets/{approvals-BGU-XGIO.js → approvals-BYi84gwl.js} +3 -3
  102. package/dist/web/server/assets/{approvals-DCaVw1Mz.js → approvals-Bz123liE.js} +59 -21
  103. package/dist/web/server/assets/{attention-BxkTf337.js → attention-B35JvhOL.js} +3 -3
  104. package/dist/web/server/assets/{attention-w21GQIGH.js → attention-C1pPWSXw.js} +3 -3
  105. package/dist/web/server/assets/{attention-CM-Ks1N9.js → attention-UhqPK_Ro.js} +3 -3
  106. package/dist/web/server/assets/{auth-byfSx6Ja.js → auth-7BHTH-KS.js} +5 -5
  107. package/dist/web/server/assets/{config-BDCJdtj2.js → config-BMiajTZP.js} +10 -5
  108. package/dist/web/server/assets/{config-1w2GiJUn.js → config-BlMkHStx.js} +3 -3
  109. package/dist/web/server/assets/config-ExacOzHX.js +958 -0
  110. package/dist/web/server/assets/{createServerRpc-pU-IQV2Y.js → createServerRpc-B7Yr1K8i.js} +1 -1
  111. package/dist/web/server/assets/{createSsrRpc-DFsIAfFd.js → createSsrRpc-6LHNEsSK.js} +1 -1
  112. package/dist/web/server/assets/{daemon-client-2NRMzlTr.js → daemon-client-CI5NHwNC.js} +16 -4
  113. package/dist/web/server/assets/{inbox-CzlKT9Wg.js → inbox-Dv55rGYq.js} +4 -4
  114. package/dist/web/server/assets/{inbox-BLUzA5sQ.js → inbox-RweGCV3D.js} +3 -3
  115. package/dist/web/server/assets/{inbox-ZvDNAjaM.js → inbox-cu3jLzNP.js} +4 -4
  116. package/dist/web/server/assets/{learning-B7PBkvER.js → learning-BaKM6qaw.js} +4 -4
  117. package/dist/web/server/assets/{learning-8zav4pIT.js → learning-C2upa1CE.js} +4 -4
  118. package/dist/web/server/assets/{learning-Cq7K2heb.js → learning-CbtNlqYo.js} +76 -23
  119. package/dist/web/server/assets/{login-CmWvxfTK.js → login-BNj3U6ok.js} +22 -12
  120. package/dist/web/server/assets/{login-Bx5LEyGP.js → login-Be8eP4Jp.js} +2 -2
  121. package/dist/web/server/assets/{mcp-DIWTogHM.js → mcp-B0goWO8Q.js} +3 -3
  122. package/dist/web/server/assets/{mcp-2pwT9HU8.js → mcp-BvnvlVFd.js} +3 -3
  123. package/dist/web/server/assets/mcp-CiBs8unU.js +332 -0
  124. package/dist/web/server/assets/members-BP3UTqu1.js +12 -0
  125. package/dist/web/server/assets/members-DHYTVgwb.js +100 -0
  126. package/dist/web/server/assets/members-DiYtr-zm.js +12 -0
  127. package/dist/web/server/assets/{memory-Dngc13Nw.js → memory-BqV-Afy1.js} +4 -4
  128. package/dist/web/server/assets/{memory-a8Kz57Kh.js → memory-CkXUgf5s.js} +150 -48
  129. package/dist/web/server/assets/{memory-pZ4pZT7p.js → memory-DTFEmfF3.js} +4 -4
  130. package/dist/web/server/assets/{policy-Bb-vEgqg.js → policy-18fdTFO4.js} +3 -3
  131. package/dist/web/server/assets/{policy-C30AeFIb.js → policy-BddK4xom.js} +3 -3
  132. package/dist/web/server/assets/{policy-BcGELcc9.js → policy-Bvvav8fh.js} +4 -4
  133. package/dist/web/server/assets/public-origin-xV0lm0Ty.js +25 -0
  134. package/dist/web/server/assets/router-pjH2t8Rl.js +813 -0
  135. package/dist/web/server/assets/{routes-B6cMLIPk.js → routes-BgHisAxH.js} +22 -5
  136. package/dist/web/server/assets/{routes-BrA-U_Hm.js → routes-COtkyZG1.js} +5 -5
  137. package/dist/web/server/assets/{routes-C19CMLZt.js → routes-DAU2g7Uz.js} +4 -4
  138. package/dist/web/server/assets/services-ClyGBU7W.js +83 -0
  139. package/dist/web/server/assets/{services-C4szdhPx.js → services-DgoU1udp.js} +3 -3
  140. package/dist/web/server/assets/{services-wG8I06oS.js → services-DozuJtzX.js} +3 -3
  141. package/dist/web/server/assets/{skills-BZdqiDig.js → skills-CYih9TJw.js} +47 -6
  142. package/dist/web/server/assets/{skills-a9VSWHWq.js → skills-DfGSJTCB.js} +3 -3
  143. package/dist/web/server/assets/{skills-C-UgkuCW.js → skills-Dw3cI3si.js} +3 -3
  144. package/dist/web/server/assets/start-D4Wq9iTa.js +70 -0
  145. package/dist/web/server/assets/{teams-OIkHuxy1.js → teams-BJuoNOTB.js} +3 -3
  146. package/dist/web/server/assets/{teams-CpQ0YgWf.js → teams-DF3RViAg.js} +3 -3
  147. package/dist/web/server/assets/{teams-DEpQR2QA.js → teams-DFnuc660.js} +3 -3
  148. package/dist/web/server/assets/{teams-DqfrEoH6.js → teams-DPnAX4wD.js} +4 -4
  149. package/dist/web/server/assets/{teams-jBtvnk59.js → teams-TV6nbEN_.js} +101 -10
  150. package/dist/web/server/assets/{teams-MjqgsmC3.js → teams-be05ci17.js} +3 -3
  151. package/dist/web/server/assets/{telegram-DNgCWebD.js → telegram-1AWL6lFw.js} +3 -3
  152. package/dist/web/server/assets/{telegram-XwyKSEtj.js → telegram-DbOFpTFm.js} +3 -3
  153. package/dist/web/server/assets/{telegram-CBF8SJzB.js → telegram-Dj4Wwhd6.js} +262 -43
  154. package/dist/web/server/assets/tokens-DDwtO15P.js +359 -0
  155. package/dist/web/server/assets/tokens-DjM8GsgS.js +19 -0
  156. package/dist/web/server/assets/{tokens-BnwFbV7T.js → tokens-_2hptHP6.js} +4 -4
  157. package/dist/web/server/assets/{triggers-BZPl5CHs.js → triggers-BvjYNmmY.js} +4 -4
  158. package/dist/web/server/assets/triggers-CHk9O3rU.js +367 -0
  159. package/dist/web/server/assets/{triggers-D79NoSXk.js → triggers-Nezfw-Bd.js} +4 -4
  160. package/dist/web/server/assets/utils-C_uf36nf.js +8 -0
  161. package/dist/web/server/assets/{welcome-DDB11L5j.js → welcome-B8nchXdF.js} +9 -8
  162. package/dist/web/server/server.js +29 -33
  163. package/dist/web-server.js +4 -0
  164. package/dist/web-server.js.map +1 -1
  165. package/dist/worker.js +1744 -362
  166. package/dist/worker.js.map +1 -1
  167. package/package.json +7 -6
  168. package/dist/web/client/assets/AgentTabs-R6gSjIj3.js +0 -1
  169. package/dist/web/client/assets/ConfigPage-BCwfgyjD.js +0 -1
  170. package/dist/web/client/assets/FieldRow-avAFLgYW.js +0 -1
  171. package/dist/web/client/assets/Page-CwNuDhWz.js +0 -1
  172. package/dist/web/client/assets/ProductionTeamPolicyEditor-CsWpTsy6.js +0 -7
  173. package/dist/web/client/assets/SectionTabs-DZzy01az.js +0 -1
  174. package/dist/web/client/assets/TeamTabs-DvqWieCq.js +0 -1
  175. package/dist/web/client/assets/_id-BUuz7Drv.js +0 -1
  176. package/dist/web/client/assets/_id-BegmVRmT.js +0 -1
  177. package/dist/web/client/assets/_id-BrA4Crh_.js +0 -1
  178. package/dist/web/client/assets/activity-n0Fw9_w3.js +0 -1
  179. package/dist/web/client/assets/agents-B33VQtbl.js +0 -1
  180. package/dist/web/client/assets/agents-C50pdxp7.js +0 -1
  181. package/dist/web/client/assets/approvals-DVW0hX8h.js +0 -1
  182. package/dist/web/client/assets/attention-B2490jcN.js +0 -1
  183. package/dist/web/client/assets/chevron-down-B0aNNOyn.js +0 -1
  184. package/dist/web/client/assets/config-CMMxxeX2.js +0 -3
  185. package/dist/web/client/assets/context-DcFzsfZ3.js +0 -1
  186. package/dist/web/client/assets/index-3nrK5iFr.js +0 -12
  187. package/dist/web/client/assets/learning-BUX1MFu2.js +0 -1
  188. package/dist/web/client/assets/login-CMWpGDKv.js +0 -1
  189. package/dist/web/client/assets/mcp-lZV08dqc.js +0 -1
  190. package/dist/web/client/assets/members-DAeaY2Cy.js +0 -1
  191. package/dist/web/client/assets/memory-HmMWxxdF.js +0 -1
  192. package/dist/web/client/assets/plus-CPzRJg1V.js +0 -1
  193. package/dist/web/client/assets/routes-BNk270AM.js +0 -1
  194. package/dist/web/client/assets/services-DtrpJrdo.js +0 -1
  195. package/dist/web/client/assets/skills-Cv93A3P3.js +0 -1
  196. package/dist/web/client/assets/styles-DMltY3su.css +0 -2
  197. package/dist/web/client/assets/teams-CCFnSNKi.js +0 -1
  198. package/dist/web/client/assets/telegram-BF1vdgDB.js +0 -1
  199. package/dist/web/client/assets/tokens-BXj6hWW-.js +0 -1
  200. package/dist/web/client/assets/triggers-NT24i4hZ.js +0 -1
  201. package/dist/web/client/assets/welcome-D0cy68eZ.js +0 -1
  202. package/dist/web/client/assets/wire-constants-BhVKOCQa.js +0 -74
  203. package/dist/web/server/assets/FieldRow-B4eTegH9.js +0 -94
  204. package/dist/web/server/assets/config-BmixCpz-.js +0 -372
  205. package/dist/web/server/assets/context-B-XDf6Ym.js +0 -12
  206. package/dist/web/server/assets/context-BXGo5Vfi.js +0 -12
  207. package/dist/web/server/assets/context-DrkrIWun.js +0 -47
  208. package/dist/web/server/assets/mcp-BJjLIdP0.js +0 -261
  209. package/dist/web/server/assets/members-CCv17cjQ.js +0 -46
  210. package/dist/web/server/assets/members-CW5aBe81.js +0 -12
  211. package/dist/web/server/assets/members-VdPwD6CE.js +0 -12
  212. package/dist/web/server/assets/router-CIVjTgNp.js +0 -537
  213. package/dist/web/server/assets/services-B9yBWDX0.js +0 -79
  214. package/dist/web/server/assets/start-WH8WzTL_.js +0 -4
  215. package/dist/web/server/assets/tokens-DPDMSN5O.js +0 -12
  216. package/dist/web/server/assets/tokens-yyTSfBSN.js +0 -186
  217. package/dist/web/server/assets/triggers-CSNNzRIi.js +0 -216
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { defineCommand as defineCommand26, runCommand, showUsage } from "citty";
4
+ import { defineCommand as defineCommand28, runCommand, showUsage } from "citty";
5
5
 
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "bazilion",
9
- version: "0.12.2",
9
+ version: "0.14.0",
10
10
  description: "Multi-agent runtime CLI \u2014 spawn LLM agents, manage profiles/teams/skills, and run the local daemon.",
11
11
  license: "MIT",
12
12
  repository: {
@@ -49,15 +49,16 @@ var package_default = {
49
49
  access: "public"
50
50
  },
51
51
  dependencies: {
52
- "@earendil-works/pi-agent-core": "0.83.0",
53
- "@earendil-works/pi-ai": "0.83.0",
54
- "@earendil-works/pi-coding-agent": "0.83.0",
52
+ "@earendil-works/pi-agent-core": "0.84.3",
53
+ "@earendil-works/pi-ai": "0.84.3",
54
+ "@earendil-works/pi-coding-agent": "0.84.3",
55
55
  "@hono/node-server": "^2.0.6",
56
56
  "@mozilla/readability": "^0.6.0",
57
57
  "@playwright/browser-chromium": "^1.61.0",
58
58
  "@tanstack/react-router": "1.170.16",
59
59
  "@tobilu/qmd": "^2.5.3",
60
60
  "adm-zip": "^0.5.17",
61
+ "age-encryption": "0.3.0",
61
62
  citty: "^0.2.2",
62
63
  dompurify: "^3.4.11",
63
64
  grammy: "^1.44.0",
@@ -96,15 +97,17 @@ async function resolveToken(src) {
96
97
  }
97
98
  function createClient(cfg) {
98
99
  async function authHeaders() {
99
- const token = await resolveToken(cfg.token);
100
+ const token = cfg.token === null ? null : await resolveToken(cfg.token);
101
+ const extra = typeof cfg.headers === "function" ? await cfg.headers() : cfg.headers ?? {};
100
102
  return {
101
- authorization: `Bearer ${token}`,
103
+ ...token ? { authorization: `Bearer ${token}` } : {},
102
104
  // Astro 6 enabled `security.checkOrigin` by default for server mode:
103
105
  // any state-changing request without an `Origin` matching the server
104
106
  // host is rejected with 403. Browsers set this automatically; `fetch`
105
107
  // in Node / React Native does not. Stamping the server's own URL is
106
108
  // always valid for a legitimate client.
107
- origin: cfg.serverUrl
109
+ origin: cfg.serverUrl,
110
+ ...extra
108
111
  };
109
112
  }
110
113
  async function request(method, path, body) {
@@ -229,15 +232,15 @@ function columnize(rows, gap = " ") {
229
232
  if (rows.length === 0) return [];
230
233
  const colCount = rows[0]?.length ?? 0;
231
234
  const widths = new Array(colCount).fill(0);
232
- for (const row of rows) {
235
+ for (const row2 of rows) {
233
236
  for (let i = 0; i < colCount; i++) {
234
- const cell = row[i] ?? "";
237
+ const cell = row2[i] ?? "";
235
238
  const prev = widths[i] ?? 0;
236
239
  if (cell.length > prev) widths[i] = cell.length;
237
240
  }
238
241
  }
239
242
  return rows.map(
240
- (row) => row.map((cell, i) => {
243
+ (row2) => row2.map((cell, i) => {
241
244
  if (i === colCount - 1) return cell;
242
245
  return (cell ?? "").padEnd(widths[i] ?? 0);
243
246
  }).join(gap)
@@ -1357,7 +1360,9 @@ var openaiLoginCmd = defineCommand4({
1357
1360
  console.log("(if it doesn't open automatically, paste the URL shown below)");
1358
1361
  const oauth = openaiCodexProvider().auth.oauth;
1359
1362
  if (!oauth) throw new Error("Pi openai-codex provider does not expose OAuth");
1363
+ const loginController = new AbortController();
1360
1364
  const creds = await oauth.login({
1365
+ signal: loginController.signal,
1361
1366
  notify: (event) => {
1362
1367
  if (event.type === "auth_url") {
1363
1368
  console.log("");
@@ -1444,11 +1449,20 @@ var authCommand = defineCommand4({
1444
1449
  });
1445
1450
 
1446
1451
  // src/commands/backup.ts
1447
- import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
1452
+ import {
1453
+ createCipheriv,
1454
+ createDecipheriv,
1455
+ createHash as createHash3,
1456
+ pbkdf2Sync,
1457
+ randomBytes,
1458
+ randomUUID as randomUUID2
1459
+ } from "crypto";
1448
1460
  import {
1449
1461
  chmodSync as chmodSync2,
1450
1462
  closeSync as closeSync2,
1451
1463
  copyFileSync,
1464
+ cpSync,
1465
+ createReadStream,
1452
1466
  createWriteStream,
1453
1467
  existsSync as existsSync2,
1454
1468
  fsyncSync as fsyncSync2,
@@ -1458,21 +1472,24 @@ import {
1458
1472
  openSync as openSync2,
1459
1473
  readdirSync,
1460
1474
  readFileSync as readFileSync4,
1475
+ readSync,
1461
1476
  renameSync as renameSync2,
1462
- rmSync as rmSync2
1477
+ rmSync as rmSync2,
1478
+ writeFileSync as writeFileSync2
1463
1479
  } from "fs";
1464
1480
  import { tmpdir } from "os";
1465
1481
  import { basename as basename3, dirname as dirname2, isAbsolute, parse, posix, relative, resolve as resolve2 } from "path";
1466
- import { DatabaseSync } from "node:sqlite";
1482
+ import { DatabaseSync, backup as sqliteBackup } from "node:sqlite";
1467
1483
  import { Readable } from "stream";
1468
1484
  import { pipeline } from "stream/promises";
1485
+ import { Decrypter, Encrypter } from "age-encryption";
1469
1486
  import { defineCommand as defineCommand5 } from "citty";
1470
1487
  import { extract, list as list3 } from "tar";
1471
1488
 
1472
1489
  // src/backup-schema.ts
1473
1490
  import { createHash } from "crypto";
1474
1491
  var CANONICAL_MIGRATION = "0001_init";
1475
- var CANONICAL_SCHEMA_HASH = "7c0867f97bd6a7d9c8a89f43f6ae7e5259c3976d40cd81a2061c43c30b7590a1";
1492
+ var CANONICAL_SCHEMA_HASH = "4bde209eb0bf9851bf1f01fc96263f165a1799c81e720ddea463169a0d6c0d51";
1476
1493
  var CANONICAL_OBJECTS = [
1477
1494
  ["index", "agent_triggers_agent"],
1478
1495
  ["index", "agent_triggers_enabled"],
@@ -1497,6 +1514,8 @@ var CANONICAL_OBJECTS = [
1497
1514
  ["index", "trigger_dispatches_claimable"],
1498
1515
  ["index", "trigger_dispatches_trigger_time"],
1499
1516
  ["index", "web_tokens_active"],
1517
+ ["index", "web_sessions_active"],
1518
+ ["index", "web_sessions_device"],
1500
1519
  ["table", "agent_skills"],
1501
1520
  ["table", "agent_loop_break_events"],
1502
1521
  ["table", "agent_lesson_proposals"],
@@ -1531,9 +1550,11 @@ var CANONICAL_OBJECTS = [
1531
1550
  ["table", "team_templates"],
1532
1551
  ["table", "teams"],
1533
1552
  ["table", "telegram_allowed_users"],
1553
+ ["table", "telegram_pairing_challenge"],
1534
1554
  ["table", "template_instantiations"],
1535
1555
  ["table", "trigger_dispatches"],
1536
1556
  ["table", "web_tokens"],
1557
+ ["table", "web_sessions"],
1537
1558
  ["trigger", "create_team_policy"],
1538
1559
  ["trigger", "prevent_detached_team_policy_delete"],
1539
1560
  ["trigger", "validate_source_binding_insert"],
@@ -1549,7 +1570,7 @@ function assertCanonicalBackupSchema(db) {
1549
1570
  const migrations = db.prepare("SELECT version FROM schema_migrations ORDER BY version").all();
1550
1571
  if (migrations.length !== 1 || migrations[0]?.version !== CANONICAL_MIGRATION) {
1551
1572
  throw new Error(
1552
- `schema_migrations must contain exactly ${CANONICAL_MIGRATION}; found ` + (migrations.map((row) => row.version).join(", ") || "none")
1573
+ `schema_migrations must contain exactly ${CANONICAL_MIGRATION}; found ` + (migrations.map((row2) => row2.version).join(", ") || "none")
1553
1574
  );
1554
1575
  }
1555
1576
  const rows = db.prepare(
@@ -1557,20 +1578,20 @@ function assertCanonicalBackupSchema(db) {
1557
1578
  FROM sqlite_schema
1558
1579
  WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'`
1559
1580
  ).all();
1560
- const byKey = new Map(rows.map((row) => [`${row.type}\0${row.name}`, row]));
1581
+ const byKey = new Map(rows.map((row2) => [`${row2.type}\0${row2.name}`, row2]));
1561
1582
  const canonicalKeys = new Set(CANONICAL_OBJECTS.map(([type, name]) => `${type}\0${name}`));
1562
- for (const row of rows) {
1563
- if (!canonicalKeys.has(`${row.type}\0${row.name}`)) {
1564
- throw new Error(`unexpected schema ${row.type} is not canonical: ${row.name}`);
1583
+ for (const row2 of rows) {
1584
+ if (!canonicalKeys.has(`${row2.type}\0${row2.name}`)) {
1585
+ throw new Error(`unexpected schema ${row2.type} is not canonical: ${row2.name}`);
1565
1586
  }
1566
1587
  }
1567
1588
  const canonicalRows = [];
1568
1589
  for (const [type, name] of CANONICAL_OBJECTS) {
1569
- const row = byKey.get(`${type}\0${name}`);
1570
- if (!row) throw new Error(`required canonical schema ${type} is missing: ${name}`);
1571
- canonicalRows.push(row);
1590
+ const row2 = byKey.get(`${type}\0${name}`);
1591
+ if (!row2) throw new Error(`required canonical schema ${type} is missing: ${name}`);
1592
+ canonicalRows.push(row2);
1572
1593
  }
1573
- const payload = canonicalRows.map((row) => [row.type, row.name, row.tbl_name, normalizeSql(row.sql)].join("\0")).join("\n");
1594
+ const payload = canonicalRows.map((row2) => [row2.type, row2.name, row2.tbl_name, normalizeSql(row2.sql)].join("\0")).join("\n");
1574
1595
  const actualHash = createHash("sha256").update(payload).digest("hex");
1575
1596
  if (actualHash !== CANONICAL_SCHEMA_HASH) {
1576
1597
  throw new Error(
@@ -1886,18 +1907,34 @@ async function acquireHomeRestoreLock(home) {
1886
1907
 
1887
1908
  // src/commands/backup.ts
1888
1909
  var createCmd = defineCommand5({
1889
- meta: { name: "create", description: "Download a tar.gz backup of ~/.bazilion from the server" },
1910
+ meta: { name: "create", description: "Create a client-side encrypted backup of ~/.bazilion" },
1890
1911
  args: {
1891
1912
  output: {
1892
1913
  type: "positional",
1893
1914
  required: false,
1894
- description: "Output file path (default: ./bazilion-backup-YYYY-MM-DD.tar.gz)"
1915
+ description: "Output path (default: .tar.gz.age with --recipient, otherwise .tar.gz)"
1916
+ },
1917
+ recipient: {
1918
+ type: "string",
1919
+ description: "age recipient (age1\u2026 or age1pq1\u2026); encrypts before writing output"
1920
+ },
1921
+ plaintext: {
1922
+ type: "boolean",
1923
+ description: "Explicitly permit a sensitive unencrypted .tar.gz backup"
1895
1924
  }
1896
1925
  },
1897
1926
  async run({ args }) {
1927
+ if (args.recipient && args.plaintext) {
1928
+ throw new Error("--recipient and --plaintext are mutually exclusive");
1929
+ }
1930
+ if (!args.recipient && !args.plaintext) {
1931
+ throw new Error("choose --recipient <age1\u2026> for encryption or explicitly pass --plaintext");
1932
+ }
1898
1933
  const cfg = loadClientConfig();
1899
1934
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1900
- const outAbs = resolve2(args.output ?? `bazilion-backup-${date}.tar.gz`);
1935
+ const defaultSuffix = args.recipient ? ".tar.gz.age" : ".tar.gz";
1936
+ const outAbs = resolve2(args.output ?? `bazilion-backup-${date}${defaultSuffix}`);
1937
+ if (existsSync2(outAbs)) throw new Error(`refusing to overwrite existing backup: ${outAbs}`);
1901
1938
  const configuredHome = resolve2(resolveCliPaths().home);
1902
1939
  const outputFromHome = relative(configuredHome, outAbs);
1903
1940
  if (!outputFromHome || !outputFromHome.startsWith("..") && !isAbsolute(outputFromHome)) {
@@ -1905,7 +1942,10 @@ var createCmd = defineCommand5({
1905
1942
  `backup output must be outside BAZILION_HOME (${configuredHome}) to avoid nesting backups`
1906
1943
  );
1907
1944
  }
1908
- console.log(`downloading backup \u2192 ${outAbs}`);
1945
+ if (!args.recipient) {
1946
+ console.warn("warning: creating a plaintext backup containing all Bazilion credentials");
1947
+ }
1948
+ console.log(`${args.recipient ? "encrypting" : "downloading"} backup \u2192 ${outAbs}`);
1909
1949
  const res = await fetch(`${cfg.serverUrl}/api/backup`, {
1910
1950
  headers: { authorization: `Bearer ${cfg.token}`, origin: cfg.serverUrl }
1911
1951
  });
@@ -1921,11 +1961,25 @@ var createCmd = defineCommand5({
1921
1961
  const downloadDir = mkdtempSync(resolve2(tmpdir(), "bazilion-download-"));
1922
1962
  const partial = resolve2(downloadDir, "backup.tar.gz");
1923
1963
  try {
1924
- await pipeline(
1925
- Readable.fromWeb(res.body),
1926
- createWriteStream(partial, { flags: "wx", mode: 384 })
1927
- );
1928
- await validateArchive(partial);
1964
+ if (args.recipient) {
1965
+ const [encryptSource, validateSource] = res.body.tee();
1966
+ const encrypter = new Encrypter();
1967
+ encrypter.addRecipient(args.recipient);
1968
+ const encrypted = await encrypter.encrypt(encryptSource);
1969
+ await Promise.all([
1970
+ pipeline(
1971
+ Readable.fromWeb(encrypted),
1972
+ createWriteStream(partial, { flags: "wx", mode: 384 })
1973
+ ),
1974
+ validateArchiveReadable(Readable.fromWeb(validateSource))
1975
+ ]);
1976
+ } else {
1977
+ await pipeline(
1978
+ Readable.fromWeb(res.body),
1979
+ createWriteStream(partial, { flags: "wx", mode: 384 })
1980
+ );
1981
+ }
1982
+ if (!args.recipient) await validateArchive(partial);
1929
1983
  try {
1930
1984
  renameSync2(partial, outAbs);
1931
1985
  } catch (error) {
@@ -2055,6 +2109,27 @@ async function validateArchive(file) {
2055
2109
  entries.push({ path, type: entry2.type, linkpath: entry2.linkpath });
2056
2110
  }
2057
2111
  });
2112
+ return validateArchiveEntries(entries);
2113
+ }
2114
+ async function validateArchiveReadable(stream) {
2115
+ const entries = [];
2116
+ const parser = list3({
2117
+ strict: true,
2118
+ onentry(entry2) {
2119
+ if (entry2.meta) return;
2120
+ const path = normalizeArchivePath(entry2.path);
2121
+ if (!path) return;
2122
+ if (!EXTRACTABLE_ENTRY_TYPES.has(entry2.type)) {
2123
+ throw new Error(`backup contains unsupported ${entry2.type} entry: ${path}`);
2124
+ }
2125
+ if (entry2.type === "SymbolicLink") validateArchiveSymbolicLink(path, entry2.linkpath);
2126
+ entries.push({ path, type: entry2.type, linkpath: entry2.linkpath });
2127
+ }
2128
+ });
2129
+ await pipeline(stream, parser);
2130
+ return validateArchiveEntries(entries);
2131
+ }
2132
+ function validateArchiveEntries(entries) {
2058
2133
  const byPath = /* @__PURE__ */ new Map();
2059
2134
  for (const entry2 of entries) {
2060
2135
  if (byPath.has(entry2.path)) throw new Error(`backup contains a duplicate path: ${entry2.path}`);
@@ -2113,7 +2188,7 @@ function validateDatabase(path, authToken) {
2113
2188
  db = new DatabaseSync(path, { readOnly: true });
2114
2189
  const integrity = db.prepare("PRAGMA integrity_check").all();
2115
2190
  if (integrity.length !== 1 || integrity[0]?.integrity_check !== "ok") {
2116
- const detail = integrity.map((row) => row.integrity_check).join("; ") || "no result";
2191
+ const detail = integrity.map((row2) => row2.integrity_check).join("; ") || "no result";
2117
2192
  throw new Error(`integrity_check failed: ${detail}`);
2118
2193
  }
2119
2194
  const foreignKeyErrors = db.prepare("PRAGMA foreign_key_check").all();
@@ -2124,7 +2199,7 @@ function validateDatabase(path, authToken) {
2124
2199
  const tokenHash = createHash3("sha256").update(authToken).digest("hex");
2125
2200
  const activeBootstrap = db.prepare(
2126
2201
  `SELECT 1 FROM web_tokens
2127
- WHERE token_hash = ? AND label = 'bootstrap' AND revoked_at IS NULL
2202
+ WHERE token_hash = ? AND kind = 'bootstrap' AND revoked_at IS NULL
2128
2203
  LIMIT 1`
2129
2204
  ).get(tokenHash);
2130
2205
  if (!activeBootstrap) {
@@ -2187,8 +2262,8 @@ function rebaseRestoredHomeDirectories(database, payload, targetHome) {
2187
2262
  const collection = table;
2188
2263
  const stagedRoot = resolve2(payload, collection);
2189
2264
  const targetRoot = resolve2(targetHome, collection);
2190
- for (const row of rows) {
2191
- const id = validateEntityId(row.id, idPattern, `${table} row`);
2265
+ for (const row2 of rows) {
2266
+ const id = validateEntityId(row2.id, idPattern, `${table} row`);
2192
2267
  const rowLabel = `${table} row ${JSON.stringify(id)}`;
2193
2268
  const stagedDir = containedChildDirectory(stagedRoot, id, rowLabel);
2194
2269
  requireExtractedDirectory(stagedDir, rowLabel);
@@ -2200,8 +2275,8 @@ function rebaseRestoredHomeDirectories(database, payload, targetHome) {
2200
2275
  }
2201
2276
  }
2202
2277
  const stagedTeamsRoot = resolve2(payload, "teams");
2203
- for (const row of teams) {
2204
- const id = validateEntityId(row.id, SLUG_ID, "teams row");
2278
+ for (const row2 of teams) {
2279
+ const id = validateEntityId(row2.id, SLUG_ID, "teams row");
2205
2280
  const rowLabel = `teams row ${JSON.stringify(id)}`;
2206
2281
  requireExtractedTeamSlot(containedChildDirectory(stagedTeamsRoot, id, rowLabel), rowLabel);
2207
2282
  }
@@ -2222,8 +2297,8 @@ function rebaseRestoredHomeDirectories(database, payload, targetHome) {
2222
2297
  const readProfile = db.prepare("SELECT dir FROM profiles WHERE id = ?");
2223
2298
  const readAgent = db.prepare("SELECT dir FROM agents WHERE id = ?");
2224
2299
  for (const rewrite of rewrites) {
2225
- const row = rewrite.table === "profiles" ? readProfile.get(rewrite.id) : readAgent.get(rewrite.id);
2226
- if (row?.dir !== rewrite.dir) {
2300
+ const row2 = rewrite.table === "profiles" ? readProfile.get(rewrite.id) : readAgent.get(rewrite.id);
2301
+ if (row2?.dir !== rewrite.dir) {
2227
2302
  throw new Error(
2228
2303
  `could not verify rebased ${rewrite.table} row ${JSON.stringify(rewrite.id)}`
2229
2304
  );
@@ -2261,6 +2336,14 @@ function removeRebuildableQmdIndexes(payload) {
2261
2336
  }
2262
2337
  }
2263
2338
  }
2339
+ function revokeRestoredBrowserSessions(database) {
2340
+ const db = new DatabaseSync(database);
2341
+ try {
2342
+ db.prepare("UPDATE web_sessions SET revoked_at = ? WHERE revoked_at IS NULL").run(Date.now());
2343
+ } finally {
2344
+ db.close();
2345
+ }
2346
+ }
2264
2347
  function installValidatedPayload(payload, targetHome, staging, lifecycle, operations = { rename: renameSync2 }) {
2265
2348
  const previous = resolve2(staging, "previous-home");
2266
2349
  const hadPrevious = existsSync2(targetHome);
@@ -2374,6 +2457,37 @@ function fsyncDirectory(path) {
2374
2457
  if (fd !== null) closeSync2(fd);
2375
2458
  }
2376
2459
  }
2460
+ function fsyncFile(path) {
2461
+ const fd = openSync2(path, "r");
2462
+ try {
2463
+ fsyncSync2(fd);
2464
+ } finally {
2465
+ closeSync2(fd);
2466
+ }
2467
+ }
2468
+ var AGE_HEADER = Buffer.from("age-encryption.org/v1\n", "ascii");
2469
+ function isAgeEncrypted(path) {
2470
+ const fd = openSync2(path, "r");
2471
+ try {
2472
+ const header = Buffer.alloc(AGE_HEADER.length);
2473
+ return readSync(fd, header, 0, header.length, 0) === header.length && header.equals(AGE_HEADER);
2474
+ } finally {
2475
+ closeSync2(fd);
2476
+ }
2477
+ }
2478
+ function readAgeIdentity(rawPath) {
2479
+ const path = resolve2(rawPath);
2480
+ const entry2 = lstatSync2(path);
2481
+ if (!entry2.isFile()) throw new Error(`age identity is not a regular file: ${path}`);
2482
+ if (process.platform !== "win32" && (entry2.mode & 63) !== 0) {
2483
+ throw new Error(`age identity must be owner-only (chmod 600): ${path}`);
2484
+ }
2485
+ const identities = readFileSync4(path, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
2486
+ if (identities.length !== 1 || !identities[0]?.startsWith("AGE-SECRET-KEY-")) {
2487
+ throw new Error("age identity file must contain exactly one supported AGE-SECRET-KEY entry");
2488
+ }
2489
+ return identities[0];
2490
+ }
2377
2491
  var restoreCmd = defineCommand5({
2378
2492
  meta: {
2379
2493
  name: "restore",
@@ -2389,6 +2503,10 @@ var restoreCmd = defineCommand5({
2389
2503
  force: {
2390
2504
  type: "boolean",
2391
2505
  description: "Overwrite existing non-empty home (destroys existing data)"
2506
+ },
2507
+ identity: {
2508
+ type: "string",
2509
+ description: "Path to an age identity file required for encrypted backups"
2392
2510
  }
2393
2511
  },
2394
2512
  async run({ args }) {
@@ -2429,7 +2547,36 @@ var restoreCmd = defineCommand5({
2429
2547
  const payload = resolve2(staging, "payload");
2430
2548
  let preserveStaging = false;
2431
2549
  try {
2432
- copyFileSync(file, stagedArchive);
2550
+ if (isAgeEncrypted(file)) {
2551
+ if (!args.identity) {
2552
+ throw new Error("encrypted backup requires --identity <owner-only age identity file>");
2553
+ }
2554
+ const identity = readAgeIdentity(args.identity);
2555
+ const decrypter = new Decrypter();
2556
+ decrypter.addIdentity(identity);
2557
+ let decrypted;
2558
+ try {
2559
+ decrypted = await decrypter.decrypt(
2560
+ Readable.toWeb(createReadStream(file))
2561
+ );
2562
+ } catch {
2563
+ throw new Error("backup decryption failed: wrong identity or invalid encrypted header");
2564
+ }
2565
+ try {
2566
+ await pipeline(
2567
+ Readable.fromWeb(decrypted),
2568
+ createWriteStream(stagedArchive, { flags: "wx", mode: 384 })
2569
+ );
2570
+ } catch {
2571
+ throw new Error("backup decryption failed: ciphertext is truncated or modified");
2572
+ }
2573
+ } else {
2574
+ if (args.identity) {
2575
+ throw new Error("encrypted-only restore requested, but the backup is plaintext");
2576
+ }
2577
+ copyFileSync(file, stagedArchive);
2578
+ chmodSync2(stagedArchive, 384);
2579
+ }
2433
2580
  await validateArchive(stagedArchive);
2434
2581
  mkdirSync2(payload);
2435
2582
  await extract({
@@ -2451,6 +2598,7 @@ var restoreCmd = defineCommand5({
2451
2598
  const authToken = validateAuthFile(authFile);
2452
2599
  validateDatabase(database, authToken);
2453
2600
  rebaseRestoredHomeDirectories(database, payload, targetHome);
2601
+ revokeRestoredBrowserSessions(database);
2454
2602
  validateDatabase(database, authToken);
2455
2603
  removeRebuildableQmdIndexes(payload);
2456
2604
  console.log(`installing validated backup ${file} \u2192 ${targetHome}`);
@@ -2481,11 +2629,200 @@ var restoreCmd = defineCommand5({
2481
2629
  console.log("start the server with: bazilion serve (migrations apply automatically)");
2482
2630
  }
2483
2631
  });
2632
+ function decryptSecretEnvelope(raw, password) {
2633
+ const envelope = JSON.parse(raw);
2634
+ const key = pbkdf2Sync(password, Buffer.from(envelope.salt, "hex"), 1e5, 32, "sha256");
2635
+ const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(envelope.iv, "hex"));
2636
+ decipher.setAuthTag(Buffer.from(envelope.tag, "hex"));
2637
+ return Buffer.concat([
2638
+ decipher.update(Buffer.from(envelope.data, "hex")),
2639
+ decipher.final()
2640
+ ]).toString("utf8");
2641
+ }
2642
+ function encryptSecretEnvelope(value, password) {
2643
+ const salt = randomBytes(16);
2644
+ const key = pbkdf2Sync(password, salt, 1e5, 32, "sha256");
2645
+ const iv = randomBytes(12);
2646
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
2647
+ const data = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
2648
+ return JSON.stringify({
2649
+ salt: salt.toString("hex"),
2650
+ iv: iv.toString("hex"),
2651
+ tag: cipher.getAuthTag().toString("hex"),
2652
+ data: data.toString("hex")
2653
+ });
2654
+ }
2655
+ function credentialInventory(home) {
2656
+ const authFile = resolve2(home, "auth.json");
2657
+ const database = resolve2(home, "bazilion.db");
2658
+ const token = validateAuthFile(authFile);
2659
+ validateDatabase(database, token);
2660
+ const db = new DatabaseSync(database, { readOnly: true });
2661
+ try {
2662
+ const activeTokens = db.prepare(
2663
+ "SELECT COUNT(*) AS count FROM web_tokens WHERE revoked_at IS NULL AND kind = 'device' AND expires_at > ?"
2664
+ ).get(Date.now());
2665
+ const secretKeys = db.prepare("SELECT key FROM secrets ORDER BY key").all().map((row2) => row2.key);
2666
+ return { bootstrap: true, activeTokens: activeTokens.count, secretKeys };
2667
+ } finally {
2668
+ db.close();
2669
+ }
2670
+ }
2671
+ var inventoryCmd = defineCommand5({
2672
+ meta: { name: "inventory", description: "List credential classes captured by a backup/home" },
2673
+ args: { home: { type: "string", description: "Override BAZILION_HOME" } },
2674
+ run({ args }) {
2675
+ const home = resolve2(resolveCliPaths(args.home).home);
2676
+ const inventory = credentialInventory(home);
2677
+ console.log("credential classes present (values are never shown):");
2678
+ console.log(` bootstrap token: ${inventory.bootstrap ? "present" : "missing"}`);
2679
+ console.log(` active device/web/mobile tokens: ${inventory.activeTokens}`);
2680
+ for (const key of inventory.secretKeys) console.log(` stored secret: ${key}`);
2681
+ }
2682
+ });
2683
+ var recoveryGuideCmd = defineCommand5({
2684
+ meta: { name: "recovery-guide", description: "Print the credential-exposure recovery checklist" },
2685
+ run() {
2686
+ console.log("Bazilion credential exposure recovery:");
2687
+ console.log("1. Stop the Bazilion daemon and preserve the suspected archive for investigation.");
2688
+ console.log(
2689
+ "2. Run `bazilion backup inventory`, then `bazilion backup rotate-bootstrap --yes`."
2690
+ );
2691
+ console.log("3. Regenerate the Telegram bot token with BotFather and save the replacement.");
2692
+ console.log(
2693
+ "4. Disconnect/revoke the affected OpenAI session or grant, then authenticate again."
2694
+ );
2695
+ console.log("5. Rotate every provider or MCP credential named by the inventory.");
2696
+ console.log("6. Create and verify a fresh recipient-encrypted backup after external rotation.");
2697
+ console.log(
2698
+ "Local bootstrap rotation does not revoke copied Telegram/OpenAI/provider credentials."
2699
+ );
2700
+ }
2701
+ });
2702
+ var rotateBootstrapCmd = defineCommand5({
2703
+ meta: {
2704
+ name: "rotate-bootstrap",
2705
+ description: "Offline crash-safe bootstrap rotation; revokes every other local token"
2706
+ },
2707
+ args: {
2708
+ home: { type: "string", description: "Override BAZILION_HOME" },
2709
+ yes: { type: "boolean", required: true, description: "Confirm token revocation and home swap" }
2710
+ },
2711
+ async run({ args }) {
2712
+ if (!args.yes) throw new Error("--yes is required");
2713
+ const paths = resolveCliPaths(args.home);
2714
+ const targetHome = resolve2(paths.home);
2715
+ if (targetHome === parse(targetHome).root) {
2716
+ throw new Error(`refusing to rotate filesystem root: ${targetHome}`);
2717
+ }
2718
+ const lock = await acquireHomeRestoreLock(targetHome);
2719
+ const parent = dirname2(targetHome);
2720
+ const staging = mkdtempSync(resolve2(parent, ".bazilion-rotate-"));
2721
+ const payload = resolve2(staging, "payload");
2722
+ let preserveStaging = false;
2723
+ try {
2724
+ for (const serverUrl of configuredLoopbackUrls()) {
2725
+ if (await probeServerRunning(serverUrl)) {
2726
+ throw new Error(`bazilion server appears to be running at ${serverUrl} \u2014 stop it first.`);
2727
+ }
2728
+ }
2729
+ const oldAuth = JSON.parse(readFileSync4(paths.authFile, "utf8"));
2730
+ if (typeof oldAuth.token !== "string" || !oldAuth.token) {
2731
+ throw new Error("auth.json is missing the bootstrap token");
2732
+ }
2733
+ validateDatabase(resolve2(targetHome, "bazilion.db"), oldAuth.token);
2734
+ cpSync(targetHome, payload, {
2735
+ recursive: true,
2736
+ dereference: false,
2737
+ verbatimSymlinks: true,
2738
+ preserveTimestamps: true
2739
+ });
2740
+ const stagedDb = resolve2(payload, "bazilion.db");
2741
+ rmSync2(stagedDb, { force: true });
2742
+ const sourceDb = new DatabaseSync(resolve2(targetHome, "bazilion.db"), { readOnly: true });
2743
+ try {
2744
+ await sqliteBackup(sourceDb, stagedDb);
2745
+ } finally {
2746
+ sourceDb.close();
2747
+ }
2748
+ for (const suffix of ["-wal", "-shm", "-journal"])
2749
+ rmSync2(`${stagedDb}${suffix}`, { force: true });
2750
+ const newToken = randomBytes(24).toString("hex");
2751
+ const db = new DatabaseSync(stagedDb);
2752
+ try {
2753
+ db.exec("PRAGMA foreign_keys = ON; BEGIN IMMEDIATE");
2754
+ try {
2755
+ const rows = db.prepare("SELECT key, envelope FROM secrets ORDER BY key").all();
2756
+ const updateSecret = db.prepare(
2757
+ "UPDATE secrets SET envelope = ?, updated_at = ? WHERE key = ?"
2758
+ );
2759
+ for (const row2 of rows) {
2760
+ let value;
2761
+ try {
2762
+ value = decryptSecretEnvelope(row2.envelope, oldAuth.token);
2763
+ } catch {
2764
+ throw new Error(`stored secret is unreadable and rotation was aborted: ${row2.key}`);
2765
+ }
2766
+ updateSecret.run(encryptSecretEnvelope(value, newToken), Date.now(), row2.key);
2767
+ }
2768
+ const oldHash = createHash3("sha256").update(oldAuth.token).digest("hex");
2769
+ const newHash = createHash3("sha256").update(newToken).digest("hex");
2770
+ const bootstrap = db.prepare(
2771
+ "UPDATE web_tokens SET token_hash = ?, last_used_at = NULL, revoked_at = NULL WHERE token_hash = ? AND kind = 'bootstrap' AND revoked_at IS NULL"
2772
+ ).run(newHash, oldHash);
2773
+ if (bootstrap.changes !== 1) throw new Error("active bootstrap token row was not unique");
2774
+ const revokedAt = Date.now();
2775
+ db.prepare(
2776
+ "UPDATE web_tokens SET revoked_at = ? WHERE kind != 'bootstrap' AND revoked_at IS NULL"
2777
+ ).run(revokedAt);
2778
+ db.prepare("UPDATE web_sessions SET revoked_at = ? WHERE revoked_at IS NULL").run(
2779
+ revokedAt
2780
+ );
2781
+ db.exec("COMMIT");
2782
+ } catch (error) {
2783
+ db.exec("ROLLBACK");
2784
+ throw error;
2785
+ }
2786
+ } finally {
2787
+ db.close();
2788
+ }
2789
+ const stagedAuth = resolve2(payload, "auth.json");
2790
+ const authPartial = resolve2(payload, `.auth.json.${randomUUID2()}.installing`);
2791
+ writeFileSync2(authPartial, `${JSON.stringify({ ...oldAuth, token: newToken }, null, 2)}
2792
+ `, {
2793
+ mode: 384,
2794
+ flag: "wx"
2795
+ });
2796
+ renameSync2(authPartial, stagedAuth);
2797
+ chmodSync2(stagedAuth, 384);
2798
+ validateDatabase(stagedDb, newToken);
2799
+ const verification = credentialInventory(payload);
2800
+ if (!verification.bootstrap) throw new Error("rotated credential inventory is invalid");
2801
+ fsyncFile(stagedDb);
2802
+ fsyncFile(stagedAuth);
2803
+ fsyncDirectory(payload);
2804
+ try {
2805
+ installValidatedPayload(payload, targetHome, staging, lock);
2806
+ } catch (error) {
2807
+ if (error instanceof RestoreRecoveryRequiredError) preserveStaging = true;
2808
+ throw error;
2809
+ }
2810
+ console.log("bootstrap token rotated; all non-bootstrap Bazilion tokens were revoked");
2811
+ console.log("external Telegram, OpenAI, provider, and MCP credentials were not revoked");
2812
+ } finally {
2813
+ if (!preserveStaging) rmSync2(staging, { recursive: true, force: true });
2814
+ lock.release();
2815
+ }
2816
+ }
2817
+ });
2484
2818
  var backupCommand = defineCommand5({
2485
- meta: { name: "backup", description: "Create or restore a ~/.bazilion tar.gz backup" },
2819
+ meta: { name: "backup", description: "Encrypted backup, restore, and credential recovery" },
2486
2820
  subCommands: {
2487
2821
  create: createCmd,
2488
- restore: restoreCmd
2822
+ restore: restoreCmd,
2823
+ inventory: inventoryCmd,
2824
+ "recovery-guide": recoveryGuideCmd,
2825
+ "rotate-bootstrap": rotateBootstrapCmd
2489
2826
  }
2490
2827
  // `bazilion backup` with no positional falls through to citty's subcommand
2491
2828
  // prompt ("No command specified"). The previous default (auto-download to
@@ -2927,7 +3264,7 @@ var doctorCommand = defineCommand9({
2927
3264
  meta: { name: "doctor", description: "Diagnose your bazilion install" },
2928
3265
  async run() {
2929
3266
  const client = createClient2();
2930
- const r = await client.get("/api/health");
3267
+ const r = await client.get("/api/health/details");
2931
3268
  function check(label, condition, hint) {
2932
3269
  const mark = condition ? "\u2713" : "\u2717";
2933
3270
  const tail = !condition && hint ? ` \u2014 ${hint}` : "";
@@ -2972,22 +3309,24 @@ var doctorCommand = defineCommand9({
2972
3309
  }
2973
3310
  }
2974
3311
  console.log(
2975
- ` \u2713 lmstudio: ${r.providers.lmstudio.baseURL}${r.providers.lmstudio.hasKey ? " (api key set)" : ""}`
3312
+ ` \u2713 lmstudio: ${r.providers.lmstudio.customEndpointConfigured ? "custom endpoint configured" : "default local endpoint"}${r.providers.lmstudio.keyConfigured ? " (API key configured)" : ""}`
3313
+ );
3314
+ console.log(
3315
+ ` \u2713 ollama: ${r.providers.ollama.customEndpointConfigured ? "custom endpoint configured" : "default local endpoint"}`
2976
3316
  );
2977
- console.log(` \u2713 ollama: ${r.providers.ollama.baseURL}`);
2978
3317
  console.log();
2979
3318
  console.log("web search (at least one needed for web_search tool)");
2980
- if (r.webSearch.bravePreview) {
2981
- console.log(` \u2713 Brave Search (BRAVE_API_KEY set, ${r.webSearch.bravePreview})`);
3319
+ if (r.webSearch.braveConfigured) {
3320
+ console.log(" \u2713 Brave Search configured");
2982
3321
  } else {
2983
3322
  console.log(" - Brave Search (set BRAVE_API_KEY \u2014 free at https://brave.com/search/api/)");
2984
3323
  }
2985
- if (r.webSearch.searxngUrl) {
2986
- console.log(` \u2713 SearXNG: ${r.webSearch.searxngUrl}`);
3324
+ if (r.webSearch.searxngConfigured) {
3325
+ console.log(" \u2713 SearXNG configured");
2987
3326
  } else {
2988
3327
  console.log(" - SearXNG (set SEARXNG_URL if you self-host one)");
2989
3328
  }
2990
- if (!r.webSearch.bravePreview && !r.webSearch.searxngUrl) {
3329
+ if (!r.webSearch.braveConfigured && !r.webSearch.searxngConfigured) {
2991
3330
  console.log(" \u26A0 no search backend \u2014 web_search will error until one is configured");
2992
3331
  }
2993
3332
  console.log();
@@ -3004,35 +3343,7 @@ var doctorCommand = defineCommand9({
3004
3343
  ` ${r.scheduler.enabled ? "\u2713" : "-"} scheduler${r.scheduler.enabled ? ` (tick ${r.scheduler.tickMs}ms)` : " (BAZILION_SCHEDULER=off)"}`
3005
3344
  );
3006
3345
  console.log();
3007
- console.log("agent shell security");
3008
- if (!r.shellSecurity.ok) {
3009
- check("valid shell-security configuration", false, r.shellSecurity.error);
3010
- } else {
3011
- if (r.shellSecurity.approvalMode === "dangerous") {
3012
- console.log(" - dangerous-command approval enabled");
3013
- console.log(
3014
- " web and TTY CLI turns can allow once; background and non-TTY turns auto-deny"
3015
- );
3016
- } else {
3017
- console.log(" - dangerous-command approval off");
3018
- console.log(" set BAZILION_BASH_APPROVAL=dangerous to opt in");
3019
- }
3020
- if (r.shellSecurity.sandboxMode === "docker") {
3021
- console.log(` - Docker sandbox configured (${r.shellSecurity.sandboxImage})`);
3022
- console.log(
3023
- " workspace-only writable mount \xB7 bounded inputs/skills/memory read-only \xB7 network disabled"
3024
- );
3025
- console.log(" host coding tools hidden");
3026
- console.log(
3027
- " policy syntax is valid; Docker and image availability are checked on execution"
3028
- );
3029
- console.log(" commands fail closed if either is unavailable");
3030
- } else {
3031
- console.log(" - sandbox off (agent shell and coding tools run on the host)");
3032
- console.log(" approval is a host-execution tripwire, not a filesystem sandbox");
3033
- console.log(" set BAZILION_BASH_SANDBOX=docker to opt in");
3034
- }
3035
- }
3346
+ for (const line of executionSecurityDoctorLines(r)) console.log(line);
3036
3347
  console.log();
3037
3348
  console.log("operational counts");
3038
3349
  console.log(` ${r.triggers.active} active trigger(s), ${r.triggers.disabled} disabled`);
@@ -3041,7 +3352,7 @@ var doctorCommand = defineCommand9({
3041
3352
  const hasProfiles = (r.database?.ok ? r.database.profiles : 0) > 0;
3042
3353
  const hasAgents = (r.database?.ok ? r.database.totalAgents : 0) > 0;
3043
3354
  console.log();
3044
- if (!r.ok) {
3355
+ if (doctorHasIssues(r)) {
3045
3356
  console.log("issues found \u2717");
3046
3357
  process.exit(1);
3047
3358
  }
@@ -3063,9 +3374,150 @@ var doctorCommand = defineCommand9({
3063
3374
  }
3064
3375
  }
3065
3376
  });
3377
+ function executionSecurityDoctorLines(report) {
3378
+ const configured = report.executionSecurity.configuredOperatorHttp;
3379
+ const protectedTurns = report.executionSecurity.protectedUnattendedTurns;
3380
+ const lines = ["configured operator HTTP (legacy \xB7 unprotected)"];
3381
+ if (!report.shellSecurity.ok) {
3382
+ lines.push(checkLine("valid shell-security configuration", false, report.shellSecurity.error));
3383
+ } else {
3384
+ if (report.shellSecurity.approvalMode === "dangerous") {
3385
+ lines.push(
3386
+ " - dangerous-command approval enabled",
3387
+ " web and TTY CLI turns can allow once; non-interactive operator requests auto-deny"
3388
+ );
3389
+ } else {
3390
+ lines.push(
3391
+ " - dangerous-command approval off",
3392
+ " set BAZILION_BASH_APPROVAL=dangerous to opt in"
3393
+ );
3394
+ }
3395
+ if (report.shellSecurity.sandboxMode === "docker") {
3396
+ lines.push(
3397
+ ` - coding surface: Docker only (${report.shellSecurity.sandboxImage})`,
3398
+ " workspace-only writable mount \xB7 bounded inputs/skills/memory read-only \xB7 network disabled",
3399
+ " host coding tools hidden",
3400
+ " policy syntax is valid; Docker and image availability are checked on execution",
3401
+ " commands fail closed if either is unavailable"
3402
+ );
3403
+ } else {
3404
+ lines.push(
3405
+ " - coding surface: host tools",
3406
+ " approval is a host-execution tripwire, not a filesystem sandbox",
3407
+ " set BAZILION_BASH_SANDBOX=docker to opt in"
3408
+ );
3409
+ }
3410
+ }
3411
+ lines.push(` - browser: ${configured.browser}`, ` - MCP: ${configured.mcp}`, "");
3412
+ lines.push(
3413
+ "protected unattended turns baseline (Telegram, schedules, inbox, approvals)",
3414
+ checkLine("protected base runtime ready", protectedTurns.baseRuntimeReady),
3415
+ " - coding surface: Docker only",
3416
+ checkLine(
3417
+ `Docker ready (${protectedTurns.docker.image})`,
3418
+ protectedTurns.docker.ready,
3419
+ protectedTurns.docker.reason ?? void 0
3420
+ ),
3421
+ checkLine("OpenAI Codex enabled", protectedTurns.openaiCodex.enabled),
3422
+ checkLine("ChatGPT connected", protectedTurns.openaiCodex.connected),
3423
+ ` - OpenAI access: ${openAIAccessStatus(protectedTurns.openaiCodex)}`,
3424
+ checkLine("OpenAI Codex baseline eligible", protectedTurns.openaiCodex.baselineEligible),
3425
+ ` \u2713 browser: ${protectedTurns.browser}`,
3426
+ ` \u2713 MCP: ${protectedTurns.mcp}`,
3427
+ " - every turn separately validates its selected normal/review model, bound OAuth refresh, and mounts/paths"
3428
+ );
3429
+ if (protectedTurns.remediation) {
3430
+ lines.push(` remediation: ${protectedTurns.remediation}`);
3431
+ }
3432
+ return lines;
3433
+ }
3434
+ function doctorHasIssues(report) {
3435
+ return !report.ok || !report.protectedWorkBaselineReady;
3436
+ }
3437
+ function openAIAccessStatus(status) {
3438
+ if (status.accessCurrent) return "current";
3439
+ if (status.refreshOnNextTurn) return "refresh on next turn";
3440
+ return "unavailable";
3441
+ }
3442
+ function checkLine(label, condition, hint) {
3443
+ const mark = condition ? "\u2713" : "\u2717";
3444
+ const tail = !condition && hint ? ` \u2014 ${hint}` : "";
3445
+ return ` ${mark} ${label}${tail}`;
3446
+ }
3066
3447
 
3067
- // src/commands/inbox.ts
3448
+ // src/commands/gateway.ts
3449
+ import { execFile } from "child_process";
3450
+ import { promisify } from "util";
3068
3451
  import { defineCommand as defineCommand10 } from "citty";
3452
+ var execFileAsync = promisify(execFile);
3453
+ function exactHttpsOrigin(raw) {
3454
+ if (!raw) throw new Error("BAZILION_PUBLIC_ORIGIN is required");
3455
+ const url = new URL(raw);
3456
+ if (url.protocol !== "https:" || url.username || url.password || url.pathname !== "/" || url.search || url.hash || url.origin !== raw.replace(/\/$/, "")) {
3457
+ throw new Error("BAZILION_PUBLIC_ORIGIN must be an exact HTTPS origin");
3458
+ }
3459
+ return url;
3460
+ }
3461
+ function listenerIsLoopback(output, port) {
3462
+ const suffix = `:${port}`;
3463
+ const listeners = output.split("\n").filter((line) => line.trim()).filter((line) => line.split(/\s+/).some((field) => field.endsWith(suffix)));
3464
+ return listeners.length > 0 && listeners.every((line) => /(?:127\.0\.0\.1|\[?::1\]?):\d+/.test(line));
3465
+ }
3466
+ function hasFunnel(value) {
3467
+ if (Array.isArray(value)) return value.some(hasFunnel);
3468
+ if (!value || typeof value !== "object") return false;
3469
+ return Object.entries(value).some(
3470
+ ([key, nested]) => /funnel/i.test(key) && nested !== false && nested !== null || hasFunnel(nested)
3471
+ );
3472
+ }
3473
+ var preflightCmd = defineCommand10({
3474
+ meta: { name: "preflight", description: "Read-only private Tailscale gateway safety check" },
3475
+ async run() {
3476
+ const publicOrigin = exactHttpsOrigin(process.env.BAZILION_PUBLIC_ORIGIN);
3477
+ const daemonPort = Number(process.env.PORT ?? 4321);
3478
+ const webPort = Number(process.env.WEB_PORT ?? 4322);
3479
+ const { stdout: listeners } = await execFileAsync("ss", ["-ltnH"], { timeout: 5e3 });
3480
+ if (!listenerIsLoopback(listeners, daemonPort)) {
3481
+ throw new Error(`daemon port ${daemonPort} is not listening exclusively on loopback`);
3482
+ }
3483
+ if (!listenerIsLoopback(listeners, webPort)) {
3484
+ throw new Error(`web port ${webPort} is not listening exclusively on loopback`);
3485
+ }
3486
+ let serveStatus;
3487
+ try {
3488
+ const { stdout: stdout2 } = await execFileAsync("tailscale", ["serve", "status", "--json"], {
3489
+ timeout: 1e4,
3490
+ maxBuffer: 1024 * 1024
3491
+ });
3492
+ serveStatus = JSON.parse(stdout2);
3493
+ } catch {
3494
+ throw new Error("could not obtain unambiguous `tailscale serve status --json` evidence");
3495
+ }
3496
+ if (hasFunnel(serveStatus))
3497
+ throw new Error("Tailscale Funnel is enabled; private Serve is required");
3498
+ const serialized = JSON.stringify(serveStatus);
3499
+ if (!serialized.includes(`127.0.0.1:${webPort}`) && !serialized.includes(`localhost:${webPort}`)) {
3500
+ throw new Error(`Tailscale Serve does not clearly target the loopback web port ${webPort}`);
3501
+ }
3502
+ const health = await createClient2().get("/api/health/details");
3503
+ if (!health.protectedWorkBaselineReady) {
3504
+ throw new Error("BAZ-027 protected-turn baseline is not ready");
3505
+ }
3506
+ console.log("private gateway preflight passed");
3507
+ console.log(`public origin: ${publicOrigin.origin}`);
3508
+ console.log(`daemon: loopback:${daemonPort}`);
3509
+ console.log(`web: loopback:${webPort}`);
3510
+ console.log("Tailscale: private Serve target present; Funnel absent");
3511
+ console.log("hosted Agent turns: protected baseline ready");
3512
+ }
3513
+ });
3514
+ var gatewayCommand = defineCommand10({
3515
+ meta: { name: "gateway", description: "Inspect the private web gateway posture" },
3516
+ subCommands: { preflight: preflightCmd }
3517
+ });
3518
+
3519
+ // src/commands/inbox.ts
3520
+ import { defineCommand as defineCommand11 } from "citty";
3069
3521
  function decodeText(payload) {
3070
3522
  try {
3071
3523
  const obj = JSON.parse(payload);
@@ -3081,7 +3533,7 @@ function messageRow(m) {
3081
3533
  const preview = text2.length > 60 ? `${text2.slice(0, 60)}\u2026` : text2;
3082
3534
  return [m.id, state, when, `from:${m.fromAgentId.slice(0, 8)}`, `"${preview}"`];
3083
3535
  }
3084
- var listCmd3 = defineCommand10({
3536
+ var listCmd3 = defineCommand11({
3085
3537
  meta: { name: "list", description: "List messages in an agent inbox" },
3086
3538
  args: {
3087
3539
  agent: { type: "positional", required: true, description: "Recipient agent id" },
@@ -3100,7 +3552,7 @@ var listCmd3 = defineCommand10({
3100
3552
  for (const line of columnize(messages.map(messageRow))) console.log(line);
3101
3553
  }
3102
3554
  });
3103
- var showCmd2 = defineCommand10({
3555
+ var showCmd2 = defineCommand11({
3104
3556
  meta: { name: "show", description: "Show a single message in full" },
3105
3557
  args: {
3106
3558
  id: { type: "positional", required: true, description: "Message id" }
@@ -3120,7 +3572,7 @@ var showCmd2 = defineCommand10({
3120
3572
  console.log(decodeText(m.payload));
3121
3573
  }
3122
3574
  });
3123
- var readCmd = defineCommand10({
3575
+ var readCmd = defineCommand11({
3124
3576
  meta: { name: "read", description: "Mark a message as read" },
3125
3577
  args: {
3126
3578
  id: { type: "positional", required: true, description: "Message id" }
@@ -3131,7 +3583,7 @@ var readCmd = defineCommand10({
3131
3583
  console.log(`marked read: ${args.id}`);
3132
3584
  }
3133
3585
  });
3134
- var loopBreaksCmd = defineCommand10({
3586
+ var loopBreaksCmd = defineCommand11({
3135
3587
  meta: { name: "loop-breaks", description: "List stopped agent-message chains" },
3136
3588
  args: {
3137
3589
  agent: { type: "positional", required: true, description: "Agent id" },
@@ -3159,7 +3611,7 @@ var loopBreaksCmd = defineCommand10({
3159
3611
  console.log(line);
3160
3612
  }
3161
3613
  });
3162
- var inboxCommand = defineCommand10({
3614
+ var inboxCommand = defineCommand11({
3163
3615
  meta: { name: "inbox", description: "Inspect agent inboxes" },
3164
3616
  subCommands: {
3165
3617
  list: listCmd3,
@@ -3170,8 +3622,8 @@ var inboxCommand = defineCommand10({
3170
3622
  });
3171
3623
 
3172
3624
  // src/commands/login.ts
3173
- import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
3174
- import { defineCommand as defineCommand11 } from "citty";
3625
+ import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
3626
+ import { defineCommand as defineCommand12 } from "citty";
3175
3627
  function normalizeServer(raw) {
3176
3628
  let url;
3177
3629
  try {
@@ -3185,13 +3637,17 @@ function normalizeServer(raw) {
3185
3637
  return `${url.origin}${url.pathname.replace(/\/$/, "")}`;
3186
3638
  }
3187
3639
  async function verifyToken(server, token) {
3188
- const res = await fetch(`${server}/api/health`, {
3640
+ const res = await fetch(`${server}/api/auth/whoami`, {
3189
3641
  headers: { authorization: `Bearer ${token}`, origin: server }
3190
3642
  });
3191
3643
  if (res.status === 401) throw new Error("server rejected token (401)");
3192
- if (!res.ok) throw new Error(`server returned ${res.status} from /api/health`);
3644
+ if (!res.ok) throw new Error(`server returned ${res.status} from /api/auth/whoami`);
3645
+ const owner = await res.json();
3646
+ if (owner.publicOrigin && owner.publicOrigin !== new URL(server).origin) {
3647
+ throw new Error("server canonical private origin does not match --server");
3648
+ }
3193
3649
  }
3194
- var loginCommand = defineCommand11({
3650
+ var loginCommand = defineCommand12({
3195
3651
  meta: {
3196
3652
  name: "login",
3197
3653
  description: "Save a remote bazilion server + token pair to auth.json"
@@ -3199,7 +3655,7 @@ var loginCommand = defineCommand11({
3199
3655
  args: {
3200
3656
  server: { type: "string", description: "Remote server URL (http[s]://...)" },
3201
3657
  token: { type: "string", description: "Token issued by `bazilion token create`" },
3202
- skipVerify: { type: "boolean", description: "Don't probe /api/health before saving" },
3658
+ skipVerify: { type: "boolean", description: "Don't verify /api/auth/whoami before saving" },
3203
3659
  clear: { type: "boolean", description: "Remove the stored remote instead of setting one" }
3204
3660
  },
3205
3661
  async run({ args }) {
@@ -3216,7 +3672,7 @@ var loginCommand = defineCommand11({
3216
3672
  return;
3217
3673
  }
3218
3674
  delete auth.remote;
3219
- writeFileSync2(paths.authFile, `${JSON.stringify(auth, null, 2)}
3675
+ writeFileSync3(paths.authFile, `${JSON.stringify(auth, null, 2)}
3220
3676
  `, { mode: 384 });
3221
3677
  console.log("cleared stored remote; CLI will fall back to the local daemon");
3222
3678
  return;
@@ -3231,7 +3687,7 @@ var loginCommand = defineCommand11({
3231
3687
  await verifyToken(server, args.token);
3232
3688
  }
3233
3689
  auth.remote = { server, token: args.token };
3234
- writeFileSync2(paths.authFile, `${JSON.stringify(auth, null, 2)}
3690
+ writeFileSync3(paths.authFile, `${JSON.stringify(auth, null, 2)}
3235
3691
  `, { mode: 384 });
3236
3692
  console.log(`saved remote: ${server}`);
3237
3693
  console.log("CLI will now target this server unless BAZILION_SERVER is set in the env.");
@@ -3239,7 +3695,7 @@ var loginCommand = defineCommand11({
3239
3695
  });
3240
3696
 
3241
3697
  // src/commands/mcp.ts
3242
- import { defineCommand as defineCommand12 } from "citty";
3698
+ import { defineCommand as defineCommand13 } from "citty";
3243
3699
  function serverRow(s) {
3244
3700
  const where = s.transport === "stdio" ? `${s.command} ${s.args.join(" ")}`.trim() : s.url ?? "";
3245
3701
  return [
@@ -3251,7 +3707,7 @@ function serverRow(s) {
3251
3707
  where
3252
3708
  ];
3253
3709
  }
3254
- var addCmd = defineCommand12({
3710
+ var addCmd = defineCommand13({
3255
3711
  meta: { name: "add", description: "Register an MCP server" },
3256
3712
  args: {
3257
3713
  name: { type: "positional", required: true, description: "Unique name ([a-zA-Z0-9_])" },
@@ -3278,7 +3734,7 @@ var addCmd = defineCommand12({
3278
3734
  console.log(`${server.id} ${server.name} ${server.transport}`);
3279
3735
  }
3280
3736
  });
3281
- var listCmd4 = defineCommand12({
3737
+ var listCmd4 = defineCommand13({
3282
3738
  meta: { name: "list", description: "List MCP servers" },
3283
3739
  async run() {
3284
3740
  const client = createClient2();
@@ -3290,7 +3746,7 @@ var listCmd4 = defineCommand12({
3290
3746
  for (const line of columnize(servers.map(serverRow))) console.log(line);
3291
3747
  }
3292
3748
  });
3293
- var showCmd3 = defineCommand12({
3749
+ var showCmd3 = defineCommand13({
3294
3750
  meta: { name: "show", description: "Show one MCP server" },
3295
3751
  args: { id: { type: "positional", required: true } },
3296
3752
  async run({ args }) {
@@ -3299,7 +3755,7 @@ var showCmd3 = defineCommand12({
3299
3755
  console.log(JSON.stringify(server, null, 2));
3300
3756
  }
3301
3757
  });
3302
- var rmCmd2 = defineCommand12({
3758
+ var rmCmd2 = defineCommand13({
3303
3759
  meta: { name: "rm", description: "Delete an MCP server" },
3304
3760
  args: { id: { type: "positional", required: true } },
3305
3761
  async run({ args }) {
@@ -3308,7 +3764,7 @@ var rmCmd2 = defineCommand12({
3308
3764
  console.log(`removed MCP server ${args.id}`);
3309
3765
  }
3310
3766
  });
3311
- var enableCmd = defineCommand12({
3767
+ var enableCmd = defineCommand13({
3312
3768
  meta: { name: "enable", description: "Enable an MCP server" },
3313
3769
  args: { id: { type: "positional", required: true } },
3314
3770
  async run({ args }) {
@@ -3317,7 +3773,7 @@ var enableCmd = defineCommand12({
3317
3773
  console.log(`enabled MCP server ${args.id}`);
3318
3774
  }
3319
3775
  });
3320
- var disableCmd = defineCommand12({
3776
+ var disableCmd = defineCommand13({
3321
3777
  meta: { name: "disable", description: "Disable an MCP server" },
3322
3778
  args: { id: { type: "positional", required: true } },
3323
3779
  async run({ args }) {
@@ -3326,7 +3782,7 @@ var disableCmd = defineCommand12({
3326
3782
  console.log(`disabled MCP server ${args.id}`);
3327
3783
  }
3328
3784
  });
3329
- var testCmd = defineCommand12({
3785
+ var testCmd = defineCommand13({
3330
3786
  meta: { name: "test", description: "Connect to a server and list its tools" },
3331
3787
  args: { id: { type: "positional", required: true } },
3332
3788
  async run({ args }) {
@@ -3344,7 +3800,7 @@ var testCmd = defineCommand12({
3344
3800
  for (const t of tools) console.log(` ${t.name} ${t.description}`);
3345
3801
  }
3346
3802
  });
3347
- var mcpCommand = defineCommand12({
3803
+ var mcpCommand = defineCommand13({
3348
3804
  meta: { name: "mcp", description: "Manage MCP servers" },
3349
3805
  subCommands: {
3350
3806
  add: addCmd,
@@ -3358,11 +3814,11 @@ var mcpCommand = defineCommand12({
3358
3814
  });
3359
3815
 
3360
3816
  // src/commands/memory.ts
3361
- import { defineCommand as defineCommand13 } from "citty";
3817
+ import { defineCommand as defineCommand14 } from "citty";
3362
3818
  function encodeKey(key) {
3363
3819
  return key.split("/").map(encodeURIComponent).join("/");
3364
3820
  }
3365
- var writeCmd = defineCommand13({
3821
+ var writeCmd = defineCommand14({
3366
3822
  meta: { name: "write", description: "Write an entry into a team's shared memory" },
3367
3823
  args: {
3368
3824
  team: { type: "positional", required: true, description: "Team slug" },
@@ -3378,7 +3834,7 @@ var writeCmd = defineCommand13({
3378
3834
  console.log(`wrote ${entry2.key} (${entry2.content.length} bytes)`);
3379
3835
  }
3380
3836
  });
3381
- var readCmd2 = defineCommand13({
3837
+ var readCmd2 = defineCommand14({
3382
3838
  meta: { name: "read", description: "Read an entry from a team's shared memory" },
3383
3839
  args: {
3384
3840
  team: { type: "positional", required: true, description: "Team slug" },
@@ -3392,7 +3848,7 @@ var readCmd2 = defineCommand13({
3392
3848
  console.log(entry2.content);
3393
3849
  }
3394
3850
  });
3395
- var searchCmd = defineCommand13({
3851
+ var searchCmd = defineCommand14({
3396
3852
  meta: { name: "search", description: "Search a team's shared memory" },
3397
3853
  args: {
3398
3854
  team: { type: "positional", required: true, description: "Team slug" },
@@ -3413,7 +3869,7 @@ var searchCmd = defineCommand13({
3413
3869
  }
3414
3870
  }
3415
3871
  });
3416
- var listCmd5 = defineCommand13({
3872
+ var listCmd5 = defineCommand14({
3417
3873
  meta: { name: "list", description: "List all entries in a team's shared memory" },
3418
3874
  args: {
3419
3875
  team: { type: "positional", required: true, description: "Team slug" }
@@ -3430,7 +3886,7 @@ var listCmd5 = defineCommand13({
3430
3886
  }
3431
3887
  }
3432
3888
  });
3433
- var rmCmd3 = defineCommand13({
3889
+ var rmCmd3 = defineCommand14({
3434
3890
  meta: { name: "rm", description: "Remove an entry from a team's shared memory" },
3435
3891
  args: {
3436
3892
  team: { type: "positional", required: true, description: "Team slug" },
@@ -3442,7 +3898,7 @@ var rmCmd3 = defineCommand13({
3442
3898
  console.log(`removed ${args.key}`);
3443
3899
  }
3444
3900
  });
3445
- var memoryCommand = defineCommand13({
3901
+ var memoryCommand = defineCommand14({
3446
3902
  meta: {
3447
3903
  name: "memory",
3448
3904
  description: "Manage a team's shared memory (BM25-indexed markdown notes)"
@@ -3458,8 +3914,8 @@ var memoryCommand = defineCommand13({
3458
3914
 
3459
3915
  // src/commands/profile.ts
3460
3916
  import { spawn as spawn2 } from "child_process";
3461
- import { randomBytes } from "crypto";
3462
- import { readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
3917
+ import { randomBytes as randomBytes2 } from "crypto";
3918
+ import { readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
3463
3919
  import { tmpdir as tmpdir2 } from "os";
3464
3920
  import { join as join4 } from "path";
3465
3921
 
@@ -3474,7 +3930,7 @@ var PROFILE_FILES = [
3474
3930
  ];
3475
3931
 
3476
3932
  // src/commands/profile.ts
3477
- import { defineCommand as defineCommand14 } from "citty";
3933
+ import { defineCommand as defineCommand15 } from "citty";
3478
3934
  function splitCsv(v) {
3479
3935
  if (!v) return void 0;
3480
3936
  const out = v.split(",").map((s) => s.trim()).filter(Boolean);
@@ -3485,7 +3941,7 @@ function parseSkillsMode(v) {
3485
3941
  if (v === "all" || v === "selected") return v;
3486
3942
  throw new Error(`--skills-mode must be 'all' or 'selected', got '${v}'`);
3487
3943
  }
3488
- var createCmd2 = defineCommand14({
3944
+ var createCmd2 = defineCommand15({
3489
3945
  meta: { name: "create", description: "Create a new profile" },
3490
3946
  args: {
3491
3947
  id: { type: "positional", required: true, description: "Profile slug" },
@@ -3548,7 +4004,7 @@ var createCmd2 = defineCommand14({
3548
4004
  console.log(`created profile ${profile.id} at ${profile.dir}`);
3549
4005
  }
3550
4006
  });
3551
- var listCmd6 = defineCommand14({
4007
+ var listCmd6 = defineCommand15({
3552
4008
  meta: { name: "list", description: "List profiles" },
3553
4009
  async run() {
3554
4010
  const client = createClient2();
@@ -3561,7 +4017,7 @@ var listCmd6 = defineCommand14({
3561
4017
  for (const line of columnize(rows)) console.log(line);
3562
4018
  }
3563
4019
  });
3564
- var showCmd4 = defineCommand14({
4020
+ var showCmd4 = defineCommand15({
3565
4021
  meta: { name: "show", description: "Show profile details" },
3566
4022
  args: {
3567
4023
  id: { type: "positional", required: true }
@@ -3586,7 +4042,7 @@ var showCmd4 = defineCommand14({
3586
4042
  }
3587
4043
  }
3588
4044
  });
3589
- var editCmd2 = defineCommand14({
4045
+ var editCmd2 = defineCommand15({
3590
4046
  meta: { name: "edit", description: "Open a profile file in $EDITOR" },
3591
4047
  args: {
3592
4048
  id: { type: "positional", required: true },
@@ -3605,8 +4061,8 @@ var editCmd2 = defineCommand14({
3605
4061
  const { content } = await client.get(
3606
4062
  `/api/profiles/${args.id}/files/${file}`
3607
4063
  );
3608
- const tmpPath = join4(tmpdir2(), `bazilion-${args.id}-${file}-${randomBytes(4).toString("hex")}`);
3609
- writeFileSync3(tmpPath, content);
4064
+ const tmpPath = join4(tmpdir2(), `bazilion-${args.id}-${file}-${randomBytes2(4).toString("hex")}`);
4065
+ writeFileSync4(tmpPath, content);
3610
4066
  try {
3611
4067
  const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
3612
4068
  const code = await new Promise((resolve4, reject) => {
@@ -3631,7 +4087,7 @@ var editCmd2 = defineCommand14({
3631
4087
  }
3632
4088
  }
3633
4089
  });
3634
- var updateCmd = defineCommand14({
4090
+ var updateCmd = defineCommand15({
3635
4091
  meta: {
3636
4092
  name: "update",
3637
4093
  description: "Update profile settings (name, model, skills-mode, skills)"
@@ -3665,7 +4121,7 @@ var updateCmd = defineCommand14({
3665
4121
  console.log(`updated profile ${profile.id}`);
3666
4122
  }
3667
4123
  });
3668
- var deleteCmd2 = defineCommand14({
4124
+ var deleteCmd2 = defineCommand15({
3669
4125
  meta: { name: "delete", description: "Permanently delete a profile and its files" },
3670
4126
  args: {
3671
4127
  id: { type: "positional", required: true }
@@ -3676,7 +4132,7 @@ var deleteCmd2 = defineCommand14({
3676
4132
  console.log(`deleted profile ${args.id}`);
3677
4133
  }
3678
4134
  });
3679
- var profileCommand = defineCommand14({
4135
+ var profileCommand = defineCommand15({
3680
4136
  meta: { name: "profile", description: "Manage profiles" },
3681
4137
  subCommands: {
3682
4138
  create: createCmd2,
@@ -3689,8 +4145,8 @@ var profileCommand = defineCommand14({
3689
4145
  });
3690
4146
 
3691
4147
  // src/commands/provider.ts
3692
- import { defineCommand as defineCommand15 } from "citty";
3693
- var listCmd7 = defineCommand15({
4148
+ import { defineCommand as defineCommand16 } from "citty";
4149
+ var listCmd7 = defineCommand16({
3694
4150
  meta: {
3695
4151
  name: "list",
3696
4152
  description: "List all providers with enabled/disabled state and curated model counts"
@@ -3716,7 +4172,7 @@ var listCmd7 = defineCommand15({
3716
4172
  for (const line of columnize(rows)) console.log(line);
3717
4173
  }
3718
4174
  });
3719
- var modelsCmd = defineCommand15({
4175
+ var modelsCmd = defineCommand16({
3720
4176
  meta: {
3721
4177
  name: "models",
3722
4178
  description: "Show curated + catalog + live models for one provider"
@@ -3748,7 +4204,7 @@ var modelsCmd = defineCommand15({
3748
4204
  }
3749
4205
  }
3750
4206
  });
3751
- var modelsSetCmd = defineCommand15({
4207
+ var modelsSetCmd = defineCommand16({
3752
4208
  meta: {
3753
4209
  name: "models-set",
3754
4210
  description: "Replace the curated model list for a provider (comma-separated)"
@@ -3773,7 +4229,7 @@ var modelsSetCmd = defineCommand15({
3773
4229
  for (const m of saved.models) console.log(` ${m}`);
3774
4230
  }
3775
4231
  });
3776
- var enableCmd2 = defineCommand15({
4232
+ var enableCmd2 = defineCommand16({
3777
4233
  meta: { name: "enable", description: "Toggle a provider on so agents can use it" },
3778
4234
  args: {
3779
4235
  name: { type: "positional", required: true, description: "Provider id (e.g. anthropic)" }
@@ -3787,7 +4243,7 @@ var enableCmd2 = defineCommand15({
3787
4243
  console.log(`${res.name}: enabled`);
3788
4244
  }
3789
4245
  });
3790
- var disableCmd2 = defineCommand15({
4246
+ var disableCmd2 = defineCommand16({
3791
4247
  meta: {
3792
4248
  name: "disable",
3793
4249
  description: "Toggle a provider off \u2014 agents will refuse its model strings"
@@ -3804,7 +4260,7 @@ var disableCmd2 = defineCommand15({
3804
4260
  console.log(`${res.name}: disabled`);
3805
4261
  }
3806
4262
  });
3807
- var testCmd2 = defineCommand15({
4263
+ var testCmd2 = defineCommand16({
3808
4264
  meta: { name: "test", description: "Send a single chat to a model and print the reply" },
3809
4265
  args: {
3810
4266
  model: {
@@ -3825,7 +4281,7 @@ var testCmd2 = defineCommand15({
3825
4281
  }
3826
4282
  }
3827
4283
  });
3828
- var providerCommand = defineCommand15({
4284
+ var providerCommand = defineCommand16({
3829
4285
  meta: { name: "provider", description: "Manage and test LLM providers" },
3830
4286
  subCommands: {
3831
4287
  list: listCmd7,
@@ -3838,8 +4294,8 @@ var providerCommand = defineCommand15({
3838
4294
  });
3839
4295
 
3840
4296
  // src/commands/send.ts
3841
- import { defineCommand as defineCommand16 } from "citty";
3842
- var sendCommand = defineCommand16({
4297
+ import { defineCommand as defineCommand17 } from "citty";
4298
+ var sendCommand = defineCommand17({
3843
4299
  meta: {
3844
4300
  name: "send",
3845
4301
  description: "Send a message from one agent to another"
@@ -3873,11 +4329,11 @@ var sendCommand = defineCommand16({
3873
4329
  import { spawn as spawn3 } from "child_process";
3874
4330
  import { existsSync as existsSync5 } from "fs";
3875
4331
  import { join as join5 } from "path";
3876
- import { defineCommand as defineCommand17 } from "citty";
4332
+ import { defineCommand as defineCommand18 } from "citty";
3877
4333
  var bundledDaemonEntry2 = join5(import.meta.dirname, "daemon.js");
3878
4334
  var sourceDaemonEntry2 = join5(import.meta.dirname, "..", "..", "..", "daemon", "src", "index.ts");
3879
4335
  var daemonEntry2 = existsSync5(bundledDaemonEntry2) ? bundledDaemonEntry2 : sourceDaemonEntry2;
3880
- var serveCommand = defineCommand17({
4336
+ var serveCommand = defineCommand18({
3881
4337
  meta: {
3882
4338
  name: "serve",
3883
4339
  description: "Start the bazilion daemon (HTTP API)"
@@ -3943,17 +4399,54 @@ var serveCommand = defineCommand17({
3943
4399
  }
3944
4400
  });
3945
4401
 
4402
+ // src/commands/session.ts
4403
+ import { defineCommand as defineCommand19 } from "citty";
4404
+ function row(session) {
4405
+ const state = session.revokedAt ? "revoked" : session.absoluteExpiresAt <= Date.now() || session.idleExpiresAt <= Date.now() ? "expired" : "active";
4406
+ return [
4407
+ session.id,
4408
+ state,
4409
+ session.current ? "current" : "",
4410
+ session.deviceLabel,
4411
+ `idle: ${new Date(session.idleExpiresAt).toISOString()}`,
4412
+ `absolute: ${new Date(session.absoluteExpiresAt).toISOString()}`
4413
+ ];
4414
+ }
4415
+ var listCmd8 = defineCommand19({
4416
+ meta: { name: "list", description: "List browser sessions" },
4417
+ async run() {
4418
+ const { sessions } = await createClient2().get("/api/sessions");
4419
+ if (sessions.length === 0) {
4420
+ console.log("(no active browser sessions)");
4421
+ return;
4422
+ }
4423
+ for (const line of columnize(sessions.map(row))) console.log(line);
4424
+ }
4425
+ });
4426
+ var revokeCmd = defineCommand19({
4427
+ meta: { name: "revoke", description: "Revoke one browser session" },
4428
+ args: { id: { type: "positional", required: true } },
4429
+ async run({ args }) {
4430
+ await createClient2().del(`/api/sessions/${encodeURIComponent(args.id)}`);
4431
+ console.log(`revoked browser session ${args.id}`);
4432
+ }
4433
+ });
4434
+ var sessionCommand = defineCommand19({
4435
+ meta: { name: "session", description: "Manage browser sessions" },
4436
+ subCommands: { list: listCmd8, revoke: revokeCmd }
4437
+ });
4438
+
3946
4439
  // src/commands/skill.ts
3947
4440
  import { existsSync as existsSync6, readFileSync as readFileSync7, statSync } from "fs";
3948
4441
  import { basename as basename4, resolve as resolve3 } from "path";
3949
- import { defineCommand as defineCommand18 } from "citty";
4442
+ import { defineCommand as defineCommand20 } from "citty";
3950
4443
  function printFindings(findings, prefix = " ") {
3951
4444
  for (const f of findings) {
3952
4445
  const line = f.line ? ` line ${f.line}` : "";
3953
4446
  console.log(`${prefix}${f.severity}: ${f.code}${line} - ${f.message}`);
3954
4447
  }
3955
4448
  }
3956
- var listCmd8 = defineCommand18({
4449
+ var listCmd9 = defineCommand20({
3957
4450
  meta: { name: "list", description: "List installed skills (or those attached to an agent)" },
3958
4451
  args: {
3959
4452
  agent: { type: "string", description: "Filter to skills attached to this agent" }
@@ -3994,7 +4487,7 @@ var listCmd8 = defineCommand18({
3994
4487
  }
3995
4488
  }
3996
4489
  });
3997
- var importCmd = defineCommand18({
4490
+ var importCmd = defineCommand20({
3998
4491
  meta: {
3999
4492
  name: "import",
4000
4493
  description: "Import skills from openclaw, a directory, or a local .zip archive"
@@ -4063,7 +4556,7 @@ var importCmd = defineCommand18({
4063
4556
  }
4064
4557
  }
4065
4558
  });
4066
- var rmCmd4 = defineCommand18({
4559
+ var rmCmd4 = defineCommand20({
4067
4560
  meta: { name: "rm", description: "Remove an installed skill" },
4068
4561
  args: {
4069
4562
  name: { type: "positional", required: true }
@@ -4074,10 +4567,10 @@ var rmCmd4 = defineCommand18({
4074
4567
  console.log(`removed skill ${args.name}`);
4075
4568
  }
4076
4569
  });
4077
- var skillCommand = defineCommand18({
4570
+ var skillCommand = defineCommand20({
4078
4571
  meta: { name: "skill", description: "Manage the skill library" },
4079
4572
  subCommands: {
4080
- list: listCmd8,
4573
+ list: listCmd9,
4081
4574
  import: importCmd,
4082
4575
  rm: rmCmd4
4083
4576
  }
@@ -4086,11 +4579,11 @@ var skillCommand = defineCommand18({
4086
4579
  // src/commands/team.ts
4087
4580
  import { readFileSync as readFileSync9 } from "fs";
4088
4581
  import { stdin as stdin2 } from "process";
4089
- import { defineCommand as defineCommand20 } from "citty";
4582
+ import { defineCommand as defineCommand22 } from "citty";
4090
4583
 
4091
4584
  // src/commands/team-policy.ts
4092
4585
  import { readFileSync as readFileSync8 } from "fs";
4093
- import { defineCommand as defineCommand19 } from "citty";
4586
+ import { defineCommand as defineCommand21 } from "citty";
4094
4587
 
4095
4588
  // src/team-interchange.ts
4096
4589
  var TEAM_POLICY_INTERCHANGE_VERSION = 1;
@@ -4338,7 +4831,7 @@ function printPolicy(detail) {
4338
4831
  console.log(line);
4339
4832
  }
4340
4833
  }
4341
- var show2 = defineCommand19({
4834
+ var show2 = defineCommand21({
4342
4835
  meta: { name: "show", description: "Show the one effective live policy for a Team" },
4343
4836
  args: {
4344
4837
  team: { type: "positional", required: true },
@@ -4352,7 +4845,7 @@ var show2 = defineCommand19({
4352
4845
  else printPolicy(detail);
4353
4846
  }
4354
4847
  });
4355
- var exportCommand = defineCommand19({
4848
+ var exportCommand = defineCommand21({
4356
4849
  meta: { name: "export", description: "Export a revisioned Team policy document" },
4357
4850
  args: { team: { type: "positional", required: true } },
4358
4851
  async run({ args }) {
@@ -4376,7 +4869,7 @@ function printEdgeDiff(current, edges) {
4376
4869
  for (const value of diff2.added) console.log(` + ${value}`);
4377
4870
  for (const value of diff2.removed) console.log(` - ${value}`);
4378
4871
  }
4379
- var importCommand = defineCommand19({
4872
+ var importCommand = defineCommand21({
4380
4873
  meta: {
4381
4874
  name: "import",
4382
4875
  description: "Validate, diff, and explicitly replace a Team policy"
@@ -4431,7 +4924,7 @@ var importCommand = defineCommand19({
4431
4924
  console.log(`updated Team ${args.team} policy to revision ${updated.teamPolicy.revision}`);
4432
4925
  }
4433
4926
  });
4434
- var diff = defineCommand19({
4927
+ var diff = defineCommand21({
4435
4928
  meta: { name: "diff", description: "Compare live policy with its retained baseline source" },
4436
4929
  args: {
4437
4930
  team: { type: "positional", required: true },
@@ -4455,7 +4948,7 @@ var diff = defineCommand19({
4455
4948
  );
4456
4949
  }
4457
4950
  });
4458
- var evaluate = defineCommand19({
4951
+ var evaluate = defineCommand21({
4459
4952
  meta: {
4460
4953
  name: "evaluate",
4461
4954
  description: "Evaluate a communication path without sending or recording a block"
@@ -4486,7 +4979,7 @@ var evaluate = defineCommand19({
4486
4979
  );
4487
4980
  }
4488
4981
  });
4489
- var blocks = defineCommand19({
4982
+ var blocks = defineCommand21({
4490
4983
  meta: { name: "blocks", description: "List paginated durable communication denials" },
4491
4984
  args: {
4492
4985
  team: { type: "positional", required: true },
@@ -4535,7 +5028,7 @@ var blocks = defineCommand19({
4535
5028
  if (page.nextCursor) console.log(`next cursor: ${page.nextCursor}`);
4536
5029
  }
4537
5030
  });
4538
- var teamPolicyCommand = defineCommand19({
5031
+ var teamPolicyCommand = defineCommand21({
4539
5032
  meta: { name: "policy", description: "Inspect and manage the Team-owned live policy" },
4540
5033
  subCommands: {
4541
5034
  show: show2,
@@ -4548,7 +5041,7 @@ var teamPolicyCommand = defineCommand19({
4548
5041
  });
4549
5042
 
4550
5043
  // src/commands/team.ts
4551
- var addCmd2 = defineCommand20({
5044
+ var addCmd2 = defineCommand22({
4552
5045
  meta: {
4553
5046
  name: "add",
4554
5047
  description: "Register a team at ~/.bazilion/teams/<slug>/ (use --link to point at an existing tree)"
@@ -4572,7 +5065,7 @@ var addCmd2 = defineCommand20({
4572
5065
  console.log(`registered team ${g.id} at ${g.path}`);
4573
5066
  }
4574
5067
  });
4575
- var listCmd9 = defineCommand20({
5068
+ var listCmd10 = defineCommand22({
4576
5069
  meta: { name: "list", description: "List teams" },
4577
5070
  async run() {
4578
5071
  const client = createClient2();
@@ -4585,7 +5078,7 @@ var listCmd9 = defineCommand20({
4585
5078
  for (const line of columnize(rows)) console.log(line);
4586
5079
  }
4587
5080
  });
4588
- var rmCmd5 = defineCommand20({
5081
+ var rmCmd5 = defineCommand22({
4589
5082
  meta: { name: "rm", description: "Remove a team registration" },
4590
5083
  args: {
4591
5084
  id: { type: "positional", required: true }
@@ -4606,7 +5099,7 @@ async function readStdin() {
4606
5099
  for await (const chunk of stdin2) chunks.push(chunk);
4607
5100
  return Buffer.concat(chunks).toString("utf8");
4608
5101
  }
4609
- var userMdShowCmd = defineCommand20({
5102
+ var userMdShowCmd = defineCommand22({
4610
5103
  meta: { name: "show", description: "Print the team's USER.md" },
4611
5104
  args: { id: { type: "positional", required: true } },
4612
5105
  async run({ args }) {
@@ -4616,7 +5109,7 @@ var userMdShowCmd = defineCommand20({
4616
5109
  if (g.userMd && !g.userMd.endsWith("\n")) process.stdout.write("\n");
4617
5110
  }
4618
5111
  });
4619
- var userMdSetCmd = defineCommand20({
5112
+ var userMdSetCmd = defineCommand22({
4620
5113
  meta: {
4621
5114
  name: "set",
4622
5115
  description: "Replace the team's USER.md (from --file, --from-stdin, or inline text)"
@@ -4642,7 +5135,7 @@ var userMdSetCmd = defineCommand20({
4642
5135
  console.log(`updated USER.md for team ${args.id} (${content.length} bytes)`);
4643
5136
  }
4644
5137
  });
4645
- var userMdClearCmd = defineCommand20({
5138
+ var userMdClearCmd = defineCommand22({
4646
5139
  meta: { name: "clear", description: "Clear the team's USER.md to empty" },
4647
5140
  args: { id: { type: "positional", required: true } },
4648
5141
  async run({ args }) {
@@ -4652,7 +5145,7 @@ var userMdClearCmd = defineCommand20({
4652
5145
  console.log(`cleared USER.md for team ${args.id}`);
4653
5146
  }
4654
5147
  });
4655
- var userMdCmd = defineCommand20({
5148
+ var userMdCmd = defineCommand22({
4656
5149
  meta: { name: "user-md", description: "View or edit a team's USER.md" },
4657
5150
  subCommands: {
4658
5151
  show: userMdShowCmd,
@@ -4660,7 +5153,7 @@ var userMdCmd = defineCommand20({
4660
5153
  clear: userMdClearCmd
4661
5154
  }
4662
5155
  });
4663
- var topicFormatShowCmd = defineCommand20({
5156
+ var topicFormatShowCmd = defineCommand22({
4664
5157
  meta: { name: "show", description: "Print the team's Telegram topic-name template" },
4665
5158
  args: { id: { type: "positional", required: true } },
4666
5159
  async run({ args }) {
@@ -4669,7 +5162,7 @@ var topicFormatShowCmd = defineCommand20({
4669
5162
  console.log(g.telegramTopicNameFormat ?? "(default naming)");
4670
5163
  }
4671
5164
  });
4672
- var topicFormatSetCmd = defineCommand20({
5165
+ var topicFormatSetCmd = defineCommand22({
4673
5166
  meta: {
4674
5167
  name: "set",
4675
5168
  description: "Set the topic-name template. Tokens: {agent.name} {team.name} {team.slug} (must include {agent.name})"
@@ -4689,7 +5182,7 @@ var topicFormatSetCmd = defineCommand20({
4689
5182
  console.log(`set topic-name template for team ${args.id}: ${g.telegramTopicNameFormat}`);
4690
5183
  }
4691
5184
  });
4692
- var topicFormatClearCmd = defineCommand20({
5185
+ var topicFormatClearCmd = defineCommand22({
4693
5186
  meta: { name: "clear", description: "Clear the template (revert to built-in naming)" },
4694
5187
  args: { id: { type: "positional", required: true } },
4695
5188
  async run({ args }) {
@@ -4699,7 +5192,7 @@ var topicFormatClearCmd = defineCommand20({
4699
5192
  console.log(`cleared topic-name template for team ${args.id}`);
4700
5193
  }
4701
5194
  });
4702
- var topicFormatCmd = defineCommand20({
5195
+ var topicFormatCmd = defineCommand22({
4703
5196
  meta: {
4704
5197
  name: "topic-format",
4705
5198
  description: "View or edit a team's Telegram forum-topic name template"
@@ -4710,11 +5203,11 @@ var topicFormatCmd = defineCommand20({
4710
5203
  clear: topicFormatClearCmd
4711
5204
  }
4712
5205
  });
4713
- var teamCommand = defineCommand20({
5206
+ var teamCommand = defineCommand22({
4714
5207
  meta: { name: "team", description: "Manage teams (collaboration contexts)" },
4715
5208
  subCommands: {
4716
5209
  add: addCmd2,
4717
- list: listCmd9,
5210
+ list: listCmd10,
4718
5211
  rm: rmCmd5,
4719
5212
  "user-md": userMdCmd,
4720
5213
  "topic-format": topicFormatCmd,
@@ -4724,7 +5217,7 @@ var teamCommand = defineCommand20({
4724
5217
 
4725
5218
  // src/commands/team-template.ts
4726
5219
  import { readFileSync as readFileSync10 } from "fs";
4727
- import { defineCommand as defineCommand21 } from "citty";
5220
+ import { defineCommand as defineCommand23 } from "citty";
4728
5221
  function jsonFile(path) {
4729
5222
  try {
4730
5223
  return JSON.parse(readFileSync10(path, "utf8"));
@@ -4752,7 +5245,7 @@ function printTeam(detail) {
4752
5245
  console.log(line);
4753
5246
  }
4754
5247
  }
4755
- var list4 = defineCommand21({
5248
+ var list4 = defineCommand23({
4756
5249
  meta: { name: "list", description: "List canonical Team templates" },
4757
5250
  args: { json: { type: "boolean", description: "Emit stable JSON" } },
4758
5251
  async run({ args }) {
@@ -4761,12 +5254,12 @@ var list4 = defineCommand21({
4761
5254
  if (!rows.length) return console.log("(no Team templates)");
4762
5255
  for (const line of columnize([
4763
5256
  ["id", "revision", "slots", "name"],
4764
- ...rows.map((row) => [row.id, String(row.currentRevision), String(row.slotCount), row.name])
5257
+ ...rows.map((row2) => [row2.id, String(row2.currentRevision), String(row2.slotCount), row2.name])
4765
5258
  ]))
4766
5259
  console.log(line);
4767
5260
  }
4768
5261
  });
4769
- var show3 = defineCommand21({
5262
+ var show3 = defineCommand23({
4770
5263
  meta: { name: "show", description: "Show a canonical Team template" },
4771
5264
  args: {
4772
5265
  id: { type: "positional", required: true },
@@ -4780,7 +5273,7 @@ var show3 = defineCommand21({
4780
5273
  else printTeam(detail);
4781
5274
  }
4782
5275
  });
4783
- var exportCommand2 = defineCommand21({
5276
+ var exportCommand2 = defineCommand23({
4784
5277
  meta: {
4785
5278
  name: "export",
4786
5279
  description: "Export a portable versioned Team-template document to stdout"
@@ -4820,7 +5313,7 @@ function printImportDiff(current, document) {
4820
5313
  for (const value of diff2.added) console.log(` + ${value}`);
4821
5314
  for (const value of diff2.removed) console.log(` - ${value}`);
4822
5315
  }
4823
- var importCommand2 = defineCommand21({
5316
+ var importCommand2 = defineCommand23({
4824
5317
  meta: {
4825
5318
  name: "import",
4826
5319
  description: "Validate, diff, and explicitly apply a portable Team-template document"
@@ -4901,7 +5394,7 @@ var importCommand2 = defineCommand21({
4901
5394
  );
4902
5395
  }
4903
5396
  });
4904
- var teamTemplateCommand = defineCommand21({
5397
+ var teamTemplateCommand = defineCommand23({
4905
5398
  meta: {
4906
5399
  name: "team-template",
4907
5400
  description: "Inspect and exchange canonical Team templates"
@@ -4910,8 +5403,8 @@ var teamTemplateCommand = defineCommand21({
4910
5403
  });
4911
5404
 
4912
5405
  // src/commands/telegram.ts
4913
- import { defineCommand as defineCommand22 } from "citty";
4914
- var setCmd2 = defineCommand22({
5406
+ import { defineCommand as defineCommand24 } from "citty";
5407
+ var setCmd2 = defineCommand24({
4915
5408
  meta: {
4916
5409
  name: "set",
4917
5410
  description: "Save bot token + supergroup chat ID (paired write)"
@@ -4929,7 +5422,7 @@ var setCmd2 = defineCommand22({
4929
5422
  console.log(`saved \xB7 token ${state.botTokenPreview} \xB7 chat ${state.chatId}`);
4930
5423
  }
4931
5424
  });
4932
- var clearCmd = defineCommand22({
5425
+ var clearCmd = defineCommand24({
4933
5426
  meta: {
4934
5427
  name: "clear",
4935
5428
  description: "Remove stored bot token + chat ID"
@@ -4940,7 +5433,7 @@ var clearCmd = defineCommand22({
4940
5433
  console.log("cleared");
4941
5434
  }
4942
5435
  });
4943
- var showCmd5 = defineCommand22({
5436
+ var showCmd5 = defineCommand24({
4944
5437
  meta: {
4945
5438
  name: "show",
4946
5439
  description: "Show what credentials are stored (token is masked)"
@@ -4961,7 +5454,7 @@ var showCmd5 = defineCommand22({
4961
5454
  }
4962
5455
  }
4963
5456
  });
4964
- var reconnectCmd = defineCommand22({
5457
+ var reconnectCmd = defineCommand24({
4965
5458
  meta: {
4966
5459
  name: "reconnect",
4967
5460
  description: "Apply a pending supergroup chat-id migration + re-activate the bot"
@@ -4972,7 +5465,7 @@ var reconnectCmd = defineCommand22({
4972
5465
  console.log(`reconnected \xB7 chat ${state.chatId}`);
4973
5466
  }
4974
5467
  });
4975
- var configCmd = defineCommand22({
5468
+ var configCmd = defineCommand24({
4976
5469
  meta: {
4977
5470
  name: "config",
4978
5471
  description: "Manage Telegram credentials (bot token + chat ID)"
@@ -4983,7 +5476,7 @@ var configCmd = defineCommand22({
4983
5476
  show: showCmd5
4984
5477
  }
4985
5478
  });
4986
- var healthCmd = defineCommand22({
5479
+ var healthCmd = defineCommand24({
4987
5480
  meta: {
4988
5481
  name: "health",
4989
5482
  description: "Run the four-step preflight against the Telegram Bot API"
@@ -5013,11 +5506,16 @@ var healthCmd = defineCommand22({
5013
5506
  console.log(line(p.isForum, "forum topics enabled", String(p.isForum)));
5014
5507
  console.log(line(p.hasManageTopics, "can_manage_topics", String(p.hasManageTopics)));
5015
5508
  console.log(line(p.privacyModeOff, "Privacy Mode is OFF", String(p.privacyModeOff)));
5016
- const allOk = p.botUsername.length > 0 && p.chatTitle.length > 0 && p.isForum && p.hasManageTopics && p.privacyModeOff;
5509
+ console.log(line(p.chatIsPrivate, "supergroup is private", String(p.chatIsPrivate)));
5510
+ console.log(line(p.memberCount === 2, "one owner + bot", String(p.memberCount ?? "unknown")));
5511
+ if (p.ownerPresent !== null) {
5512
+ console.log(line(p.ownerPresent, "paired owner is present", String(p.ownerPresent)));
5513
+ }
5514
+ const allOk = p.botUsername.length > 0 && p.chatTitle.length > 0 && p.isForum && p.hasManageTopics && p.privacyModeOff && p.chatIsPrivate && p.memberCount === 2 && p.ownerPresent !== false;
5017
5515
  if (!allOk) process.exitCode = 1;
5018
5516
  }
5019
5517
  });
5020
- var botStatusCmd = defineCommand22({
5518
+ var botStatusCmd = defineCommand24({
5021
5519
  meta: {
5022
5520
  name: "status",
5023
5521
  description: "Show polling state of the live bot"
@@ -5042,7 +5540,7 @@ var botStatusCmd = defineCommand22({
5042
5540
  if (!p.running) process.exitCode = 1;
5043
5541
  }
5044
5542
  });
5045
- var botRestartCmd = defineCommand22({
5543
+ var botRestartCmd = defineCommand24({
5046
5544
  meta: {
5047
5545
  name: "restart",
5048
5546
  description: "Force-restart the bot (tears down + brings up from current creds)"
@@ -5053,7 +5551,7 @@ var botRestartCmd = defineCommand22({
5053
5551
  console.log("restart requested");
5054
5552
  }
5055
5553
  });
5056
- var botCmd = defineCommand22({
5554
+ var botCmd = defineCommand24({
5057
5555
  meta: {
5058
5556
  name: "bot",
5059
5557
  description: "Inspect / control the live polling bot"
@@ -5063,7 +5561,7 @@ var botCmd = defineCommand22({
5063
5561
  restart: botRestartCmd
5064
5562
  }
5065
5563
  });
5066
- var bindCmd = defineCommand22({
5564
+ var bindCmd = defineCommand24({
5067
5565
  meta: {
5068
5566
  name: "bind",
5069
5567
  description: "Create a Telegram topic for an agent (manual fallback for /talk)"
@@ -5081,7 +5579,7 @@ var bindCmd = defineCommand22({
5081
5579
  console.log(` ${result.deepLink}`);
5082
5580
  }
5083
5581
  });
5084
- var unbindCmd = defineCommand22({
5582
+ var unbindCmd = defineCommand24({
5085
5583
  meta: {
5086
5584
  name: "unbind",
5087
5585
  description: "Clear an agent's Telegram topic binding (topic stays in Telegram as orphan)"
@@ -5095,7 +5593,7 @@ var unbindCmd = defineCommand22({
5095
5593
  console.log(`unbound agent ${args.agent}`);
5096
5594
  }
5097
5595
  });
5098
- var listBindingsCmd = defineCommand22({
5596
+ var listBindingsCmd = defineCommand24({
5099
5597
  meta: {
5100
5598
  name: "list",
5101
5599
  description: "List agents and their Telegram topic bindings"
@@ -5123,24 +5621,23 @@ var listBindingsCmd = defineCommand22({
5123
5621
  }
5124
5622
  }
5125
5623
  });
5126
- var allowCmd = defineCommand22({
5624
+ var allowCmd = defineCommand24({
5127
5625
  meta: { name: "allow", description: "Add a Telegram user id to the allowlist" },
5128
5626
  args: {
5129
5627
  userId: { type: "positional", required: true, description: "Numeric Telegram user id" },
5130
- label: { type: "string", description: "Optional human label" },
5131
- owner: { type: "boolean", description: "Grant owner role (can manage the allowlist)" }
5628
+ label: { type: "string", description: "Optional human label" }
5132
5629
  },
5133
5630
  async run({ args }) {
5134
5631
  const client = createClient2();
5135
5632
  const u = await client.post("/api/config/telegram/acl", {
5136
5633
  userId: Number(args.userId),
5137
5634
  label: args.label ?? null,
5138
- role: args.owner ? "owner" : "member"
5635
+ role: "member"
5139
5636
  });
5140
5637
  console.log(`allowed ${u.userId} (${u.role})`);
5141
5638
  }
5142
5639
  });
5143
- var denyCmd = defineCommand22({
5640
+ var denyCmd = defineCommand24({
5144
5641
  meta: { name: "deny", description: "Remove a Telegram user id from the allowlist" },
5145
5642
  args: { userId: { type: "positional", required: true } },
5146
5643
  async run({ args }) {
@@ -5149,13 +5646,13 @@ var denyCmd = defineCommand22({
5149
5646
  console.log(`removed ${args.userId}`);
5150
5647
  }
5151
5648
  });
5152
- var allowedCmd = defineCommand22({
5649
+ var allowedCmd = defineCommand24({
5153
5650
  meta: { name: "allowed", description: "List allowlisted Telegram users" },
5154
5651
  async run() {
5155
5652
  const client = createClient2();
5156
5653
  const users = await client.get("/api/config/telegram/acl");
5157
5654
  if (users.length === 0) {
5158
- console.log("(allowlist empty \u2014 open; first user to message becomes owner)");
5655
+ console.log("(allowlist empty \u2014 unpaired and closed)");
5159
5656
  return;
5160
5657
  }
5161
5658
  for (const u of users) {
@@ -5164,7 +5661,56 @@ var allowedCmd = defineCommand22({
5164
5661
  }
5165
5662
  }
5166
5663
  });
5167
- var telegramCommand = defineCommand22({
5664
+ var pairingCreateCmd = defineCommand24({
5665
+ meta: { name: "create", description: "Generate a one-time Telegram owner pairing code" },
5666
+ async run() {
5667
+ const result = await createClient2().post(
5668
+ "/api/config/telegram/pairing/challenge"
5669
+ );
5670
+ console.log(`pairing code: ${result.code}`);
5671
+ console.log(`expires: ${new Date(result.expiresAt).toISOString()}`);
5672
+ console.log("send `/pair <code>` in the configured \u2699 bazilion service topic");
5673
+ }
5674
+ });
5675
+ var pairingStatusCmd = defineCommand24({
5676
+ meta: { name: "status", description: "Show Telegram owner pairing status" },
5677
+ async run() {
5678
+ const result = await createClient2().get("/api/config/telegram/pairing");
5679
+ console.log(`paired: ${result.paired}`);
5680
+ console.log(`owner: ${result.ownerUserId ?? "(none)"}`);
5681
+ console.log(`challenge active: ${result.challengeActive}`);
5682
+ if (result.challengeExpiresAt)
5683
+ console.log(`expires: ${new Date(result.challengeExpiresAt).toISOString()}`);
5684
+ }
5685
+ });
5686
+ var pairingCancelCmd = defineCommand24({
5687
+ meta: { name: "cancel", description: "Cancel the active Telegram pairing code" },
5688
+ async run() {
5689
+ await createClient2().del("/api/config/telegram/pairing/challenge");
5690
+ console.log("pairing challenge cancelled");
5691
+ }
5692
+ });
5693
+ var pairingResetCmd = defineCommand24({
5694
+ meta: { name: "reset", description: "Revoke the Telegram owner and require re-pairing" },
5695
+ args: {
5696
+ yes: { type: "boolean", description: "Confirm destructive owner reset", required: true }
5697
+ },
5698
+ async run({ args }) {
5699
+ if (!args.yes) throw new Error("--yes is required");
5700
+ await createClient2().post("/api/config/telegram/pairing/reset", { confirm: true });
5701
+ console.log("Telegram owner reset; ingress is closed until re-paired");
5702
+ }
5703
+ });
5704
+ var pairingCmd = defineCommand24({
5705
+ meta: { name: "pairing", description: "Manage single-owner Telegram pairing" },
5706
+ subCommands: {
5707
+ create: pairingCreateCmd,
5708
+ status: pairingStatusCmd,
5709
+ cancel: pairingCancelCmd,
5710
+ reset: pairingResetCmd
5711
+ }
5712
+ });
5713
+ var telegramCommand = defineCommand24({
5168
5714
  meta: {
5169
5715
  name: "telegram",
5170
5716
  description: "Telegram integration: credentials, health, bot lifecycle, bindings, access"
@@ -5179,47 +5725,42 @@ var telegramCommand = defineCommand22({
5179
5725
  list: listBindingsCmd,
5180
5726
  allow: allowCmd,
5181
5727
  deny: denyCmd,
5182
- allowed: allowedCmd
5728
+ allowed: allowedCmd,
5729
+ pairing: pairingCmd
5183
5730
  }
5184
5731
  });
5185
5732
 
5186
5733
  // src/commands/token.ts
5187
- import { networkInterfaces } from "os";
5188
- import { defineCommand as defineCommand23 } from "citty";
5734
+ import { defineCommand as defineCommand25 } from "citty";
5189
5735
  import qrcode from "qrcode-terminal";
5190
5736
  function tokenRow(t) {
5191
5737
  const last = t.lastUsedAt ? new Date(t.lastUsedAt).toISOString() : "(never)";
5192
- const state = t.revokedAt ? "revoked" : "active";
5193
- return [t.id, state, t.label, `last: ${last}`];
5194
- }
5195
- function detectLanOrigin(port) {
5196
- const candidates = [];
5197
- for (const ifs of Object.values(networkInterfaces())) {
5198
- for (const i of ifs ?? []) {
5199
- if (i.family === "IPv4" && !i.internal) candidates.push(i.address);
5200
- }
5201
- }
5202
- const [first, ...rest] = candidates;
5203
- if (!first) return null;
5204
- const origin = `http://${first}:${port}`;
5205
- return rest.length > 0 ? { origin, warning: `multiple LAN IPs found (${candidates.join(", ")}) \u2014 using ${first}` } : { origin };
5738
+ const expired = t.expiresAt !== null && t.expiresAt <= Date.now();
5739
+ const state = t.revokedAt ? "revoked" : expired ? "expired" : "active";
5740
+ const expires = t.expiresAt ? new Date(t.expiresAt).toISOString() : "(never)";
5741
+ return [t.id, t.kind, state, t.label, `expires: ${expires}`, `last: ${last}`];
5206
5742
  }
5207
5743
  function resolveQrServer(override) {
5208
- if (override) return override.replace(/\/$/, "");
5744
+ const raw = override ?? loadClientConfig().serverUrl;
5745
+ const url = new URL(raw);
5746
+ const loopback = ["127.0.0.1", "localhost", "[::1]", "::1"].includes(url.hostname);
5747
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
5748
+ throw new Error("pairing server must use HTTPS (HTTP is allowed only for loopback development)");
5749
+ }
5750
+ if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
5751
+ throw new Error(
5752
+ "pairing server must be an exact origin without credentials, path, query, or fragment"
5753
+ );
5754
+ }
5209
5755
  const cfg = loadClientConfig();
5210
- const current = new URL(cfg.serverUrl);
5211
- const isLoopback = current.hostname === "127.0.0.1" || current.hostname === "localhost" || current.hostname === "::1";
5212
- if (!isLoopback) return cfg.serverUrl.replace(/\/$/, "");
5213
- const detected = detectLanOrigin(current.port || "4321");
5214
- if (!detected) {
5756
+ if (loopback && !override && new URL(cfg.serverUrl).hostname === url.hostname) {
5215
5757
  throw new Error(
5216
- "server URL is loopback-only and no LAN interface was found. Pass --server http://<host>:<port> so the mobile client knows where to connect."
5758
+ "server URL is loopback-only. Pass --server https://<private-tailnet-host> for mobile pairing."
5217
5759
  );
5218
5760
  }
5219
- if (detected.warning) console.warn(`\u26A0 ${detected.warning}`);
5220
- return detected.origin;
5761
+ return url.origin;
5221
5762
  }
5222
- var createCmd3 = defineCommand23({
5763
+ var createCmd3 = defineCommand25({
5223
5764
  meta: { name: "create", description: "Mint a new web token (shown once)" },
5224
5765
  args: {
5225
5766
  label: { type: "positional", required: true, description: "Human-readable label" },
@@ -5230,14 +5771,26 @@ var createCmd3 = defineCommand23({
5230
5771
  server: {
5231
5772
  type: "string",
5232
5773
  description: "Server URL to embed in the pairing QR (default: detect LAN IP; ignored without --qr)"
5774
+ },
5775
+ expiresDays: {
5776
+ type: "string",
5777
+ description: "Device lifetime in days (default 90, maximum 365)"
5233
5778
  }
5234
5779
  },
5235
5780
  async run({ args }) {
5236
5781
  const client = createClient2();
5237
- const body = { label: args.label };
5782
+ const expiresInDays = args.expiresDays === void 0 ? void 0 : Number(args.expiresDays);
5783
+ if (expiresInDays !== void 0 && !Number.isInteger(expiresInDays)) {
5784
+ throw new Error("--expires-days must be an integer");
5785
+ }
5786
+ const body = { label: args.label, expiresInDays };
5238
5787
  const res = await client.post("/api/tokens", body);
5239
5788
  console.log(`id: ${res.meta.id}`);
5240
5789
  console.log(`label: ${res.meta.label}`);
5790
+ console.log(`kind: ${res.meta.kind}`);
5791
+ console.log(
5792
+ `expires: ${res.meta.expiresAt ? new Date(res.meta.expiresAt).toISOString() : "(never)"}`
5793
+ );
5241
5794
  console.log(`token: ${res.token}`);
5242
5795
  console.log("");
5243
5796
  console.log("store the token now \u2014 it is not recoverable later.");
@@ -5250,7 +5803,7 @@ var createCmd3 = defineCommand23({
5250
5803
  qrcode.generate(pairUrl, { small: true }, (qr) => console.log(qr));
5251
5804
  }
5252
5805
  });
5253
- var listCmd10 = defineCommand23({
5806
+ var listCmd11 = defineCommand25({
5254
5807
  meta: { name: "list", description: "List web tokens" },
5255
5808
  args: {
5256
5809
  all: { type: "boolean", description: "Include revoked tokens" }
@@ -5266,7 +5819,7 @@ var listCmd10 = defineCommand23({
5266
5819
  for (const line of columnize(tokens.map(tokenRow))) console.log(line);
5267
5820
  }
5268
5821
  });
5269
- var showLocalCmd = defineCommand23({
5822
+ var showLocalCmd = defineCommand25({
5270
5823
  meta: {
5271
5824
  name: "show-local",
5272
5825
  description: "Print the bootstrap web token stored in ~/.bazilion/auth.json"
@@ -5276,7 +5829,7 @@ var showLocalCmd = defineCommand23({
5276
5829
  console.log(readAuthFile(paths.authFile).token);
5277
5830
  }
5278
5831
  });
5279
- var revokeCmd = defineCommand23({
5832
+ var revokeCmd2 = defineCommand25({
5280
5833
  meta: { name: "revoke", description: "Revoke a web token" },
5281
5834
  args: {
5282
5835
  id: { type: "positional", required: true }
@@ -5287,19 +5840,19 @@ var revokeCmd = defineCommand23({
5287
5840
  console.log(`revoked token ${args.id}`);
5288
5841
  }
5289
5842
  });
5290
- var tokenCommand = defineCommand23({
5843
+ var tokenCommand = defineCommand25({
5291
5844
  meta: { name: "token", description: "Manage web tokens for API/CLI clients" },
5292
5845
  subCommands: {
5293
5846
  create: createCmd3,
5294
- list: listCmd10,
5295
- revoke: revokeCmd,
5847
+ list: listCmd11,
5848
+ revoke: revokeCmd2,
5296
5849
  "show-local": showLocalCmd
5297
5850
  }
5298
5851
  });
5299
5852
 
5300
5853
  // src/commands/trigger.ts
5301
- import { defineCommand as defineCommand24 } from "citty";
5302
- var addCmd3 = defineCommand24({
5854
+ import { defineCommand as defineCommand26 } from "citty";
5855
+ var addCmd3 = defineCommand26({
5303
5856
  meta: { name: "add", description: "Add an interval / cron trigger to an agent" },
5304
5857
  args: {
5305
5858
  agent: { type: "positional", required: true },
@@ -5347,7 +5900,7 @@ function triggerRow(t) {
5347
5900
  const msgPreview = t.message.length > 60 ? `${t.message.slice(0, 60)}\u2026` : t.message;
5348
5901
  return [t.id, state, spec, `last: ${last}`, `"${msgPreview}"`];
5349
5902
  }
5350
- var listCmd11 = defineCommand24({
5903
+ var listCmd12 = defineCommand26({
5351
5904
  meta: { name: "list", description: "List triggers for an agent" },
5352
5905
  args: {
5353
5906
  agent: { type: "positional", required: true }
@@ -5364,7 +5917,7 @@ var listCmd11 = defineCommand24({
5364
5917
  for (const line of columnize(triggers.map(triggerRow))) console.log(line);
5365
5918
  }
5366
5919
  });
5367
- var rmCmd6 = defineCommand24({
5920
+ var rmCmd6 = defineCommand26({
5368
5921
  meta: { name: "rm", description: "Delete a trigger" },
5369
5922
  args: {
5370
5923
  id: { type: "positional", required: true }
@@ -5375,7 +5928,7 @@ var rmCmd6 = defineCommand24({
5375
5928
  console.log(`removed trigger ${args.id}`);
5376
5929
  }
5377
5930
  });
5378
- var enableCmd3 = defineCommand24({
5931
+ var enableCmd3 = defineCommand26({
5379
5932
  meta: { name: "enable", description: "Enable a trigger" },
5380
5933
  args: {
5381
5934
  id: { type: "positional", required: true }
@@ -5387,7 +5940,7 @@ var enableCmd3 = defineCommand24({
5387
5940
  console.log(`enabled trigger ${args.id}`);
5388
5941
  }
5389
5942
  });
5390
- var disableCmd3 = defineCommand24({
5943
+ var disableCmd3 = defineCommand26({
5391
5944
  meta: { name: "disable", description: "Disable a trigger" },
5392
5945
  args: {
5393
5946
  id: { type: "positional", required: true }
@@ -5399,7 +5952,7 @@ var disableCmd3 = defineCommand24({
5399
5952
  console.log(`disabled trigger ${args.id}`);
5400
5953
  }
5401
5954
  });
5402
- var historyCmd = defineCommand24({
5955
+ var historyCmd = defineCommand26({
5403
5956
  meta: { name: "history", description: "Show recent dispatches for a trigger" },
5404
5957
  args: {
5405
5958
  id: { type: "positional", required: true },
@@ -5427,11 +5980,11 @@ var historyCmd = defineCommand24({
5427
5980
  for (const line of columnize(rows)) console.log(line);
5428
5981
  }
5429
5982
  });
5430
- var triggerCommand = defineCommand24({
5983
+ var triggerCommand = defineCommand26({
5431
5984
  meta: { name: "trigger", description: "Manage scheduled agent triggers" },
5432
5985
  subCommands: {
5433
5986
  add: addCmd3,
5434
- list: listCmd11,
5987
+ list: listCmd12,
5435
5988
  rm: rmCmd6,
5436
5989
  enable: enableCmd3,
5437
5990
  disable: disableCmd3,
@@ -5442,7 +5995,7 @@ var triggerCommand = defineCommand24({
5442
5995
  // src/commands/uninstall.ts
5443
5996
  import { existsSync as existsSync7, readdirSync as readdirSync2, rmSync as rmSync4 } from "fs";
5444
5997
  import { join as join6 } from "path";
5445
- import { defineCommand as defineCommand25 } from "citty";
5998
+ import { defineCommand as defineCommand27 } from "citty";
5446
5999
  function makeLineReader() {
5447
6000
  let buffer = "";
5448
6001
  let ended = false;
@@ -5490,7 +6043,7 @@ function removePath(p) {
5490
6043
  rmSync4(p, { recursive: true, force: true });
5491
6044
  return true;
5492
6045
  }
5493
- var uninstallCommand = defineCommand25({
6046
+ var uninstallCommand = defineCommand27({
5494
6047
  meta: {
5495
6048
  name: "uninstall",
5496
6049
  description: "Wipe bazilion state from ~/.bazilion (or BAZILION_HOME)"
@@ -5561,7 +6114,7 @@ var uninstallCommand = defineCommand25({
5561
6114
 
5562
6115
  // src/index.ts
5563
6116
  var VERSION = package_default.version;
5564
- var main = defineCommand26({
6117
+ var main = defineCommand28({
5565
6118
  meta: {
5566
6119
  name: "bazilion",
5567
6120
  version: VERSION,
@@ -5580,11 +6133,13 @@ var main = defineCommand26({
5580
6133
  mcp: mcpCommand,
5581
6134
  provider: providerCommand,
5582
6135
  send: sendCommand,
6136
+ session: sessionCommand,
5583
6137
  inbox: inboxCommand,
5584
6138
  config: configCommand,
5585
6139
  serve: serveCommand,
5586
6140
  dashboard: dashboardCommand,
5587
6141
  doctor: doctorCommand,
6142
+ gateway: gatewayCommand,
5588
6143
  backup: backupCommand,
5589
6144
  trigger: triggerCommand,
5590
6145
  token: tokenCommand,
@@ -5684,7 +6239,7 @@ function printTopLevelHelp() {
5684
6239
  },
5685
6240
  {
5686
6241
  title: "ops",
5687
- items: [["backup", "Download a tar.gz backup of ~/.bazilion"]]
6242
+ items: [["backup", "Encrypted backup, restore, and credential recovery"]]
5688
6243
  },
5689
6244
  {
5690
6245
  title: "remote",