xaq-cli 0.1.0-nightly.95 → 0.1.0-nightly.97

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 (343) hide show
  1. package/README.md +25 -96
  2. package/dist/admin-BjqfG6Qm.js +63 -0
  3. package/dist/admin-BjqfG6Qm.js.map +1 -0
  4. package/dist/{version-BxH7OuWD.js → agent-api-C7e5yLIF.js} +13 -3
  5. package/dist/agent-api-C7e5yLIF.js.map +1 -0
  6. package/dist/{audit-dW-RK5o8.js → audit-DWWc7dPE.js} +2 -2
  7. package/dist/audit-DWWc7dPE.js.map +1 -0
  8. package/dist/audit-DYyijFCg.js +2 -0
  9. package/dist/auth-Budz0MTj.js +2 -0
  10. package/dist/{auth-D4QQM-EQ.js → auth-CCd2LeD4.js} +25 -116
  11. package/dist/auth-CCd2LeD4.js.map +1 -0
  12. package/dist/brand-CvCkj6M3.js +10 -0
  13. package/dist/brand-CvCkj6M3.js.map +1 -0
  14. package/dist/bus-Cz0flS37.js.map +1 -1
  15. package/dist/cli.js +46 -62
  16. package/dist/cli.js.map +1 -1
  17. package/dist/commands-DwZ58TEU.js +76 -0
  18. package/dist/commands-DwZ58TEU.js.map +1 -0
  19. package/dist/config-CbCgBqSS.js +2 -0
  20. package/dist/config-CkgsOY8d.js +72 -0
  21. package/dist/config-CkgsOY8d.js.map +1 -0
  22. package/dist/{connect-I716brsi.js → connect-BkQWDEbz.js} +48 -78
  23. package/dist/connect-BkQWDEbz.js.map +1 -0
  24. package/dist/{crypto-s8x6up-l.js → crypto-1fJvt9vL.js} +4 -4
  25. package/dist/crypto-1fJvt9vL.js.map +1 -0
  26. package/dist/{db-4CHRuBhV.js → db-Bp6AHwuY.js} +57 -1
  27. package/dist/db-Bp6AHwuY.js.map +1 -0
  28. package/dist/db-o7iOl66c.js +2 -0
  29. package/dist/fsutil-DqjKP1sG.js +91 -0
  30. package/dist/fsutil-DqjKP1sG.js.map +1 -0
  31. package/dist/glob-BsEofuNI.js +31 -0
  32. package/dist/glob-BsEofuNI.js.map +1 -0
  33. package/dist/{instance-DXo6XPrx.js → instance-Bxcxr_5i.js} +11 -15
  34. package/dist/instance-Bxcxr_5i.js.map +1 -0
  35. package/dist/instance-CBp-yf2c.js +2 -0
  36. package/dist/listen-Ba__NCsd.js +2 -0
  37. package/dist/{listen-DLZVpzxM.js → listen-DR3HAzmp.js} +684 -479
  38. package/dist/listen-DR3HAzmp.js.map +1 -0
  39. package/dist/mcpheader-BtXLh1fd.js +2 -0
  40. package/dist/mcpheader-Dk8rd-cc.js +91 -0
  41. package/dist/mcpheader-Dk8rd-cc.js.map +1 -0
  42. package/dist/mcpsetup-B5u5E7Wg.js +2 -0
  43. package/dist/mcpsetup-CANwFTJv.js +106 -0
  44. package/dist/mcpsetup-CANwFTJv.js.map +1 -0
  45. package/dist/{node-check-CjTk6VNL.js → node-check-D_iHK8Xv.js} +2 -2
  46. package/dist/{node-check-CjTk6VNL.js.map → node-check-D_iHK8Xv.js.map} +1 -1
  47. package/dist/paths-BNJY8PxY.js +2 -0
  48. package/dist/paths-BpQZZxo2.js +33 -0
  49. package/dist/paths-BpQZZxo2.js.map +1 -0
  50. package/dist/profile-ColHDvCM.js +2 -0
  51. package/dist/profile-DyERKzt8.js +56 -0
  52. package/dist/profile-DyERKzt8.js.map +1 -0
  53. package/dist/recipes-B_yblSB0.js +73 -0
  54. package/dist/recipes-B_yblSB0.js.map +1 -0
  55. package/dist/{server-TeB2QMu0.js → server-BpCA0-96.js} +14670 -13807
  56. package/dist/server-BpCA0-96.js.map +1 -0
  57. package/dist/service-4NU4Fo8w.js +2 -0
  58. package/dist/service-BBBFprAO.js +2 -0
  59. package/dist/service-BY81gJ-r.js +345 -0
  60. package/dist/service-BY81gJ-r.js.map +1 -0
  61. package/dist/service-n9tjMClN.js +702 -0
  62. package/dist/service-n9tjMClN.js.map +1 -0
  63. package/dist/settings-9Zi6hY5k.js +105 -0
  64. package/dist/settings-9Zi6hY5k.js.map +1 -0
  65. package/dist/settings-D1j4_mPM.js +2 -0
  66. package/dist/{setup-token-D1urJ8_0.js → setup-token-DBqR_h01.js} +14 -3
  67. package/dist/setup-token-DBqR_h01.js.map +1 -0
  68. package/dist/skill/SKILL.md +59 -125
  69. package/dist/skill-CAJikZ13.js +2 -0
  70. package/dist/{skill-Dgc-4Dop.js → skill-DnNPunVP.js} +5 -32
  71. package/dist/skill-DnNPunVP.js.map +1 -0
  72. package/dist/types-CYC5FTTW.js +76 -0
  73. package/dist/types-CYC5FTTW.js.map +1 -0
  74. package/dist/update-B3bM0ugd.js +2 -0
  75. package/dist/update-g9ZxzsXp.js +200 -0
  76. package/dist/update-g9ZxzsXp.js.map +1 -0
  77. package/dist/web/assets/_agentId-BvaQG-tR.js +1 -0
  78. package/dist/web/assets/_app-CQbzdQ-f.js +65 -0
  79. package/dist/web/assets/_app-CRxSVRZC.js +1 -0
  80. package/dist/web/assets/_connectionId-BGGBcFKu.js +1 -0
  81. package/dist/web/assets/account-status-C1R_LrKu.js +1 -0
  82. package/dist/web/assets/{accounts-B7RnCRcO.js → accounts-BZb6ud2I.js} +1 -1
  83. package/dist/web/assets/accounts-list-BAOGsm4O.js +1 -0
  84. package/dist/web/assets/{activity-CCx7fall.js → activity-CT6gDBCM.js} +1 -1
  85. package/dist/web/assets/add-connection-De8PZuQ8.js +1 -0
  86. package/dist/web/assets/agent-create-form-DGWoy5We.js +1 -0
  87. package/dist/web/assets/agents-Cm6JBDWX.js +1 -0
  88. package/dist/web/assets/approvals-BqDkPWUw.js +1 -0
  89. package/dist/web/assets/attention-DiBDnnBb.js +1 -0
  90. package/dist/web/assets/audit-DzQEfZVv.js +1 -0
  91. package/dist/web/assets/{audit-bits-BwxIq8Ey.js → audit-bits-DNrRoxNW.js} +1 -1
  92. package/dist/web/assets/{bell-ring-DK6w_rCY.js → bell-ring-PZBWDvmO.js} +1 -1
  93. package/dist/web/assets/{bot-Bn2x9oDR.js → bot-5UD8wUVm.js} +1 -1
  94. package/dist/web/assets/calendar-notice-DkFHt4ti.js +1 -0
  95. package/dist/web/assets/{calendar-plus-Cw-rdRP4.js → calendar-plus-BDq46Q7N.js} +1 -1
  96. package/dist/web/assets/calendar-setup-CEl5NPl5.js +2 -0
  97. package/dist/web/assets/{card-BzmG-kj3.js → card-COveS5n1.js} +1 -1
  98. package/dist/web/assets/chevron-right-CWhkVHI_.js +1 -0
  99. package/dist/web/assets/choice-cards-D-7Epumq.js +1 -0
  100. package/dist/web/assets/{circle-arrow-up-BOsQxGE6.js → circle-arrow-up-BL1Wv4re.js} +1 -1
  101. package/dist/web/assets/{circle-x-LdeVWUm8.js → circle-x-syG328XT.js} +1 -1
  102. package/dist/web/assets/cli-8ZDvmFIA.js +1 -0
  103. package/dist/web/assets/{command-BpKHrn7v.js → command-PwiiXP5q.js} +1 -1
  104. package/dist/web/assets/common-BO_oENWW.js +1 -0
  105. package/dist/web/assets/connect-agent-guide-DfmSTOKl.js +1 -0
  106. package/dist/web/assets/connect-host-7pc5ZpIk.js +6 -0
  107. package/dist/web/assets/connection-row-BKtfr6GU.js +1 -0
  108. package/dist/web/assets/connections-CL0SyQJr.js +1 -0
  109. package/dist/web/assets/{empty-C51wCMWD.js → empty-CbF89XKL.js} +1 -1
  110. package/dist/web/assets/events-DT9ZXZNY.js +1 -0
  111. package/dist/web/assets/{extension-logic-CoXmt48r.js → extension-logic-LLxYuVYE.js} +1 -1
  112. package/dist/web/assets/extensions-wmxNgRkC.js +1 -0
  113. package/dist/web/assets/{eye-CoLxbPpd.js → eye-Cqv9CEp3.js} +1 -1
  114. package/dist/web/assets/field-Ct5zKOTW.js +1 -0
  115. package/dist/web/assets/get-started-CILWKxVM.js +2 -0
  116. package/dist/web/assets/hidden-chars-Bz67_gic.js +1 -0
  117. package/dist/web/assets/index-B9ChkdSI.js +11 -0
  118. package/dist/web/assets/index-YkRHpJLd.css +2 -0
  119. package/dist/web/assets/{input-BIhnmVzt.js → input-DZiwLylq.js} +1 -1
  120. package/dist/web/assets/{input-group-C7zwc-La.js → input-group-u_wHEZ2T.js} +1 -1
  121. package/dist/web/assets/labels-Ba-IDt5l.js +1 -0
  122. package/dist/web/assets/{laptop-Dg78vqxv.js → laptop-C5FJFJir.js} +1 -1
  123. package/dist/web/assets/last-call-DnZn_JO5.js +1 -0
  124. package/dist/web/assets/{link-BqzoyrlW.js → link-vmjWcZQd.js} +2 -2
  125. package/dist/web/assets/listener-handled-Cp4gSBKg.js +1 -0
  126. package/dist/web/assets/login-CmaugrQp.js +1 -0
  127. package/dist/web/assets/{mail-C-_AsmfS.js → mail-NPYozWRl.js} +1 -1
  128. package/dist/web/assets/mutations-CUTWZ45i.js +1 -0
  129. package/dist/web/assets/{network-C1NgG9ce.js → network-CnblVULp.js} +1 -1
  130. package/dist/web/assets/pairing-BNTG9r6e.js +1 -0
  131. package/dist/web/assets/pairing-notifier-CIxTT6fB.js +1 -0
  132. package/dist/web/assets/password-input-CRJFGH__.js +1 -0
  133. package/dist/web/assets/{pause-BiqIIg3_.js → pause-F3dqa6Zb.js} +1 -1
  134. package/dist/web/assets/{pencil-4agDtv-2.js → pencil-KBnFUh3P.js} +1 -1
  135. package/dist/web/assets/{play-DOYlcT2H.js → play-Svq9DSpt.js} +1 -1
  136. package/dist/web/assets/{plug-zap-BIS300eG.js → plug-zap-DmMxZyY4.js} +1 -1
  137. package/dist/web/assets/plus-h71nbUvh.js +1 -0
  138. package/dist/web/assets/{preload-helper-DfR7asKt.js → preload-helper-D7dmOltH.js} +1 -1
  139. package/dist/web/assets/public-url-BZjKFYQt.js +1 -0
  140. package/dist/web/assets/{puzzle-CzCFaju9.js → puzzle-BHw3f5sO.js} +1 -1
  141. package/dist/web/assets/{queries-BjsuOklb.js → queries-BGtTBrso.js} +1 -1
  142. package/dist/web/assets/refresh-cw-p2YJ3IF9.js +1 -0
  143. package/dist/web/assets/{revealed-keys-IXK9dnIp.js → revealed-keys-xgw7JL1_.js} +1 -1
  144. package/dist/web/assets/{search-xGYGO1Ha.js → search-mZfGMHED.js} +1 -1
  145. package/dist/web/assets/settings-Pdrzg8TI.js +1 -0
  146. package/dist/web/assets/setup-DE3TbMMD.js +1 -0
  147. package/dist/web/assets/{shield-alert-CXrGTCpP.js → shield-alert-BOhOk4BG.js} +1 -1
  148. package/dist/web/assets/{shield-check-BzPhCy38.js → shield-check-DbtGrXbF.js} +1 -1
  149. package/dist/web/assets/{stamp-CUXkVNmk.js → stamp-sD0wJGuC.js} +1 -1
  150. package/dist/web/assets/step-url-ZlAlg-oF.js +1 -0
  151. package/dist/web/assets/stream-s93kwgV_.js +1 -0
  152. package/dist/web/assets/switch-C-QtsLy6.js +1 -0
  153. package/dist/web/assets/{terminal-cTIBu49y.js → terminal-BdNyTQBn.js} +1 -1
  154. package/dist/web/assets/test-email-button-DZUH-5kl.js +1 -0
  155. package/dist/web/assets/{textarea-CpNC8lSI.js → textarea-YrDZ0ZU6.js} +1 -1
  156. package/dist/web/assets/toggle-group-9UoSyTjN.js +1 -0
  157. package/dist/web/assets/{trash-B_4LNyAw.js → trash-DNhqFm4u.js} +1 -1
  158. package/dist/web/assets/{use-sign-out-lT5Dw1TB.js → use-sign-out-BfPM6VfF.js} +1 -1
  159. package/dist/web/assets/{useInfiniteQuery-CrM9IVMV.js → useInfiniteQuery-Cnl5W11l.js} +1 -1
  160. package/dist/web/assets/useQuery-2tiO9_DU.js +1 -0
  161. package/dist/web/index.html +7 -7
  162. package/npm-shrinkwrap.json +24 -20
  163. package/package.json +2 -3
  164. package/dist/admin-C594a6IO.js +0 -2
  165. package/dist/admin-UP7MUEHk.js +0 -218
  166. package/dist/admin-UP7MUEHk.js.map +0 -1
  167. package/dist/admin-zQmabaP9.js +0 -162
  168. package/dist/admin-zQmabaP9.js.map +0 -1
  169. package/dist/agent-B8a-Ivyd.js +0 -2
  170. package/dist/agent-mYT2noTp.js +0 -52
  171. package/dist/agent-mYT2noTp.js.map +0 -1
  172. package/dist/api-DOVCuxO-.js +0 -97
  173. package/dist/api-DOVCuxO-.js.map +0 -1
  174. package/dist/audit-dW-RK5o8.js.map +0 -1
  175. package/dist/audit-hQnqfGxA.js +0 -2
  176. package/dist/auth-CcvstbI5.js +0 -2
  177. package/dist/auth-D4QQM-EQ.js.map +0 -1
  178. package/dist/brand-Bg9VPAym.js +0 -55
  179. package/dist/brand-Bg9VPAym.js.map +0 -1
  180. package/dist/cli-D0jJLJZg.js +0 -196
  181. package/dist/cli-D0jJLJZg.js.map +0 -1
  182. package/dist/cli-DXH8dy5A.js +0 -126
  183. package/dist/cli-DXH8dy5A.js.map +0 -1
  184. package/dist/commands-CCqOfZK8.js +0 -167
  185. package/dist/commands-CCqOfZK8.js.map +0 -1
  186. package/dist/commands-D63LCLiR.js +0 -118
  187. package/dist/commands-D63LCLiR.js.map +0 -1
  188. package/dist/config-BCKSx-sT.js +0 -2
  189. package/dist/config-DaBg-G1g.js +0 -116
  190. package/dist/config-DaBg-G1g.js.map +0 -1
  191. package/dist/connect-I716brsi.js.map +0 -1
  192. package/dist/crypto-s8x6up-l.js.map +0 -1
  193. package/dist/db-4CHRuBhV.js.map +0 -1
  194. package/dist/db-Br2FHvQ-.js +0 -2
  195. package/dist/hermes-B_YoOADz.js +0 -1022
  196. package/dist/hermes-B_YoOADz.js.map +0 -1
  197. package/dist/hermes-BwsRDFyI.js +0 -3
  198. package/dist/ids-DtsPJB4V.js +0 -16
  199. package/dist/ids-DtsPJB4V.js.map +0 -1
  200. package/dist/import-y1bjWoGq.js +0 -915
  201. package/dist/import-y1bjWoGq.js.map +0 -1
  202. package/dist/instance-DXo6XPrx.js.map +0 -1
  203. package/dist/instance-HA_7Nr24.js +0 -2
  204. package/dist/listen-B6TV_qUM.js +0 -2
  205. package/dist/listen-DLZVpzxM.js.map +0 -1
  206. package/dist/manage-C6pUOFQK.js +0 -2
  207. package/dist/manage-EK_UQB4B.js +0 -627
  208. package/dist/manage-EK_UQB4B.js.map +0 -1
  209. package/dist/mcpheader-C3EhpA7D.js +0 -243
  210. package/dist/mcpheader-C3EhpA7D.js.map +0 -1
  211. package/dist/mcpheader-DOwvJTUJ.js +0 -2
  212. package/dist/mcpsetup-ChBN1058.js +0 -1053
  213. package/dist/mcpsetup-ChBN1058.js.map +0 -1
  214. package/dist/mcpsetup-sfobhJnl.js +0 -2
  215. package/dist/own-dirs-J4uXJQri.js +0 -97
  216. package/dist/own-dirs-J4uXJQri.js.map +0 -1
  217. package/dist/paths-Bk9CoSYL.js +0 -2
  218. package/dist/paths-Bo6qtWNh.js +0 -50
  219. package/dist/paths-Bo6qtWNh.js.map +0 -1
  220. package/dist/profile-BWK08YwL.js +0 -2
  221. package/dist/profile-QqEY5L3k.js +0 -395
  222. package/dist/profile-QqEY5L3k.js.map +0 -1
  223. package/dist/prompt-BEH311I7.js +0 -129
  224. package/dist/prompt-BEH311I7.js.map +0 -1
  225. package/dist/recipes-BOhG7jcd.js +0 -3
  226. package/dist/recipes-BsCIHrrP.js +0 -314
  227. package/dist/recipes-BsCIHrrP.js.map +0 -1
  228. package/dist/registry-pQa9LtBu.js +0 -146
  229. package/dist/registry-pQa9LtBu.js.map +0 -1
  230. package/dist/report-BLbtzUlC.js +0 -44
  231. package/dist/report-BLbtzUlC.js.map +0 -1
  232. package/dist/report-D2D_6E5O.js +0 -2
  233. package/dist/run-DnMULIb1.js +0 -1258
  234. package/dist/run-DnMULIb1.js.map +0 -1
  235. package/dist/send-Dk-t8ia-.js +0 -108
  236. package/dist/send-Dk-t8ia-.js.map +0 -1
  237. package/dist/server-TeB2QMu0.js.map +0 -1
  238. package/dist/service-BXOZ8gfE.js +0 -2
  239. package/dist/service-CuJQEcrt.js +0 -459
  240. package/dist/service-CuJQEcrt.js.map +0 -1
  241. package/dist/service-D-yMNgeK.js +0 -2
  242. package/dist/service-DoSR8dSk.js +0 -1185
  243. package/dist/service-DoSR8dSk.js.map +0 -1
  244. package/dist/settings-BRKLADPA.js +0 -193
  245. package/dist/settings-BRKLADPA.js.map +0 -1
  246. package/dist/settings-CsWDxt64.js +0 -2
  247. package/dist/settings-cli-BLWjS8VR.js +0 -95
  248. package/dist/settings-cli-BLWjS8VR.js.map +0 -1
  249. package/dist/setup-token-D1urJ8_0.js.map +0 -1
  250. package/dist/skill-DAsfVipI.js +0 -2
  251. package/dist/skill-Dgc-4Dop.js.map +0 -1
  252. package/dist/smoke-DIWhlw6m.js +0 -5555
  253. package/dist/smoke-DIWhlw6m.js.map +0 -1
  254. package/dist/sources-Dxmv7B2R.js +0 -593
  255. package/dist/sources-Dxmv7B2R.js.map +0 -1
  256. package/dist/spawn-Dk6A4Bz4.js +0 -165
  257. package/dist/spawn-Dk6A4Bz4.js.map +0 -1
  258. package/dist/ssrf-BlVpGZ7C.js +0 -181
  259. package/dist/ssrf-BlVpGZ7C.js.map +0 -1
  260. package/dist/types-BhJDbSzE.js +0 -135
  261. package/dist/types-BhJDbSzE.js.map +0 -1
  262. package/dist/update-BSRYrxCc.js +0 -2
  263. package/dist/update-DSvn3KhM.js +0 -387
  264. package/dist/update-DSvn3KhM.js.map +0 -1
  265. package/dist/user-server-BgZT7jeQ.js +0 -281
  266. package/dist/user-server-BgZT7jeQ.js.map +0 -1
  267. package/dist/version-BxH7OuWD.js.map +0 -1
  268. package/dist/web/assets/_agentId-Cm5xOd3H.js +0 -1
  269. package/dist/web/assets/_app-DR2AOi4H.js +0 -1
  270. package/dist/web/assets/_app-GQz7-m3q.js +0 -65
  271. package/dist/web/assets/_connectionId-BgJh0Dwy.js +0 -1
  272. package/dist/web/assets/_webhookId-CnsKkWgu.js +0 -1
  273. package/dist/web/assets/account-status-Dlj6KI3_.js +0 -1
  274. package/dist/web/assets/accounts-list-WOZUU-wR.js +0 -1
  275. package/dist/web/assets/add-connection-BYUVCTFR.js +0 -1
  276. package/dist/web/assets/agent-create-form-B1x9LbXZ.js +0 -1
  277. package/dist/web/assets/agents-B70pFptg.js +0 -1
  278. package/dist/web/assets/approvals-B1aYLiqt.js +0 -1
  279. package/dist/web/assets/approve._id-ByNdQCHf.js +0 -1
  280. package/dist/web/assets/arrow-left-CpZKEh_6.js +0 -1
  281. package/dist/web/assets/arrow-right-Bng4fWo0.js +0 -1
  282. package/dist/web/assets/attention-C0UrgmFs.js +0 -1
  283. package/dist/web/assets/audit-CeqRzFvk.js +0 -1
  284. package/dist/web/assets/calendar-notice-xDcRDi1v.js +0 -1
  285. package/dist/web/assets/calendar-setup-BtFkAeIS.js +0 -2
  286. package/dist/web/assets/check-check-Cce3q3Ky.js +0 -1
  287. package/dist/web/assets/checkbox-DlQ_74fJ.js +0 -1
  288. package/dist/web/assets/chevron-right-7GQHKI9_.js +0 -1
  289. package/dist/web/assets/chips-input-C9x108wA.js +0 -1
  290. package/dist/web/assets/choice-cards-Cex2m0j8.js +0 -1
  291. package/dist/web/assets/common-C4yJ-deN.js +0 -1
  292. package/dist/web/assets/connect-agent-guide-BtOZf3_q.js +0 -1
  293. package/dist/web/assets/connect-host-DI3tWU6X.js +0 -6
  294. package/dist/web/assets/connection-row-DikIBK8Q.js +0 -1
  295. package/dist/web/assets/connections-BKZ6yBTg.js +0 -1
  296. package/dist/web/assets/data-table-BfTfX2sW.js +0 -1
  297. package/dist/web/assets/events-xVIuhqZv.js +0 -1
  298. package/dist/web/assets/extensions-BD4k1WH0.js +0 -1
  299. package/dist/web/assets/eye-off-CRRRSSfB.js +0 -1
  300. package/dist/web/assets/field-Bld9RLD8.js +0 -1
  301. package/dist/web/assets/get-started-QYra5MBR.js +0 -2
  302. package/dist/web/assets/hidden-chars-DD6tq-Mk.js +0 -1
  303. package/dist/web/assets/index-DtYkC_1W.css +0 -2
  304. package/dist/web/assets/index-pS1CdkB5.js +0 -11
  305. package/dist/web/assets/label-multi-select-BgbVIHK0.js +0 -1
  306. package/dist/web/assets/labels-CY3s_f4D.js +0 -1
  307. package/dist/web/assets/last-call-BtUKW__e.js +0 -1
  308. package/dist/web/assets/link-Bjve-GxQ.js +0 -1
  309. package/dist/web/assets/listener-handled-CcrvY1Eo.js +0 -1
  310. package/dist/web/assets/login-DRfA42SF.js +0 -1
  311. package/dist/web/assets/mcp-call-9wGuKXyg.js +0 -1
  312. package/dist/web/assets/mcp-call-card-BZIt9zKX.js +0 -3
  313. package/dist/web/assets/message-square-quote-DQU8XNrk.js +0 -1
  314. package/dist/web/assets/mutations-B37Yslj1.js +0 -1
  315. package/dist/web/assets/pairing-CGexDPs8.js +0 -1
  316. package/dist/web/assets/pairing-notifier-ClVDyIH0.js +0 -1
  317. package/dist/web/assets/paperclip-yTjPJM-n.js +0 -1
  318. package/dist/web/assets/password-input-CV7heAOR.js +0 -1
  319. package/dist/web/assets/plug-UCmqKuZA.js +0 -1
  320. package/dist/web/assets/plus-CiafWVCV.js +0 -1
  321. package/dist/web/assets/public-url-CoeDBSCn.js +0 -1
  322. package/dist/web/assets/refresh-cw-CW9r0UtC.js +0 -1
  323. package/dist/web/assets/rotate-ccw-Bmedroly.js +0 -1
  324. package/dist/web/assets/select-CzWdsR3B.js +0 -1
  325. package/dist/web/assets/send-D_o_npWJ.js +0 -1
  326. package/dist/web/assets/settings-Hbo4ZU22.js +0 -1
  327. package/dist/web/assets/setup-1OEfpUw9.js +0 -1
  328. package/dist/web/assets/sheet-DWabx8oX.js +0 -1
  329. package/dist/web/assets/sparkles-Wm7CCJD-.js +0 -1
  330. package/dist/web/assets/step-url-B0U5jHfh.js +0 -1
  331. package/dist/web/assets/stream-ByAu9jPp.js +0 -1
  332. package/dist/web/assets/tag-e85mWMYi.js +0 -1
  333. package/dist/web/assets/test-email-button-CZhJr9YD.js +0 -1
  334. package/dist/web/assets/toggle-group-BxG-nfdu.js +0 -1
  335. package/dist/web/assets/use-delivery-speed-DpLVVpS7.js +0 -1
  336. package/dist/web/assets/use-label-names-CA-DHl8j.js +0 -1
  337. package/dist/web/assets/useQuery-D6DdXQL5.js +0 -1
  338. package/dist/web/assets/useRouterState-CGq8BUu5.js +0 -1
  339. package/dist/web/assets/webhook-CvoCD3cR.js +0 -1
  340. package/dist/web/assets/webhook-bits-BG9wW-rp.js +0 -1
  341. package/dist/web/assets/webhook-sheet-DfEiqVuf.js +0 -34
  342. package/dist/web/assets/webhooks-BTv7Xf-m.js +0 -1
  343. package/dist/web/assets/x-C799zbGJ.js +0 -1
package/README.md CHANGED
@@ -4,118 +4,47 @@
4
4
  [![npm](https://img.shields.io/npm/v/xaq-cli)](https://www.npmjs.com/package/xaq-cli)
5
5
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
6
6
 
7
- A self-hosted MCP broker for AI agents (formerly agentinbox, see [Upgrading from agentinbox](#upgrading-from-agentinbox)). One small server holds all your credentials (Notion, WHOOP, T3 Code, any MCP
8
- server, Gmail and Google Calendar) and gives each agent a single MCP endpoint with only the tools it's allowed. Risky actions
9
- (deleting, sending, publishing, payments) can wait for your one-tap approval, and every call is audited.
10
-
11
- ```sh
12
- npx xaq-cli
13
- ```
14
-
15
- Open the link it prints and pick what to connect first: Notion, another MCP server, or Gmail and Calendar.
16
- Pre-made connections (Notion, T3 Code, WHOOP, Brightspace) are extensions: the console installs the one you pick
17
- from the official registry, then connects it. Only Gmail and Calendar need a Google Cloud project; everything else
18
- is a few clicks. The console then connects your first agent. Requires Node.js 22.13 or newer (code extensions such
19
- as WHOOP need 22.23.2, 24.18.1 or 26.5.1 or newer on those lines).
20
-
21
- From the terminal on the same machine: install an extension first, `npx xaq-cli ext add notion` (or `t3code`,
22
- `whoop`, `brightspace`), then `npx xaq-cli connections add notion` (`t3code --url http://<your T3 server>:3773`, or
23
- any MCP server URL),
24
- and `npx xaq-cli import --from hermes` (also `claude-code`, `cursor`, `codex`) to move an agent's existing MCP
25
- servers behind the broker. Details: [Connections](docs/reference.md#connections-mcp-broker).
26
-
27
- For Gmail, Google Calendar is supported too (optional). It needs the Calendar API enabled in your Google Cloud project, the
28
- Calendar scopes on the consent screen, Calendar added to each account, and `calendar.read` (and, to make changes,
29
- `calendar.write`) on the agent's grant. The console's setup covers all of it; existing installs use Overview → "Add
30
- Google Calendar". Details: [docs/calendar.md](docs/calendar.md).
31
-
32
- Agents can attach files to drafts, replies, forwards and new mail: bytes they have, a file from a directory you allow
33
- on xaq's machine (off by default: Settings → Attachments from files, or `npx xaq-cli settings attachment-dirs add <dir>`),
34
- another message's attachment, or a Google Drive file (with the agent granted the account's Google Workspace connection, and Drive enabled for the account). Gmail's 25 MB per message
35
- applies. Details: [Attachments](docs/reference.md#attachments).
36
-
37
- ## Extensions
38
-
39
- Integrations are extensions: `npx xaq-cli ext add notion` installs one from the official
40
- [xaq-extensions](https://github.com/wynn-dev/xaq-extensions) registry, pinned to a commit and an integrity hash, and
41
- `ext list`, `ext update` (shows the change, asks first; nothing updates on its own) and `ext remove` manage them, as
42
- does the console's Extensions page. A GitHub URL, another git URL or a local directory works too, as a third-party
43
- extension, after a warning: a `code` extension runs on your machine with network access. Node's permission model
44
- limits its files, child processes and workers, not its network. Details: [Extensions](docs/reference.md#extensions).
7
+ A self-hosted MCP broker for AI agents. One small server holds your credentials (Notion, WHOOP, T3 Code, any MCP
8
+ server, Gmail and Google Calendar) and gives each agent one MCP endpoint with only the tools it's allowed. Every call
9
+ is audited.
45
10
 
46
11
  ![Overview](docs/screenshots/overview.png)
47
12
 
48
- ## Live check
13
+ ## Install and first run
49
14
 
50
- There is no test account, so `smoke google-workspace` checks Gmail attachments and the Google Workspace extension
51
- on the real one, end to end. Run it on xaq's machine while xaq is running:
15
+ Needs Node.js 22.13 or newer.
52
16
 
53
17
  ```sh
54
- npx xaq-cli smoke google-workspace --scratch
18
+ npx xaq-cli
55
19
  ```
56
20
 
57
- It prints its plan and asks first. It starts by removing what earlier runs left behind, and lists anything it can't
58
- remove for you to check. Existing files and mail are only read; it prints their names and the email's subject,
59
- never their content, and it never fetches an attachment that would be saved to disk. Every write goes into one new Drive folder, `xaq-smoke-<UTC timestamp>`, and the only
60
- mail is one draft to the account itself, which is never sent. A temporary agent makes the calls through `/mcp`, so
61
- risky ones go through approval, and the command approves its own. At the end the draft is deleted, the folder goes
62
- to the Drive trash, and the agent and the temporary settings are removed, also after a failure, Ctrl-C, SIGTERM or
63
- SIGHUP. You get a
64
- PASS/FAIL table and a "Left behind" list, which should say nothing. Details:
65
- [Live check](docs/reference.md#live-check-smoke-google-workspace).
66
-
67
- <!-- rename-xaq: keep-start -->
68
- ## Upgrading from agentinbox
69
-
70
- xaq is the new name of agentinbox (the npm package `agentinbox-cli` is now `xaq-cli`). An existing install keeps
71
- working as it is; to move it to the new names, run one command per service, on the machine it runs on:
21
+ Open the link it prints, set the admin password, and pick what to connect first: Notion, another MCP server, or Gmail
22
+ and Calendar (which need your own Google Cloud project; the console walks you through it). The console then connects
23
+ your first agent. To keep xaq running in the background, also after a reboot:
72
24
 
73
25
  ```sh
74
- npx xaq-cli service install # the daemon
75
- npx xaq-cli listen service install # each listener (add --profile <name> for other profiles)
76
- npx xaq-cli skill install --target <claude|codex|…> --url <your server> # each agent's skill, if you installed it
26
+ npx xaq-cli service install
77
27
  ```
78
28
 
79
- Each `service install` finds the agentinbox service (launchd `com.unlaboredlabs.agentinbox…`, systemd
80
- `agentinbox.service` / `agentinbox-listen-<profile>.service`), stops it and waits until it is gone, then installs and
81
- starts the xaq one (launchd `sh.xaq.daemon` / `sh.xaq.listen.<profile>`, systemd `xaq.service` /
82
- `xaq-listen-<profile>.service`). The two never run at the same time, and if the new one doesn't start, the old one
83
- is put back exactly as it was. Running it again is a normal reinstall. Mail that arrives meanwhile waits on the daemon
84
- and is delivered once the listener is back: nothing is lost or delivered twice.
85
-
86
- **Kept:**
87
- - the data directory: `~/.agentinbox` (or the `--data` directory the old service used) stays in use, with its
88
- `master.key`, `agentinbox.db` and settings. Nothing is moved; `~/.xaq` is only for new installs.
89
- - every agent key (`aib_…`), Gmail and Calendar connection, grant, webhook and Pub/Sub setup;
90
- - the listener's profiles, keys, logs and event cursor (`~/.config/agentinbox`, `~/.local/state/agentinbox`,
91
- `~/.local/share/agentinbox` stay in use until the matching `…/xaq` directory holds xaq's own files);
92
- - the names your agents already use: a profile set up with agentinbox keeps its MCP server name `agentinbox`
93
- (`agentinbox-<profile>`), so Hermes routes confined to `["mcp-agentinbox"]`, cron jobs limited to
94
- `["agentinbox"]`, the Hermes route `/webhooks/agentinbox…` and Claude Code's `mcp__agentinbox` keep working;
95
- - `AGENTINBOX_*` environment variables: still read when the `XAQ_*` one isn't set, and `--exec` commands still get
96
- `AGENTINBOX_EVENT_FILE` and the other ids next to the new `XAQ_*` ones.
97
-
98
- Another, unrelated program is also called xaq and may have its own `~/.config/xaq` (or `~/.xaq`). xaq counts such
99
- a directory as its own only when it holds xaq's files (`profiles/`, `listen/`, `app/`; in `~/.xaq` its database or
100
- `master.key`), checked without reading anything. Otherwise an existing agentinbox directory is used; and where there
101
- is none, xaq only adds its own files next to the other program's and never changes, renames or deletes anything it
102
- didn't create.
29
+ ## A short tour
103
30
 
104
- **Changes:**
105
- - the command is `xaq` (`npx xaq-cli …`); `agentinbox` (from the `agentinbox-cli` package, now a shim) and
106
- `npx agentinbox-cli …` still work: they run xaq after saying so;
107
- - the service names above, and the services write `XAQ_*` variables;
108
- - new profiles and setups use `xaq` / `xaq-<profile>` for the MCP server and Hermes route, and the skill is
109
- installed as `xaq` (replacing the `agentinbox` one);
110
- - the console, logs and messages say xaq.
111
- <!-- rename-xaq: keep-end -->
31
+ - **Connections.** Add an MCP server by URL, or pick a pre-made one (Notion, T3 Code, WHOOP, Brightspace). Pre-made
32
+ connections are extensions from the official [xaq-extensions](https://github.com/wynn-dev/xaq-extensions) registry,
33
+ pinned to a commit and an integrity hash.
34
+ - **Gmail and Google Calendar.** Search, read, draft, send and organize mail; read calendars, find free time and
35
+ schedule. PDFs and Office files come back as text and tables.
36
+ - **Agents.** Each agent gets one key and one endpoint, `<your server>/mcp`, and sees only what you granted it: read
37
+ only or full access per mailbox and connection, sends limited to the recipients you allow. Pair
38
+ one from its own machine with `npx xaq-cli connect <url>`; you approve it in the console.
39
+ - **Events.** New mail and calendar changes reach agents through `wait_for_events`, or wake an agent on a laptop with
40
+ [`xaq listen`](docs/listen.md).
41
+ - **The console.** Settings, connections, extensions, agents, live events and the audit trail.
112
42
 
113
43
  ## More
114
44
 
115
- - [Reference](docs/reference.md): features, configuration, running as a service or behind a proxy, security model,
116
- webhooks, data and backups, development
117
- - [Waking local agents with `xaq listen`](docs/listen.md), e.g. `npx xaq-cli listen --recipe claude-code`
118
- - [Screenshots](docs/screenshots/README.md)
45
+ - [Reference](docs/reference.md): commands, connections and extensions, Google setup, access, events,
46
+ attachments, running in production, security model, data and backups, development
47
+ - [Waking local agents with `xaq listen`](docs/listen.md)
119
48
 
120
49
  ## License
121
50
 
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import { _ as ApiError, a as TransportError, d as resolveListen, g as hasAccess, h as describeGrants, m as readStatus, o as checkTransport, p as pidAlive, r as outputLine, u as describeFilter, y as getSelf } from "./listen-DR3HAzmp.js";
3
+ import { i as safeText, r as formatDuration } from "./ui-B8Sd4Y7Y.js";
4
+ import { n as NPX } from "./agent-api-C7e5yLIF.js";
5
+ import { a as logPath, s as profilePath } from "./paths-BpQZZxo2.js";
6
+ import { t as loadProfile } from "./profile-DyERKzt8.js";
7
+ import { a as installedServiceLabel } from "./service-BY81gJ-r.js";
8
+ import pc from "picocolors";
9
+ //#region server/listen/admin.ts
10
+ const out = (s = "") => process.stdout.write(`${s}\n`);
11
+ const ago = (iso) => iso ? `${formatDuration(Date.now() - Date.parse(iso))} ago` : "never";
12
+ /** `listen status`: the pairing, the saved settings, the service, the key's access and the last run. */
13
+ async function printStatus() {
14
+ const profile = loadProfile();
15
+ const st = readStatus();
16
+ const alive = st ? pidAlive(st.pid) && !st.stoppedAt : false;
17
+ out(`${pc.bold("Profile")} ${pc.gray(profilePath())}`);
18
+ out(`${pc.bold("Agent")} ${safeText(profile.agentName)} ${pc.gray(`(${profile.agentId})`)}`);
19
+ out(`${pc.bold("Daemon")} ${profile.url}`);
20
+ out(`${pc.bold("Consumer")} ${st?.consumer ?? profile.consumer}`);
21
+ const saved = resolveListen(profile, {}).config;
22
+ const exec = alive && st?.exec !== void 0 ? st.exec : saved.exec;
23
+ out(`${pc.bold("Runs")} ${exec ? `exec ${safeText(exec, 120)}` : pc.gray("nothing saved (`listen service install --recipe claude-code` saves it)")}`);
24
+ out(`${pc.bold("Events")} ${alive && st?.filters || describeFilter(saved)}`);
25
+ out(`${pc.bold("Output")} ${outputLine(exec ?? null, logPath())}`);
26
+ let svc = "not installed";
27
+ if (process.platform === "linux" || process.platform === "darwin") {
28
+ const installed = installedServiceLabel();
29
+ if (installed) svc = `installed (${installed}; details: ${NPX} listen service status)`;
30
+ }
31
+ out(`${pc.bold("Service")} ${svc}`);
32
+ try {
33
+ await checkTransport(profile.url, { insecure: profile.insecure });
34
+ const self = await getSelf(profile.url, profile.apiKey, 5e3);
35
+ out(`${pc.bold("Key")} ${pc.green("valid")}`);
36
+ const lines = describeGrants(self.grants, self.connections);
37
+ out(`${pc.bold("Access")} ${hasAccess(self) ? lines[0] : pc.yellow(lines[0])}`);
38
+ for (const l of lines.slice(1)) out(` ${l}`);
39
+ } catch (err) {
40
+ const revoked = err instanceof ApiError && (err.status === 401 || err.status === 403);
41
+ const why = err instanceof ApiError && err.status === 0 ? err.cause : err instanceof Error ? err.message.split("\n")[0] : String(err);
42
+ if (err instanceof TransportError) out(`${pc.bold("Key")} ${pc.red(`not checked: ${why}`)}`);
43
+ else out(`${pc.bold("Key")} ${revoked ? pc.red(`rejected: ${err.message}`) : pc.yellow(`not checked (${why})`)}`);
44
+ }
45
+ if (!st) out(`${pc.bold("Listener")} ${pc.gray("has not run yet")}`);
46
+ else {
47
+ const state = !alive ? pc.gray(`not running${st.stoppedAt ? ` (stopped ${ago(st.stoppedAt)}${st.exitReason ? `: ${safeText(st.exitReason, 160)}` : ""})` : " (exited unexpectedly)"}`) : st.connected ? pc.green(`running (pid ${st.pid}), connected ${ago(st.connectedSince)}`) : pc.yellow(`running (pid ${st.pid}), disconnected${st.lastDisconnect ? ` ${ago(st.lastDisconnect.at)}: ${safeText(st.lastDisconnect.reason, 160)}` : ""}`);
48
+ out(`${pc.bold("Listener")} ${state}`);
49
+ out(`${pc.bold("Cursor")} acked #${st.ackedSeq} · received #${st.receivedSeq}${alive && st.running ? ` · ${st.running} in progress` : ""}${st.skipped ? ` · ${st.skipped} skipped by filters` : ""} · last event ${ago(st.lastEventAt)}`);
50
+ if (st.recent.length) {
51
+ out(pc.bold("Recent"));
52
+ for (const r of st.recent.slice(0, 8)) {
53
+ const icon = r.outcome === "succeeded" ? pc.green("✓") : r.outcome === "failed" ? pc.red("✖") : r.outcome === "retrying" ? pc.yellow("↻") : pc.gray("·");
54
+ out(` ${icon} #${r.seq} ${r.type} ${pc.gray(`${ago(r.at)}${r.attempts > 1 ? `, ${r.attempts} attempts` : ""}${r.error && r.outcome !== "succeeded" ? `, ${safeText(r.error, 100)}` : ""}`)}`);
55
+ }
56
+ }
57
+ }
58
+ out(`${pc.bold("Log")} ${logPath()}`);
59
+ }
60
+ //#endregion
61
+ export { printStatus };
62
+
63
+ //# sourceMappingURL=admin-BjqfG6Qm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-BjqfG6Qm.js","names":[],"sources":["../server/listen/admin.ts"],"sourcesContent":["import pc from \"picocolors\";\nimport { ApiError, getSelf } from \"./api.ts\";\nimport { describeGrants, hasAccess } from \"./grants.ts\";\nimport { outputLine } from \"./listen.ts\";\nimport { describeFilter, resolveListen } from \"./options.ts\";\nimport { logPath, profilePath } from \"./paths.ts\";\nimport { loadProfile } from \"./profile.ts\";\nimport { installedServiceLabel } from \"./service.ts\";\nimport { pidAlive, readStatus } from \"./state.ts\";\nimport { checkTransport, TransportError } from \"./transport.ts\";\nimport { formatDuration, safeText } from \"./ui.ts\";\nimport { NPX } from \"./version.ts\";\n\nconst out = (s = \"\") => process.stdout.write(`${s}\\n`);\nconst ago = (iso: string | null | undefined) => (iso ? `${formatDuration(Date.now() - Date.parse(iso))} ago` : \"never\");\n\n/** `listen status`: the pairing, the saved settings, the service, the key's access and the last run. */\nexport async function printStatus(): Promise<void> {\n const profile = loadProfile();\n const st = readStatus();\n const alive = st ? pidAlive(st.pid) && !st.stoppedAt : false;\n\n out(`${pc.bold(\"Profile\")} ${pc.gray(profilePath())}`);\n out(`${pc.bold(\"Agent\")} ${safeText(profile.agentName)} ${pc.gray(`(${profile.agentId})`)}`);\n out(`${pc.bold(\"Daemon\")} ${profile.url}`);\n out(`${pc.bold(\"Consumer\")} ${st?.consumer ?? profile.consumer}`);\n // What the running listener uses (flags included), else the saved settings.\n const saved = resolveListen(profile, {}).config;\n const exec = alive && st?.exec !== undefined ? st.exec : saved.exec;\n out(`${pc.bold(\"Runs\")} ${exec ? `exec ${safeText(exec, 120)}` : pc.gray(\"nothing saved (`listen service install --recipe claude-code` saves it)\")}`);\n out(`${pc.bold(\"Events\")} ${(alive && st?.filters) || describeFilter(saved)}`);\n out(`${pc.bold(\"Output\")} ${outputLine(exec ?? null, logPath())}`);\n\n let svc = \"not installed\";\n if (process.platform === \"linux\" || process.platform === \"darwin\") {\n const installed = installedServiceLabel();\n if (installed) svc = `installed (${installed}; details: ${NPX} listen service status)`;\n }\n out(`${pc.bold(\"Service\")} ${svc}`);\n\n try {\n // Never send the key over a connection `listen` itself would refuse.\n await checkTransport(profile.url, { insecure: profile.insecure });\n const self = await getSelf(profile.url, profile.apiKey, 5000);\n out(`${pc.bold(\"Key\")} ${pc.green(\"valid\")}`);\n // The access the console actually granted, in plain words (what the agent should report to its human).\n const lines = describeGrants(self.grants, self.connections);\n out(`${pc.bold(\"Access\")} ${hasAccess(self) ? lines[0] : pc.yellow(lines[0]!)}`);\n for (const l of lines.slice(1)) out(` ${l}`);\n } catch (err) {\n const revoked = err instanceof ApiError && (err.status === 401 || err.status === 403);\n const why = err instanceof ApiError && err.status === 0 ? (err.cause as string) : err instanceof Error ? err.message.split(\"\\n\")[0] : String(err);\n if (err instanceof TransportError) out(`${pc.bold(\"Key\")} ${pc.red(`not checked: ${why}`)}`);\n else out(`${pc.bold(\"Key\")} ${revoked ? pc.red(`rejected: ${(err as Error).message}`) : pc.yellow(`not checked (${why})`)}`);\n }\n\n if (!st) {\n out(`${pc.bold(\"Listener\")} ${pc.gray(\"has not run yet\")}`);\n } else {\n const state = !alive\n ? pc.gray(`not running${st.stoppedAt ? ` (stopped ${ago(st.stoppedAt)}${st.exitReason ? `: ${safeText(st.exitReason, 160)}` : \"\"})` : \" (exited unexpectedly)\"}`)\n : st.connected\n ? pc.green(`running (pid ${st.pid}), connected ${ago(st.connectedSince)}`)\n : pc.yellow(`running (pid ${st.pid}), disconnected${st.lastDisconnect ? ` ${ago(st.lastDisconnect.at)}: ${safeText(st.lastDisconnect.reason, 160)}` : \"\"}`);\n out(`${pc.bold(\"Listener\")} ${state}`);\n out(\n `${pc.bold(\"Cursor\")} acked #${st.ackedSeq} · received #${st.receivedSeq}${alive && st.running ? ` · ${st.running} in progress` : \"\"}${st.skipped ? ` · ${st.skipped} skipped by filters` : \"\"} · last event ${ago(st.lastEventAt)}`,\n );\n if (st.recent.length) {\n out(pc.bold(\"Recent\"));\n for (const r of st.recent.slice(0, 8)) {\n const icon = r.outcome === \"succeeded\" ? pc.green(\"✓\") : r.outcome === \"failed\" ? pc.red(\"✖\") : r.outcome === \"retrying\" ? pc.yellow(\"↻\") : pc.gray(\"·\");\n out(` ${icon} #${r.seq} ${r.type} ${pc.gray(`${ago(r.at)}${r.attempts > 1 ? `, ${r.attempts} attempts` : \"\"}${r.error && r.outcome !== \"succeeded\" ? `, ${safeText(r.error, 100)}` : \"\"}`)}`);\n }\n }\n }\n out(`${pc.bold(\"Log\")} ${logPath()}`);\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { f as NPM_PACKAGE } from "./types-BhJDbSzE.js";
2
+ import { c as NPM_PACKAGE } from "./types-CYC5FTTW.js";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  //#region server/listen/version.ts
@@ -15,6 +15,16 @@ function readVersion() {
15
15
  }
16
16
  const version = readVersion();
17
17
  //#endregion
18
- export { version as n, NPX as t };
18
+ //#region shared/agent-api.ts
19
+ /** The agent tools the CLI and the console know (recipes, connect commands). */
20
+ const AGENT_TOOLS = [
21
+ "claude-code",
22
+ "codex",
23
+ "cursor",
24
+ "hermes",
25
+ "other"
26
+ ];
27
+ //#endregion
28
+ export { NPX as n, version as r, AGENT_TOOLS as t };
19
29
 
20
- //# sourceMappingURL=version-BxH7OuWD.js.map
30
+ //# sourceMappingURL=agent-api-C7e5yLIF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-api-C7e5yLIF.js","names":[],"sources":["../server/listen/version.ts","../shared/agent-api.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { NPM_PACKAGE } from \"../../shared/types.ts\";\n\n/** How printed instructions invoke the CLI without a global install. */\nexport const NPX = `npx ${NPM_PACKAGE}`;\n\n/** package.json sits above dist/ (bundled) or two levels above server/listen/ (source). */\nfunction readVersion(): string {\n for (const rel of [\"..\", \"../..\"]) {\n try {\n const pkg = JSON.parse(readFileSync(join(import.meta.dirname, rel, \"package.json\"), \"utf8\")) as { name?: string; version?: string };\n if (pkg.name === NPM_PACKAGE && pkg.version) return pkg.version;\n } catch {\n // Try the next candidate.\n }\n }\n return \"0.0.0\";\n}\n\nexport const version = readVersion();\n","// Agent-facing HTTP API under /agent/v1. Used by `npx xaq-cli connect` (device-flow pairing)\n// and `npx xaq-cli listen` (outbound event stream that wakes local agents).\n//\n// Routes:\n// POST /agent/v1/pairings PairingStartInput → PairingStart (public; rate-limited)\n// POST /agent/v1/pairings/poll { deviceCode } → PairingPoll (public; ≥ `interval` s apart)\n// GET /agent/v1/me → AgentSelf (bearer aib_ key)\n// GET /agent/v1/events/stream → text/event-stream (bearer aib_ key)\n// ?consumer=<name>&after=<seq>&types=a,b&include_body=1&start=now|beginning\n// Each event: `id: <seq>` `event: mail` `data: <EventPayload JSON>`; `: ping` comments every 15 s;\n// `event: revoked` `data: {\"reason\": …}` then close when the key, agent or grant stops being valid.\n// Resumes after max(after, Last-Event-ID, the consumer's acked cursor). A consumer seen for the\n// first time starts at the head (newest event the agent can see: only new mail), or at 0 with\n// `start=beginning` (honored only for a new consumer). Consumer names: 1-100 of [A-Za-z0-9._-].\n// POST /agent/v1/events/ack { consumer, seq } → { ackedSeq } (bearer aib_ key)\n//\n// Requests must not carry Origin or Sec-Fetch-* headers (browsers are refused), POST bodies must be\n// `Content-Type: application/json`, and the Host must be one this daemon answers to (DNS-rebinding guard).\n// `after`, Last-Event-ID and acks are capped at the newest event the agent can see.\n//\n// Access errors on /me and the stream (and the matching `revoked` reasons):\n// reversible (keep the key, retry later): 403 agent_disabled, 403 no_read_grant (stream only)\n// permanent (pair again): 401 unauthorized (unknown or rotated key; revoked reasons\n// key_rotated and agent_deleted)\n//\n// Pairing (approved design): the CLI starts a pairing and shows `userCode` + `verificationUrl`; a human\n// approves that exact code in the console (choosing the access, read by default). Only then is\n// the API key generated; it is handed to the polling CLI exactly once and written to a 0600 file.\n// Codes are short-lived and single-use; requests, approvals, denials, expiry and key delivery are audited.\n\nimport type { AccessLevel } from \"./types.ts\";\n\n/** The agent tools the CLI and the console know (recipes, connect commands). */\nexport const AGENT_TOOLS = [\"claude-code\", \"codex\", \"cursor\", \"hermes\", \"other\"] as const;\nexport type AgentTool = (typeof AGENT_TOOLS)[number];\n\nexport interface PairingStartInput {\n /** Human-readable agent name, e.g. \"Hermes on wynn-mbp\". */\n name: string;\n description?: string | null;\n /** What the agent asks for. The human decides in the console. */\n requested?: {\n accounts?: string[]; // account emails\n reason?: string;\n };\n client?: { hostname?: string; platform?: string; tool?: AgentTool; version?: string };\n}\n\nexport interface PairingStart {\n /** Secret the CLI polls with. Never shown to the human. */\n deviceCode: string;\n /** Short code the human matches in the console, e.g. \"K7QM-2XPD\". */\n userCode: string;\n /** Console page that opens this request. */\n verificationUrl: string;\n expiresAt: string;\n /** Minimum seconds between polls. */\n interval: number;\n}\n\nexport type PairingPoll =\n | { status: \"pending\" }\n /**\n * Polled faster than the current interval (RFC 8628): `interval` is the previous one + 5 s and is\n * enforced for this pairing from now on, so keep polling at least that far apart.\n */\n | { status: \"slow_down\"; interval: number }\n | {\n status: \"approved\";\n /** Delivered exactly once; later polls return \"consumed\". */\n apiKey: string;\n agent: { id: string; name: string };\n mcpUrl: string;\n skillUrl: string;\n }\n | { status: \"denied\"; note: string | null }\n | { status: \"expired\" }\n | { status: \"consumed\" };\n\nexport interface AgentSelf {\n id: string;\n name: string;\n grants: {\n accountId: string;\n accountEmail: string;\n level: AccessLevel;\n /** Recipient allowlist for sends and calendar attendees; empty = anyone. */\n allowTo: string[];\n }[];\n /**\n * This agent's listener consumers (absent from older daemons). `startSeq`: where the cursor started\n * when the consumer was created (mail at or below it predates that listener; what `listen --backfill`\n * replays); null when unknown (created before the daemon recorded it).\n */\n consumers?: { consumer: string; startSeq: number | null; ackedSeq: number; connected: boolean }[];\n /**\n * MCP broker connections this agent may use (absent from older daemons): the\n * connection's name, its tool prefix (`<slug>__`) and this agent's level. Nothing else about the connection.\n */\n connections?: { name: string; slug: string; level: AccessLevel }[];\n mcpUrl: string;\n skillUrl: string;\n /** Newest event seq this agent can see: where a new consumer starts (\"from now on\"). */\n headSeq: number;\n}\n"],"mappings":";;;;;;AAMA,MAAa,MAAM,OAAO;;AAG1B,SAAS,cAAsB;CAC7B,KAAK,MAAM,OAAO,CAAC,MAAM,OAAO,GAC9B,IAAI;EACF,MAAM,MAAM,KAAK,MAAM,aAAa,KAAK,YAAY,SAAS,KAAK,cAAc,GAAG,MAAM,CAAC;EAC3F,IAAI,IAAI,SAAA,aAAwB,IAAI,SAAS,OAAO,IAAI;CAC1D,QAAQ,CAER;CAEF,OAAO;AACT;AAEA,MAAa,UAAU,YAAY;;;;ACYnC,MAAa,cAAc;CAAC;CAAe;CAAS;CAAU;CAAU;AAAO"}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as now, r as json } from "./db-4CHRuBhV.js";
2
+ import { i as now, r as json } from "./db-Bp6AHwuY.js";
3
3
  //#region server/audit.ts
4
4
  /**
5
5
  * SQL condition for an MCP tool call in audit_log: an agent actor and an action without a "." (tool
@@ -76,4 +76,4 @@ var AuditLog = class {
76
76
  //#endregion
77
77
  export { mcpToolCallSql as n, AuditLog as t };
78
78
 
79
- //# sourceMappingURL=audit-dW-RK5o8.js.map
79
+ //# sourceMappingURL=audit-DWWc7dPE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-DWWc7dPE.js","names":[],"sources":["../server/audit.ts"],"sourcesContent":["import type { ActorType, AuditEntry, AuditOutcome, Page } from \"../shared/types.ts\";\nimport type { Bus } from \"./bus.ts\";\nimport { type Db, json, now } from \"./db/index.ts\";\n\nexport interface AuditInput {\n actorType: ActorType;\n actorId?: string | null;\n /** MCP tool name or console action, e.g. \"send_message\", \"agent.create\", \"account.connect\". */\n action: string;\n accountId?: string | null;\n target?: string | null;\n outcome: AuditOutcome;\n /** Required for denials: the guardrail that blocked it (\"allow_to\", \"read_only\", …). */\n reason?: string | null;\n durationMs?: number | null;\n /** Small, non-sensitive context. Never message bodies, keys or tokens. */\n detail?: Record<string, unknown> | null;\n}\n\n/**\n * SQL condition for an MCP tool call in audit_log: an agent actor and an action without a \".\" (tool\n * names are like \"send_message\"; \"pairing.key_delivered\" and console actions have a dot). The same\n * rule as isMcpToolAction in shared/types.ts (used by the console). `t` is the table alias, if any.\n *\n * Every outcome counts, including calls rejected before a tool ran (unknown tool, invalid arguments,\n * a malformed tools/call): those are recorded with outcome \"error\" (see rejectedCall in\n * server/mcp/server.ts), so the Overview's \"Tool calls\" counts them. Anything that needs proof that\n * an agent works (anyAgentVerified, the wizard's checks) adds `outcome = 'ok'`.\n */\nexport function mcpToolCallSql(t?: string): string {\n const c = t ? `${t}.` : \"\";\n return `${c}actor_type = 'agent' AND instr(${c}action, '.') = 0`;\n}\n\nexport interface AuditQuery {\n agentId?: string;\n accountId?: string;\n outcome?: AuditOutcome;\n action?: string;\n /** Opaque cursor from a previous page. */\n cursor?: string | null;\n limit?: number;\n}\n\ninterface AuditRow {\n id: number;\n ts: string;\n actor_type: ActorType;\n actor_id: string | null;\n actor_name: string | null;\n action: string;\n account_id: string | null;\n target: string | null;\n outcome: AuditOutcome;\n reason: string | null;\n duration_ms: number | null;\n detail: string | null;\n}\n\nconst SELECT = `SELECT a.*, CASE a.actor_type WHEN 'agent' THEN ag.name WHEN 'admin' THEN 'admin' ELSE NULL END AS actor_name\n FROM audit_log a LEFT JOIN agents ag ON a.actor_type = 'agent' AND ag.id = a.actor_id`;\n\nconst toEntry = (r: AuditRow): AuditEntry => ({\n id: r.id,\n ts: r.ts,\n actorType: r.actor_type,\n actorId: r.actor_id,\n actorName: r.actor_name,\n action: r.action,\n accountId: r.account_id,\n target: r.target,\n outcome: r.outcome,\n reason: r.reason,\n durationMs: r.duration_ms,\n detail: json.parse<Record<string, unknown> | null>(r.detail, null),\n});\n\n/** Append-only audit log. Every agent tool call and every admin mutation is recorded. */\nexport class AuditLog {\n constructor(\n private readonly db: Db,\n private readonly bus: Bus,\n ) {}\n\n record(input: AuditInput): AuditEntry {\n const { lastInsertRowid } = this.db.run(\n `INSERT INTO audit_log (ts, actor_type, actor_id, action, account_id, target, outcome, reason, duration_ms, detail)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n now(),\n input.actorType,\n input.actorId ?? null,\n input.action,\n input.accountId ?? null,\n input.target ?? null,\n input.outcome,\n input.reason ?? null,\n input.durationMs ?? null,\n input.detail ? json.str(input.detail) : null,\n );\n const entry = toEntry(this.db.get<AuditRow>(`${SELECT} WHERE a.id = ?`, lastInsertRowid)!);\n this.bus.emit(\"audit\", entry);\n return entry;\n }\n\n /** Newest first. */\n list(q: AuditQuery = {}): Page<AuditEntry> {\n const where: string[] = [];\n const params: (string | number)[] = [];\n const add = (clause: string, value: string | number) => {\n where.push(clause);\n params.push(value);\n };\n if (q.agentId) add(\"a.actor_id = ?\", q.agentId);\n if (q.accountId) add(\"a.account_id = ?\", q.accountId);\n if (q.outcome) add(\"a.outcome = ?\", q.outcome);\n if (q.action) add(\"a.action = ?\", q.action);\n const before = Number(q.cursor);\n if (q.cursor && Number.isInteger(before)) add(\"a.id < ?\", before);\n const limit = Math.min(Math.max(q.limit ?? 50, 1), 200);\n const rows = this.db.all<AuditRow>(\n `${SELECT} ${where.length ? \"WHERE \" + where.join(\" AND \") : \"\"} ORDER BY a.id DESC LIMIT ?`,\n ...params,\n limit + 1,\n );\n const items = rows.slice(0, limit).map(toEntry);\n return { items, nextCursor: rows.length > limit ? String(items.at(-1)!.id) : null };\n }\n\n /** Deletes entries older than `beforeIso`. Returns the number removed. */\n prune(beforeIso: string): number {\n return this.db.run(\"DELETE FROM audit_log WHERE ts < ?\", beforeIso).changes;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,SAAgB,eAAe,GAAoB;CACjD,MAAM,IAAI,IAAI,GAAG,EAAE,KAAK;CACxB,OAAO,GAAG,EAAE,iCAAiC,EAAE;AACjD;AA2BA,MAAM,SAAS;;AAGf,MAAM,WAAW,OAA6B;CAC5C,IAAI,EAAE;CACN,IAAI,EAAE;CACN,WAAW,EAAE;CACb,SAAS,EAAE;CACX,WAAW,EAAE;CACb,QAAQ,EAAE;CACV,WAAW,EAAE;CACb,QAAQ,EAAE;CACV,SAAS,EAAE;CACX,QAAQ,EAAE;CACV,YAAY,EAAE;CACd,QAAQ,KAAK,MAAsC,EAAE,QAAQ,IAAI;AACnE;;AAGA,IAAa,WAAb,MAAsB;CAED;CACA;CAFnB,YACE,IACA,KACA;EAFiB,KAAA,KAAA;EACA,KAAA,MAAA;CAChB;CAEH,OAAO,OAA+B;EACpC,MAAM,EAAE,oBAAoB,KAAK,GAAG,IAClC;+CAEA,IAAI,GACJ,MAAM,WACN,MAAM,WAAW,MACjB,MAAM,QACN,MAAM,aAAa,MACnB,MAAM,UAAU,MAChB,MAAM,SACN,MAAM,UAAU,MAChB,MAAM,cAAc,MACpB,MAAM,SAAS,KAAK,IAAI,MAAM,MAAM,IAAI,IAC1C;EACA,MAAM,QAAQ,QAAQ,KAAK,GAAG,IAAc,GAAG,OAAO,kBAAkB,eAAe,CAAE;EACzF,KAAK,IAAI,KAAK,SAAS,KAAK;EAC5B,OAAO;CACT;;CAGA,KAAK,IAAgB,CAAC,GAAqB;EACzC,MAAM,QAAkB,CAAC;EACzB,MAAM,SAA8B,CAAC;EACrC,MAAM,OAAO,QAAgB,UAA2B;GACtD,MAAM,KAAK,MAAM;GACjB,OAAO,KAAK,KAAK;EACnB;EACA,IAAI,EAAE,SAAS,IAAI,kBAAkB,EAAE,OAAO;EAC9C,IAAI,EAAE,WAAW,IAAI,oBAAoB,EAAE,SAAS;EACpD,IAAI,EAAE,SAAS,IAAI,iBAAiB,EAAE,OAAO;EAC7C,IAAI,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM;EAC1C,MAAM,SAAS,OAAO,EAAE,MAAM;EAC9B,IAAI,EAAE,UAAU,OAAO,UAAU,MAAM,GAAG,IAAI,YAAY,MAAM;EAChE,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,EAAE,SAAS,IAAI,CAAC,GAAG,GAAG;EACtD,MAAM,OAAO,KAAK,GAAG,IACnB,GAAG,OAAO,GAAG,MAAM,SAAS,WAAW,MAAM,KAAK,OAAO,IAAI,GAAG,8BAChE,GAAG,QACH,QAAQ,CACV;EACA,MAAM,QAAQ,KAAK,MAAM,GAAG,KAAK,CAAC,CAAC,IAAI,OAAO;EAC9C,OAAO;GAAE;GAAO,YAAY,KAAK,SAAS,QAAQ,OAAO,MAAM,GAAG,EAAE,CAAC,CAAE,EAAE,IAAI;EAAK;CACpF;;CAGA,MAAM,WAA2B;EAC/B,OAAO,KAAK,GAAG,IAAI,sCAAsC,SAAS,CAAC,CAAC;CACtE;AACF"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { t as AuditLog } from "./audit-DWWc7dPE.js";
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { l as currentSetupToken, f as isClaimed, _ as resetAdminPassword } from "./auth-CCd2LeD4.js";
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as HttpError } from "./errors-FbMS3QBo.js";
3
- import { i as sha256 } from "./crypto-s8x6up-l.js";
4
- import { i as regenerateSetupToken, n as ensureSetupToken } from "./setup-token-D1urJ8_0.js";
5
- import { isIP, isIPv4, isIPv6 } from "node:net";
3
+ import { i as sha256 } from "./crypto-1fJvt9vL.js";
4
+ import { i as regenerateSetupToken, n as ensureSetupToken } from "./setup-token-DBqR_h01.js";
5
+ import { BlockList, isIP, isIPv6 } from "node:net";
6
6
  import { getConnInfo } from "@hono/node-server/conninfo";
7
7
  import { deleteCookie, getCookie, setCookie } from "hono/cookie";
8
8
  import { randomBytes } from "node:crypto";
@@ -129,104 +129,6 @@ function clientKey(ip) {
129
129
  ]).slice(0, 4).map((g) => g.replace(/^0+(?=.)/, "")).join(":")}::/64`;
130
130
  }
131
131
  //#endregion
132
- //#region server/auth/proxy.ts
133
- function parseV4(ip) {
134
- return ip.split(".").reduce((acc, part) => acc << 8n | BigInt(Number(part)), 0n);
135
- }
136
- function parseAddress(raw) {
137
- const ip = raw.trim().replace(/^\[|\]$/g, "").replace(/%.*$/, "").toLowerCase();
138
- if (isIPv4(ip)) return {
139
- v6: false,
140
- n: parseV4(ip)
141
- };
142
- if (!isIPv6(ip)) return null;
143
- const mapped = /^(?:0{0,4}:){0,5}:?ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(ip);
144
- if (mapped && isIPv4(mapped[1])) return {
145
- v6: false,
146
- n: parseV4(mapped[1])
147
- };
148
- let text = ip;
149
- const v4Tail = /(\d+\.\d+\.\d+\.\d+)$/.exec(text);
150
- if (v4Tail) {
151
- const n = parseV4(v4Tail[1]);
152
- text = text.slice(0, -v4Tail[1].length) + `${(n >> 16n).toString(16)}:${(n & 65535n).toString(16)}`;
153
- }
154
- const [head = "", tail] = text.split("::");
155
- const left = head ? head.split(":") : [];
156
- const right = tail ? tail.split(":") : [];
157
- const groups = tail === void 0 ? left : [
158
- ...left,
159
- ...Array(8 - left.length - right.length).fill("0"),
160
- ...right
161
- ];
162
- if (groups.length !== 8) return null;
163
- return {
164
- v6: true,
165
- n: groups.reduce((acc, g) => acc << 16n | BigInt(Number.parseInt(g || "0", 16)), 0n)
166
- };
167
- }
168
- /**
169
- * Parses "10.0.0.5", "10.0.0.0/8", "fd00::/8", "::ffff:10.0.0.0/104"; null when invalid. A /0
170
- * (trust everyone) is refused: it would let any client pick its own address.
171
- */
172
- function parseRange(entry) {
173
- const parts = entry.trim().split("/");
174
- if (parts.length > 2) return null;
175
- const [ip = "", prefix] = parts;
176
- const addr = parseAddress(ip);
177
- if (!addr) return null;
178
- let bits = addr.v6 ? 128 : 32;
179
- if (prefix !== void 0) {
180
- if (!/^\d{1,3}$/.test(prefix)) return null;
181
- bits = Number(prefix);
182
- if (!addr.v6 && ip.includes(":")) bits -= 96;
183
- if (bits <= 0 || bits > (addr.v6 ? 128 : 32)) return null;
184
- }
185
- const width = BigInt(addr.v6 ? 128 : 32);
186
- const mask = bits === 0 ? 0n : (1n << BigInt(bits)) - 1n << width - BigInt(bits);
187
- return {
188
- v6: addr.v6,
189
- base: addr.n & mask,
190
- bits
191
- };
192
- }
193
- function inRange(addr, r) {
194
- if (addr.v6 !== r.v6) return false;
195
- const width = BigInt(r.v6 ? 128 : 32);
196
- const mask = r.bits === 0 ? 0n : (1n << BigInt(r.bits)) - 1n << width - BigInt(r.bits);
197
- return (addr.n & mask) === r.base;
198
- }
199
- const LOOPBACK = [parseRange("127.0.0.0/8"), parseRange("::1/128")];
200
- let cache;
201
- /** TRUST_PROXY_FROM as ranges. Invalid entries are reported once and ignored. */
202
- function trustedRanges(raw) {
203
- if (cache?.raw === raw) return cache.ranges;
204
- const ranges = [];
205
- const invalid = [];
206
- for (const entry of raw.split(/[,\s]+/).filter(Boolean)) {
207
- const r = parseRange(entry);
208
- if (r) ranges.push(r);
209
- else invalid.push(entry);
210
- }
211
- if (invalid.length) process.stderr.write(`xaq: ignoring invalid TRUST_PROXY_FROM entries: ${invalid.join(", ")}\n`);
212
- cache = {
213
- raw,
214
- ranges
215
- };
216
- return ranges;
217
- }
218
- /**
219
- * Whether forwarding headers from this socket peer may be believed: loopback always (a proxy on
220
- * the same host), anything else only when listed in TRUST_PROXY_FROM (IPs or CIDRs, comma-separated).
221
- * The daemon usually binds 0.0.0.0, so trusting whole private ranges would let any LAN or tailnet
222
- * peer forge its address.
223
- */
224
- function isTrustedProxy(peer, trustFrom = process.env.TRUST_PROXY_FROM ?? "") {
225
- const addr = parseAddress(peer);
226
- if (!addr) return false;
227
- return [...LOOPBACK, ...trustedRanges(trustFrom)].some((r) => inRange(addr, r));
228
- }
229
- //#endregion
230
132
  //#region server/auth/sessions.ts
231
133
  const SESSION_TTL_MS = 2592e6;
232
134
  /** Sliding refresh never extends a session past this age: sign in again after 90 days. */
@@ -294,7 +196,7 @@ var SessionStore = class {
294
196
  //#endregion
295
197
  //#region server/auth/index.ts
296
198
  const SESSION_COOKIE = "aib_session";
297
- const CSRF_HEADER = "x-agentinbox-csrf";
199
+ const CSRF_HEADER = "x-xaq-csrf";
298
200
  const MAX_PASSWORD_LENGTH = 1024;
299
201
  /** Hands out at most `max` slots; waiters are served in order. */
300
202
  var Semaphore = class {
@@ -362,12 +264,6 @@ function socketAddress(c) {
362
264
  return null;
363
265
  }
364
266
  }
365
- /**
366
- * The client address. X-Forwarded-For is honored only with TRUST_PROXY and only when the
367
- * connection comes from a trusted proxy (loopback, or TRUST_PROXY_FROM); it is read from the
368
- * right, since proxies append the address they saw and entries further left are client-controlled.
369
- * TRUST_PROXY_HOPS (default 1) is the number of proxies in front of xaq.
370
- */
371
267
  /** Some proxies (Azure, IIS) append ":port" to X-Forwarded-For entries. */
372
268
  function stripPort(entry) {
373
269
  const v6 = /^\[([^\]]+)\](?::\d+)?$/.exec(entry);
@@ -375,20 +271,33 @@ function stripPort(entry) {
375
271
  const v4 = /^(\d{1,3}(?:\.\d{1,3}){3}):\d+$/.exec(entry);
376
272
  return v4 ? v4[1] : entry;
377
273
  }
274
+ const LOOPBACK = new BlockList();
275
+ LOOPBACK.addSubnet("127.0.0.0", 8, "ipv4");
276
+ LOOPBACK.addAddress("::1", "ipv6");
277
+ /**
278
+ * Forwarding headers are believed only from a loopback peer (a proxy on the same host): the daemon usually
279
+ * binds 0.0.0.0, so trusting other peers would let any LAN or tailnet peer forge its address.
280
+ */
281
+ function isTrustedProxy(peer) {
282
+ const ip = peer.replace(/%.*$/, "").replace(/^::ffff:(?=\d+\.\d+\.\d+\.\d+$)/i, "");
283
+ const v = isIP(ip);
284
+ return v !== 0 && LOOPBACK.check(ip, v === 4 ? "ipv4" : "ipv6");
285
+ }
378
286
  let warnedUntrustedProxy = false;
379
- /** Upgrades from the old private-range rule silently lost proxy trust; say so once. */
380
287
  function warnUntrustedProxy(peer) {
381
288
  if (warnedUntrustedProxy) return;
382
289
  warnedUntrustedProxy = true;
383
- process.stderr.write(`xaq: ignoring X-Forwarded-For from ${peer}: TRUST_PROXY only trusts loopback proxies unless TRUST_PROXY_FROM lists them (e.g. TRUST_PROXY_FROM=${peer}).\n`);
290
+ process.stderr.write(`xaq: ignoring X-Forwarded-For from ${peer}: TRUST_PROXY only trusts a proxy on this machine (loopback).\n`);
384
291
  }
292
+ /**
293
+ * The client address. X-Forwarded-For is honored only with TRUST_PROXY and only from a loopback proxy; its
294
+ * last entry is the client (the one proxy in front of xaq appended it; entries further left are client-controlled).
295
+ */
385
296
  function clientIp(c, trustProxy) {
386
297
  const peer = socketAddress(c);
387
298
  if (trustProxy && peer && !isTrustedProxy(peer) && c.req.header("x-forwarded-for")) warnUntrustedProxy(peer);
388
299
  if (trustProxy && peer && isTrustedProxy(peer)) {
389
- const hops = Math.max(1, Number.parseInt(process.env.TRUST_PROXY_HOPS ?? "1", 10) || 1);
390
- const chain = (c.req.header("x-forwarded-for") ?? "").split(",").map((s) => stripPort(s.trim())).filter(Boolean);
391
- const forwarded = chain[chain.length - hops];
300
+ const forwarded = (c.req.header("x-forwarded-for") ?? "").split(",").map((s) => stripPort(s.trim())).filter(Boolean).at(-1);
392
301
  if (forwarded && isIP(forwarded)) return forwarded;
393
302
  }
394
303
  return peer ?? "unknown";
@@ -452,7 +361,7 @@ const MUTATING = /* @__PURE__ */ new Set([
452
361
  */
453
362
  function csrfGuard() {
454
363
  return async (c, next) => {
455
- if (MUTATING.has(c.req.method) && c.req.header("x-agentinbox-csrf") !== "1") throw new HttpError(403, "csrf", `Missing ${CSRF_HEADER} header`, `Send "${CSRF_HEADER}: 1" with every mutating /api request.`);
364
+ if (MUTATING.has(c.req.method) && c.req.header("x-xaq-csrf") !== "1") throw new HttpError(403, "csrf", `Missing ${CSRF_HEADER} header`, `Send "${CSRF_HEADER}: 1" with every mutating /api request.`);
456
365
  await next();
457
366
  };
458
367
  }
@@ -545,6 +454,6 @@ function rejection(c, check) {
545
454
  return check.lockedMinutes ? lockedOut(c, check.lockedMinutes) : null;
546
455
  }
547
456
  //#endregion
548
- export { clientKey as S, sessionToken as _, adminAuth as a, BAN_MS as b, csrfGuard as c, hasSession as d, isClaimed as f, resetAdminPassword as g, requireAdmin as h, Semaphore as i, currentSetupToken as l, rejection as m, MAX_PASSWORD_LENGTH as n, checkSecret as o, lockedOut as p, SESSION_COOKIE as r, clientIp as s, CSRF_HEADER as t, endSession as u, startSession as v, LoginLimiter as x, SESSION_MAX_AGE_MS as y };
457
+ export { clientKey as S, resetAdminPassword as _, adminAuth as a, BAN_MS as b, csrfGuard as c, hasSession as d, isClaimed as f, requireAdmin as g, rejection as h, Semaphore as i, currentSetupToken as l, lockedOut as m, MAX_PASSWORD_LENGTH as n, checkSecret as o, isTrustedProxy as p, SESSION_COOKIE as r, clientIp as s, CSRF_HEADER as t, endSession as u, sessionToken as v, LoginLimiter as x, startSession as y };
549
458
 
550
- //# sourceMappingURL=auth-D4QQM-EQ.js.map
459
+ //# sourceMappingURL=auth-CCd2LeD4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-CCd2LeD4.js","names":[],"sources":["../server/auth/lockout.ts","../server/auth/sessions.ts","../server/auth/index.ts"],"sourcesContent":["import { isIPv6 } from \"node:net\";\nimport { sha256 } from \"../crypto.ts\";\n\n/** Distinct wrong secrets per client within the window before a ban. */\nexport const MAX_FAILURES = 5;\n/** Raw failed attempts per client within the window (repeats of the same wrong secret included). */\nexport const MAX_ATTEMPTS = 30;\n/** Distinct wrong secrets across all clients within the window before every attempt is slowed down. */\nexport const GLOBAL_MAX_FAILURES = 100;\n/** Delay per attempt while the global budget is exceeded. */\nexport const GLOBAL_SLOWDOWN_MS = 2000;\nexport const WINDOW_MS = 15 * 60_000;\nexport const BAN_MS = 15 * 60_000;\n/** Tracked clients; the least recently seen is dropped beyond this. */\nexport const MAX_ENTRIES = 10_000;\n\ninterface Entry {\n tried: Set<string>;\n attempts: number;\n since: number;\n bannedUntil: number;\n}\n\n/**\n * Failed sign-in limiter. Per client (IPv4 address or IPv6 /64): 5 distinct wrong secrets or 30\n * attempts within 15 minutes ban it for 15 minutes. A secret already known to be wrong is\n * rejected without hashing (`knownBad`) and counts only as an attempt, so a client retrying a stale\n * password can't spend every attempt, and can't make the server hash it again either. Past a global\n * budget of distinct failures, every attempt is slowed down (never refused, so many clients can't\n * lock the owner out).\n */\nexport class LoginLimiter {\n private readonly entries = new Map<string, Entry>();\n private global = { failures: 0, since: 0 };\n\n constructor(private readonly clock: () => number = Date.now) {}\n\n /** Minutes until this client may try again, or 0. */\n bannedMinutes(ip: string): number {\n const t = this.clock();\n const entry = this.entries.get(clientKey(ip));\n const until = entry && entry.bannedUntil > t ? entry.bannedUntil : 0;\n return until > t ? Math.ceil((until - t) / 60_000) : 0;\n }\n\n /** The global failure budget is exhausted: slow every attempt down. */\n overGlobalBudget(): boolean {\n return this.global.failures >= GLOBAL_MAX_FAILURES && this.clock() - this.global.since < WINDOW_MS;\n }\n\n /** The client already failed with exactly this secret in the current window. */\n knownBad(ip: string, secret: string): boolean {\n const entry = this.current(clientKey(ip), this.clock());\n return !!entry?.tried.has(sha256(secret));\n }\n\n /** Records a failure; returns true when it started a ban for this client. `global: false` keeps it out of the global budget. */\n fail(ip: string, secret: string, opts: { global?: boolean } = {}): boolean {\n return this.record(ip, secret, opts).banned;\n }\n\n /**\n * Counts an attempt with this secret now, before it is verified, so concurrent requests can't\n * all pass the ban check first. `countedGlobally` says whether it used global budget (refund it\n * with `refundGlobal` when the secret turns out to be right).\n */\n record(ip: string, secret: string, opts: { global?: boolean } = {}): { banned: boolean; countedGlobally: boolean } {\n const t = this.clock();\n const key = clientKey(ip);\n const entry = this.current(key, t) ?? { tried: new Set<string>(), attempts: 0, since: t, bannedUntil: 0 };\n const hash = sha256(secret);\n let countedGlobally = false;\n if (!entry.tried.has(hash)) {\n entry.tried.add(hash);\n if (opts.global !== false) {\n if (t - this.global.since >= WINDOW_MS) this.global = { failures: 0, since: t };\n this.global.failures++;\n countedGlobally = true;\n }\n }\n entry.attempts++;\n const banned = entry.tried.size >= MAX_FAILURES || entry.attempts >= MAX_ATTEMPTS;\n if (banned) entry.bannedUntil = t + BAN_MS;\n this.touch(key, entry);\n return { banned, countedGlobally };\n }\n\n refundGlobal(): void {\n if (this.global.failures > 0) this.global.failures--;\n }\n\n clear(ip: string): void {\n this.entries.delete(clientKey(ip));\n }\n\n /** After the password changes, previously wrong secrets may be right. */\n reset(): void {\n this.entries.clear();\n this.global = { failures: 0, since: 0 };\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n /** The client's entry if its window (or ban) is still running. */\n private current(key: string, t: number): Entry | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (entry.bannedUntil > t || (!entry.bannedUntil && t - entry.since < WINDOW_MS)) return entry;\n this.entries.delete(key);\n return undefined;\n }\n\n /** Map order doubles as LRU order: re-insert on use, evict from the front. */\n private touch(key: string, entry: Entry): void {\n this.entries.delete(key);\n this.entries.set(key, entry);\n while (this.entries.size > MAX_ENTRIES) this.entries.delete(this.entries.keys().next().value!);\n }\n}\n\n/** IPv4 addresses as is (also when IPv4-mapped); IPv6 by /64, since one client usually owns the whole prefix. */\nexport function clientKey(ip: string): string {\n const addr = ip.replace(/%.*$/, \"\").toLowerCase();\n const mapped = /^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/.exec(addr);\n if (mapped) return mapped[1]!;\n if (!isIPv6(addr)) return addr;\n const [head = \"\", tail] = addr.split(\"::\");\n const left = head ? head.split(\":\") : [];\n const right = tail ? tail.split(\":\") : [];\n const groups = tail === undefined ? left : [...left, ...Array(8 - left.length - right.length).fill(\"0\"), ...right];\n return `${groups\n .slice(0, 4)\n .map((g) => g.replace(/^0+(?=.)/, \"\"))\n .join(\":\")}::/64`;\n}\n","import { randomBytes } from \"node:crypto\";\nimport { sha256 } from \"../crypto.ts\";\nimport type { Db } from \"../db/index.ts\";\n\nexport const SESSION_TTL_MS = 30 * 24 * 3600_000;\n/** Sliding refresh never extends a session past this age: sign in again after 90 days. */\nexport const SESSION_MAX_AGE_MS = 90 * 24 * 3600_000;\n/** Sliding expiry is pushed forward at most once per this interval, to avoid a write per request. */\nconst REFRESH_AFTER_MS = 24 * 3600_000;\n\nexport interface SessionCheck {\n valid: boolean;\n /** The expiry was extended: re-send the cookie so the browser keeps it as long. */\n refreshed: boolean;\n}\n\n/** Admin console sessions. Only sha256(token) is stored. */\nexport class SessionStore {\n constructor(\n private readonly db: Db,\n private readonly clock: () => number = Date.now,\n ) {}\n\n create(meta: { ip?: string | null; userAgent?: string | null } = {}): string {\n const token = randomBytes(32).toString(\"base64url\");\n const t = this.clock();\n this.db.run(\n \"INSERT INTO admin_sessions (token_hash, created_at, expires_at, ip, user_agent) VALUES (?, ?, ?, ?, ?)\",\n sha256(token),\n new Date(t).toISOString(),\n new Date(t + SESSION_TTL_MS).toISOString(),\n meta.ip ?? null,\n meta.userAgent?.slice(0, 300) ?? null,\n );\n return token;\n }\n\n check(token: string | undefined | null): SessionCheck {\n if (!token) return { valid: false, refreshed: false };\n const hash = sha256(token);\n const row = this.db.get<{ created_at: string; expires_at: string }>(\n \"SELECT created_at, expires_at FROM admin_sessions WHERE token_hash = ?\",\n hash,\n );\n if (!row) return { valid: false, refreshed: false };\n const t = this.clock();\n const expires = Date.parse(row.expires_at);\n const hardLimit = Date.parse(row.created_at) + SESSION_MAX_AGE_MS;\n if (!(expires > t) || !(hardLimit > t)) {\n this.db.run(\"DELETE FROM admin_sessions WHERE token_hash = ?\", hash);\n return { valid: false, refreshed: false };\n }\n const next = Math.min(t + SESSION_TTL_MS, hardLimit);\n if (next - expires < REFRESH_AFTER_MS) return { valid: true, refreshed: false };\n this.db.run(\"UPDATE admin_sessions SET expires_at = ? WHERE token_hash = ?\", new Date(next).toISOString(), hash);\n return { valid: true, refreshed: true };\n }\n\n delete(token: string): void {\n this.db.run(\"DELETE FROM admin_sessions WHERE token_hash = ?\", sha256(token));\n }\n\n /** Revokes every session, optionally keeping one (the caller's own). */\n deleteAll(exceptToken?: string | null): number {\n if (exceptToken) return this.db.run(\"DELETE FROM admin_sessions WHERE token_hash != ?\", sha256(exceptToken)).changes;\n return this.db.run(\"DELETE FROM admin_sessions\").changes;\n }\n\n pruneExpired(): number {\n return this.db.run(\"DELETE FROM admin_sessions WHERE expires_at <= ?\", new Date(this.clock()).toISOString()).changes;\n }\n}\n","import { BlockList, isIP } from \"node:net\";\nimport { getConnInfo } from \"@hono/node-server/conninfo\";\nimport type { Context, MiddlewareHandler } from \"hono\";\nimport { deleteCookie, getCookie, setCookie } from \"hono/cookie\";\nimport type { AppEnv } from \"../api/util.ts\";\nimport type { CoreContext } from \"../context.ts\";\nimport { HttpError } from \"../util/errors.ts\";\nimport { clientKey, GLOBAL_SLOWDOWN_MS, LoginLimiter } from \"./lockout.ts\";\nimport { SESSION_TTL_MS, SessionStore } from \"./sessions.ts\";\nimport { ensureSetupToken, regenerateSetupToken } from \"./setup-token.ts\";\n\nexport const SESSION_COOKIE = \"aib_session\";\nexport const CSRF_HEADER = \"x-xaq-csrf\";\nexport const MIN_PASSWORD_LENGTH = 10;\nexport const MAX_PASSWORD_LENGTH = 1024;\n\n/** Secret checks (scrypt) allowed at once: libuv's default thread pool size. */\nexport const MAX_CONCURRENT_VERIFICATIONS = 4;\n/** Requests from one client that may wait behind its in-flight check; more get 429. */\nexport const MAX_QUEUED_PER_CLIENT = 4;\n\n/** Hands out at most `max` slots; waiters are served in order. */\nexport class Semaphore {\n private active = 0;\n private readonly waiters: (() => void)[] = [];\n\n constructor(private readonly max: number) {}\n\n get inUse(): number {\n return this.active;\n }\n\n async acquire(): Promise<() => void> {\n if (this.active < this.max) this.active++;\n else await new Promise<void>((resolve) => this.waiters.push(resolve));\n let released = false;\n return () => {\n if (released) return;\n released = true;\n const next = this.waiters.shift();\n if (next) next();\n else this.active--;\n };\n }\n}\n\n/** Per-context admin auth state: the session store, the failed-login limiter and verification queues. */\nexport interface AdminAuth {\n sessions: SessionStore;\n limiter: LoginLimiter;\n /** One secret check per client at a time: the tail of each client's queue. */\n lanes: Map<string, { tail: Promise<void>; queued: number }>;\n verifySlots: Semaphore;\n}\n\n/** What admin auth needs from the context (the CLI builds this without loading the master key). */\nexport type AuthCore = Pick<CoreContext, \"db\" | \"settings\" | \"audit\" | \"config\">;\n\nconst instances = new WeakMap<AuthCore, AdminAuth>();\n\nexport function adminAuth(ctx: AuthCore): AdminAuth {\n let auth = instances.get(ctx);\n if (!auth) {\n auth = { sessions: new SessionStore(ctx.db), limiter: new LoginLimiter(), lanes: new Map(), verifySlots: new Semaphore(MAX_CONCURRENT_VERIFICATIONS) };\n instances.set(ctx, auth);\n }\n return auth;\n}\n\n/** Claimed = an admin password exists. Before that only the setup-token claim works. */\nexport const isClaimed = (ctx: AuthCore): boolean => !!ctx.settings.getState(\"admin.passwordHash\");\n\n/** The current setup token while unclaimed (created on demand), otherwise null. */\nexport function currentSetupToken(ctx: AuthCore): string | null {\n return isClaimed(ctx) ? null : ensureSetupToken(ctx.config.dataDir);\n}\n\n/** `xaq reset-password`: back to the unclaimed state with a fresh setup link. */\nexport function resetAdminPassword(ctx: AuthCore): string {\n const auth = adminAuth(ctx);\n ctx.db.tx(() => {\n ctx.settings.deleteState(\"admin.passwordHash\");\n auth.sessions.deleteAll();\n });\n auth.limiter.reset();\n ctx.audit.record({ actorType: \"system\", action: \"admin.password_reset\", outcome: \"ok\" });\n return regenerateSetupToken(ctx.config.dataDir);\n}\n\nfunction socketAddress(c: Context): string | null {\n try {\n return getConnInfo(c).remote.address ?? null;\n } catch {\n // No Node socket (e.g. app.request() without bindings).\n return null;\n }\n}\n\n/** Some proxies (Azure, IIS) append \":port\" to X-Forwarded-For entries. */\nfunction stripPort(entry: string): string {\n const v6 = /^\\[([^\\]]+)\\](?::\\d+)?$/.exec(entry);\n if (v6) return v6[1]!;\n const v4 = /^(\\d{1,3}(?:\\.\\d{1,3}){3}):\\d+$/.exec(entry);\n return v4 ? v4[1]! : entry;\n}\n\nconst LOOPBACK = new BlockList();\nLOOPBACK.addSubnet(\"127.0.0.0\", 8, \"ipv4\");\nLOOPBACK.addAddress(\"::1\", \"ipv6\");\n\n/**\n * Forwarding headers are believed only from a loopback peer (a proxy on the same host): the daemon usually\n * binds 0.0.0.0, so trusting other peers would let any LAN or tailnet peer forge its address.\n */\nexport function isTrustedProxy(peer: string): boolean {\n const ip = peer.replace(/%.*$/, \"\").replace(/^::ffff:(?=\\d+\\.\\d+\\.\\d+\\.\\d+$)/i, \"\");\n const v = isIP(ip);\n return v !== 0 && LOOPBACK.check(ip, v === 4 ? \"ipv4\" : \"ipv6\");\n}\n\nlet warnedUntrustedProxy = false;\n\nfunction warnUntrustedProxy(peer: string): void {\n if (warnedUntrustedProxy) return;\n warnedUntrustedProxy = true;\n process.stderr.write(`xaq: ignoring X-Forwarded-For from ${peer}: TRUST_PROXY only trusts a proxy on this machine (loopback).\\n`);\n}\n\n/**\n * The client address. X-Forwarded-For is honored only with TRUST_PROXY and only from a loopback proxy; its\n * last entry is the client (the one proxy in front of xaq appended it; entries further left are client-controlled).\n */\nexport function clientIp(c: Context, trustProxy: boolean): string {\n const peer = socketAddress(c);\n if (trustProxy && peer && !isTrustedProxy(peer) && c.req.header(\"x-forwarded-for\")) warnUntrustedProxy(peer);\n if (trustProxy && peer && isTrustedProxy(peer)) {\n const chain = (c.req.header(\"x-forwarded-for\") ?? \"\")\n .split(\",\")\n .map((s) => stripPort(s.trim()))\n .filter(Boolean);\n const forwarded = chain.at(-1);\n if (forwarded && isIP(forwarded)) return forwarded;\n }\n return peer ?? \"unknown\";\n}\n\nfunction isSecure(c: Context, trustProxy: boolean): boolean {\n if (new URL(c.req.url).protocol === \"https:\") return true;\n const peer = socketAddress(c);\n return trustProxy && !!peer && isTrustedProxy(peer) && c.req.header(\"x-forwarded-proto\")?.split(\",\")[0]?.trim().toLowerCase() === \"https\";\n}\n\nexport function sessionToken(c: Context): string | undefined {\n return getCookie(c, SESSION_COOKIE) || undefined;\n}\n\nfunction writeSessionCookie(c: Context, token: string, trustProxy: boolean): void {\n setCookie(c, SESSION_COOKIE, token, {\n httpOnly: true,\n sameSite: \"Lax\",\n path: \"/\",\n secure: isSecure(c, trustProxy),\n maxAge: Math.floor(SESSION_TTL_MS / 1000),\n });\n}\n\n/** Creates a session for the requester and sets the cookie. */\nexport function startSession(c: Context, ctx: AuthCore): void {\n const token = adminAuth(ctx).sessions.create({ ip: clientIp(c, ctx.config.trustProxy), userAgent: c.req.header(\"user-agent\") });\n writeSessionCookie(c, token, ctx.config.trustProxy);\n}\n\nexport function endSession(c: Context, ctx: AuthCore): void {\n const token = sessionToken(c);\n if (token) adminAuth(ctx).sessions.delete(token);\n deleteCookie(c, SESSION_COOKIE, { path: \"/\" });\n}\n\n/** Whether the request carries a valid session; refreshes the cookie when the expiry slid forward. */\nexport function hasSession(c: Context, ctx: AuthCore): boolean {\n const token = sessionToken(c);\n const check = adminAuth(ctx).sessions.check(token);\n if (check.refreshed && token) writeSessionCookie(c, token, ctx.config.trustProxy);\n return check.valid;\n}\n\n/** Requires an admin session on every route it guards. */\nexport function requireAdmin(ctx: AuthCore): MiddlewareHandler<AppEnv> {\n return async (c, next) => {\n if (!hasSession(c, ctx)) {\n if (!isClaimed(ctx)) {\n throw new HttpError(401, \"setup_required\", \"This instance has not been claimed yet\", \"Open the setup link printed by xaq on startup.\");\n }\n throw new HttpError(401, \"unauthorized\", \"Sign in to continue\");\n }\n c.set(\"admin\", true);\n await next();\n };\n}\n\nconst MUTATING = new Set([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\n/**\n * CSRF guard: a custom header can't be sent cross-site without a CORS preflight (which this server\n * never approves), and SameSite=Lax already keeps the cookie off cross-site POSTs.\n */\nexport function csrfGuard(): MiddlewareHandler<AppEnv> {\n return async (c, next) => {\n if (MUTATING.has(c.req.method) && c.req.header(CSRF_HEADER) !== \"1\") {\n throw new HttpError(403, \"csrf\", `Missing ${CSRF_HEADER} header`, `Send \"${CSRF_HEADER}: 1\" with every mutating /api request.`);\n }\n await next();\n };\n}\n\n/** 429 response for a locked-out address. */\nexport function lockedOut(c: Context, minutes: number): Response {\n c.header(\"Retry-After\", String(minutes * 60));\n const message = `Too many failed attempts. Try again in ${minutes} minute${minutes === 1 ? \"\" : \"s\"}.`;\n return c.json({ error: { code: \"locked_out\", message } }, 429);\n}\n\nexport type CheckResult =\n | { ok: true }\n | { ok: false; lockedMinutes: number; nowBanned: boolean; /** Too many concurrent attempts from this client. */ busy?: boolean };\n\nexport interface CheckOptions {\n /**\n * Count toward (and be slowed down by) the instance-wide failure budget. Off for the setup claim:\n * it's only open while unclaimed and needs the token.\n */\n global?: boolean;\n /** Test hook: how long to wait while over the global budget. */\n slowdownMs?: number;\n}\n\n/**\n * Verifies a secret under the failed-login limiter.\n * - Each client gets one check at a time (a few more may queue, the rest get `busy`), and at most\n * MAX_CONCURRENT_VERIFICATIONS run at once overall.\n * - The attempt is recorded before `verify` (scrypt) runs and refunded if it succeeds, so\n * concurrent requests can't all slip past the ban.\n * - Locked-out clients and secrets this client already failed with never reach `verify`.\n * - While the instance-wide budget is exhausted, each attempt waits a little first; it is never refused.\n */\nexport async function checkSecret(\n ctx: AuthCore,\n ip: string,\n secret: string,\n verify: () => boolean | Promise<boolean>,\n opts: CheckOptions = {},\n): Promise<CheckResult> {\n const auth = adminAuth(ctx);\n const key = clientKey(ip);\n const lane = auth.lanes.get(key) ?? { tail: Promise.resolve(), queued: 0 };\n if (lane.queued > MAX_QUEUED_PER_CLIENT) return { ok: false, lockedMinutes: 0, nowBanned: false, busy: true };\n lane.queued++;\n auth.lanes.set(key, lane);\n const previous = lane.tail;\n let done!: () => void;\n lane.tail = new Promise<void>((resolve) => (done = resolve));\n try {\n await previous;\n return await checkOnce(auth, ip, secret, verify, opts);\n } finally {\n done();\n if (--lane.queued === 0) auth.lanes.delete(key);\n }\n}\n\nasync function checkOnce(auth: AdminAuth, ip: string, secret: string, verify: () => boolean | Promise<boolean>, opts: CheckOptions): Promise<CheckResult> {\n const { limiter } = auth;\n const global = opts.global !== false;\n if (global && limiter.overGlobalBudget() && !limiter.bannedMinutes(ip)) {\n await new Promise((r) => setTimeout(r, opts.slowdownMs ?? GLOBAL_SLOWDOWN_MS));\n }\n // From here to the reservation everything is synchronous.\n const locked = limiter.bannedMinutes(ip);\n if (locked) return { ok: false, lockedMinutes: locked, nowBanned: false };\n if (limiter.knownBad(ip, secret)) return { ok: false, lockedMinutes: 0, nowBanned: limiter.fail(ip, secret, { global }) };\n const reservation = limiter.record(ip, secret, { global });\n const release = await auth.verifySlots.acquire();\n let ok = false;\n try {\n ok = await verify();\n } finally {\n release();\n }\n if (!ok) return { ok: false, lockedMinutes: 0, nowBanned: reservation.banned };\n limiter.clear(ip);\n if (reservation.countedGlobally) limiter.refundGlobal();\n return { ok: true };\n}\n\n/** The 429 for a rejected check (locked out or too many at once), or null to continue. */\nexport function rejection(c: Context, check: CheckResult): Response | null {\n if (check.ok) return null;\n if (check.busy) {\n c.header(\"Retry-After\", \"2\");\n return c.json({ error: { code: \"too_many_requests\", message: \"Another sign-in attempt from your address is still being checked. Try again in a moment.\" } }, 429);\n }\n return check.lockedMinutes ? lockedOut(c, check.lockedMinutes) : null;\n}\n"],"mappings":";;;;;;;;AAYA,MAAa,SAAS;;AAEtB,MAAa,cAAc;;;;;;;;;AAiB3B,IAAa,eAAb,MAA0B;CAIK;CAH7B,0BAA2B,IAAI,IAAmB;CAClD,SAAiB;EAAE,UAAU;EAAG,OAAO;CAAE;CAEzC,YAAY,QAAuC,KAAK,KAAK;EAAhC,KAAA,QAAA;CAAiC;;CAG9D,cAAc,IAAoB;EAChC,MAAM,IAAI,KAAK,MAAM;EACrB,MAAM,QAAQ,KAAK,QAAQ,IAAI,UAAU,EAAE,CAAC;EAC5C,MAAM,QAAQ,SAAS,MAAM,cAAc,IAAI,MAAM,cAAc;EACnE,OAAO,QAAQ,IAAI,KAAK,MAAM,QAAQ,KAAK,GAAM,IAAI;CACvD;;CAGA,mBAA4B;EAC1B,OAAO,KAAK,OAAO,YAAA,OAAmC,KAAK,MAAM,IAAI,KAAK,OAAO,QAAA;CACnF;;CAGA,SAAS,IAAY,QAAyB;EAE5C,OAAO,CAAC,CADM,KAAK,QAAQ,UAAU,EAAE,GAAG,KAAK,MAAM,CACxC,CAAC,EAAE,MAAM,IAAI,OAAO,MAAM,CAAC;CAC1C;;CAGA,KAAK,IAAY,QAAgB,OAA6B,CAAC,GAAY;EACzE,OAAO,KAAK,OAAO,IAAI,QAAQ,IAAI,CAAC,CAAC;CACvC;;;;;;CAOA,OAAO,IAAY,QAAgB,OAA6B,CAAC,GAAkD;EACjH,MAAM,IAAI,KAAK,MAAM;EACrB,MAAM,MAAM,UAAU,EAAE;EACxB,MAAM,QAAQ,KAAK,QAAQ,KAAK,CAAC,KAAK;GAAE,uBAAO,IAAI,IAAY;GAAG,UAAU;GAAG,OAAO;GAAG,aAAa;EAAE;EACxG,MAAM,OAAO,OAAO,MAAM;EAC1B,IAAI,kBAAkB;EACtB,IAAI,CAAC,MAAM,MAAM,IAAI,IAAI,GAAG;GAC1B,MAAM,MAAM,IAAI,IAAI;GACpB,IAAI,KAAK,WAAW,OAAO;IACzB,IAAI,IAAI,KAAK,OAAO,SAAA,KAAoB,KAAK,SAAS;KAAE,UAAU;KAAG,OAAO;IAAE;IAC9E,KAAK,OAAO;IACZ,kBAAkB;GACpB;EACF;EACA,MAAM;EACN,MAAM,SAAS,MAAM,MAAM,QAAA,KAAwB,MAAM,YAAA;EACzD,IAAI,QAAQ,MAAM,cAAc,IAAI;EACpC,KAAK,MAAM,KAAK,KAAK;EACrB,OAAO;GAAE;GAAQ;EAAgB;CACnC;CAEA,eAAqB;EACnB,IAAI,KAAK,OAAO,WAAW,GAAG,KAAK,OAAO;CAC5C;CAEA,MAAM,IAAkB;EACtB,KAAK,QAAQ,OAAO,UAAU,EAAE,CAAC;CACnC;;CAGA,QAAc;EACZ,KAAK,QAAQ,MAAM;EACnB,KAAK,SAAS;GAAE,UAAU;GAAG,OAAO;EAAE;CACxC;CAEA,IAAI,OAAe;EACjB,OAAO,KAAK,QAAQ;CACtB;;CAGA,QAAgB,KAAa,GAA8B;EACzD,MAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;EAClC,IAAI,CAAC,OAAO,OAAO,KAAA;EACnB,IAAI,MAAM,cAAc,KAAM,CAAC,MAAM,eAAe,IAAI,MAAM,QAAA,KAAoB,OAAO;EACzF,KAAK,QAAQ,OAAO,GAAG;CAEzB;;CAGA,MAAc,KAAa,OAAoB;EAC7C,KAAK,QAAQ,OAAO,GAAG;EACvB,KAAK,QAAQ,IAAI,KAAK,KAAK;EAC3B,OAAO,KAAK,QAAQ,OAAO,aAAa,KAAK,QAAQ,OAAO,KAAK,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAM;CAC/F;AACF;;AAGA,SAAgB,UAAU,IAAoB;CAC5C,MAAM,OAAO,GAAG,QAAQ,QAAQ,EAAE,CAAC,CAAC,YAAY;CAChD,MAAM,SAAS,gCAAgC,KAAK,IAAI;CACxD,IAAI,QAAQ,OAAO,OAAO;CAC1B,IAAI,CAAC,OAAO,IAAI,GAAG,OAAO;CAC1B,MAAM,CAAC,OAAO,IAAI,QAAQ,KAAK,MAAM,IAAI;CACzC,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;CACvC,MAAM,QAAQ,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;CAExC,OAAO,IADQ,SAAS,KAAA,IAAY,OAAO;EAAC,GAAG;EAAM,GAAG,MAAM,IAAI,KAAK,SAAS,MAAM,MAAM,CAAC,CAAC,KAAK,GAAG;EAAG,GAAG;CAAK,EAAA,CAE9G,MAAM,GAAG,CAAC,CAAC,CACX,KAAK,MAAM,EAAE,QAAQ,YAAY,EAAE,CAAC,CAAC,CACrC,KAAK,GAAG,EAAE;AACf;;;ACpIA,MAAa,iBAAiB;;AAE9B,MAAa,qBAAqB;;AAElC,MAAM,mBAAmB;;AASzB,IAAa,eAAb,MAA0B;CAEL;CACA;CAFnB,YACE,IACA,QAAuC,KAAK,KAC5C;EAFiB,KAAA,KAAA;EACA,KAAA,QAAA;CAChB;CAEH,OAAO,OAA0D,CAAC,GAAW;EAC3E,MAAM,QAAQ,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;EAClD,MAAM,IAAI,KAAK,MAAM;EACrB,KAAK,GAAG,IACN,0GACA,OAAO,KAAK,GACZ,IAAI,KAAK,CAAC,CAAC,CAAC,YAAY,GACxB,IAAI,KAAK,IAAI,cAAc,CAAC,CAAC,YAAY,GACzC,KAAK,MAAM,MACX,KAAK,WAAW,MAAM,GAAG,GAAG,KAAK,IACnC;EACA,OAAO;CACT;CAEA,MAAM,OAAgD;EACpD,IAAI,CAAC,OAAO,OAAO;GAAE,OAAO;GAAO,WAAW;EAAM;EACpD,MAAM,OAAO,OAAO,KAAK;EACzB,MAAM,MAAM,KAAK,GAAG,IAClB,0EACA,IACF;EACA,IAAI,CAAC,KAAK,OAAO;GAAE,OAAO;GAAO,WAAW;EAAM;EAClD,MAAM,IAAI,KAAK,MAAM;EACrB,MAAM,UAAU,KAAK,MAAM,IAAI,UAAU;EACzC,MAAM,YAAY,KAAK,MAAM,IAAI,UAAU,IAAI;EAC/C,IAAI,EAAE,UAAU,MAAM,EAAE,YAAY,IAAI;GACtC,KAAK,GAAG,IAAI,mDAAmD,IAAI;GACnE,OAAO;IAAE,OAAO;IAAO,WAAW;GAAM;EAC1C;EACA,MAAM,OAAO,KAAK,IAAI,IAAI,gBAAgB,SAAS;EACnD,IAAI,OAAO,UAAU,kBAAkB,OAAO;GAAE,OAAO;GAAM,WAAW;EAAM;EAC9E,KAAK,GAAG,IAAI,iEAAiE,IAAI,KAAK,IAAI,CAAC,CAAC,YAAY,GAAG,IAAI;EAC/G,OAAO;GAAE,OAAO;GAAM,WAAW;EAAK;CACxC;CAEA,OAAO,OAAqB;EAC1B,KAAK,GAAG,IAAI,mDAAmD,OAAO,KAAK,CAAC;CAC9E;;CAGA,UAAU,aAAqC;EAC7C,IAAI,aAAa,OAAO,KAAK,GAAG,IAAI,oDAAoD,OAAO,WAAW,CAAC,CAAC,CAAC;EAC7G,OAAO,KAAK,GAAG,IAAI,4BAA4B,CAAC,CAAC;CACnD;CAEA,eAAuB;EACrB,OAAO,KAAK,GAAG,IAAI,oDAAoD,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;CAC/G;AACF;;;AC5DA,MAAa,iBAAiB;AAC9B,MAAa,cAAc;AAE3B,MAAa,sBAAsB;;AAQnC,IAAa,YAAb,MAAuB;CAIQ;CAH7B,SAAiB;CACjB,UAA2C,CAAC;CAE5C,YAAY,KAA8B;EAAb,KAAA,MAAA;CAAc;CAE3C,IAAI,QAAgB;EAClB,OAAO,KAAK;CACd;CAEA,MAAM,UAA+B;EACnC,IAAI,KAAK,SAAS,KAAK,KAAK,KAAK;OAC5B,MAAM,IAAI,SAAe,YAAY,KAAK,QAAQ,KAAK,OAAO,CAAC;EACpE,IAAI,WAAW;EACf,aAAa;GACX,IAAI,UAAU;GACd,WAAW;GACX,MAAM,OAAO,KAAK,QAAQ,MAAM;GAChC,IAAI,MAAM,KAAK;QACV,KAAK;EACZ;CACF;AACF;AAcA,MAAM,4BAAY,IAAI,QAA6B;AAEnD,SAAgB,UAAU,KAA0B;CAClD,IAAI,OAAO,UAAU,IAAI,GAAG;CAC5B,IAAI,CAAC,MAAM;EACT,OAAO;GAAE,UAAU,IAAI,aAAa,IAAI,EAAE;GAAG,SAAS,IAAI,aAAa;GAAG,uBAAO,IAAI,IAAI;GAAG,aAAa,IAAI,UAAA,CAAsC;EAAE;EACrJ,UAAU,IAAI,KAAK,IAAI;CACzB;CACA,OAAO;AACT;;AAGA,MAAa,aAAa,QAA2B,CAAC,CAAC,IAAI,SAAS,SAAS,oBAAoB;;AAGjG,SAAgB,kBAAkB,KAA8B;CAC9D,OAAO,UAAU,GAAG,IAAI,OAAO,iBAAiB,IAAI,OAAO,OAAO;AACpE;;AAGA,SAAgB,mBAAmB,KAAuB;CACxD,MAAM,OAAO,UAAU,GAAG;CAC1B,IAAI,GAAG,SAAS;EACd,IAAI,SAAS,YAAY,oBAAoB;EAC7C,KAAK,SAAS,UAAU;CAC1B,CAAC;CACD,KAAK,QAAQ,MAAM;CACnB,IAAI,MAAM,OAAO;EAAE,WAAW;EAAU,QAAQ;EAAwB,SAAS;CAAK,CAAC;CACvF,OAAO,qBAAqB,IAAI,OAAO,OAAO;AAChD;AAEA,SAAS,cAAc,GAA2B;CAChD,IAAI;EACF,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,WAAW;CAC1C,QAAQ;EAEN,OAAO;CACT;AACF;;AAGA,SAAS,UAAU,OAAuB;CACxC,MAAM,KAAK,0BAA0B,KAAK,KAAK;CAC/C,IAAI,IAAI,OAAO,GAAG;CAClB,MAAM,KAAK,kCAAkC,KAAK,KAAK;CACvD,OAAO,KAAK,GAAG,KAAM;AACvB;AAEA,MAAM,WAAW,IAAI,UAAU;AAC/B,SAAS,UAAU,aAAa,GAAG,MAAM;AACzC,SAAS,WAAW,OAAO,MAAM;;;;;AAMjC,SAAgB,eAAe,MAAuB;CACpD,MAAM,KAAK,KAAK,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,oCAAoC,EAAE;CAClF,MAAM,IAAI,KAAK,EAAE;CACjB,OAAO,MAAM,KAAK,SAAS,MAAM,IAAI,MAAM,IAAI,SAAS,MAAM;AAChE;AAEA,IAAI,uBAAuB;AAE3B,SAAS,mBAAmB,MAAoB;CAC9C,IAAI,sBAAsB;CAC1B,uBAAuB;CACvB,QAAQ,OAAO,MAAM,sCAAsC,KAAK,gEAAgE;AAClI;;;;;AAMA,SAAgB,SAAS,GAAY,YAA6B;CAChE,MAAM,OAAO,cAAc,CAAC;CAC5B,IAAI,cAAc,QAAQ,CAAC,eAAe,IAAI,KAAK,EAAE,IAAI,OAAO,iBAAiB,GAAG,mBAAmB,IAAI;CAC3G,IAAI,cAAc,QAAQ,eAAe,IAAI,GAAG;EAK9C,MAAM,aAJS,EAAE,IAAI,OAAO,iBAAiB,KAAK,GAAA,CAC/C,MAAM,GAAG,CAAC,CACV,KAAK,MAAM,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,CAC/B,OAAO,OACY,CAAC,CAAC,GAAG,EAAE;EAC7B,IAAI,aAAa,KAAK,SAAS,GAAG,OAAO;CAC3C;CACA,OAAO,QAAQ;AACjB;AAEA,SAAS,SAAS,GAAY,YAA8B;CAC1D,IAAI,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,aAAa,UAAU,OAAO;CACrD,MAAM,OAAO,cAAc,CAAC;CAC5B,OAAO,cAAc,CAAC,CAAC,QAAQ,eAAe,IAAI,KAAK,EAAE,IAAI,OAAO,mBAAmB,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,YAAY,MAAM;AACpI;AAEA,SAAgB,aAAa,GAAgC;CAC3D,OAAO,UAAU,GAAA,aAAiB,KAAK,KAAA;AACzC;AAEA,SAAS,mBAAmB,GAAY,OAAe,YAA2B;CAChF,UAAU,GAAG,gBAAgB,OAAO;EAClC,UAAU;EACV,UAAU;EACV,MAAM;EACN,QAAQ,SAAS,GAAG,UAAU;EAC9B,QAAQ,KAAK,MAAM,iBAAiB,GAAI;CAC1C,CAAC;AACH;;AAGA,SAAgB,aAAa,GAAY,KAAqB;CAE5D,mBAAmB,GADL,UAAU,GAAG,CAAC,CAAC,SAAS,OAAO;EAAE,IAAI,SAAS,GAAG,IAAI,OAAO,UAAU;EAAG,WAAW,EAAE,IAAI,OAAO,YAAY;CAAE,CACnG,GAAG,IAAI,OAAO,UAAU;AACpD;AAEA,SAAgB,WAAW,GAAY,KAAqB;CAC1D,MAAM,QAAQ,aAAa,CAAC;CAC5B,IAAI,OAAO,UAAU,GAAG,CAAC,CAAC,SAAS,OAAO,KAAK;CAC/C,aAAa,GAAG,gBAAgB,EAAE,MAAM,IAAI,CAAC;AAC/C;;AAGA,SAAgB,WAAW,GAAY,KAAwB;CAC7D,MAAM,QAAQ,aAAa,CAAC;CAC5B,MAAM,QAAQ,UAAU,GAAG,CAAC,CAAC,SAAS,MAAM,KAAK;CACjD,IAAI,MAAM,aAAa,OAAO,mBAAmB,GAAG,OAAO,IAAI,OAAO,UAAU;CAChF,OAAO,MAAM;AACf;;AAGA,SAAgB,aAAa,KAA0C;CACrE,OAAO,OAAO,GAAG,SAAS;EACxB,IAAI,CAAC,WAAW,GAAG,GAAG,GAAG;GACvB,IAAI,CAAC,UAAU,GAAG,GAChB,MAAM,IAAI,UAAU,KAAK,kBAAkB,0CAA0C,gDAAgD;GAEvI,MAAM,IAAI,UAAU,KAAK,gBAAgB,qBAAqB;EAChE;EACA,EAAE,IAAI,SAAS,IAAI;EACnB,MAAM,KAAK;CACb;AACF;AAEA,MAAM,2BAAW,IAAI,IAAI;CAAC;CAAQ;CAAO;CAAS;AAAQ,CAAC;;;;;AAM3D,SAAgB,YAAuC;CACrD,OAAO,OAAO,GAAG,SAAS;EACxB,IAAI,SAAS,IAAI,EAAE,IAAI,MAAM,KAAK,EAAE,IAAI,OAAA,YAAkB,MAAM,KAC9D,MAAM,IAAI,UAAU,KAAK,QAAQ,WAAW,YAAY,UAAU,SAAS,YAAY,uCAAuC;EAEhI,MAAM,KAAK;CACb;AACF;;AAGA,SAAgB,UAAU,GAAY,SAA2B;CAC/D,EAAE,OAAO,eAAe,OAAO,UAAU,EAAE,CAAC;CAC5C,MAAM,UAAU,0CAA0C,QAAQ,SAAS,YAAY,IAAI,KAAK,IAAI;CACpG,OAAO,EAAE,KAAK,EAAE,OAAO;EAAE,MAAM;EAAc;CAAQ,EAAE,GAAG,GAAG;AAC/D;;;;;;;;;;AAyBA,eAAsB,YACpB,KACA,IACA,QACA,QACA,OAAqB,CAAC,GACA;CACtB,MAAM,OAAO,UAAU,GAAG;CAC1B,MAAM,MAAM,UAAU,EAAE;CACxB,MAAM,OAAO,KAAK,MAAM,IAAI,GAAG,KAAK;EAAE,MAAM,QAAQ,QAAQ;EAAG,QAAQ;CAAE;CACzE,IAAI,KAAK,SAAA,GAAgC,OAAO;EAAE,IAAI;EAAO,eAAe;EAAG,WAAW;EAAO,MAAM;CAAK;CAC5G,KAAK;CACL,KAAK,MAAM,IAAI,KAAK,IAAI;CACxB,MAAM,WAAW,KAAK;CACtB,IAAI;CACJ,KAAK,OAAO,IAAI,SAAe,YAAa,OAAO,OAAQ;CAC3D,IAAI;EACF,MAAM;EACN,OAAO,MAAM,UAAU,MAAM,IAAI,QAAQ,QAAQ,IAAI;CACvD,UAAU;EACR,KAAK;EACL,IAAI,EAAE,KAAK,WAAW,GAAG,KAAK,MAAM,OAAO,GAAG;CAChD;AACF;AAEA,eAAe,UAAU,MAAiB,IAAY,QAAgB,QAA0C,MAA0C;CACxJ,MAAM,EAAE,YAAY;CACpB,MAAM,SAAS,KAAK,WAAW;CAC/B,IAAI,UAAU,QAAQ,iBAAiB,KAAK,CAAC,QAAQ,cAAc,EAAE,GACnE,MAAM,IAAI,SAAS,MAAM,WAAW,GAAG,KAAK,cAAA,GAAgC,CAAC;CAG/E,MAAM,SAAS,QAAQ,cAAc,EAAE;CACvC,IAAI,QAAQ,OAAO;EAAE,IAAI;EAAO,eAAe;EAAQ,WAAW;CAAM;CACxE,IAAI,QAAQ,SAAS,IAAI,MAAM,GAAG,OAAO;EAAE,IAAI;EAAO,eAAe;EAAG,WAAW,QAAQ,KAAK,IAAI,QAAQ,EAAE,OAAO,CAAC;CAAE;CACxH,MAAM,cAAc,QAAQ,OAAO,IAAI,QAAQ,EAAE,OAAO,CAAC;CACzD,MAAM,UAAU,MAAM,KAAK,YAAY,QAAQ;CAC/C,IAAI,KAAK;CACT,IAAI;EACF,KAAK,MAAM,OAAO;CACpB,UAAU;EACR,QAAQ;CACV;CACA,IAAI,CAAC,IAAI,OAAO;EAAE,IAAI;EAAO,eAAe;EAAG,WAAW,YAAY;CAAO;CAC7E,QAAQ,MAAM,EAAE;CAChB,IAAI,YAAY,iBAAiB,QAAQ,aAAa;CACtD,OAAO,EAAE,IAAI,KAAK;AACpB;;AAGA,SAAgB,UAAU,GAAY,OAAqC;CACzE,IAAI,MAAM,IAAI,OAAO;CACrB,IAAI,MAAM,MAAM;EACd,EAAE,OAAO,eAAe,GAAG;EAC3B,OAAO,EAAE,KAAK,EAAE,OAAO;GAAE,MAAM;GAAqB,SAAS;EAA2F,EAAE,GAAG,GAAG;CAClK;CACA,OAAO,MAAM,gBAAgB,UAAU,GAAG,MAAM,aAAa,IAAI;AACnE"}
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ //#region server/brand.ts
3
+ /** `XAQ_<name>`. */
4
+ const envVar = (name, env = process.env) => env[`XAQ_${name}`];
5
+ /** Whether this process runs under `service install` (XAQ_SERVICE=1). */
6
+ const underService = (env = process.env) => envVar("SERVICE", env) === "1";
7
+ //#endregion
8
+ export { underService as n, envVar as t };
9
+
10
+ //# sourceMappingURL=brand-CvCkj6M3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-CvCkj6M3.js","names":[],"sources":["../server/brand.ts"],"sourcesContent":["export const PRODUCT = \"xaq\";\n\n/** `XAQ_<name>`. */\nexport const envVar = (name: string, env: NodeJS.ProcessEnv = process.env): string | undefined => env[`XAQ_${name}`];\n\n/** Whether this process runs under `service install` (XAQ_SERVICE=1). */\nexport const underService = (env: NodeJS.ProcessEnv = process.env) => envVar(\"SERVICE\", env) === \"1\";\n"],"mappings":";;;AAGA,MAAa,UAAU,MAAc,MAAyB,QAAQ,QAA4B,IAAI,OAAO;;AAG7G,MAAa,gBAAgB,MAAyB,QAAQ,QAAQ,OAAO,WAAW,GAAG,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"bus-Cz0flS37.js","names":[],"sources":["../server/bus.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\nimport type { Account, Agent, Approval, AuditEntry, Delivery, MailEvent, PairingRequest, Settings } from \"../shared/types.ts\";\n\n/**\n * In-process notifications. Fired *after* the change is committed to the database, so listeners\n * can read it back. Used by the console SSE stream, `wait_for_events`, the webhook worker and the\n * ingest loop (settings changes). Listeners must not throw.\n */\nexport interface BusEvents {\n /** A new event row was committed (and its webhook deliveries enqueued). */\n event: [MailEvent];\n /** A delivery was created or changed state. */\n delivery: [Delivery];\n /** An approval was created or decided. */\n approval: [Approval];\n audit: [AuditEntry];\n /** An account was connected or its status/sync state changed. */\n account: [Account];\n settings: [Settings];\n /**\n * An agent's access changed: live listener streams re-check the key/grants and close on revocation.\n * Emitted by policy on update (incl. disable), key rotation, deletion and grant upsert/delete.\n */\n agent: [{ agentId: string; reason: \"updated\" | \"key_rotated\" | \"deleted\" | \"grant_changed\" }];\n pairing: [PairingRequest];\n /**\n * The agent as the console shows it, after its state changed: listener connect/disconnect/ack,\n * key use (lastUsedAt/lastMcpAt writes) and access changes. Coalesced per agent (policy.publishAgent).\n * Sent to the console stream as `{ kind: \"agent\" }`.\n */\n agentState: [Agent];\n}\n\nexport type Bus = EventEmitter<BusEvents>;\n\nexport function createBus(): Bus {\n const bus = new EventEmitter<BusEvents>();\n // Every SSE client and pending wait_for_events call adds a listener.\n bus.setMaxListeners(0);\n return bus;\n}\n"],"mappings":";;;AAmCA,SAAgB,YAAiB;CAC/B,MAAM,MAAM,IAAI,aAAwB;CAExC,IAAI,gBAAgB,CAAC;CACrB,OAAO;AACT"}
1
+ {"version":3,"file":"bus-Cz0flS37.js","names":[],"sources":["../server/bus.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\nimport type { Account, Agent, AuditEntry, MailEvent, PairingRequest, Settings } from \"../shared/types.ts\";\n\n/**\n * In-process notifications. Fired *after* the change is committed to the database, so listeners\n * can read it back. Used by the console SSE stream, `wait_for_events` and the\n * ingest loop (settings changes). Listeners must not throw.\n */\nexport interface BusEvents {\n /** A new event row was committed. */\n event: [MailEvent];\n audit: [AuditEntry];\n /** An account was connected or its status/sync state changed. */\n account: [Account];\n settings: [Settings];\n /**\n * An agent's access changed: live listener streams re-check the key/grants and close on revocation.\n * Emitted by policy on update (incl. disable), key rotation, deletion and grant upsert/delete.\n */\n agent: [{ agentId: string; reason: \"updated\" | \"key_rotated\" | \"deleted\" | \"grant_changed\" }];\n pairing: [PairingRequest];\n /**\n * The agent as the console shows it, after its state changed: listener connect/disconnect/ack,\n * key use (lastUsedAt/lastMcpAt writes) and access changes. Coalesced per agent (policy.publishAgent).\n * Sent to the console stream as `{ kind: \"agent\" }`.\n */\n agentState: [Agent];\n}\n\nexport type Bus = EventEmitter<BusEvents>;\n\nexport function createBus(): Bus {\n const bus = new EventEmitter<BusEvents>();\n // Every SSE client and pending wait_for_events call adds a listener.\n bus.setMaxListeners(0);\n return bus;\n}\n"],"mappings":";;;AA+BA,SAAgB,YAAiB;CAC/B,MAAM,MAAM,IAAI,aAAwB;CAExC,IAAI,gBAAgB,CAAC;CACrB,OAAO;AACT"}