xaq-cli 0.0.0 → 0.1.0-nightly.71

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 (227) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -0
  3. package/dist/admin-5gThRwgj.js +162 -0
  4. package/dist/admin-5gThRwgj.js.map +1 -0
  5. package/dist/admin-Btrm8mCd.js +218 -0
  6. package/dist/admin-Btrm8mCd.js.map +1 -0
  7. package/dist/admin-CWofnT_Q.js +2 -0
  8. package/dist/api-BFpElwqK.js +89 -0
  9. package/dist/api-BFpElwqK.js.map +1 -0
  10. package/dist/audit-BB7jK9Z5.js +2 -0
  11. package/dist/audit-BVYknW2i.js +79 -0
  12. package/dist/audit-BVYknW2i.js.map +1 -0
  13. package/dist/auth-CXmBdMqw.js +2 -0
  14. package/dist/auth-D-GmwNv-.js +550 -0
  15. package/dist/auth-D-GmwNv-.js.map +1 -0
  16. package/dist/brand--ZB8jTn1.js +32 -0
  17. package/dist/brand--ZB8jTn1.js.map +1 -0
  18. package/dist/bus-Cz0flS37.js +12 -0
  19. package/dist/bus-Cz0flS37.js.map +1 -0
  20. package/dist/bus-OKIGut72.js +2 -0
  21. package/dist/cli.js +234 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/commands-BNVLjYXA.js +167 -0
  24. package/dist/commands-BNVLjYXA.js.map +1 -0
  25. package/dist/commands-CdA6-WE7.js +112 -0
  26. package/dist/commands-CdA6-WE7.js.map +1 -0
  27. package/dist/config-C9XmYZWO.js +94 -0
  28. package/dist/config-C9XmYZWO.js.map +1 -0
  29. package/dist/config-CNVi0v5t.js +2 -0
  30. package/dist/connect-TtJ9n0Rw.js +362 -0
  31. package/dist/connect-TtJ9n0Rw.js.map +1 -0
  32. package/dist/content-C1lKMxFS.js +1170 -0
  33. package/dist/content-C1lKMxFS.js.map +1 -0
  34. package/dist/content-worker.js +28 -0
  35. package/dist/content-worker.js.map +1 -0
  36. package/dist/crypto-fv2fj3S8.js +75 -0
  37. package/dist/crypto-fv2fj3S8.js.map +1 -0
  38. package/dist/db-BEUEUPyP.js +561 -0
  39. package/dist/db-BEUEUPyP.js.map +1 -0
  40. package/dist/db-Q5BQuwga.js +2 -0
  41. package/dist/errors-FbMS3QBo.js +39 -0
  42. package/dist/errors-FbMS3QBo.js.map +1 -0
  43. package/dist/filter-Ck6OMxvu.js +158 -0
  44. package/dist/filter-Ck6OMxvu.js.map +1 -0
  45. package/dist/hermes-CKyzt1mH.js +3 -0
  46. package/dist/hermes-DHnDTYJp.js +1023 -0
  47. package/dist/hermes-DHnDTYJp.js.map +1 -0
  48. package/dist/import-DsN5NZT3.js +910 -0
  49. package/dist/import-DsN5NZT3.js.map +1 -0
  50. package/dist/instance-CwWbFbJ8.js +2 -0
  51. package/dist/instance-Dj9TpJB2.js +195 -0
  52. package/dist/instance-Dj9TpJB2.js.map +1 -0
  53. package/dist/listen-B9iO7HgQ.js +1568 -0
  54. package/dist/listen-B9iO7HgQ.js.map +1 -0
  55. package/dist/listen-CXVKG9XD.js +2 -0
  56. package/dist/manage-Bo6COB4p.js +477 -0
  57. package/dist/manage-Bo6COB4p.js.map +1 -0
  58. package/dist/manage-DoXqvjnU.js +2 -0
  59. package/dist/mcpheader-B8c5YO1B.js +2 -0
  60. package/dist/mcpheader-C9H4vudu.js +185 -0
  61. package/dist/mcpheader-C9H4vudu.js.map +1 -0
  62. package/dist/mcpsetup-BnHudwpq.js +2 -0
  63. package/dist/mcpsetup-DXXo2qjf.js +1053 -0
  64. package/dist/mcpsetup-DXXo2qjf.js.map +1 -0
  65. package/dist/node-check-CjTk6VNL.js +24 -0
  66. package/dist/node-check-CjTk6VNL.js.map +1 -0
  67. package/dist/paths-d-C2kdjo.js +2 -0
  68. package/dist/paths-yzOKD6ow.js +46 -0
  69. package/dist/paths-yzOKD6ow.js.map +1 -0
  70. package/dist/profile-CcMZ4hn7.js +2 -0
  71. package/dist/profile-NmMKAczz.js +222 -0
  72. package/dist/profile-NmMKAczz.js.map +1 -0
  73. package/dist/prompt-CKMQx_MJ.js +114 -0
  74. package/dist/prompt-CKMQx_MJ.js.map +1 -0
  75. package/dist/recipes-C7TIDlRj.js +3 -0
  76. package/dist/recipes-DG_-doGq.js +314 -0
  77. package/dist/recipes-DG_-doGq.js.map +1 -0
  78. package/dist/send-Dk-t8ia-.js +108 -0
  79. package/dist/send-Dk-t8ia-.js.map +1 -0
  80. package/dist/server-CDqDp1q3.js +20640 -0
  81. package/dist/server-CDqDp1q3.js.map +1 -0
  82. package/dist/service-BVn3soZZ.js +766 -0
  83. package/dist/service-BVn3soZZ.js.map +1 -0
  84. package/dist/service-BaWGPmSd.js +2 -0
  85. package/dist/service-C7jfo-Ex.js +457 -0
  86. package/dist/service-C7jfo-Ex.js.map +1 -0
  87. package/dist/service-DxDyKDQ-.js +2 -0
  88. package/dist/settings-B5qSHxRM.js +134 -0
  89. package/dist/settings-B5qSHxRM.js.map +1 -0
  90. package/dist/settings-DQvZaMN1.js +2 -0
  91. package/dist/setup-token-CwuSzfLK.js +48 -0
  92. package/dist/setup-token-CwuSzfLK.js.map +1 -0
  93. package/dist/skill/SKILL.md +445 -0
  94. package/dist/skill-1UQj3qgW.js +69 -0
  95. package/dist/skill-1UQj3qgW.js.map +1 -0
  96. package/dist/skill-pelq18JQ.js +2 -0
  97. package/dist/ssrf-BlVpGZ7C.js +181 -0
  98. package/dist/ssrf-BlVpGZ7C.js.map +1 -0
  99. package/dist/types-BhJDbSzE.js +135 -0
  100. package/dist/types-BhJDbSzE.js.map +1 -0
  101. package/dist/ui-B8Sd4Y7Y.js +81 -0
  102. package/dist/ui-B8Sd4Y7Y.js.map +1 -0
  103. package/dist/urls-Dp8pBGMU.js +2 -0
  104. package/dist/urls-U_LfYV61.js +27 -0
  105. package/dist/urls-U_LfYV61.js.map +1 -0
  106. package/dist/version-BxH7OuWD.js +20 -0
  107. package/dist/version-BxH7OuWD.js.map +1 -0
  108. package/dist/web/assets/_agentId-DonOCOlC.js +1 -0
  109. package/dist/web/assets/_app-B7t5lEia.js +1 -0
  110. package/dist/web/assets/_app-DMTGqYpM.js +65 -0
  111. package/dist/web/assets/_connectionId-DR5vFXOg.js +1 -0
  112. package/dist/web/assets/_webhookId-LfUlsULp.js +1 -0
  113. package/dist/web/assets/account-status-CEszs13C.js +1 -0
  114. package/dist/web/assets/accounts-5m7aiXuZ.js +1 -0
  115. package/dist/web/assets/accounts-list-YfJzThZa.js +1 -0
  116. package/dist/web/assets/activity-DOIe7SAg.js +1 -0
  117. package/dist/web/assets/add-connection-C8JR-13R.js +1 -0
  118. package/dist/web/assets/agent-create-form-CPxnPYH4.js +1 -0
  119. package/dist/web/assets/agents-B2Qj5KxT.js +1 -0
  120. package/dist/web/assets/approvals-BGaVaoFy.js +1 -0
  121. package/dist/web/assets/approve._id-BLrQnzPM.js +1 -0
  122. package/dist/web/assets/arrow-left-DZVAXoKj.js +1 -0
  123. package/dist/web/assets/arrow-right-C2zcqRr2.js +1 -0
  124. package/dist/web/assets/attention-gO9Nw5uW.js +1 -0
  125. package/dist/web/assets/audit-BPXXre8S.js +1 -0
  126. package/dist/web/assets/audit-bits-CGyIprzK.js +1 -0
  127. package/dist/web/assets/bell-ring-ig3zn1iS.js +1 -0
  128. package/dist/web/assets/bot-Zk76q_39.js +1 -0
  129. package/dist/web/assets/calendar-notice-DsCfq3VJ.js +1 -0
  130. package/dist/web/assets/calendar-plus-Cycfkk0y.js +1 -0
  131. package/dist/web/assets/calendar-setup-Pn61Wj1n.js +2 -0
  132. package/dist/web/assets/card-GSO5o-Ug.js +1 -0
  133. package/dist/web/assets/check-check-Ds1bPIGv.js +1 -0
  134. package/dist/web/assets/checkbox-B937U-Fd.js +1 -0
  135. package/dist/web/assets/chevron-right-Cqh4mDk1.js +1 -0
  136. package/dist/web/assets/chips-input-2xWDofmE.js +1 -0
  137. package/dist/web/assets/choice-cards-CgAIqBGX.js +1 -0
  138. package/dist/web/assets/circle-x-DcU-0zVh.js +1 -0
  139. package/dist/web/assets/command-khhto70y.js +1 -0
  140. package/dist/web/assets/common-BPZV8TFb.js +1 -0
  141. package/dist/web/assets/connect-agent-guide-CJdw31p2.js +1 -0
  142. package/dist/web/assets/connect-host-lr2c3xWh.js +6 -0
  143. package/dist/web/assets/connection-row-DYx_5o0c.js +1 -0
  144. package/dist/web/assets/connections-C4NOFb5R.js +1 -0
  145. package/dist/web/assets/data-table-kW1pkRB5.js +1 -0
  146. package/dist/web/assets/empty-DzTDioBP.js +1 -0
  147. package/dist/web/assets/events-C9zJnPKK.js +1 -0
  148. package/dist/web/assets/eye-B9gT1QSq.js +1 -0
  149. package/dist/web/assets/eye-off-Diktqz_g.js +1 -0
  150. package/dist/web/assets/field-C6ejurju.js +1 -0
  151. package/dist/web/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  152. package/dist/web/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  153. package/dist/web/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  154. package/dist/web/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  155. package/dist/web/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  156. package/dist/web/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  157. package/dist/web/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  158. package/dist/web/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  159. package/dist/web/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  160. package/dist/web/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  161. package/dist/web/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  162. package/dist/web/assets/get-started-C38bq1gZ.js +2 -0
  163. package/dist/web/assets/hidden-chars-Dr9TxfvF.js +1 -0
  164. package/dist/web/assets/index-DGzo3kGh.js +11 -0
  165. package/dist/web/assets/index-zKOCQLGX.css +2 -0
  166. package/dist/web/assets/input-CWwJgozn.js +1 -0
  167. package/dist/web/assets/input-group-HG8OXcJ4.js +1 -0
  168. package/dist/web/assets/label-multi-select-sH8mllw8.js +1 -0
  169. package/dist/web/assets/labels-B5UqVwFo.js +1 -0
  170. package/dist/web/assets/laptop-BxJ4Nbj7.js +1 -0
  171. package/dist/web/assets/last-call-D-O5O94z.js +1 -0
  172. package/dist/web/assets/listener-handled-HNCuVrQ7.js +1 -0
  173. package/dist/web/assets/login-DaeKclcf.js +1 -0
  174. package/dist/web/assets/mail-C5fm2pTC.js +1 -0
  175. package/dist/web/assets/mcp-call-UoOTPFyh.js +1 -0
  176. package/dist/web/assets/mcp-call-card-Cr-OEDX2.js +3 -0
  177. package/dist/web/assets/message-square-quote-se8Bf3RP.js +1 -0
  178. package/dist/web/assets/mutations-CLPqJ6cj.js +1 -0
  179. package/dist/web/assets/network-igNw0VVx.js +1 -0
  180. package/dist/web/assets/pairing-XjuaVr2u.js +1 -0
  181. package/dist/web/assets/pairing-notifier-KPzOEZSO.js +1 -0
  182. package/dist/web/assets/paperclip-znS4iTrg.js +1 -0
  183. package/dist/web/assets/password-input-DV3aqcNf.js +1 -0
  184. package/dist/web/assets/pause-o-dK3khK.js +1 -0
  185. package/dist/web/assets/pencil-DkPFAhEq.js +1 -0
  186. package/dist/web/assets/play-DvCHND93.js +1 -0
  187. package/dist/web/assets/plug-CXAniEiR.js +1 -0
  188. package/dist/web/assets/plug-zap-BwR-l5wb.js +1 -0
  189. package/dist/web/assets/plus-B_TUslu3.js +1 -0
  190. package/dist/web/assets/preload-helper-Cc_aKyUb.js +1 -0
  191. package/dist/web/assets/public-url-rF2Xjyel.js +1 -0
  192. package/dist/web/assets/queries-Cv3nXx0U.js +42 -0
  193. package/dist/web/assets/refresh-cw-Ch5FNrby.js +1 -0
  194. package/dist/web/assets/revealed-keys-C_9NtJPY.js +1 -0
  195. package/dist/web/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  196. package/dist/web/assets/rotate-ccw-B-mzPDk0.js +1 -0
  197. package/dist/web/assets/search-CTVKGomS.js +1 -0
  198. package/dist/web/assets/select-Bb806bpq.js +1 -0
  199. package/dist/web/assets/send-DuMVuYs1.js +1 -0
  200. package/dist/web/assets/settings-BWauhfFf.js +1 -0
  201. package/dist/web/assets/setup-B7bNKQOp.js +1 -0
  202. package/dist/web/assets/sheet-CYPwpUsH.js +1 -0
  203. package/dist/web/assets/shield-alert-CSYB-ThV.js +1 -0
  204. package/dist/web/assets/shield-check-BulpsYqK.js +1 -0
  205. package/dist/web/assets/sparkles-CUTiob4K.js +1 -0
  206. package/dist/web/assets/stamp-BNXUGNa3.js +1 -0
  207. package/dist/web/assets/step-url-51aMNak-.js +1 -0
  208. package/dist/web/assets/stream-Du7fY5_Y.js +1 -0
  209. package/dist/web/assets/tag-I5_-1jFL.js +1 -0
  210. package/dist/web/assets/terminal-BvxDFTJc.js +1 -0
  211. package/dist/web/assets/test-email-button-DYi7V_6X.js +1 -0
  212. package/dist/web/assets/toggle-group-Bdo1gmQ1.js +1 -0
  213. package/dist/web/assets/trash-Ci0aICEl.js +1 -0
  214. package/dist/web/assets/use-delivery-speed-BBh7W2q6.js +1 -0
  215. package/dist/web/assets/use-label-names-BXicesnP.js +1 -0
  216. package/dist/web/assets/use-sign-out-CPrY_oVO.js +1 -0
  217. package/dist/web/assets/useInfiniteQuery-DJLIM8yJ.js +1 -0
  218. package/dist/web/assets/useQuery-MrTwwNy5.js +1 -0
  219. package/dist/web/assets/useRouterState-D4OBK8l5.js +1 -0
  220. package/dist/web/assets/webhook-DjeeZiNC.js +1 -0
  221. package/dist/web/assets/webhook-bits-jw-y2D3u.js +1 -0
  222. package/dist/web/assets/webhook-sheet-B8h0skDe.js +34 -0
  223. package/dist/web/assets/webhooks-Cfsdpoyd.js +1 -0
  224. package/dist/web/assets/x-MF2TZ9aG.js +1 -0
  225. package/dist/web/favicon.svg +1 -0
  226. package/dist/web/index.html +27 -0
  227. package/package.json +98 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-BVn3soZZ.js","names":["PACKAGE"],"sources":["../server/service.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { createServer } from \"node:net\";\nimport { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir, tmpdir, userInfo } from \"node:os\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport { CLI_NAME, NPM_PACKAGE as PACKAGE } from \"../shared/types.ts\";\nimport { readSetupToken } from \"./auth/setup-token.ts\";\nimport { envVar, LEGACY_PRODUCT } from \"./brand.ts\";\nimport { type Config, type ConfigInput, dataFiles, legacyDataDir } from \"./config.ts\";\nimport { Crypto } from \"./crypto.ts\";\nimport { assertMasterKeyUsable, cliCommand, type InstanceInfo, runningInstance, serviceInstallCommand } from \"./instance.ts\";\nimport { consoleUrls, loopbackUrl } from \"./urls.ts\";\n\n/** Installs xaq as a background service: systemd on Linux, launchd on macOS. */\n\nexport const SERVICE_NAME = \"xaq\";\nexport const LAUNCHD_LABEL = \"sh.xaq.daemon\";\n/**\n * The service's names before the rename (agentinbox). `service install` stops and replaces a service\n * installed under them (rolling back to it on failure); `status` and `uninstall` find it too.\n */\nexport const LEGACY_SERVICE_NAME = \"agentinbox\";\nexport const LEGACY_LAUNCHD_LABEL = \"com.unlaboredlabs.agentinbox\";\n/** The package a pre-rename service install put into `<data>/app`. */\nexport const LEGACY_PACKAGE = \"agentinbox-cli\";\n\n/** A service's names: the launchd label and the systemd unit (without `.service`). */\nexport interface UnitId {\n launchd: string;\n systemd: string;\n}\nexport const DAEMON_UNIT: UnitId = { launchd: LAUNCHD_LABEL, systemd: SERVICE_NAME };\nexport const LEGACY_DAEMON_UNIT: UnitId = { launchd: LEGACY_LAUNCHD_LABEL, systemd: LEGACY_SERVICE_NAME };\nconst npm = process.platform === \"win32\" ? \"npm.cmd\" : \"npm\";\n/** Package root: one level above the running module (dist/cli.js or server/service.ts). */\nconst ROOT = resolve(import.meta.dirname, \"..\");\n\n/**\n * Settings that come from the environment rather than flags. They are kept in `<data>/service.env`\n * (mode 0600) so re-running `service install` from a shell without them (the documented update\n * path) keeps them. Set one to an empty value to drop it.\n */\nexport const SERVICE_ENV_KEYS = [\"XAQ_MASTER_KEY\", \"TRUST_PROXY\", \"TRUST_PROXY_HOPS\", \"TRUST_PROXY_FROM\", \"XAQ_LOG_LEVEL\"] as const;\n/** Their names before the rename: still read from the shell and from a service.env an older install wrote. */\nconst LEGACY_ENV_KEYS: Partial<Record<(typeof SERVICE_ENV_KEYS)[number], string>> = { XAQ_MASTER_KEY: \"AGENTINBOX_MASTER_KEY\", XAQ_LOG_LEVEL: \"AGENTINBOX_LOG_LEVEL\" };\n\ntype ServiceConfig = Pick<Config, \"dataDir\" | \"port\" | \"host\" | \"dbPath\" | \"masterKeyPath\">;\n\nexport const serviceEnvPath = (dataDir: string) => join(dataDir, \"service.env\");\n\n// ───────────── Pure rendering (unit-tested) ─────────────\n\nexport interface ServiceSpec {\n /** Node binary. */\n execPath: string;\n /** The installed dist/cli.js. */\n entry: string;\n dataDir: string;\n port: number;\n host: string;\n /** Extra environment from service.env (SERVICE_ENV_KEYS). */\n extraEnv: Record<string, string>;\n /** systemd: system unit (root) rather than a user unit. */\n system?: boolean;\n}\n\n/** Plain variables written into the unit/plist. Secrets never go into the systemd unit. */\nfunction plainEnv(spec: ServiceSpec): Record<string, string> {\n return { XAQ_DATA: spec.dataDir, PORT: String(spec.port), HOST: spec.host, XAQ_SERVICE: \"1\" };\n}\n\n/** systemd word quoting for ExecStart/Environment: double quotes, escaped backslashes, quotes and specifiers. */\nexport const sdQuote = (s: string) => `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"').replace(/%/g, \"%%\")}\"`;\n\n/**\n * The systemd unit. Everything from service.env (including XAQ_MASTER_KEY) is loaded with\n * EnvironmentFile=, so the world-readable unit holds no secrets. EnvironmentFile takes an\n * unquoted absolute path (systemd ignores quoted ones), which is why data dirs with whitespace are refused.\n */\nexport function renderSystemdUnit(spec: ServiceSpec): string {\n const vars = Object.entries(plainEnv(spec))\n .map(([k, v]) => `Environment=${sdQuote(`${k}=${v}`)}`)\n .join(\"\\n\");\n const envFile = Object.keys(spec.extraEnv).length ? `\\nEnvironmentFile=${serviceEnvPath(spec.dataDir).replace(/%/g, \"%%\")}` : \"\";\n // network-online.target only exists for system units; a user unit waiting on it never starts it.\n const network = spec.system ? \"\\nAfter=network-online.target\\nWants=network-online.target\" : \"\";\n return `[Unit]\nDescription=xaq (MCP broker for AI agents)${network}\n\n[Service]\nExecStart=${sdQuote(spec.execPath)} ${sdQuote(spec.entry)} start --no-open\n${vars}${envFile}\nRestart=on-failure\nRestartSec=3\nTimeoutStopSec=20\n\n[Install]\nWantedBy=${spec.system ? \"multi-user.target\" : \"default.target\"}\n`;\n}\n\n/** The launchd plist. Written with mode 0600, since it may carry XAQ_MASTER_KEY. */\n/** Escapes text for a plist <string>. */\nexport const xmlEscape = (s: string) => s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n\nexport function renderLaunchdPlist(spec: ServiceSpec): string {\n const esc = xmlEscape;\n const vars = Object.entries({ ...plainEnv(spec), ...spec.extraEnv })\n .map(([k, v]) => ` <key>${esc(k)}</key><string>${esc(v)}</string>`)\n .join(\"\\n\");\n const log = esc(dataFiles(spec.dataDir).log);\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key><string>${LAUNCHD_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${esc(spec.execPath)}</string>\n <string>${esc(spec.entry)}</string>\n <string>start</string>\n <string>--no-open</string>\n </array>\n <key>EnvironmentVariables</key>\n <dict>\n${vars}\n </dict>\n <key>RunAtLoad</key><true/>\n <key>KeepAlive</key><true/>\n <key>StandardOutPath</key><string>${log}</string>\n <key>StandardErrorPath</key><string>${log}</string>\n</dict>\n</plist>\n`;\n}\n\n/** Parses service.env (KEY=value lines). */\nexport function parseEnvFile(text: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const line of text.split(\"\\n\")) {\n const m = /^\\s*([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line);\n if (m) out[m[1]!] = m[2]!.trim();\n }\n return out;\n}\n\n/**\n * The service's env-derived settings: the current shell wins, previous install fills the gaps,\n * an explicitly empty value removes the setting. The pre-rename names (AGENTINBOX_MASTER_KEY…) count\n * where the new one isn't given, and are written back under the new name.\n */\nexport function mergeServiceEnv(previous: Record<string, string>, current: NodeJS.ProcessEnv): Record<string, string> {\n const out: Record<string, string> = {};\n for (const key of SERVICE_ENV_KEYS) {\n const old = LEGACY_ENV_KEYS[key];\n const value = key in current ? current[key] : old && old in current ? current[old] : (previous[key] ?? (old ? previous[old] : undefined));\n // A single comma-separated token, so systemd's EnvironmentFile parsing can't split it.\n if (value) out[key] = key === \"TRUST_PROXY_FROM\" ? value.split(/[\\s,]+/).filter(Boolean).join(\",\") : value;\n }\n return out;\n}\n\n/** Fails early (before anything is touched) when the master key can't work. */\nexport function validateMasterKey(value: string | undefined): void {\n if (!value) return;\n try {\n if (!/^[A-Za-z0-9+/]+={0,2}$/.test(value.trim())) throw new Error(\"not base64\");\n new Crypto(Buffer.from(value, \"base64\"));\n } catch {\n throw new Error(\"XAQ_MASTER_KEY must be 32 random bytes in base64 (e.g. `openssl rand -base64 32`).\");\n }\n}\n\n// ───────────── Install ─────────────\n\nfunction readPreviousEnv(dataDir: string): Record<string, string> {\n try {\n return parseEnvFile(readFileSync(serviceEnvPath(dataDir), \"utf8\"));\n } catch {\n return {};\n }\n}\n\nfunction writeServiceEnv(dataDir: string, env: Record<string, string>): void {\n const file = serviceEnvPath(dataDir);\n if (!Object.keys(env).length) {\n rmSync(file, { force: true });\n return;\n }\n for (const [k, v] of Object.entries(env)) {\n if (/[\\r\\n]/.test(v)) throw new Error(`${k} must be a single line.`);\n }\n writeFileSync(file, serviceEnvText(env), { mode: 0o600 });\n}\n\n/**\n * service.env's content. For one release, XAQ_MASTER_KEY and XAQ_LOG_LEVEL are also written under their\n * pre-rename names, so going back to agentinbox-cli by hand (its `service install` reads only those)\n * still finds the key and starts.\n */\nexport function serviceEnvText(env: Record<string, string>): string {\n const lines = Object.entries(env).map(([k, v]) => `${k}=${v}\\n`);\n for (const [key, old] of Object.entries(LEGACY_ENV_KEYS)) {\n const value = env[key];\n if (value && old) lines.push(`${old}=${value}\\n`);\n }\n return lines.join(\"\");\n}\n\n/**\n * Installs the package into `staging` (a fresh private npm prefix), so a failed npm install leaves the\n * current install untouched. From an installed package that is the latest release; from a source\n * checkout, the local build. Returns `staging`. The one install path for the daemon and the listeners.\n */\nexport function stagePackage(staging: string, opts: { where: string; requireWeb: boolean }): string {\n const fromSource = basename(dirname(ROOT)) !== \"node_modules\";\n let spec = `${PACKAGE}@latest`;\n let tmp: string | undefined;\n if (fromSource) {\n if (!existsSync(join(ROOT, \"dist/cli.js\")) || (opts.requireWeb && !existsSync(join(ROOT, \"dist/web/index.html\")))) {\n throw new Error(\"Run `pnpm build` before installing the service from a source checkout.\");\n }\n tmp = mkdtempSync(join(tmpdir(), \"xaq-pack-\"));\n const out = execFileSync(npm, [\"pack\", \"--ignore-scripts\", \"--pack-destination\", tmp, \"--loglevel=error\"], { cwd: ROOT, encoding: \"utf8\" });\n spec = join(tmp, out.trim().split(\"\\n\").pop()!);\n }\n\n console.log(fromSource ? ` Installing this build into ${opts.where}…` : ` Installing the latest release into ${opts.where}…`);\n rmSync(staging, { recursive: true, force: true });\n mkdirSync(staging, { recursive: true });\n writeFileSync(join(staging, \"package.json\"), '{ \"private\": true }\\n');\n try {\n execFileSync(npm, [\"install\", spec, \"--omit=dev\", \"--prefer-online\", \"--no-audit\", \"--no-fund\", \"--loglevel=error\"], { cwd: staging, stdio: \"inherit\" });\n if (!existsSync(installedEntry(staging))) throw new Error(\"the installed package has no dist/cli.js\");\n } catch (err) {\n rmSync(staging, { recursive: true, force: true });\n throw new Error(`Installing xaq failed, so the existing service was left as it was. ${err instanceof Error ? err.message : \"\"}`.trim());\n } finally {\n if (tmp) rmSync(tmp, { recursive: true, force: true });\n }\n return staging;\n}\n\n/** The CLI entry inside an install made by stagePackage. */\nexport const installedEntry = (app: string) => join(app, \"node_modules\", PACKAGE, \"dist/cli.js\");\n\n/** Installs into `<dir>/app.new` (see stagePackage). Returns the staging directory. */\nexport const stageApp = (dir: string): string => stagePackage(join(dir, \"app.new\"), { where: \"the data directory\", requireWeb: true });\n\n/**\n * Moves a staged install into place as `app` (the current one becomes `app.old`). `restore()` puts\n * the previous install back; `commit()` deletes it. If the move itself fails, nothing changes.\n */\nexport function swapIn(staging: string, app: string): { restore(): void; commit(): void } {\n const old = `${app}.old`;\n rmSync(old, { recursive: true, force: true });\n if (existsSync(app)) renameSync(app, old);\n try {\n renameSync(staging, app);\n } catch (err) {\n if (existsSync(old)) renameSync(old, app);\n throw err;\n }\n return {\n restore() {\n rmSync(app, { recursive: true, force: true });\n if (existsSync(old)) renameSync(old, app);\n },\n commit() {\n rmSync(old, { recursive: true, force: true });\n },\n };\n}\n\n/** Deletes `<dir>/app`, `app.new` and `app.old`, but only where they really are our own installs (xaq-cli, or agentinbox-cli from before the rename). */\nexport function removeOwnInstall(dir: string): void {\n for (const name of [\"app\", \"app.new\", \"app.old\"]) {\n const path = join(dir, name);\n if ([PACKAGE, LEGACY_PACKAGE].some((p) => existsSync(join(path, \"node_modules\", p)))) rmSync(path, { recursive: true, force: true });\n }\n}\n\n// ───────────── System integration (injectable for tests) ─────────────\n\nexport interface ServiceOps {\n platform: NodeJS.Platform;\n /** systemd: install a system unit (running as root) rather than a user unit. */\n root: boolean;\n /** Runs a command, throwing on a non-zero exit. */\n exec(cmd: string, args: string[], opts?: { quiet?: boolean }): void;\n /** Runs a command and returns its stdout, throwing on a non-zero exit. */\n capture(cmd: string, args: string[]): string;\n /** Installs the package into a staging directory and returns it (see stageApp). */\n stage(dataDir: string): string;\n /** Directory for the systemd unit or the launchd plist. */\n unitDir(): string;\n /** Resolves true once /api/health returns a real HealthResponse, false if it never does. */\n waitReady(config: ServiceConfig): Promise<boolean>;\n /** The live process holding the data directory's instance lock (runningInstance), if any. */\n instance(dataDir: string): InstanceInfo | null;\n /** Whether something already listens on host:port (port 0 never is). */\n portInUse(host: string, port: number): Promise<boolean>;\n uid: number;\n user: string;\n}\n\nexport const systemOps = (): ServiceOps => {\n const root = process.getuid?.() === 0;\n const ops: ServiceOps = {\n platform: process.platform,\n root,\n exec: (cmd, args, opts) => void execFileSync(cmd, args, { stdio: opts?.quiet ? \"ignore\" : \"inherit\" }),\n capture: (cmd, args) => execFileSync(cmd, args, { encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"pipe\"] }),\n stage: stageApp,\n unitDir: () => {\n if (process.platform === \"darwin\") return join(homedir(), \"Library/LaunchAgents\");\n if (root) return \"/etc/systemd/system\";\n unitDirCache ??= join(userManagerConfigHome(ops) ?? join(homedir(), \".config\"), \"systemd/user\");\n return unitDirCache;\n },\n waitReady: waitForHealth,\n instance: runningInstance,\n portInUse,\n uid: process.getuid?.() ?? 0,\n user: userInfo().username,\n };\n let unitDirCache: string | undefined;\n return ops;\n};\n\n/**\n * XDG_CONFIG_HOME as the systemd user manager sees it (where `systemctl --user` looks for units),\n * which can differ from this shell's. Null when unset there or the manager can't be asked.\n */\nexport function userManagerConfigHome(ops: Pick<ServiceOps, \"capture\">): string | null {\n try {\n const env = ops.capture(\"systemctl\", [\"--user\", \"show-environment\"]);\n return /^XDG_CONFIG_HOME=(.+)$/m.exec(env)?.[1]?.trim() || null;\n } catch {\n return null;\n }\n}\n\n/** The unit file (systemd) or plist (launchd) for a service's names. */\nexport const unitPath = (ops: Pick<ServiceOps, \"platform\" | \"unitDir\">, id: UnitId) =>\n join(ops.unitDir(), ops.platform === \"darwin\" ? `${id.launchd}.plist` : `${id.systemd}.service`);\nconst unitFile = (ops: ServiceOps) => unitPath(ops, DAEMON_UNIT);\nconst unitLabel = (ops: Pick<ServiceOps, \"platform\">, id: UnitId) => (ops.platform === \"darwin\" ? id.launchd : id.systemd);\nexport const systemctl = (ops: ServiceOps, ...args: string[]) => ops.exec(\"systemctl\", ops.root ? args : [\"--user\", ...args]);\nexport const launchdTarget = (ops: Pick<ServiceOps, \"uid\">) => `gui/${ops.uid}`;\n\nexport function assertSupported(ops: ServiceOps): void {\n if (ops.platform !== \"linux\" && ops.platform !== \"darwin\") {\n throw new Error(\n `Service install supports Linux (systemd) and macOS (launchd). Elsewhere, install the command (\\`npm i -g ${PACKAGE}\\`) and run \\`${CLI_NAME} start\\` under your own supervisor.`,\n );\n }\n}\n\nexport const readText = (file: string) => {\n try {\n return readFileSync(file, \"utf8\");\n } catch {\n return null;\n }\n};\n\n/** Where to look when the service doesn't start. */\nexport function serviceHints(ops: ServiceOps, env: NodeJS.ProcessEnv = process.env, id: UnitId = DAEMON_UNIT): string[] {\n if (ops.platform === \"darwin\") {\n return [`launchctl print ${launchdTarget(ops)}/${id.launchd}`, \"and the log file xaq.log (agentinbox.log from before the rename) in the data directory\"];\n }\n const scope = ops.root ? \"\" : \" --user\";\n const hints = [`systemctl${scope} status ${id.systemd}`, `journalctl${scope} -u ${id.systemd} -e`];\n if (!ops.root && !env.XDG_RUNTIME_DIR) {\n hints.push(\n \"XDG_RUNTIME_DIR is not set, so there may be no systemd user session (common over `su` or `sudo -u`). \" +\n `Log in as ${ops.user} directly (e.g. over ssh), or run \\`sudo loginctl enable-linger ${ops.user}\\` and then ` +\n `\\`export XDG_RUNTIME_DIR=/run/user/${ops.uid}\\` before installing.`,\n );\n }\n return hints;\n}\n\nconst firstLine = (err: unknown) => (err instanceof Error ? err.message : String(err)).split(\"\\n\")[0];\n\n/** First line of a failed command's stderr (what launchctl / systemctl said), else of the error. */\nexport const commandFailure = (err: unknown) => {\n const stderr = String((err as { stderr?: unknown })?.stderr ?? \"\").trim();\n return (stderr || (err instanceof Error ? err.message : String(err))).split(\"\\n\")[0]!;\n};\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/** Whether launchd has a job with this label loaded (any `launchctl print` failure counts as not loaded). */\nexport function launchdLoaded(ops: Pick<ServiceOps, \"capture\" | \"uid\">, label: string): boolean {\n try {\n ops.capture(\"launchctl\", [\"print\", `${launchdTarget(ops)}/${label}`]);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Loads the launchd job in `file`, replacing a loaded job with the same label. `launchctl bootout` returns\n * before the old job is torn down (launchd gives it up to 20 s to exit), and `bootstrap` of that label fails\n * with \"5: Input/output error\" until it is. So: boot it out, wait until `launchctl print` no longer finds it,\n * then bootstrap; on failure, retry with growing delays. Throws launchd's reason when every attempt failed.\n */\nexport async function bootstrapLaunchd(\n ops: Pick<ServiceOps, \"exec\" | \"capture\" | \"uid\">,\n label: string,\n file: string,\n opts: { attempts?: number; pollMs?: number; unloadTimeoutMs?: number } = {},\n): Promise<void> {\n const { attempts = 5, pollMs = 250, unloadTimeoutMs = 30_000 } = opts;\n let last: unknown = null;\n // One unload budget for all attempts: a job that never unloads costs 30 s, not 30 s per attempt.\n const deadline = Date.now() + unloadTimeoutMs;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n ops.exec(\"launchctl\", [\"bootout\", `${launchdTarget(ops)}/${label}`], { quiet: true });\n } catch {\n // Not loaded.\n }\n await waitUnloaded(ops, label, deadline, pollMs);\n try {\n ops.capture(\"launchctl\", [\"bootstrap\", launchdTarget(ops), file]);\n return;\n } catch (err) {\n last = err;\n if (attempt < attempts) await sleep(pollMs * 4 * Math.min(2 ** (attempt - 1), 4));\n }\n }\n const stillLoaded = launchdLoaded(ops, label);\n throw new Error(`launchctl bootstrap failed: ${commandFailure(last)}${stillLoaded ? ` (a job labelled ${label} is still loaded and wouldn't unload)` : \"\"}`);\n}\n\n/** Polls `launchctl print` until launchd no longer lists the label, or the deadline passes. */\nasync function waitUnloaded(ops: Pick<ServiceOps, \"capture\" | \"uid\">, label: string, deadline: number, pollMs: number): Promise<void> {\n while (launchdLoaded(ops, label) && Date.now() < deadline) await sleep(pollMs);\n}\n\n/** What a service was doing before it was stopped, so a rollback can put it back exactly so. */\nexport interface UnitState {\n /** launchd: the job was loaded; systemd: the unit was active. */\n running: boolean;\n /** systemd only: the unit was enabled (started at boot). */\n enabled: boolean;\n}\n\nexport function unitState(ops: Pick<ServiceOps, \"platform\" | \"root\" | \"capture\" | \"uid\">, id: UnitId): UnitState {\n if (ops.platform === \"darwin\") return { running: launchdLoaded(ops, id.launchd), enabled: false };\n const ask = (verb: string) => {\n try {\n return ops.capture(\"systemctl\", [...(ops.root ? [] : [\"--user\"]), verb, id.systemd]).trim();\n } catch (err) {\n // is-active / is-enabled exit non-zero for \"inactive\" / \"disabled\"; the word is still on stdout.\n return String((err as { stdout?: unknown })?.stdout ?? \"\").trim();\n }\n };\n const active = ask(\"is-active\");\n return { running: active === \"active\" || active === \"activating\" || active === \"reloading\", enabled: ask(\"is-enabled\") === \"enabled\" };\n}\n\n/**\n * Stops a service and waits until it is really gone, so a replacement never runs next to it (they would\n * fight over the port and the data directory). systemd's stop waits by itself; launchd's bootout returns\n * at once and tears the job down asynchronously. Throws when it is still running afterwards.\n */\nexport async function stopAndWait(ops: ServiceOps, id: UnitId, opts: { pollMs?: number; unloadTimeoutMs?: number } = {}): Promise<void> {\n const { pollMs = 250, unloadTimeoutMs = 30_000 } = opts;\n if (ops.platform === \"darwin\") {\n try {\n ops.exec(\"launchctl\", [\"bootout\", `${launchdTarget(ops)}/${id.launchd}`], { quiet: true });\n } catch {\n // Not loaded.\n }\n await waitUnloaded(ops, id.launchd, Date.now() + unloadTimeoutMs, pollMs);\n if (launchdLoaded(ops, id.launchd)) throw new Error(`launchd still has ${id.launchd} loaded after booting it out`);\n return;\n }\n try {\n systemctl(ops, \"disable\", \"--now\", id.systemd);\n } catch {\n // Not loaded or already stopped: checked below.\n }\n if (unitState(ops, id).running) throw new Error(`systemd still reports ${id.systemd} running after stopping it`);\n}\n\n/** Puts a stopped service's unit file back and returns it to the state it was in (see unitState). */\nexport async function restoreUnit(ops: ServiceOps, id: UnitId, file: string, text: string, state: UnitState, pollMs?: number): Promise<void> {\n writeFileSync(file, text, { mode: ops.platform === \"darwin\" ? 0o600 : 0o644 });\n if (ops.platform === \"linux\") {\n systemctl(ops, \"daemon-reload\");\n if (state.enabled) systemctl(ops, \"enable\", id.systemd);\n if (state.running) systemctl(ops, \"start\", id.systemd);\n } else if (state.running) {\n await bootstrapLaunchd(ops, id.launchd, file, { pollMs });\n }\n}\n\n/** Fails before anything is touched when systemd can't be reached (e.g. no user bus over su). */\nexport function preflight(ops: ServiceOps): void {\n if (ops.platform !== \"linux\") return;\n try {\n systemctl(ops, \"daemon-reload\");\n } catch (err) {\n const hints = serviceHints(ops).map((h) => ` ${h}`).join(\"\\n\");\n throw new Error(`Can't talk to systemd${ops.root ? \"\" : \" (user session)\"}: ${firstLine(err)}. Nothing was changed.\\n${hints}`);\n }\n}\n\n/** Whether host:port can't be bound because something already listens there. */\nfunction portInUse(host: string, port: number): Promise<boolean> {\n if (!port) return Promise.resolve(false);\n return new Promise((done) => {\n const probe = createServer();\n probe.once(\"error\", (err: NodeJS.ErrnoException) => done(err.code === \"EADDRINUSE\"));\n probe.listen({ host, port, exclusive: true }, () => probe.close(() => done(false)));\n });\n}\n\n/**\n * The service manager's main pid for a service (default: the xaq daemon; the listener passes its\n * own label and unit); null when it isn't running or can't be asked.\n */\nexport function serviceMainPid(ops: ServiceOps, id: UnitId = DAEMON_UNIT): number | null {\n try {\n const out =\n ops.platform === \"darwin\"\n ? (/^\\s*pid = (\\d+)/m.exec(ops.capture(\"launchctl\", [\"print\", `${launchdTarget(ops)}/${id.launchd}`]))?.[1] ?? \"\")\n : ops.capture(\"systemctl\", [...(ops.root ? [] : [\"--user\"]), \"show\", \"-p\", \"MainPID\", \"--value\", id.systemd]).trim();\n const pid = Number(out);\n return Number.isInteger(pid) && pid > 0 ? pid : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Refuses (before anything is touched) while xaq already runs outside the service: in a\n * terminal on this data directory (its instance lock), or anything on the port while the service\n * isn't running. The service could only fail to start, and the health check would hear the other one.\n * A lock holder is the service when its lock says so, or (early nightlies didn't write that flag)\n * when, with a unit installed, it is the service manager's main process. `units` are the installed\n * services (the new one and/or one from before the rename).\n */\nexport async function assertNothingElseRunning(\n config: Pick<ServiceConfig, \"dataDir\" | \"host\" | \"port\">,\n ops: ServiceOps,\n unitInstalled: boolean,\n units: UnitId[] = [DAEMON_UNIT],\n): Promise<void> {\n const other = ops.instance(config.dataDir);\n const pids = unitInstalled ? units.map((id) => serviceMainPid(ops, id)).filter((p): p is number => p !== null) : [];\n if (other && !other.service && !pids.includes(other.pid)) {\n throw new Error(\n `xaq is already running in a terminal (pid ${other.pid}${other.port ? `, port ${other.port}` : \"\"}): stop it with Ctrl-C, then run this again. ` +\n `The service uses the same data directory (${config.dataDir}), and only one xaq can use it at a time. Nothing was changed.`,\n );\n }\n // A running service (on whatever data directory) may hold the port; reinstalling stops it first.\n if (!other && !pids.length && (await ops.portInUse(config.host, config.port))) {\n throw new Error(\n `Port ${config.port} is already in use: by xaq (or agentinbox) running in a terminal with another --data directory, or by another program. ` +\n `Stop it (Ctrl-C in its terminal), or choose another port with --port. Nothing was changed.`,\n );\n }\n}\n\n/**\n * After the health check answered: whether the data directory's lock holder is the new service's own\n * process. It must be the service manager's main process (or, when that pid is unknown, say in its lock\n * that it is the service), and not the process that held the lock before the install. This checks the\n * lock holder, not who answered: xaq binds its port before taking the lock and exits when the\n * port is taken, so a service that holds the lock is the one listening. Null when it is; else why not.\n */\nexport function notTheService(owner: InstanceInfo | null, before: InstanceInfo | null, mainPid: number | null): string | null {\n if (!owner) return \"no xaq holds the data directory\";\n const same = before && owner.pid === before.pid && owner.startedAt === before.startedAt;\n const isService = mainPid !== null ? owner.pid === mainPid : !!owner.service;\n if (same || !isService) return `the xaq holding the data directory (pid ${owner.pid}) is not the service${mainPid ? ` (pid ${mainPid})` : \"\"}`;\n return null;\n}\n\n/**\n * A daemon service installed under the names from before the rename (agentinbox), if any: its unit file,\n * the file's content, and the data directory it runs on.\n */\nexport function legacyDaemonUnit(\n ops: Pick<ServiceOps, \"platform\" | \"unitDir\">,\n): { file: string; text: string; dataDir: string | null; port: number | null; host: string | null } | null {\n if (ops.platform !== \"linux\" && ops.platform !== \"darwin\") return null;\n const file = unitPath(ops, LEGACY_DAEMON_UNIT);\n const text = readText(file);\n if (text === null) return null;\n const rawPort = unitEnv(text, \"PORT\");\n const port = rawPort && /^\\d+$/.test(rawPort) && Number(rawPort) <= 65535 ? Number(rawPort) : null;\n return { file, text, dataDir: unitDataDir(text), port, host: unitEnv(text, \"HOST\") || null };\n}\n\n/**\n * Where `service install` puts the service when the command line and environment don't say: replacing a\n * service from before the rename keeps its data directory, port and host (a bind to 127.0.0.1 must not\n * widen to 0.0.0.0 on upgrade). A service already moved keeps ~/.agentinbox too, even if a ~/.xaq has\n * appeared since. `given` is what the flags (and PORT / HOST / XAQ_DATA) said.\n */\nexport function serviceInstallInput(\n given: { dataDir?: string; port?: number; host?: string },\n ops: Pick<ServiceOps, \"platform\" | \"unitDir\">,\n env: NodeJS.ProcessEnv = process.env,\n): Pick<ConfigInput, \"dataDir\" | \"port\" | \"host\"> {\n const legacy = legacyDaemonUnit(ops);\n const installed = installedDaemonDataDir(ops);\n return {\n dataDir: given.dataDir ?? (envVar(\"DATA\", env) || undefined) ?? legacy?.dataDir ?? (installed && installed === legacyDataDir() ? installed : undefined) ?? undefined,\n port: given.port ?? (env.PORT?.trim() ? undefined : (legacy?.port ?? undefined)),\n host: given.host ?? (env.HOST ? undefined : (legacy?.host ?? undefined)),\n };\n}\n\n/** The data directory the installed (xaq) daemon service runs on, if one is installed and says. */\nexport function installedDaemonDataDir(ops: Pick<ServiceOps, \"platform\" | \"unitDir\">): string | null {\n if (ops.platform !== \"linux\" && ops.platform !== \"darwin\") return null;\n const text = readText(unitPath(ops, DAEMON_UNIT));\n return text === null ? null : unitDataDir(text);\n}\n\nexport async function installService(config: ServiceConfig, ops: ServiceOps = systemOps(), opts: { launchdPollMs?: number } = {}): Promise<void> {\n assertSupported(ops);\n if (ops.platform === \"linux\" && /\\s/.test(config.dataDir)) {\n throw new Error(`The data directory \"${config.dataDir}\" contains whitespace, which systemd can't load settings from. Choose another one with --data.`);\n }\n const extraEnv = mergeServiceEnv(readPreviousEnv(config.dataDir), process.env);\n validateMasterKey(extraEnv.XAQ_MASTER_KEY);\n // The service must be able to read what's already stored, with exactly the key it will get.\n assertMasterKeyUsable(config, extraEnv.XAQ_MASTER_KEY ?? \"\");\n preflight(ops);\n const file = unitFile(ops);\n const previousUnit = readText(file);\n // A service from before the rename is replaced: stopped (and waited for) before the new one starts,\n // so the two never run at once, and put back exactly as it was if the new one fails.\n const legacy = legacyDaemonUnit(ops);\n const legacyState = legacy ? unitState(ops, LEGACY_DAEMON_UNIT) : null;\n await assertNothingElseRunning(config, ops, previousUnit !== null || legacy !== null, [DAEMON_UNIT, ...(legacy ? [LEGACY_DAEMON_UNIT] : [])]);\n const before = ops.instance(config.dataDir);\n mkdirSync(config.dataDir, { recursive: true, mode: 0o700 });\n\n const staging = ops.stage(config.dataDir);\n const previousEnv = readText(serviceEnvPath(config.dataDir));\n const app = join(config.dataDir, \"app\");\n const entry = installedEntry(app);\n const spec: ServiceSpec = { execPath: process.execPath, entry, dataDir: config.dataDir, port: config.port, host: config.host, extraEnv, system: ops.root };\n let swap: ReturnType<typeof swapIn> | null = null;\n\n try {\n if (legacy) {\n console.log(` Replacing the ${LEGACY_PRODUCT} service (${unitLabel(ops, LEGACY_DAEMON_UNIT)}) with ${unitLabel(ops, DAEMON_UNIT)}…`);\n await stopAndWait(ops, LEGACY_DAEMON_UNIT, { pollMs: opts.launchdPollMs });\n rmSync(legacy.file, { force: true });\n if (ops.platform === \"linux\") systemctl(ops, \"daemon-reload\");\n }\n uninstallService(config, ops, { quiet: true, keepApp: true });\n swap = swapIn(staging, app);\n writeServiceEnv(config.dataDir, extraEnv);\n mkdirSync(ops.unitDir(), { recursive: true });\n if (ops.platform === \"linux\") {\n writeFileSync(file, renderSystemdUnit(spec));\n systemctl(ops, \"daemon-reload\");\n systemctl(ops, \"enable\", \"--now\", SERVICE_NAME);\n } else {\n // 0600: the plist may carry XAQ_MASTER_KEY.\n writeFileSync(file, renderLaunchdPlist(spec), { mode: 0o600 });\n await bootstrapLaunchd(ops, LAUNCHD_LABEL, file, { pollMs: opts.launchdPollMs });\n }\n if (!(await ops.waitReady(config))) throw new Error(`it did not answer on port ${config.port}`);\n // Something else (a foreground xaq started meanwhile) may have answered instead.\n const wrong = notTheService(ops.instance(config.dataDir), before, serviceMainPid(ops));\n if (wrong) throw new Error(wrong);\n } catch (err) {\n await rollback();\n const hints = serviceHints(ops, process.env, legacy && previousUnit === null ? LEGACY_DAEMON_UNIT : DAEMON_UNIT)\n .map((h) => ` ${h}`)\n .join(\"\\n\");\n const restored = legacy ? `the ${LEGACY_PRODUCT} service as it was` : previousUnit ? \"the previous install\" : \"no service\";\n throw new Error(`Starting the service failed (${firstLine(err)}). Rolled back to ${restored}.\\n To find out why:\\n${hints}`);\n }\n swap!.commit();\n\n async function rollback(): Promise<void> {\n const quietly = async (fn: () => void | Promise<void>) => {\n try {\n await fn();\n } catch {\n // Best effort: report the original failure.\n }\n };\n // Before the old service comes back, the new one must be gone (launchd tears it down asynchronously).\n if (legacy) await quietly(() => stopAndWait(ops, DAEMON_UNIT, { pollMs: opts.launchdPollMs }));\n await quietly(() => uninstallService(config, ops, { quiet: true, keepApp: true }));\n await quietly(() => rmSync(file, { force: true }));\n if (ops.platform === \"linux\") await quietly(() => systemctl(ops, \"daemon-reload\"));\n await quietly(() => rmSync(staging, { recursive: true, force: true }));\n if (swap) await quietly(() => swap!.restore());\n await quietly(() => (previousEnv === null ? rmSync(serviceEnvPath(config.dataDir), { force: true }) : writeFileSync(serviceEnvPath(config.dataDir), previousEnv, { mode: 0o600 })));\n if (previousUnit !== null) {\n await quietly(async () => {\n writeFileSync(file, previousUnit, { mode: ops.platform === \"darwin\" ? 0o600 : 0o644 });\n if (ops.platform === \"linux\") {\n systemctl(ops, \"daemon-reload\");\n systemctl(ops, \"enable\", \"--now\", SERVICE_NAME);\n } else {\n await bootstrapLaunchd(ops, LAUNCHD_LABEL, file, { pollMs: opts.launchdPollMs });\n }\n });\n }\n // The pre-rename service comes back under its own name (even when stopping it is what failed),\n // running and enabled only if it was before.\n if (legacy) await quietly(() => restoreUnit(ops, LEGACY_DAEMON_UNIT, legacy.file, legacy.text, legacyState!, opts.launchdPollMs));\n }\n\n enableLinger(ops);\n\n const fromShell = (k: string) => k in process.env || (LEGACY_ENV_KEYS[k as keyof typeof LEGACY_ENV_KEYS] ?? k) in process.env;\n const kept = Object.keys(extraEnv).filter((k) => !fromShell(k));\n console.log(`\\n Installed ${SERVICE_NAME} as a ${ops.platform === \"linux\" ? \"systemd\" : \"launchd\"} service (${unitLabel(ops, DAEMON_UNIT)}).`);\n if (legacy) console.log(` It replaces the ${LEGACY_PRODUCT} service (${unitLabel(ops, LEGACY_DAEMON_UNIT)}), which was removed. Your data stays in ${config.dataDir}.`);\n console.log(\n ` It runs the Node.js at ${process.execPath} (v${process.versions.node}). If you remove or replace that Node, run \\`${serviceInstallCommand(config)}\\` again.`,\n );\n if (kept.length) console.log(` Kept ${kept.join(\", \")} from the previous install.`);\n printLinks(config);\n}\n\n/** Keeps user services running after logout and starts them at boot (systemd user units only). */\nexport function enableLinger(ops: ServiceOps): void {\n if (ops.platform !== \"linux\" || ops.root) return;\n try {\n ops.exec(\"loginctl\", [\"enable-linger\", ops.user], { quiet: true });\n } catch {\n console.warn(` Could not enable lingering. Run \\`sudo loginctl enable-linger ${ops.user}\\` so the service starts at boot.`);\n }\n}\n\n/** XAQ_DATA (AGENTINBOX_DATA in a unit from before the rename) from an installed unit or plist. */\nexport function unitDataDir(text: string): string | null {\n return unitEnv(text, \"XAQ_DATA\") ?? unitEnv(text, \"AGENTINBOX_DATA\");\n}\n\n/** An environment variable a unit (`Environment=\"K=v\"`) or plist (`<key>K</key><string>v</string>`) sets. */\nexport function unitEnv(text: string, name: string): string | null {\n const systemd = new RegExp(`^Environment=\"${name}=((?:[^\"\\\\\\\\]|\\\\\\\\.)*)\"`, \"m\").exec(text);\n if (systemd) return systemd[1]!.replace(/\\\\(.)/g, \"$1\").replace(/%%/g, \"%\");\n const plist = new RegExp(`<key>${name}</key><string>([^<]*)</string>`).exec(text);\n if (plist) return plist[1]!.replace(/&lt;/g, \"<\").replace(/&gt;/g, \">\").replace(/&amp;/g, \"&\");\n return null;\n}\n\n/**\n * Stops and removes the service, under its own name or the one from before the rename. The data\n * directory is the one the installed unit uses (falling back to `config.dataDir`), and only directories\n * that really are the service's own install are deleted; nothing is created.\n */\nexport function uninstallService(\n config: Pick<ServiceConfig, \"dataDir\">,\n ops: ServiceOps = systemOps(),\n opts: { quiet?: boolean; keepApp?: boolean } = {},\n): void {\n assertSupported(ops);\n const units = [DAEMON_UNIT, LEGACY_DAEMON_UNIT]\n .map((id) => ({ id, file: unitPath(ops, id), text: readText(unitPath(ops, id)) }))\n .filter((u): u is { id: UnitId; file: string; text: string } => u.text !== null);\n const dataDir = units.map((u) => unitDataDir(u.text)).find(Boolean) || config.dataDir;\n if (!units.length && !opts.quiet) console.log(\"The xaq service is not installed.\");\n for (const u of units) {\n if (ops.platform === \"linux\") {\n try {\n systemctl(ops, \"disable\", \"--now\", u.id.systemd);\n } catch {\n // Already stopped.\n }\n rmSync(u.file);\n systemctl(ops, \"daemon-reload\");\n } else {\n try {\n ops.exec(\"launchctl\", [\"bootout\", `${launchdTarget(ops)}/${u.id.launchd}`], { quiet: true });\n } catch {\n // Not loaded.\n }\n rmSync(u.file);\n }\n if (!opts.quiet) console.log(`Removed the ${unitLabel(ops, u.id)} service. Your data in ${dataDir} was kept.`);\n }\n // The service's own install; the data (database, keys) stays.\n if (!opts.keepApp) removeOwnInstall(dataDir);\n}\n\nexport function serviceStatus(ops: ServiceOps = systemOps()): void {\n assertSupported(ops);\n const id = existsSync(unitFile(ops)) ? DAEMON_UNIT : existsSync(unitPath(ops, LEGACY_DAEMON_UNIT)) ? LEGACY_DAEMON_UNIT : null;\n if (!id) {\n console.log(`The xaq service is not installed. Install it with \\`${cliCommand(\"service install\")}\\`.`);\n process.exitCode = 1;\n return;\n }\n if (id === LEGACY_DAEMON_UNIT) {\n console.log(`This is the service from before the rename to xaq (${unitLabel(ops, id)}). \\`${cliCommand(\"service install\")}\\` moves it to ${unitLabel(ops, DAEMON_UNIT)}, keeping your data.\\n`);\n }\n try {\n if (ops.platform === \"linux\") systemctl(ops, \"status\", id.systemd, \"--no-pager\");\n else ops.exec(\"launchctl\", [\"print\", `${launchdTarget(ops)}/${id.launchd}`]);\n } catch {\n process.exitCode = 1;\n }\n}\n/** A real HealthResponse (not the \"starting\" placeholder or something else on the port). */\nexport function isHealthResponse(body: unknown): boolean {\n const b = body as { status?: unknown; version?: unknown } | null;\n return !!b && (b.status === \"ok\" || b.status === \"degraded\") && typeof b.version === \"string\";\n}\n\n/** Polls /api/health for up to ~45 s. 503 with a HealthResponse means \"up but degraded\", which is up. */\nasync function waitForHealth(config: ServiceConfig): Promise<boolean> {\n const base = loopbackUrl(config.host, config.port);\n for (let i = 0; i < 90; i++) {\n try {\n const res = await fetch(`${base}/api/health`, { signal: AbortSignal.timeout(1000) });\n if ((res.status === 200 || res.status === 503) && isHealthResponse(await res.json().catch(() => null))) return true;\n } catch {\n // Not up yet.\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n\nfunction printLinks(config: ServiceConfig): void {\n const token = readSetupToken(config.dataDir);\n console.log(` Running at:\\n`);\n for (const url of consoleUrls(config.host, config.port)) console.log(` ${url}${token ? `/setup?token=${token}` : \"\"}`);\n if (token) console.log(`\\n Open the link above to claim the instance and finish setup.`);\n console.log();\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { t as installListenService, u as listenServiceStatus, _ as uninstallListenService } from "./service-C7jfo-Ex.js";
@@ -0,0 +1,457 @@
1
+ #!/usr/bin/env node
2
+ import { n as envVar, t as LEGACY_PRODUCT } from "./brand--ZB8jTn1.js";
3
+ import { t as NPX } from "./version-BxH7OuWD.js";
4
+ import { i as saveProfile, l as ensurePrivateDir, n as loadProfile } from "./profile-NmMKAczz.js";
5
+ import { a as listenAppDir, c as profilePath, l as profileStateDir, o as logPath, p as validProfileName, r as dataDir } from "./paths-yzOKD6ow.js";
6
+ import { f as serviceNode } from "./mcpheader-C9H4vudu.js";
7
+ import { f as isHermesRoute, n as RECIPE_NAMES, o as hermesChangeNote, x as checkTransport } from "./recipes-DG_-doGq.js";
8
+ import { g as resolveListen, r as gatewayWarning } from "./hermes-DHnDTYJp.js";
9
+ import { l as readStatus, o as acquireFileLock, s as pidAlive, t as EXIT } from "./listen-B9iO7HgQ.js";
10
+ import { B as systemOps, C as preflight, G as unitPath, K as unitState, L as stagePackage, O as restoreUnit, P as serviceMainPid, R as stopAndWait, T as removeOwnInstall, V as systemctl, Y as xmlEscape, a as LEGACY_PACKAGE, d as commandFailure, f as enableLinger, h as installedEntry, k as sdQuote, l as assertSupported, u as bootstrapLaunchd, v as launchdTarget, w as readText, z as swapIn } from "./service-BVn3soZZ.js";
11
+ import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
12
+ import { delimiter, dirname, join } from "node:path";
13
+ import pc from "picocolors";
14
+ //#region server/listen/service.ts
15
+ /**
16
+ * `xaq listen service install|uninstall|status`: one systemd user unit / launchd agent per
17
+ * profile, running from a stable install (not the npx cache). The unit only says which profile to
18
+ * run; settings and the key stay in the 0600 profile file. Installation reuses the daemon's
19
+ * machinery (server/service.ts): systemd pre-check, the user manager's unit directory, staging into
20
+ * app.new and swapping in, rollback when the new listener doesn't start.
21
+ *
22
+ * A listener installed before the rename (agentinbox-listen-<profile> / com.unlaboredlabs.agentinbox.listen.<profile>)
23
+ * is replaced by `install` for its profile: stopped and waited for before the new one starts (one listener
24
+ * per profile, never two), and put back exactly as it was if the new one fails. `status` and
25
+ * `uninstall` find either name.
26
+ */
27
+ const unitName = (profile) => `xaq-listen-${profile}`;
28
+ const launchdLabel = (profile) => `sh.xaq.listen.${profile}`;
29
+ /** The names before the rename. */
30
+ const legacyUnitName = (profile) => `${LEGACY_PRODUCT}-listen-${profile}`;
31
+ const legacyLaunchdLabel = (profile) => `com.unlaboredlabs.agentinbox.listen.${profile}`;
32
+ const listenUnit = (profile) => ({
33
+ launchd: launchdLabel(profile),
34
+ systemd: unitName(profile)
35
+ });
36
+ const legacyListenUnit = (profile) => ({
37
+ launchd: legacyLaunchdLabel(profile),
38
+ systemd: legacyUnitName(profile)
39
+ });
40
+ /**
41
+ * PATH for the service: this shell's PATH (so `claude`, `codex`… resolve as they do here) minus
42
+ * transient entries npx, npm scripts and version-manager shells add, plus node's own directory.
43
+ */
44
+ function servicePath(raw = process.env.PATH ?? "", node = process.execPath) {
45
+ const transient = /\/_npx\/|node_modules\/\.bin|node-gyp-bin|fnm_multishells|\/\.pnpm\//;
46
+ const dirs = [
47
+ dirname(process.execPath),
48
+ dirname(node),
49
+ ...raw.split(delimiter)
50
+ ].filter((d) => d && d.startsWith("/") && !transient.test(d));
51
+ if (!dirs.includes("/usr/bin")) dirs.push("/usr/local/bin", "/usr/bin", "/bin");
52
+ return [...new Set(dirs)].join(delimiter);
53
+ }
54
+ /** Environment the service needs to behave like the shell `service install` ran in. Never contains the key. */
55
+ function serviceEnvironment(node = process.execPath) {
56
+ const env = { XAQ_SERVICE: "1" };
57
+ env.PATH = servicePath(void 0, node);
58
+ for (const key of [
59
+ "XDG_CONFIG_HOME",
60
+ "XDG_STATE_HOME",
61
+ "XDG_DATA_HOME",
62
+ "CLAUDE_CONFIG_DIR",
63
+ "CODEX_HOME",
64
+ "HERMES_HOME",
65
+ "LANG"
66
+ ]) if (process.env[key]) env[key] = process.env[key];
67
+ const debug = envVar("DEBUG");
68
+ if (debug) env.XAQ_DEBUG = debug;
69
+ return env;
70
+ }
71
+ function systemdListenUnit(profile, entry, cwd, system = false, node = process.execPath) {
72
+ const vars = Object.entries(serviceEnvironment(node)).map(([k, v]) => `Environment=${sdQuote(`${k}=${v}`)}`).join("\n");
73
+ return `[Unit]
74
+ Description=xaq listener (profile ${profile})${system ? "\nAfter=network-online.target\nWants=network-online.target" : ""}
75
+ StartLimitIntervalSec=0
76
+
77
+ [Service]
78
+ ExecStart=${sdQuote(node)} ${sdQuote(entry)} listen --profile ${profile}
79
+ WorkingDirectory=${cwd.replace(/%/g, "%%")}
80
+ ${vars}
81
+ Restart=on-failure
82
+ RestartSec=10
83
+ RestartSteps=6
84
+ RestartMaxDelaySec=600
85
+ # ${EXIT.revoked}: the key was revoked or the config is invalid. Restarting can't fix that.
86
+ RestartPreventExitStatus=${EXIT.revoked}
87
+ TimeoutStopSec=20
88
+
89
+ [Install]
90
+ WantedBy=${system ? "multi-user.target" : "default.target"}
91
+ `;
92
+ }
93
+ function launchdListenPlist(profile, entry, cwd, node = process.execPath) {
94
+ const esc = xmlEscape;
95
+ const vars = Object.entries(serviceEnvironment(node)).map(([k, v]) => ` <key>${esc(k)}</key><string>${esc(v)}</string>`).join("\n");
96
+ const errLog = esc(join(profileStateDir(profile), "service-stderr.log"));
97
+ return `<?xml version="1.0" encoding="UTF-8"?>
98
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
99
+ <plist version="1.0">
100
+ <dict>
101
+ <key>Label</key><string>${esc(launchdLabel(profile))}</string>
102
+ <key>ProgramArguments</key>
103
+ <array>
104
+ <string>${esc(node)}</string>
105
+ <string>${esc(entry)}</string>
106
+ <string>listen</string>
107
+ <string>--profile</string>
108
+ <string>${esc(profile)}</string>
109
+ </array>
110
+ <key>WorkingDirectory</key><string>${esc(cwd)}</string>
111
+ <key>EnvironmentVariables</key>
112
+ <dict>
113
+ ${vars}
114
+ </dict>
115
+ <key>RunAtLoad</key><true/>
116
+ <key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>
117
+ <key>ThrottleInterval</key><integer>10</integer>
118
+ <key>StandardOutPath</key><string>/dev/null</string>
119
+ <key>StandardErrorPath</key><string>${errLog}</string>
120
+ </dict>
121
+ </plist>
122
+ `;
123
+ }
124
+ const unitFile = (ops, profile) => unitPath(ops, listenUnit(profile));
125
+ const label = (ops, id) => ops.platform === "linux" ? id.systemd : id.launchd;
126
+ /** Listener profiles with a unit file under the new names, or (`legacy`) under the names from before the rename. */
127
+ function profilesWithUnits(ops, legacy) {
128
+ const dir = ops.unitDir();
129
+ if (!existsSync(dir)) return [];
130
+ const re = ops.platform === "darwin" ? legacy ? /^com\.unlaboredlabs\.agentinbox\.listen\.(.+)\.plist$/ : /^sh\.xaq\.listen\.(.+)\.plist$/ : legacy ? /^agentinbox-listen-(.+)\.service$/ : /^xaq-listen-(.+)\.service$/;
131
+ return readdirSync(dir).map((f) => re.exec(f)?.[1]).filter((p) => !!p);
132
+ }
133
+ /** Installed listener profiles (from the unit directory), under either name. */
134
+ function installedProfiles(ops) {
135
+ return [.../* @__PURE__ */ new Set([...profilesWithUnits(ops, false), ...profilesWithUnits(ops, true)])];
136
+ }
137
+ /** The profile's installed listener service: the new one, else one from before the rename; null when neither. */
138
+ function installedListenUnit(profile, ops = systemOps()) {
139
+ if (ops.platform !== "linux" && ops.platform !== "darwin") return null;
140
+ try {
141
+ for (const [id, legacy] of [[listenUnit(profile), false], [legacyListenUnit(profile), true]]) {
142
+ const file = unitPath(ops, id);
143
+ if (existsSync(file)) return {
144
+ id,
145
+ file,
146
+ legacy
147
+ };
148
+ }
149
+ } catch {}
150
+ return null;
151
+ }
152
+ function isServiceInstalled(profile, ops = systemOps()) {
153
+ return installedListenUnit(profile, ops) !== null;
154
+ }
155
+ /** The service's name as the platform knows it (for status lines), under whichever name it is installed. */
156
+ const installedServiceLabel = (profile, ops = systemOps()) => {
157
+ const u = installedListenUnit(profile, ops);
158
+ return u ? label(ops, u.id) : null;
159
+ };
160
+ function listenerHints(ops, profile, id = listenUnit(profile)) {
161
+ if (ops.platform === "darwin") return [`launchctl print ${launchdTarget(ops)}/${id.launchd}`, `the log ${logPath(profile)}`];
162
+ const scope = ops.root ? "" : " --user";
163
+ return [
164
+ `systemctl${scope} status ${id.systemd}`,
165
+ `journalctl${scope} -u ${id.systemd} -e`,
166
+ `the log ${logPath(profile)}`
167
+ ];
168
+ }
169
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
170
+ /**
171
+ * The pid of a listener started after `since` for the profile (it writes status.json on start), once it
172
+ * has stayed up for a moment; null if none did within the timeout, or it stopped again.
173
+ */
174
+ async function waitListenerStarted(profile, since, timeoutMs = 2e4) {
175
+ const deadline = Date.now() + timeoutMs;
176
+ while (Date.now() < deadline) {
177
+ const st = readStatus(profile);
178
+ if (st && Date.parse(st.startedAt) >= since - 1e3) {
179
+ if (st.stoppedAt) return null;
180
+ if (pidAlive(st.pid)) {
181
+ await sleep(1e3);
182
+ const again = readStatus(profile);
183
+ return again?.pid === st.pid && !again.stoppedAt && pidAlive(st.pid) ? st.pid : null;
184
+ }
185
+ }
186
+ await sleep(250);
187
+ }
188
+ return null;
189
+ }
190
+ const why = commandFailure;
191
+ /** What the service manager says about the profile's listener (under `id`'s names): running (with its pid), or why not. */
192
+ function managerState(ops, profile, id = listenUnit(profile)) {
193
+ const pid = serviceMainPid(ops, id);
194
+ if (ops.platform === "darwin") {
195
+ let text;
196
+ try {
197
+ text = ops.capture("launchctl", ["print", `${launchdTarget(ops)}/${id.launchd}`]);
198
+ } catch (err) {
199
+ return {
200
+ running: false,
201
+ pid: null,
202
+ detail: `launchctl print: ${why(err)}`
203
+ };
204
+ }
205
+ const state = /^\s*state = (\S+)/m.exec(text)?.[1] ?? "unknown";
206
+ return {
207
+ running: state === "running" && pid !== null,
208
+ pid,
209
+ detail: `launchd state ${state}${pid ? `, pid ${pid}` : ""}`
210
+ };
211
+ }
212
+ let active;
213
+ try {
214
+ active = ops.capture("systemctl", [
215
+ ...ops.root ? [] : ["--user"],
216
+ "is-active",
217
+ id.systemd
218
+ ]).trim();
219
+ } catch (err) {
220
+ active = String(err?.stdout ?? "").trim() || why(err);
221
+ }
222
+ return {
223
+ running: active === "active" && pid !== null,
224
+ pid,
225
+ detail: `systemd ${active || "unknown"}${pid ? `, MainPID ${pid}` : ""}`
226
+ };
227
+ }
228
+ /** (Re)starts the profile's unit with the file just written (launchd: see bootstrapLaunchd). */
229
+ async function startUnit(ops, profile, file, pollMs = 250) {
230
+ if (ops.platform === "linux") {
231
+ systemctl(ops, "daemon-reload");
232
+ systemctl(ops, "enable", unitName(profile));
233
+ systemctl(ops, "restart", unitName(profile));
234
+ return;
235
+ }
236
+ await bootstrapLaunchd(ops, launchdLabel(profile), file, { pollMs });
237
+ }
238
+ /** Stops and removes one unit (no waiting: for uninstall and for undoing a new unit that never ran). */
239
+ function removeUnit(ops, id, file) {
240
+ if (ops.platform === "linux") {
241
+ try {
242
+ systemctl(ops, "disable", "--now", id.systemd);
243
+ } catch {}
244
+ rmSync(file);
245
+ systemctl(ops, "daemon-reload");
246
+ } else {
247
+ try {
248
+ ops.exec("launchctl", ["bootout", `${launchdTarget(ops)}/${id.launchd}`], { quiet: true });
249
+ } catch {}
250
+ rmSync(file);
251
+ }
252
+ }
253
+ /**
254
+ * Listener units from before the rename start `<app>/node_modules/agentinbox-cli/dist/cli.js`. While some
255
+ * still run from this shared install (other profiles not moved yet), it keeps that path working: a tiny
256
+ * module there runs xaq-cli, so those listeners run the new version under their old names until
257
+ * `listen service install --profile <name>` moves them too.
258
+ */
259
+ function writeLegacyEntry(app) {
260
+ const dir = join(app, "node_modules", LEGACY_PACKAGE);
261
+ mkdirSync(join(dir, "dist"), { recursive: true });
262
+ writeFileSync(join(dir, "package.json"), `${JSON.stringify({
263
+ name: LEGACY_PACKAGE,
264
+ private: true,
265
+ type: "module",
266
+ description: "Runs xaq-cli for listener services installed before the rename."
267
+ }, null, 2)}\n`);
268
+ writeFileSync(join(dir, "dist", "cli.js"), `// Listener services installed as agentinbox still start this path: run xaq-cli.\nimport "../../xaq-cli/dist/cli.js";\n`);
269
+ }
270
+ /** Stages the listeners' install (no web console needed). */
271
+ const stageListenerApp = (staging) => stagePackage(staging, {
272
+ where: dirname(staging),
273
+ requireWeb: false
274
+ });
275
+ async function installListenService(flags, deps = {}) {
276
+ const ops = deps.ops ?? systemOps();
277
+ assertSupported(ops);
278
+ const profileName = validProfileName(flags.profile ?? "default");
279
+ const profile = loadProfile(profileName);
280
+ const { config, next } = resolveListen(profileName, profile, {
281
+ ...flags,
282
+ save: true
283
+ });
284
+ if (!config.exec && !config.forward) throw new Error(`Nothing for the service to run. Pass --recipe <name> (${RECIPE_NAMES.join(", ")}), --exec "<command>" and/or --forward <url> (they are saved to profile "${profileName}").`);
285
+ await checkTransport(next.url, { insecure: config.insecure });
286
+ preflight(ops);
287
+ const shared = dataDir();
288
+ const daemonDb = [`xaq.db`, `${LEGACY_PRODUCT}.db`].find((f) => existsSync(join(shared, f)));
289
+ if (daemonDb) throw new Error(`${shared} holds a xaq daemon's data (${daemonDb}), so the listener can't install there. Run the daemon with another --data directory, or set XDG_DATA_HOME for the listener.`);
290
+ const p = profileName === "default" ? "" : ` --profile ${profileName}`;
291
+ const node = deps.node ?? serviceNode();
292
+ let runningPid = 0;
293
+ let replacedLegacy = false;
294
+ const cwd = config.cwd;
295
+ mkdirSync(profileStateDir(profileName), {
296
+ recursive: true,
297
+ mode: 448
298
+ });
299
+ if (config.cwdIsDefault) ensurePrivateDir(cwd);
300
+ mkdirSync(shared, {
301
+ recursive: true,
302
+ mode: 448
303
+ });
304
+ const releaseInstallLock = acquireFileLock(join(shared, "install.lock"), (holder) => /* @__PURE__ */ new Error(`Another listener service install is running (pid ${holder.pid}, since ${holder.startedAt}). Try again when it has finished.`));
305
+ try {
306
+ await installLocked();
307
+ } finally {
308
+ releaseInstallLock();
309
+ }
310
+ async function installLocked() {
311
+ const staging = (deps.stage ?? stageListenerApp)(join(shared, "app.new"));
312
+ saveProfile(profileName, next);
313
+ const app = listenAppDir();
314
+ const entry = installedEntry(app);
315
+ const file = unitFile(ops, profileName);
316
+ const previousUnit = readText(file);
317
+ const legacyId = legacyListenUnit(profileName);
318
+ const legacyFile = unitPath(ops, legacyId);
319
+ const legacyText = readText(legacyFile);
320
+ const legacyState = legacyText !== null ? unitState(ops, legacyId) : null;
321
+ let legacyStopped = false;
322
+ const legacyEntry = join(app, "node_modules", LEGACY_PACKAGE);
323
+ const othersOnLegacy = profilesWithUnits(ops, true).filter((o) => o !== profileName && (readText(unitPath(ops, legacyListenUnit(o))) ?? "").includes(legacyEntry));
324
+ if (othersOnLegacy.length) writeLegacyEntry(staging);
325
+ const since = Date.now();
326
+ let swap = null;
327
+ try {
328
+ swap = swapIn(staging, app);
329
+ mkdirSync(ops.unitDir(), { recursive: true });
330
+ if (ops.platform === "linux") writeFileSync(file, systemdListenUnit(profileName, entry, cwd, ops.root, node.path));
331
+ else writeFileSync(file, launchdListenPlist(profileName, entry, cwd, node.path), { mode: 384 });
332
+ if (legacyText !== null) {
333
+ legacyStopped = true;
334
+ await stopAndWait(ops, legacyId, { pollMs: deps.pollMs });
335
+ rmSync(legacyFile, { force: true });
336
+ }
337
+ await startUnit(ops, profileName, file, deps.pollMs);
338
+ const pid = await (deps.waitStarted ?? waitListenerStarted)(profileName, since);
339
+ if (!pid) throw new Error("the listener did not start");
340
+ const svc = managerState(ops, profileName);
341
+ if (!svc.running) throw new Error(`the service manager doesn't report it running (${svc.detail})`);
342
+ if (svc.pid !== pid) throw new Error(`the listener that started (pid ${pid}) isn't the service's process (${svc.detail})`);
343
+ runningPid = pid;
344
+ replacedLegacy = legacyText !== null;
345
+ } catch (err) {
346
+ const failed = [];
347
+ const step = (what, fn) => {
348
+ try {
349
+ fn();
350
+ } catch (e) {
351
+ failed.push(`${what}: ${why(e)}`);
352
+ }
353
+ };
354
+ if (swap) step("restoring the previous install", () => swap.restore());
355
+ step("restoring the profile's settings", () => saveProfile(profileName, profile));
356
+ step("removing the staged install", () => rmSync(staging, {
357
+ recursive: true,
358
+ force: true
359
+ }));
360
+ let outcome;
361
+ if (previousUnit !== null) {
362
+ step("restoring the previous unit file", () => writeFileSync(file, previousUnit, { mode: ops.platform === "darwin" ? 384 : 420 }));
363
+ const restarted = await startUnit(ops, profileName, file, deps.pollMs).then(() => null, (e) => why(e));
364
+ const svc = restarted ? null : managerState(ops, profileName);
365
+ outcome = restarted ? `Put the previous install back, but restarting it failed (${restarted}), so no listener service is running now` : svc.running ? `Put the previous install back; it is running again (${svc.detail})` : `Put the previous install back and started it, but the service manager doesn't report it running (${svc.detail})`;
366
+ } else {
367
+ if (legacyStopped) await stopAndWait(ops, listenUnit(profileName), { pollMs: deps.pollMs }).catch((e) => failed.push(`stopping the new service: ${why(e)}`));
368
+ step("removing the new service", () => {
369
+ if (existsSync(file)) removeUnit(ops, listenUnit(profileName), file);
370
+ });
371
+ outcome = legacyText !== null ? "Removed the new service again" : "Removed the new service again (no listener service is installed)";
372
+ }
373
+ if (legacyText !== null && legacyStopped) {
374
+ const restored = await restoreUnit(ops, legacyId, legacyFile, legacyText, legacyState, deps.pollMs).then(() => null, (e) => why(e));
375
+ const svc = restored || !legacyState.running ? null : managerState(ops, profileName, legacyId);
376
+ outcome += restored ? `. Putting the ${LEGACY_PRODUCT} listener (${label(ops, legacyId)}) back failed (${restored})` : svc ? `. Put the ${LEGACY_PRODUCT} listener (${label(ops, legacyId)}) back${svc.running ? `; it is running again (${svc.detail})` : `, but the service manager doesn't report it running (${svc.detail})`}` : `. Put the ${LEGACY_PRODUCT} listener (${label(ops, legacyId)}) back, stopped as it was`;
377
+ } else if (legacyText !== null) outcome += `. The ${LEGACY_PRODUCT} listener (${label(ops, legacyId)}) was left as it was`;
378
+ if (failed.length) outcome += `. These rollback steps failed too: ${failed.join("; ")}`;
379
+ const hints = listenerHints(ops, profileName, legacyText !== null && previousUnit === null ? legacyId : listenUnit(profileName)).map((h) => ` ${h}`).join("\n");
380
+ throw new Error(`Starting the listener service failed (${why(err)}). ${outcome}.\n To find out why:\n${hints}`);
381
+ }
382
+ swap.commit();
383
+ enableLinger(ops);
384
+ for (const other of profilesWithUnits(ops, false).filter((o) => o !== profileName)) restartOther(listenUnit(other));
385
+ for (const other of othersOnLegacy) restartOther(legacyListenUnit(other));
386
+ if (othersOnLegacy.length) {
387
+ const list = othersOnLegacy.map((o) => `${o} (\`${NPX} listen service install --profile ${o}\`)`).join(", ");
388
+ console.log(`\n ${pc.yellow("!")} Still under their ${LEGACY_PRODUCT} service names (running this version): ${list}. Run those to move them to xaq too.`);
389
+ }
390
+ }
391
+ function restartOther(id) {
392
+ try {
393
+ if (ops.platform === "linux") systemctl(ops, "try-restart", id.systemd);
394
+ else ops.exec("launchctl", [
395
+ "kickstart",
396
+ "-k",
397
+ `${launchdTarget(ops)}/${id.launchd}`
398
+ ], { quiet: true });
399
+ } catch {}
400
+ }
401
+ const kind = ops.platform === "linux" ? ops.root ? "systemd system service" : "systemd user service" : "launchd agent";
402
+ console.log(`\n ${pc.green("✔")} Installed the listener for profile "${profileName}" as a ${kind} (${label(ops, listenUnit(profileName))}); it is running as pid ${runningPid}.`);
403
+ if (replacedLegacy) console.log(` Replaced: the ${LEGACY_PRODUCT} listener service ${label(ops, legacyListenUnit(profileName))} (removed; the profile, its key and its cursor are unchanged)`);
404
+ console.log(` Runs: ${config.exec ? `--exec ${config.exec}` : ""}${config.exec && config.forward ? " and " : ""}${config.forward ? `--forward ${config.forward.url}` : ""}`);
405
+ console.log(` Directory: ${cwd}${config.cwdIsDefault ? " (private; pass --cwd to run in a project)" : ""}`);
406
+ console.log(` Node.js: ${node.path} (v${node.version}): ${node.why}. If you remove it, run \`${NPX} listen service install${p}\` again.`);
407
+ console.log(` Settings: ${profilePath(profileName)} (change them with \`${NPX} listen${p} … --save\`, then reinstall)`);
408
+ console.log(` Log: ${logPath(profileName)}`);
409
+ const change = hermesChangeNote(flags.recipe, config.exec);
410
+ if (change) console.log(` Note: ${change}`);
411
+ if (config.forward && isHermesRoute(config.forward.url)) {
412
+ const w = await (deps.gatewayCheck ?? gatewayWarning)(config.forward.url);
413
+ if (w) console.log(pc.yellow(` Warning: ${w}`));
414
+ }
415
+ console.log(` Status: ${NPX} listen status${p}`);
416
+ if (ops.platform === "linux") console.log(` Follow: journalctl ${ops.root ? "" : "--user "}-u ${unitName(profileName)} -f`);
417
+ console.log();
418
+ }
419
+ function uninstallListenService(profileName, quiet = false, deps = {}) {
420
+ const ops = deps.ops ?? systemOps();
421
+ assertSupported(ops);
422
+ const name = validProfileName(profileName);
423
+ const units = [listenUnit(name), legacyListenUnit(name)].map((id) => ({
424
+ id,
425
+ file: unitPath(ops, id)
426
+ })).filter((u) => existsSync(u.file));
427
+ if (!units.length) {
428
+ if (!quiet) console.log(`No listener service is installed for profile "${name}".`);
429
+ return;
430
+ }
431
+ for (const u of units) removeUnit(ops, u.id, u.file);
432
+ if (!installedProfiles(ops).length) removeOwnInstall(dataDir());
433
+ if (!quiet) console.log(`Removed the listener service for profile "${name}". The profile (and its key) was kept: ${profilePath(name)}`);
434
+ }
435
+ function listenServiceStatus(profileName, deps = {}) {
436
+ const ops = deps.ops ?? systemOps();
437
+ assertSupported(ops);
438
+ const name = validProfileName(profileName);
439
+ const p = name === "default" ? "" : ` --profile ${name}`;
440
+ const installed = installedListenUnit(name, ops);
441
+ if (!installed) {
442
+ console.log(`No listener service is installed for profile "${name}". Install one with \`${NPX} listen service install${p}\`.`);
443
+ process.exitCode = 1;
444
+ return;
445
+ }
446
+ if (installed.legacy) console.log(`This listener runs under its name from before the rename to xaq (${label(ops, installed.id)}). \`${NPX} listen service install${p}\` moves it to ${label(ops, listenUnit(name))}.\n`);
447
+ try {
448
+ if (ops.platform === "linux") systemctl(ops, "status", installed.id.systemd, "--no-pager");
449
+ else ops.exec("launchctl", ["print", `${launchdTarget(ops)}/${installed.id.launchd}`]);
450
+ } catch {
451
+ process.exitCode = 1;
452
+ }
453
+ }
454
+ //#endregion
455
+ export { uninstallListenService as _, launchdLabel as a, writeLegacyEntry as b, legacyListenUnit as c, listenUnit as d, managerState as f, systemdListenUnit as g, stageListenerApp as h, isServiceInstalled as i, legacyUnitName as l, servicePath as m, installedListenUnit as n, launchdListenPlist as o, serviceEnvironment as p, installedServiceLabel as r, legacyLaunchdLabel as s, installListenService as t, listenServiceStatus as u, unitName as v, waitListenerStarted as y };
456
+
457
+ //# sourceMappingURL=service-C7jfo-Ex.js.map