agentinbox-cli 0.0.0 → 0.1.0-nightly.10

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 (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +337 -0
  3. package/dist/admin-CXkmdTYo.js +196 -0
  4. package/dist/admin-CXkmdTYo.js.map +1 -0
  5. package/dist/audit-bItbJYFP.js +65 -0
  6. package/dist/audit-bItbJYFP.js.map +1 -0
  7. package/dist/audit-gVdDzkAE.js +2 -0
  8. package/dist/auth-0NrMvHdE.js +550 -0
  9. package/dist/auth-0NrMvHdE.js.map +1 -0
  10. package/dist/auth-CoHvbWgO.js +2 -0
  11. package/dist/bus-Cz0flS37.js +12 -0
  12. package/dist/bus-Cz0flS37.js.map +1 -0
  13. package/dist/bus-OKIGut72.js +2 -0
  14. package/dist/cli.js +220 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/commands-DZ60j8_y.js +103 -0
  17. package/dist/commands-DZ60j8_y.js.map +1 -0
  18. package/dist/config-CIvgMqLx.js +2 -0
  19. package/dist/config-v975gxv4.js +44 -0
  20. package/dist/config-v975gxv4.js.map +1 -0
  21. package/dist/connect-BHC2trBi.js +292 -0
  22. package/dist/connect-BHC2trBi.js.map +1 -0
  23. package/dist/content-BXblC_Qs.js +1162 -0
  24. package/dist/content-BXblC_Qs.js.map +1 -0
  25. package/dist/content-worker.js +28 -0
  26. package/dist/content-worker.js.map +1 -0
  27. package/dist/crypto-DYZ4R7c2.js +74 -0
  28. package/dist/crypto-DYZ4R7c2.js.map +1 -0
  29. package/dist/db-B6gLQGBK.js +2 -0
  30. package/dist/db-D0gvshCc.js +282 -0
  31. package/dist/db-D0gvshCc.js.map +1 -0
  32. package/dist/errors-FbMS3QBo.js +39 -0
  33. package/dist/errors-FbMS3QBo.js.map +1 -0
  34. package/dist/filter-DGVz6-P1.js +140 -0
  35. package/dist/filter-DGVz6-P1.js.map +1 -0
  36. package/dist/instance-BBzEsYZO.js +175 -0
  37. package/dist/instance-BBzEsYZO.js.map +1 -0
  38. package/dist/instance-DLNTj9nm.js +2 -0
  39. package/dist/listen-D8DkEhqM.js +1724 -0
  40. package/dist/listen-D8DkEhqM.js.map +1 -0
  41. package/dist/listen-DT-omyD6.js +2 -0
  42. package/dist/mcpheader-By2dIyZq.js +2 -0
  43. package/dist/mcpheader-D9rrI-aa.js +65 -0
  44. package/dist/mcpheader-D9rrI-aa.js.map +1 -0
  45. package/dist/mcpsetup-7KxroOCp.js +2 -0
  46. package/dist/mcpsetup-DJ7ebFGJ.js +266 -0
  47. package/dist/mcpsetup-DJ7ebFGJ.js.map +1 -0
  48. package/dist/paths-BCBp8vrG.js +2 -0
  49. package/dist/paths-TLp8hiMt.js +40 -0
  50. package/dist/paths-TLp8hiMt.js.map +1 -0
  51. package/dist/profile-CgGgJ4Dm.js +181 -0
  52. package/dist/profile-CgGgJ4Dm.js.map +1 -0
  53. package/dist/profile-RbN2FRz7.js +2 -0
  54. package/dist/recipes-7d_Xws4M.js +2 -0
  55. package/dist/recipes-UalOc_LB.js +100 -0
  56. package/dist/recipes-UalOc_LB.js.map +1 -0
  57. package/dist/send-Dk-t8ia-.js +108 -0
  58. package/dist/send-Dk-t8ia-.js.map +1 -0
  59. package/dist/server-Hunjpt8n.js +8103 -0
  60. package/dist/server-Hunjpt8n.js.map +1 -0
  61. package/dist/service-BAgZDh22.js +2 -0
  62. package/dist/service-DYdD92CC.js +2 -0
  63. package/dist/service-D_ZEs3Zk.js +508 -0
  64. package/dist/service-D_ZEs3Zk.js.map +1 -0
  65. package/dist/service-whbugeQp.js +295 -0
  66. package/dist/service-whbugeQp.js.map +1 -0
  67. package/dist/settings-CeEsv4CE.js +2 -0
  68. package/dist/settings-DLUStIN7.js +109 -0
  69. package/dist/settings-DLUStIN7.js.map +1 -0
  70. package/dist/setup-token-BO89xnpb.js +48 -0
  71. package/dist/setup-token-BO89xnpb.js.map +1 -0
  72. package/dist/skill/SKILL.md +277 -0
  73. package/dist/skill-BJFn2bHK.js +2 -0
  74. package/dist/skill-WKbeyNct.js +43 -0
  75. package/dist/skill-WKbeyNct.js.map +1 -0
  76. package/dist/types-3eEsBFVj.js +37 -0
  77. package/dist/types-3eEsBFVj.js.map +1 -0
  78. package/dist/ui-B8Sd4Y7Y.js +81 -0
  79. package/dist/ui-B8Sd4Y7Y.js.map +1 -0
  80. package/dist/urls-Dp8pBGMU.js +2 -0
  81. package/dist/urls-U_LfYV61.js +27 -0
  82. package/dist/urls-U_LfYV61.js.map +1 -0
  83. package/dist/web/assets/_agentId-DAj08NDq.js +1 -0
  84. package/dist/web/assets/_app-B6q_HS9y.js +1 -0
  85. package/dist/web/assets/_app-CAQ6Mu1s.js +63 -0
  86. package/dist/web/assets/_webhookId-CMgBUN2Z.js +1 -0
  87. package/dist/web/assets/account-status-omoUlgBI.js +1 -0
  88. package/dist/web/assets/accounts-DyLfACj_.js +1 -0
  89. package/dist/web/assets/accounts-list-DiXACW0-.js +1 -0
  90. package/dist/web/assets/activity-zrckFhPk.js +1 -0
  91. package/dist/web/assets/agent-create-form-DKwaY1Dg.js +1 -0
  92. package/dist/web/assets/agents-Drvjccgt.js +1 -0
  93. package/dist/web/assets/approvals-4kd0_J8X.js +2 -0
  94. package/dist/web/assets/arrow-left-DVJV4Zx7.js +1 -0
  95. package/dist/web/assets/arrow-right-4PFX6MSS.js +1 -0
  96. package/dist/web/assets/audit-76H45orR.js +1 -0
  97. package/dist/web/assets/audit-bits-B1We-fQ3.js +1 -0
  98. package/dist/web/assets/bot-DVw15lBn.js +1 -0
  99. package/dist/web/assets/card-EQTpqntn.js +1 -0
  100. package/dist/web/assets/checkbox-DDZZurgK.js +1 -0
  101. package/dist/web/assets/chevron-down-892ylB4R.js +1 -0
  102. package/dist/web/assets/chevron-right-CbBKS6-T.js +1 -0
  103. package/dist/web/assets/chips-input--xgMZpg2.js +1 -0
  104. package/dist/web/assets/circle-x-Bi4IGwky.js +1 -0
  105. package/dist/web/assets/common-PG6weTqi.js +1 -0
  106. package/dist/web/assets/connect-host-DdkOJnMu.js +1 -0
  107. package/dist/web/assets/data-table-Ca9aQZKR.js +1 -0
  108. package/dist/web/assets/dialog-CxNRCZdE.js +1 -0
  109. package/dist/web/assets/dist-B0e3Ju3r.js +1 -0
  110. package/dist/web/assets/empty-CE_WZGEC.js +1 -0
  111. package/dist/web/assets/es2015-DPaldoxG.js +41 -0
  112. package/dist/web/assets/event-row-2xoyzgbP.js +1 -0
  113. package/dist/web/assets/events-Bizq3DPC.js +1 -0
  114. package/dist/web/assets/eye-off-DjpaihHf.js +1 -0
  115. package/dist/web/assets/field-CcyMn3ey.js +1 -0
  116. package/dist/web/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  117. package/dist/web/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  118. package/dist/web/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  119. package/dist/web/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  120. package/dist/web/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  121. package/dist/web/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  122. package/dist/web/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  123. package/dist/web/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  124. package/dist/web/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  125. package/dist/web/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  126. package/dist/web/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  127. package/dist/web/assets/get-started-DXUVmyNX.js +1 -0
  128. package/dist/web/assets/grants-BhXj1Pew.js +1 -0
  129. package/dist/web/assets/index-Bd4Y5wSp.js +10 -0
  130. package/dist/web/assets/index-akvt_1UI.css +2 -0
  131. package/dist/web/assets/input-CMhmmHiH.js +1 -0
  132. package/dist/web/assets/input-group-DRs3TNaT.js +1 -0
  133. package/dist/web/assets/key-round-CVIZRCLM.js +1 -0
  134. package/dist/web/assets/labels-C_Pwium9.js +1 -0
  135. package/dist/web/assets/login-aCr-cPqJ.js +1 -0
  136. package/dist/web/assets/mail-CAVmuESo.js +1 -0
  137. package/dist/web/assets/mutations-DVlbDx0M.js +1 -0
  138. package/dist/web/assets/paperclip-QFjM4y6n.js +1 -0
  139. package/dist/web/assets/password-input-_m9Ycphv.js +1 -0
  140. package/dist/web/assets/pencil-Bf1JhsG0.js +1 -0
  141. package/dist/web/assets/play-DiPk49JH.js +1 -0
  142. package/dist/web/assets/plus-CETmK0SB.js +1 -0
  143. package/dist/web/assets/preload-helper-BFuRUfdt.js +1 -0
  144. package/dist/web/assets/progress-3ZOsbLSK.js +6 -0
  145. package/dist/web/assets/queries-a3M5_loa.js +2 -0
  146. package/dist/web/assets/refresh-cw-DOn9kD7g.js +1 -0
  147. package/dist/web/assets/search-gcYjitzV.js +1 -0
  148. package/dist/web/assets/select-C8tJBHb5.js +1 -0
  149. package/dist/web/assets/send-BhQwhuJo.js +1 -0
  150. package/dist/web/assets/settings-H7ExHD9L.js +1 -0
  151. package/dist/web/assets/settings-form-CNUionsI.js +1 -0
  152. package/dist/web/assets/setup-DLbH5Dvl.js +1 -0
  153. package/dist/web/assets/sheet-BQKpA2xa.js +1 -0
  154. package/dist/web/assets/shield-alert-CbFgd06X.js +1 -0
  155. package/dist/web/assets/stamp-CH2OvjH2.js +1 -0
  156. package/dist/web/assets/stream-C2Tr2d5O.js +1 -0
  157. package/dist/web/assets/tabs-ySiz7Mni.js +1 -0
  158. package/dist/web/assets/tag-CP72Kk6Y.js +1 -0
  159. package/dist/web/assets/test-connection-Bd2Gac0O.js +1 -0
  160. package/dist/web/assets/textarea-CCokBZIw.js +1 -0
  161. package/dist/web/assets/toggle-group-ne0pVDg6.js +1 -0
  162. package/dist/web/assets/trash-DxdMNg8B.js +1 -0
  163. package/dist/web/assets/use-sign-out-BuzlTb0Q.js +1 -0
  164. package/dist/web/assets/useInfiniteQuery-DE6WChlb.js +1 -0
  165. package/dist/web/assets/useMatch-BKUnm54_.js +1 -0
  166. package/dist/web/assets/useMutation-DeWvLw1a.js +1 -0
  167. package/dist/web/assets/useQuery-DAhf1YDH.js +1 -0
  168. package/dist/web/assets/useSelector-D70eKNYs.js +1 -0
  169. package/dist/web/assets/webhook-BWT75M78.js +1 -0
  170. package/dist/web/assets/webhook-sheet-Cy_jHIqG.js +34 -0
  171. package/dist/web/assets/webhooks-CTAj5Ovp.js +1 -0
  172. package/dist/web/assets/with-selector-CkNyXYMW.js +1 -0
  173. package/dist/web/assets/x-BHjDLXNO.js +1 -0
  174. package/dist/web/favicon.svg +1 -0
  175. package/dist/web/index.html +31 -0
  176. package/package.json +95 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-0NrMvHdE.js","names":[],"sources":["../server/auth/lockout.ts","../server/auth/proxy.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 { isIPv4, isIPv6 } from \"node:net\";\n\n/** An address as a family and a big integer, with IPv4-mapped IPv6 (::ffff:a.b.c.d) treated as IPv4. */\ninterface Addr {\n v6: boolean;\n n: bigint;\n}\n\nfunction parseV4(ip: string): bigint {\n return ip.split(\".\").reduce((acc, part) => (acc << 8n) | BigInt(Number(part)), 0n);\n}\n\nexport function parseAddress(raw: string): Addr | null {\n const ip = raw.trim().replace(/^\\[|\\]$/g, \"\").replace(/%.*$/, \"\").toLowerCase();\n if (isIPv4(ip)) return { v6: false, n: parseV4(ip) };\n if (!isIPv6(ip)) return null;\n const mapped = /^(?:0{0,4}:){0,5}:?ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/.exec(ip);\n if (mapped && isIPv4(mapped[1]!)) return { v6: false, n: parseV4(mapped[1]!) };\n // Expand an embedded IPv4 tail into two hextets, then \"::\".\n let text = ip;\n const v4Tail = /(\\d+\\.\\d+\\.\\d+\\.\\d+)$/.exec(text);\n if (v4Tail) {\n const n = parseV4(v4Tail[1]!);\n text = text.slice(0, -v4Tail[1]!.length) + `${(n >> 16n).toString(16)}:${(n & 0xffffn).toString(16)}`;\n }\n const [head = \"\", tail] = text.split(\"::\");\n const left = head ? head.split(\":\") : [];\n const right = tail ? tail.split(\":\") : [];\n const groups = tail === undefined ? left : [...left, ...Array<string>(8 - left.length - right.length).fill(\"0\"), ...right];\n if (groups.length !== 8) return null;\n return { v6: true, n: groups.reduce((acc, g) => (acc << 16n) | BigInt(Number.parseInt(g || \"0\", 16)), 0n) };\n}\n\ninterface Range {\n v6: boolean;\n base: bigint;\n bits: number;\n}\n\n/**\n * Parses \"10.0.0.5\", \"10.0.0.0/8\", \"fd00::/8\", \"::ffff:10.0.0.0/104\"; null when invalid. A /0\n * (trust everyone) is refused: it would let any client pick its own address.\n */\nexport function parseRange(entry: string): Range | null {\n const parts = entry.trim().split(\"/\");\n if (parts.length > 2) return null;\n const [ip = \"\", prefix] = parts;\n const addr = parseAddress(ip);\n if (!addr) return null;\n let bits = addr.v6 ? 128 : 32;\n if (prefix !== undefined) {\n if (!/^\\d{1,3}$/.test(prefix)) return null;\n bits = Number(prefix);\n // A mapped IPv4 range written in IPv6 form: drop the 96-bit ::ffff: prefix.\n if (!addr.v6 && ip.includes(\":\")) bits -= 96;\n if (bits <= 0 || bits > (addr.v6 ? 128 : 32)) return null;\n }\n const width = BigInt(addr.v6 ? 128 : 32);\n const mask = bits === 0 ? 0n : ((1n << BigInt(bits)) - 1n) << (width - BigInt(bits));\n return { v6: addr.v6, base: addr.n & mask, bits };\n}\n\nfunction inRange(addr: Addr, r: Range): boolean {\n if (addr.v6 !== r.v6) return false;\n const width = BigInt(r.v6 ? 128 : 32);\n const mask = r.bits === 0 ? 0n : ((1n << BigInt(r.bits)) - 1n) << (width - BigInt(r.bits));\n return (addr.n & mask) === r.base;\n}\n\nconst LOOPBACK = [parseRange(\"127.0.0.0/8\")!, parseRange(\"::1/128\")!];\n\nlet cache: { raw: string; ranges: Range[] } | undefined;\n\n/** TRUST_PROXY_FROM as ranges. Invalid entries are reported once and ignored. */\nfunction trustedRanges(raw: string): Range[] {\n if (cache?.raw === raw) return cache.ranges;\n const ranges: Range[] = [];\n const invalid: string[] = [];\n for (const entry of raw.split(/[,\\s]+/).filter(Boolean)) {\n const r = parseRange(entry);\n if (r) ranges.push(r);\n else invalid.push(entry);\n }\n if (invalid.length) process.stderr.write(`agentinbox: ignoring invalid TRUST_PROXY_FROM entries: ${invalid.join(\", \")}\\n`);\n cache = { raw, ranges };\n return ranges;\n}\n\n/**\n * Whether forwarding headers from this socket peer may be believed: loopback always (a proxy on\n * the same host), anything else only when listed in TRUST_PROXY_FROM (IPs or CIDRs, comma-separated).\n * The daemon usually binds 0.0.0.0, so trusting whole private ranges would let any LAN or tailnet\n * peer forge its address.\n */\nexport function isTrustedProxy(peer: string, trustFrom = process.env.TRUST_PROXY_FROM ?? \"\"): boolean {\n const addr = parseAddress(peer);\n if (!addr) return false;\n return [...LOOPBACK, ...trustedRanges(trustFrom)].some((r) => inRange(addr, r));\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 { 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 { isTrustedProxy } from \"./proxy.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-agentinbox-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/** `agentinbox 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/**\n * The client address. X-Forwarded-For is honored only with TRUST_PROXY and only when the\n * connection comes from a trusted proxy (loopback, or TRUST_PROXY_FROM); it is read from the\n * right, since proxies append the address they saw and entries further left are client-controlled.\n * TRUST_PROXY_HOPS (default 1) is the number of proxies in front of agentinbox.\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\nlet warnedUntrustedProxy = false;\n\n/** Upgrades from the old private-range rule silently lost proxy trust; say so once. */\nfunction warnUntrustedProxy(peer: string): void {\n if (warnedUntrustedProxy) return;\n warnedUntrustedProxy = true;\n process.stderr.write(\n `agentinbox: 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`,\n );\n}\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 hops = Math.max(1, Number.parseInt(process.env.TRUST_PROXY_HOPS ?? \"1\", 10) || 1);\n const chain = (c.req.header(\"x-forwarded-for\") ?? \"\")\n .split(\",\")\n .map((s) => stripPort(s.trim()))\n .filter(Boolean);\n const forwarded = chain[chain.length - hops];\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 agentinbox 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":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { l as currentSetupToken, f as isClaimed, g as resetAdminPassword } from "./auth-0NrMvHdE.js";
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { EventEmitter } from "node:events";
3
+ //#region server/bus.ts
4
+ function createBus() {
5
+ const bus = new EventEmitter();
6
+ bus.setMaxListeners(0);
7
+ return bus;
8
+ }
9
+ //#endregion
10
+ export { createBus as t };
11
+
12
+ //# sourceMappingURL=bus-Cz0flS37.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bus-Cz0flS37.js","names":[],"sources":["../server/bus.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\nimport type { Account, 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\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":";;;AA6BA,SAAgB,YAAiB;CAC/B,MAAM,MAAM,IAAI,aAAwB;CAExC,IAAI,gBAAgB,CAAC;CACrB,OAAO;AACT"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { t as createBus } from "./bus-Cz0flS37.js";
package/dist/cli.js ADDED
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join, resolve, sep } from "node:path";
5
+ //#region server/node-check.ts
6
+ /** The oldest Node.js agentinbox runs on (node:sqlite unflagged, import.meta.dirname). Keep in sync with package.json engines. */
7
+ const MIN_NODE = "22.13.0";
8
+ /** An explanation when `version` (e.g. process.versions.node) is too old, else null. */
9
+ function nodeVersionError(version) {
10
+ const parse = (v) => v.replace(/^v/, "").split(".").map((n) => Number.parseInt(n, 10) || 0);
11
+ const have = parse(version);
12
+ const need = parse(MIN_NODE);
13
+ for (let i = 0; i < 3; i++) {
14
+ if ((have[i] ?? 0) > need[i]) return null;
15
+ if ((have[i] ?? 0) < need[i]) return `agentinbox needs Node.js 22.13 or newer (you have ${version.replace(/^v?/, "v")}). Install a current Node.js from https://nodejs.org and try again.`;
16
+ }
17
+ return null;
18
+ }
19
+ const problem = nodeVersionError(process.versions.node);
20
+ if (problem) {
21
+ console.error(problem);
22
+ process.exit(1);
23
+ }
24
+ //#endregion
25
+ //#region server/cli.ts
26
+ const { Command, InvalidArgumentError } = await import("commander");
27
+ const { default: pc } = await import("picocolors");
28
+ process.title = "agentinbox";
29
+ const emitWarning = process.emitWarning.bind(process);
30
+ process.emitWarning = ((warning, ...rest) => {
31
+ const message = typeof warning === "string" ? warning : warning.message;
32
+ if (((typeof rest[0] === "string" ? rest[0] : rest[0]?.type) === "ExperimentalWarning" || warning.name === "ExperimentalWarning") && /SQLite/i.test(message)) return;
33
+ return emitWarning(warning, ...rest);
34
+ });
35
+ /** package.json sits one level above both dist/cli.js and server/cli.ts. */
36
+ function packageVersion() {
37
+ try {
38
+ return JSON.parse(readFileSync(join(import.meta.dirname, "..", "package.json"), "utf8")).version;
39
+ } catch {
40
+ return "0.0.0";
41
+ }
42
+ }
43
+ const version = packageVersion();
44
+ const validPort = (value) => /^\d+$/.test(value) && Number(value) <= 65535;
45
+ function parsePort(value) {
46
+ if (!validPort(value)) throw new InvalidArgumentError("Port must be a number from 0 to 65535.");
47
+ return Number(value);
48
+ }
49
+ /** --port, else a validated PORT env (a typo there would otherwise become NaN deep inside). */
50
+ function portOption(flag) {
51
+ if (flag !== void 0) return flag;
52
+ const env = process.env.PORT?.trim();
53
+ if (!env) return void 0;
54
+ if (!validPort(env)) throw new Error(`PORT="${process.env.PORT}" is not a valid port. Use a number from 0 to 65535.`);
55
+ return Number(env);
56
+ }
57
+ const withServerOptions = (cmd) => cmd.option("-p, --port <port>", "port to listen on (default 8425, env PORT)", parsePort).option("--host <host>", "bind address (default 0.0.0.0, env HOST)").option("-d, --data <dir>", "data directory (default ~/.agentinbox, env AGENTINBOX_DATA)");
58
+ const configInput = (o) => ({
59
+ port: portOption(o.port),
60
+ host: o.host,
61
+ dataDir: o.data
62
+ });
63
+ const program = new Command("agentinbox").description("Self-hosted Gmail broker for AI agents: MCP tools, webhooks, guardrails and a web console.").version(version, "-v, --version", "print the version").helpOption("-h, --help", "show help").addHelpText("after", "\nWithout installing: npx agentinbox-cli [command]. To install the command: npm i -g agentinbox-cli").showSuggestionAfterError();
64
+ withServerOptions(program.command("start", { isDefault: true }).description("run agentinbox in the foreground (default)")).option("--no-open", "don't open a browser on first run").option("--dev", "development: API only, the console runs on Vite (:5173)").allowExcessArguments().action(async (o, cmd) => {
65
+ if (cmd.args.length) throw new Error(unknownCommand(cmd.args[0]));
66
+ const { runForeground } = await import("./server-Hunjpt8n.js");
67
+ await runForeground({
68
+ ...configInput(o),
69
+ dev: !!o.dev,
70
+ open: o.open,
71
+ version
72
+ });
73
+ });
74
+ const service = program.command("service").description("run agentinbox as a background service (systemd / launchd)");
75
+ withServerOptions(service.command("install").description("install and start the service; re-run to update")).action(async (o) => {
76
+ const { loadConfig } = await import("./config-CIvgMqLx.js");
77
+ const { installService } = await import("./service-BAgZDh22.js");
78
+ await installService(loadConfig(configInput(o), version));
79
+ });
80
+ service.command("uninstall").description("stop and remove the service (keeps your data)").option("-d, --data <dir>", "data directory, if the installed service doesn't say (default ~/.agentinbox, env AGENTINBOX_DATA)").action(async (o) => {
81
+ const { uninstallService } = await import("./service-BAgZDh22.js");
82
+ uninstallService({ dataDir: resolve(o.data ?? process.env.AGENTINBOX_DATA ?? join(homedir(), ".agentinbox")) });
83
+ });
84
+ service.command("status").description("show service status").action(async () => {
85
+ const { serviceStatus } = await import("./service-BAgZDh22.js");
86
+ serviceStatus();
87
+ });
88
+ const skill = program.command("skill").description("the agentinbox agent skill (SKILL.md)");
89
+ skill.command("print").description("print SKILL.md to stdout").option("--url <url>", "agentinbox server URL to fill in (env AGENTINBOX_URL)").action(async (o) => {
90
+ const { renderSkill } = await import("./skill-BJFn2bHK.js");
91
+ process.stdout.write(renderSkill(await serverUrl(o.url)));
92
+ });
93
+ skill.command("install").description("install SKILL.md for an agent (no running server needed)").option("-t, --target <target>", "claude, codex, agents, or a skills directory path", "claude").option("--url <url>", "agentinbox server URL agents connect to (env AGENTINBOX_URL)").action(async (o) => {
94
+ const { renderSkill, SKILL_TARGETS, skillPath, writeSkill } = await import("./skill-BJFn2bHK.js");
95
+ checkSkillTarget(o.target, Object.keys(SKILL_TARGETS));
96
+ const url = await serverUrl(o.url);
97
+ const path = skillPath(o.target);
98
+ writeSkill(path, renderSkill(url));
99
+ console.log(`Installed the agentinbox skill to ${pc.cyan(path)} (server ${url}).`);
100
+ if (!o.url && !process.env.AGENTINBOX_URL) console.log(pc.dim("Agents on other machines need the server's address: re-run with --url https://your-server."));
101
+ });
102
+ program.command("reset-password").description("remove the admin password and print a new setup link").option("-d, --data <dir>", "data directory (default ~/.agentinbox, env AGENTINBOX_DATA)").option("-p, --port <port>", "port of the running instance, used for the printed link", parsePort).option("--host <host>", "host of the running instance, used for the printed link").action(async (o) => {
103
+ const dataDir = resolve(o.data ?? process.env.AGENTINBOX_DATA ?? join(homedir(), ".agentinbox"));
104
+ const dbPath = join(dataDir, "agentinbox.db");
105
+ if (!existsSync(dbPath)) throw new Error(`No agentinbox data in ${dataDir}. If agentinbox runs as a system service, run this with sudo; otherwise pass the data directory it uses with --data.`);
106
+ const { loadConfig } = await import("./config-CIvgMqLx.js");
107
+ const { Db } = await import("./db-B6gLQGBK.js");
108
+ const { createBus } = await import("./bus-OKIGut72.js");
109
+ const { SettingsStore } = await import("./settings-CeEsv4CE.js");
110
+ const { AuditLog } = await import("./audit-gVdDzkAE.js");
111
+ const { currentSetupToken, isClaimed, resetAdminPassword } = await import("./auth-CoHvbWgO.js");
112
+ const { runningInstance } = await import("./instance-DLNTj9nm.js");
113
+ const { consoleUrls } = await import("./urls-Dp8pBGMU.js");
114
+ const running = runningInstance(dataDir);
115
+ const config = loadConfig({
116
+ dataDir,
117
+ port: portOption(o.port) ?? running?.port,
118
+ host: o.host ?? (process.env.HOST || running?.host)
119
+ }, version);
120
+ const db = new Db(dbPath);
121
+ try {
122
+ const bus = createBus();
123
+ const core = {
124
+ config,
125
+ db,
126
+ settings: new SettingsStore(db, bus, config),
127
+ audit: new AuditLog(db, bus)
128
+ };
129
+ const claimed = isClaimed(core);
130
+ const token = claimed ? resetAdminPassword(core) : currentSetupToken(core);
131
+ console.log(claimed ? "\n The admin password was removed and all console sessions were signed out.\n Open this link to set a new password:\n" : "\n This instance hasn't been claimed yet, so there is no password to reset.\n Open this link to claim it:\n");
132
+ const urls = consoleUrls(config.host, config.port);
133
+ const publicUrl = core.settings.publicUrl();
134
+ if (!urls.includes(publicUrl)) urls.push(publicUrl);
135
+ for (const url of urls) console.log(` ${pc.cyan(`${url}/setup?token=${token}`)}`);
136
+ console.log(pc.dim(`\n ${running ? "The running agentinbox picks this up immediately; no restart needed." : "Start agentinbox to use it."}\n`));
137
+ } finally {
138
+ db.close();
139
+ }
140
+ });
141
+ const { registerListenCommands } = await import("./commands-DZ60j8_y.js");
142
+ registerListenCommands(program);
143
+ /**
144
+ * The URL written into SKILL.md: --url, AGENTINBOX_URL, else the agentinbox running on this
145
+ * machine (its lock file), else http://localhost:8425.
146
+ */
147
+ async function serverUrl(flag) {
148
+ let raw = flag ?? process.env.AGENTINBOX_URL;
149
+ if (!raw) {
150
+ const { runningInstance } = await import("./instance-DLNTj9nm.js");
151
+ const running = runningInstance(resolve(process.env.AGENTINBOX_DATA || join(homedir(), ".agentinbox")));
152
+ const host = running && ![
153
+ "0.0.0.0",
154
+ "::",
155
+ ""
156
+ ].includes(running.host) ? running.host : "localhost";
157
+ raw = `http://${host.includes(":") ? `[${host}]` : host}:${running?.port || process.env.PORT || 8425}`;
158
+ }
159
+ let url;
160
+ try {
161
+ url = new URL(raw);
162
+ } catch {
163
+ throw new Error(`Invalid --url "${raw}". Use a full URL such as https://mail.example.com.`);
164
+ }
165
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("--url must start with http:// or https://");
166
+ return (url.origin + url.pathname).replace(/\/+$/, "");
167
+ }
168
+ /** A known agent name, or something that is clearly a path; typos like "claud" are refused. */
169
+ function checkSkillTarget(target, known) {
170
+ if (known.includes(target) || target.includes("/") || target.includes(sep) || target.startsWith(".") || isDir(target)) return;
171
+ const near = closest(target, known);
172
+ throw new Error(`Unknown skill target "${target}".${near ? ` Did you mean ${near}?` : ""} Use ${known.join(", ")}, or a directory path such as ./skills.`);
173
+ }
174
+ function isDir(path) {
175
+ try {
176
+ return statSync(path).isDirectory();
177
+ } catch {
178
+ return false;
179
+ }
180
+ }
181
+ function unknownCommand(name) {
182
+ const near = closest(name, [...program.commands.map((c) => c.name()), "help"]);
183
+ return `unknown command '${name}'${near ? `\n(Did you mean ${near}?)` : ""}\nRun agentinbox --help for the list of commands.`;
184
+ }
185
+ /** The candidate within edit distance 2 (or a prefix match), if any. */
186
+ function closest(word, candidates) {
187
+ const distance = (a, b) => {
188
+ const row = Array.from({ length: b.length + 1 }, (_, j) => j);
189
+ for (let i = 1; i <= a.length; i++) {
190
+ let prev = row[0];
191
+ row[0] = i;
192
+ for (let j = 1; j <= b.length; j++) {
193
+ const cur = row[j];
194
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
195
+ prev = cur;
196
+ }
197
+ }
198
+ return row[b.length];
199
+ };
200
+ let best = null;
201
+ for (const c of candidates) {
202
+ const d = c.startsWith(word) && word.length >= 3 ? 0 : distance(word.toLowerCase(), c);
203
+ if (d <= 2 && (!best || d < best.d)) best = {
204
+ name: c,
205
+ d
206
+ };
207
+ }
208
+ return best?.name ?? null;
209
+ }
210
+ try {
211
+ await program.parseAsync(process.argv);
212
+ } catch (err) {
213
+ console.error(pc.red(err instanceof Error ? err.message : String(err)));
214
+ if (process.env.AGENTINBOX_DEBUG && err instanceof Error) console.error(err.stack);
215
+ process.exit(1);
216
+ }
217
+ //#endregion
218
+ export {};
219
+
220
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../server/node-check.ts","../server/cli.ts"],"sourcesContent":["/** The oldest Node.js agentinbox runs on (node:sqlite unflagged, import.meta.dirname). Keep in sync with package.json engines. */\nexport const MIN_NODE = \"22.13.0\";\n\n/** An explanation when `version` (e.g. process.versions.node) is too old, else null. */\nexport function nodeVersionError(version: string): string | null {\n const parse = (v: string) => v.replace(/^v/, \"\").split(\".\").map((n) => Number.parseInt(n, 10) || 0);\n const have = parse(version);\n const need = parse(MIN_NODE);\n for (let i = 0; i < 3; i++) {\n if ((have[i] ?? 0) > need[i]!) return null;\n if ((have[i] ?? 0) < need[i]!) return `agentinbox needs Node.js 22.13 or newer (you have ${version.replace(/^v?/, \"v\")}). Install a current Node.js from https://nodejs.org and try again.`;\n }\n return null;\n}\n\n// Runs on import, before anything that needs a newer Node is loaded.\nconst problem = nodeVersionError(process.versions.node);\nif (problem) {\n console.error(problem);\n process.exit(1);\n}\n","// Must stay the first import: exits with a clear message on a too-old Node.\nimport \"./node-check.ts\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve, sep } from \"node:path\";\nimport type { Command as CommandType } from \"commander\";\n\n// Loaded after the Node check (static imports are hoisted above it in the bundle).\nconst { Command, InvalidArgumentError } = await import(\"commander\");\nconst { default: pc } = await import(\"picocolors\");\n\nprocess.title = \"agentinbox\";\n\n// node:sqlite is stable enough for us but still warns on Node 22. This runs before any server\n// module loads: everything that touches node:sqlite is imported dynamically below.\nconst emitWarning = process.emitWarning.bind(process);\nprocess.emitWarning = ((warning: string | Error, ...rest: unknown[]) => {\n const message = typeof warning === \"string\" ? warning : warning.message;\n const type = typeof rest[0] === \"string\" ? rest[0] : (rest[0] as { type?: string } | undefined)?.type;\n if ((type === \"ExperimentalWarning\" || (warning as Error).name === \"ExperimentalWarning\") && /SQLite/i.test(message)) return;\n return (emitWarning as (...args: unknown[]) => void)(warning, ...rest);\n}) as typeof process.emitWarning;\n\n/** package.json sits one level above both dist/cli.js and server/cli.ts. */\nfunction packageVersion(): string {\n try {\n return (JSON.parse(readFileSync(join(import.meta.dirname, \"..\", \"package.json\"), \"utf8\")) as { version: string }).version;\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst version = packageVersion();\n\ninterface ServerFlags {\n port?: number;\n host?: string;\n data?: string;\n}\n\nconst validPort = (value: string) => /^\\d+$/.test(value) && Number(value) <= 65535;\n\nfunction parsePort(value: string): number {\n if (!validPort(value)) throw new InvalidArgumentError(\"Port must be a number from 0 to 65535.\");\n return Number(value);\n}\n\n/** --port, else a validated PORT env (a typo there would otherwise become NaN deep inside). */\nfunction portOption(flag: number | undefined): number | undefined {\n if (flag !== undefined) return flag;\n const env = process.env.PORT?.trim();\n if (!env) return undefined;\n if (!validPort(env)) throw new Error(`PORT=\"${process.env.PORT}\" is not a valid port. Use a number from 0 to 65535.`);\n return Number(env);\n}\n\nconst withServerOptions = (cmd: CommandType) =>\n cmd\n .option(\"-p, --port <port>\", \"port to listen on (default 8425, env PORT)\", parsePort)\n .option(\"--host <host>\", \"bind address (default 0.0.0.0, env HOST)\")\n .option(\"-d, --data <dir>\", \"data directory (default ~/.agentinbox, env AGENTINBOX_DATA)\");\n\nconst configInput = (o: ServerFlags) => ({ port: portOption(o.port), host: o.host, dataDir: o.data });\n\nconst program = new Command(\"agentinbox\")\n .description(\"Self-hosted Gmail broker for AI agents: MCP tools, webhooks, guardrails and a web console.\")\n .version(version, \"-v, --version\", \"print the version\")\n .helpOption(\"-h, --help\", \"show help\")\n .addHelpText(\"after\", \"\\nWithout installing: npx agentinbox-cli [command]. To install the command: npm i -g agentinbox-cli\")\n .showSuggestionAfterError();\n\nwithServerOptions(program.command(\"start\", { isDefault: true }).description(\"run agentinbox in the foreground (default)\"))\n .option(\"--no-open\", \"don't open a browser on first run\")\n .option(\"--dev\", \"development: API only, the console runs on Vite (:5173)\")\n // `agentinbox typo` lands here as an extra argument: report it as the unknown command it is.\n .allowExcessArguments()\n .action(async (o: ServerFlags & { open: boolean; dev?: boolean }, cmd: CommandType) => {\n if (cmd.args.length) throw new Error(unknownCommand(cmd.args[0]!));\n const { runForeground } = await import(\"./server.ts\");\n await runForeground({ ...configInput(o), dev: !!o.dev, open: o.open, version });\n });\n\nconst service = program.command(\"service\").description(\"run agentinbox as a background service (systemd / launchd)\");\n\nwithServerOptions(service.command(\"install\").description(\"install and start the service; re-run to update\")).action(async (o: ServerFlags) => {\n const { loadConfig } = await import(\"./config.ts\");\n const { installService } = await import(\"./service.ts\");\n await installService(loadConfig(configInput(o), version));\n});\n\nservice\n .command(\"uninstall\")\n .description(\"stop and remove the service (keeps your data)\")\n .option(\"-d, --data <dir>\", \"data directory, if the installed service doesn't say (default ~/.agentinbox, env AGENTINBOX_DATA)\")\n .action(async (o: ServerFlags) => {\n const { uninstallService } = await import(\"./service.ts\");\n // No loadConfig: uninstalling must not create a data directory.\n uninstallService({ dataDir: resolve(o.data ?? process.env.AGENTINBOX_DATA ?? join(homedir(), \".agentinbox\")) });\n });\n\nservice\n .command(\"status\")\n .description(\"show service status\")\n .action(async () => {\n const { serviceStatus } = await import(\"./service.ts\");\n serviceStatus();\n });\n\nconst skill = program.command(\"skill\").description(\"the agentinbox agent skill (SKILL.md)\");\n\nskill\n .command(\"print\")\n .description(\"print SKILL.md to stdout\")\n .option(\"--url <url>\", \"agentinbox server URL to fill in (env AGENTINBOX_URL)\")\n .action(async (o: { url?: string }) => {\n const { renderSkill } = await import(\"./skill/index.ts\");\n process.stdout.write(renderSkill(await serverUrl(o.url)));\n });\n\nskill\n .command(\"install\")\n .description(\"install SKILL.md for an agent (no running server needed)\")\n .option(\"-t, --target <target>\", \"claude, codex, agents, or a skills directory path\", \"claude\")\n .option(\"--url <url>\", \"agentinbox server URL agents connect to (env AGENTINBOX_URL)\")\n .action(async (o: { target: string; url?: string }) => {\n const { renderSkill, SKILL_TARGETS, skillPath, writeSkill } = await import(\"./skill/index.ts\");\n checkSkillTarget(o.target, Object.keys(SKILL_TARGETS));\n const url = await serverUrl(o.url);\n const path = skillPath(o.target);\n writeSkill(path, renderSkill(url));\n console.log(`Installed the agentinbox skill to ${pc.cyan(path)} (server ${url}).`);\n if (!o.url && !process.env.AGENTINBOX_URL) {\n console.log(pc.dim(\"Agents on other machines need the server's address: re-run with --url https://your-server.\"));\n }\n });\n\nprogram\n .command(\"reset-password\")\n .description(\"remove the admin password and print a new setup link\")\n .option(\"-d, --data <dir>\", \"data directory (default ~/.agentinbox, env AGENTINBOX_DATA)\")\n .option(\"-p, --port <port>\", \"port of the running instance, used for the printed link\", parsePort)\n .option(\"--host <host>\", \"host of the running instance, used for the printed link\")\n .action(\n async (o: ServerFlags) => {\n // Never create a data directory or master key here: a wrong --data (or a service running as\n // another user) must not silently produce a fresh instance.\n const dataDir = resolve(o.data ?? process.env.AGENTINBOX_DATA ?? join(homedir(), \".agentinbox\"));\n const dbPath = join(dataDir, \"agentinbox.db\");\n if (!existsSync(dbPath)) {\n throw new Error(\n `No agentinbox data in ${dataDir}. If agentinbox runs as a system service, run this with sudo; ` +\n \"otherwise pass the data directory it uses with --data.\",\n );\n }\n const { loadConfig } = await import(\"./config.ts\");\n const { Db } = await import(\"./db/index.ts\");\n const { createBus } = await import(\"./bus.ts\");\n const { SettingsStore } = await import(\"./settings.ts\");\n const { AuditLog } = await import(\"./audit.ts\");\n const { currentSetupToken, isClaimed, resetAdminPassword } = await import(\"./auth/index.ts\");\n const { runningInstance } = await import(\"./instance.ts\");\n const { consoleUrls } = await import(\"./urls.ts\");\n\n const running = runningInstance(dataDir);\n const config = loadConfig(\n { dataDir, port: portOption(o.port) ?? running?.port, host: o.host ?? (process.env.HOST || running?.host) },\n version,\n );\n const db = new Db(dbPath);\n try {\n const bus = createBus();\n const core = { config, db, settings: new SettingsStore(db, bus, config), audit: new AuditLog(db, bus) };\n const claimed = isClaimed(core);\n const token = claimed ? resetAdminPassword(core) : currentSetupToken(core)!;\n console.log(\n claimed\n ? \"\\n The admin password was removed and all console sessions were signed out.\\n Open this link to set a new password:\\n\"\n : \"\\n This instance hasn't been claimed yet, so there is no password to reset.\\n Open this link to claim it:\\n\",\n );\n const urls = consoleUrls(config.host, config.port);\n const publicUrl = core.settings.publicUrl();\n if (!urls.includes(publicUrl)) urls.push(publicUrl);\n for (const url of urls) console.log(` ${pc.cyan(`${url}/setup?token=${token}`)}`);\n console.log(pc.dim(`\\n ${running ? \"The running agentinbox picks this up immediately; no restart needed.\" : \"Start agentinbox to use it.\"}\\n`));\n } finally {\n db.close();\n }\n },\n);\n\n// Agent side: `connect` (device-flow pairing), `setup-mcp`, `mcp-header` and `listen …` (see\n// docs/listen.md). Imported after the Node check, like everything else.\nconst { registerListenCommands } = await import(\"./listen/commands.ts\");\nregisterListenCommands(program);\n\n/**\n * The URL written into SKILL.md: --url, AGENTINBOX_URL, else the agentinbox running on this\n * machine (its lock file), else http://localhost:8425.\n */\nasync function serverUrl(flag: string | undefined): Promise<string> {\n let raw = flag ?? process.env.AGENTINBOX_URL;\n if (!raw) {\n const { runningInstance } = await import(\"./instance.ts\");\n const running = runningInstance(resolve(process.env.AGENTINBOX_DATA || join(homedir(), \".agentinbox\")));\n const host = running && ![\"0.0.0.0\", \"::\", \"\"].includes(running.host) ? running.host : \"localhost\";\n raw = `http://${host.includes(\":\") ? `[${host}]` : host}:${running?.port || process.env.PORT || 8425}`;\n }\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(`Invalid --url \"${raw}\". Use a full URL such as https://mail.example.com.`);\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") throw new Error(\"--url must start with http:// or https://\");\n return (url.origin + url.pathname).replace(/\\/+$/, \"\");\n}\n\n/** A known agent name, or something that is clearly a path; typos like \"claud\" are refused. */\nfunction checkSkillTarget(target: string, known: string[]): void {\n if (known.includes(target) || target.includes(\"/\") || target.includes(sep) || target.startsWith(\".\") || isDir(target)) return;\n const near = closest(target, known);\n throw new Error(\n `Unknown skill target \"${target}\".${near ? ` Did you mean ${near}?` : \"\"} Use ${known.join(\", \")}, or a directory path such as ./skills.`,\n );\n}\n\nfunction isDir(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction unknownCommand(name: string): string {\n const near = closest(name, [...program.commands.map((c) => c.name()), \"help\"]);\n return `unknown command '${name}'${near ? `\\n(Did you mean ${near}?)` : \"\"}\\nRun agentinbox --help for the list of commands.`;\n}\n\n/** The candidate within edit distance 2 (or a prefix match), if any. */\nfunction closest(word: string, candidates: string[]): string | null {\n const distance = (a: string, b: string) => {\n const row = Array.from({ length: b.length + 1 }, (_, j) => j);\n for (let i = 1; i <= a.length; i++) {\n let prev = row[0]!;\n row[0] = i;\n for (let j = 1; j <= b.length; j++) {\n const cur = row[j]!;\n row[j] = Math.min(row[j]! + 1, row[j - 1]! + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));\n prev = cur;\n }\n }\n return row[b.length]!;\n };\n let best: { name: string; d: number } | null = null;\n for (const c of candidates) {\n const d = c.startsWith(word) && word.length >= 3 ? 0 : distance(word.toLowerCase(), c);\n if (d <= 2 && (!best || d < best.d)) best = { name: c, d };\n }\n return best?.name ?? null;\n}\n\ntry {\n await program.parseAsync(process.argv);\n} catch (err) {\n console.error(pc.red(err instanceof Error ? err.message : String(err)));\n if (process.env.AGENTINBOX_DEBUG && err instanceof Error) console.error(err.stack);\n process.exit(1);\n}\n"],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+ import { n as EVENT_TYPES } from "./types-3eEsBFVj.js";
3
+ import { d as NPX } from "./profile-CgGgJ4Dm.js";
4
+ import { t as DEFAULT_PROFILE } from "./paths-TLp8hiMt.js";
5
+ import { a as recipesHelp, t as TOOLS } from "./recipes-UalOc_LB.js";
6
+ import { InvalidArgumentError } from "commander";
7
+ //#region server/listen/commands.ts
8
+ /** `agentinbox connect`, `agentinbox setup-mcp` and `agentinbox listen …`, all lazily loaded. */
9
+ /**
10
+ * Options of a `listen` subcommand plus any its ancestors parsed: `listen` declares the same flags
11
+ * (--profile, --exec…), and commander lets the parent claim them wherever they appear.
12
+ */
13
+ const all = (cmd) => cmd.optsWithGlobals();
14
+ const collect = (value, previous) => [...previous ?? [], value];
15
+ const int = (name) => (value) => {
16
+ if (!/^\d+$/.test(value)) throw new InvalidArgumentError(`${name} must be a whole number.`);
17
+ return Number(value);
18
+ };
19
+ const profileOption = (cmd) => cmd.option("--profile <name>", `profile to use (default "${DEFAULT_PROFILE}")`);
20
+ function listenFlags(cmd) {
21
+ return profileOption(cmd).option("--exec <command>", "run this command for each event (via /bin/sh; the event arrives on stdin as JSON, never in the command)").option("--forward <url>", "POST each event to this localhost URL, signed per Standard Webhooks").option("--types <list>", `comma-separated event types (${EVENT_TYPES.join(", ")})`).option("--from <glob>", "only senders matching this glob, e.g. \"*@stripe.com\" (repeatable)", collect).option("--to <glob>", "only when a to/cc address matches (repeatable)", collect).option("--label <id>", "only messages with this Gmail label id, e.g. INBOX or Label_123 (repeatable)", collect).option("--subject <regex>", "only subjects matching this case-insensitive regular expression").option("--include-body", "include the cleaned message body in each event").option("--no-include-body", "don't include bodies (overrides a saved setting)").option("--concurrency <n>", "events handled at once (default 2)", int("--concurrency")).option("--timeout <seconds>", "per-event time limit; the whole process group is killed after it (default 300)", int("--timeout")).option("--max-attempts <n>", "attempts before an event goes to dead letters (default 5)", int("--max-attempts")).option("--consumer <name>", "server-side cursor name (default <hostname>-<profile>)").option("--cwd <dir>", "directory --exec commands run in (default: a private dir in the state dir; must be yours and not group/world-writable)").option("--insecure", "allow plain http to a non-loopback, non-Tailscale daemon (loud warning)").option("--save", "save these settings to the profile");
22
+ }
23
+ function registerListenCommands(program) {
24
+ program.command("connect").description("pair this machine's agent with an agentinbox daemon (approve the code in the console)").argument("<daemon-url>", "the agentinbox daemon, e.g. https://mail.example.com or http://100.101.102.103:8425").option("--name <name>", "how the agent appears in the console (default \"<Tool> on <hostname>\")").option("--tool <tool>", `the agent's tool: ${TOOLS.join(", ")}; tailors the next steps`).option("--account <email>", "mailbox to request access to (repeatable; the approver decides)", collect).option("--capabilities <list>", "capabilities to request, e.g. read,draft (the approver decides)").option("--reason <text>", "why the agent needs access (shown to the approver)").option("--setup-mcp", "also register the MCP server in the chosen tool (the key is never shown)").option("--profile <name>", `profile to save the key in (default "${DEFAULT_PROFILE}")`).option("--consumer <name>", "listener cursor name (default <hostname>-<profile>)").option("--log <file>", "also write the output to this file (created 0600; for running connect in the background)").option("--insecure", "allow plain http to a non-loopback, non-Tailscale daemon (loud warning)").option("--force", "re-pair an existing profile (keeps its listener settings)").addHelpText("after", `
25
+ Exit codes: 0 approved, 2 denied, 3 expired, 4 already consumed, 130 cancelled, 1 error.
26
+
27
+ Example:
28
+ ${NPX} connect https://mail.example.com --tool claude-code --setup-mcp`).action(async (url, flags) => {
29
+ const { runConnect } = await import("./connect-BHC2trBi.js");
30
+ process.exit(await runConnect(url, flags, { handleSignals: true }));
31
+ });
32
+ profileOption(program.command("setup-mcp").description("register the agentinbox MCP server in your agent's tool, without showing the key")).option("--tool <tool>", `${TOOLS.filter((t) => t !== "other").join(", ")} (default: the tool given to connect)`).action(async (flags) => {
33
+ const { parseTool, printMcpResult } = await import("./connect-BHC2trBi.js");
34
+ const { loadProfile } = await import("./profile-RbN2FRz7.js");
35
+ const { setupMcp, mcpTarget } = await import("./mcpsetup-7KxroOCp.js");
36
+ const { profilePath } = await import("./paths-BCBp8vrG.js");
37
+ const name = flags.profile ?? "default";
38
+ const profile = loadProfile(name);
39
+ const tool = flags.tool ? parseTool(flags.tool) : profile.tool ?? "other";
40
+ if (tool === "other") throw new Error(`Which tool? Pass --tool ${TOOLS.filter((t) => t !== "other").join("|")}.`);
41
+ const { writeClaudeMcpConfig } = await import("./mcpheader-By2dIyZq.js");
42
+ writeClaudeMcpConfig(name, `${profile.url}/mcp`);
43
+ const r = setupMcp(tool, mcpTarget(name, profile), profilePath(name));
44
+ printMcpResult(r);
45
+ if (!r.ok) process.exitCode = 1;
46
+ });
47
+ profileOption(program.command("mcp-header").description("print the agent's Authorization header as JSON for an MCP client's header helper (refuses to print to a terminal)")).action(async (flags) => {
48
+ const { printMcpHeader } = await import("./mcpheader-By2dIyZq.js");
49
+ process.exitCode = printMcpHeader(flags.profile ?? "default");
50
+ });
51
+ const listen = listenFlags(program.command("listen").description("wake a local agent on new mail: hold an outbound stream to the daemon and run a command or POST to localhost per event")).option("--dry-run", "print matching events without running anything (uses a throwaway <consumer>-dry-run cursor)").option("--from-start", "a NEW consumer starts at the oldest stored event (no effect once it exists; backfill with --consumer <name>-backfill)").option("--print-recipe <tool>", `print the ready-made --exec command for ${TOOLS.join(", ")} (with this machine's paths) and exit`).addHelpText("after", recipesHelp()).action(async (flags) => {
52
+ if (flags.printRecipe !== void 0) {
53
+ const { parseTool } = await import("./connect-BHC2trBi.js");
54
+ const { recipe } = await import("./recipes-7d_Xws4M.js");
55
+ process.stdout.write(`${recipe(parseTool(flags.printRecipe), flags.profile ?? "default").exec}\n`);
56
+ return;
57
+ }
58
+ const { runListener } = await import("./listen-DT-omyD6.js");
59
+ process.exit(await runListener({
60
+ flags,
61
+ handleSignals: true
62
+ }));
63
+ });
64
+ profileOption(listen.command("status").description("show the listener's connection, cursor, recent events and dead letters")).action(async (_o, cmd) => {
65
+ const flags = all(cmd);
66
+ const { printStatus } = await import("./admin-CXkmdTYo.js");
67
+ await printStatus(flags.profile ?? "default");
68
+ });
69
+ profileOption(listen.command("secret").description("print the forward-mode signing secret (whsec_…) for your local receiver")).option("--rotate", "generate a new secret (restart the listener afterwards)").action(async (_o, cmd) => {
70
+ const flags = all(cmd);
71
+ const { printSecret } = await import("./admin-CXkmdTYo.js");
72
+ printSecret(flags.profile ?? "default", flags);
73
+ });
74
+ listenFlags(listen.command("dead").description("list events that failed every attempt; --replay runs them again")).option("--replay", "run dead events again (successes are removed)").option("--id <eventId>", "only this event (with --replay or --clear)").option("--clear", "delete dead events without running them").option("--json", "print raw JSON lines").action(async (_o, cmd) => {
75
+ const flags = all(cmd);
76
+ const admin = await import("./admin-CXkmdTYo.js");
77
+ const profile = flags.profile ?? "default";
78
+ if (flags.clear) admin.clearDead(profile, flags.id);
79
+ else if (flags.replay) {
80
+ if (await admin.replayDead(flags) > 0) process.exitCode = 1;
81
+ } else admin.printDead(profile, flags);
82
+ });
83
+ const service = listen.command("service").description("run the listener in the background (systemd user service / launchd agent, one per profile)");
84
+ listenFlags(service.command("install").description("install and start the listener service; flags are saved to the profile. Re-run to update")).action(async (_o, cmd) => {
85
+ const flags = all(cmd);
86
+ const { installListenService } = await import("./service-DYdD92CC.js");
87
+ await installListenService(flags);
88
+ });
89
+ profileOption(service.command("uninstall").description("stop and remove the listener service (keeps the profile)")).action(async (_o, cmd) => {
90
+ const flags = all(cmd);
91
+ const { uninstallListenService } = await import("./service-DYdD92CC.js");
92
+ uninstallListenService(flags.profile ?? "default");
93
+ });
94
+ profileOption(service.command("status").description("show the service manager's status for the listener")).action(async (_o, cmd) => {
95
+ const flags = all(cmd);
96
+ const { listenServiceStatus } = await import("./service-DYdD92CC.js");
97
+ listenServiceStatus(flags.profile ?? "default");
98
+ });
99
+ }
100
+ //#endregion
101
+ export { registerListenCommands };
102
+
103
+ //# sourceMappingURL=commands-DZ60j8_y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands-DZ60j8_y.js","names":[],"sources":["../server/listen/commands.ts"],"sourcesContent":["import { type Command, InvalidArgumentError } from \"commander\";\nimport { EVENT_TYPES } from \"../../shared/types.ts\";\nimport type { ConnectFlags } from \"./connect.ts\";\nimport type { ListenFlags } from \"./options.ts\";\nimport { DEFAULT_PROFILE } from \"./paths.ts\";\nimport { recipesHelp, TOOLS } from \"./recipes.ts\";\nimport { NPX } from \"./version.ts\";\n\n/** `agentinbox connect`, `agentinbox setup-mcp` and `agentinbox listen …`, all lazily loaded. */\n\n/**\n * Options of a `listen` subcommand plus any its ancestors parsed: `listen` declares the same flags\n * (--profile, --exec…), and commander lets the parent claim them wherever they appear.\n */\nconst all = <T>(cmd: Command): T => cmd.optsWithGlobals() as T;\n\nconst collect = (value: string, previous: string[] | undefined) => [...(previous ?? []), value];\n\nconst int = (name: string) => (value: string) => {\n if (!/^\\d+$/.test(value)) throw new InvalidArgumentError(`${name} must be a whole number.`);\n return Number(value);\n};\n\nconst profileOption = (cmd: Command) => cmd.option(\"--profile <name>\", `profile to use (default \"${DEFAULT_PROFILE}\")`);\n\nfunction listenFlags(cmd: Command): Command {\n return profileOption(cmd)\n .option(\"--exec <command>\", \"run this command for each event (via /bin/sh; the event arrives on stdin as JSON, never in the command)\")\n .option(\"--forward <url>\", \"POST each event to this localhost URL, signed per Standard Webhooks\")\n .option(\"--types <list>\", `comma-separated event types (${EVENT_TYPES.join(\", \")})`)\n .option(\"--from <glob>\", 'only senders matching this glob, e.g. \"*@stripe.com\" (repeatable)', collect)\n .option(\"--to <glob>\", \"only when a to/cc address matches (repeatable)\", collect)\n .option(\"--label <id>\", \"only messages with this Gmail label id, e.g. INBOX or Label_123 (repeatable)\", collect)\n .option(\"--subject <regex>\", \"only subjects matching this case-insensitive regular expression\")\n .option(\"--include-body\", \"include the cleaned message body in each event\")\n .option(\"--no-include-body\", \"don't include bodies (overrides a saved setting)\")\n .option(\"--concurrency <n>\", \"events handled at once (default 2)\", int(\"--concurrency\"))\n .option(\"--timeout <seconds>\", \"per-event time limit; the whole process group is killed after it (default 300)\", int(\"--timeout\"))\n .option(\"--max-attempts <n>\", \"attempts before an event goes to dead letters (default 5)\", int(\"--max-attempts\"))\n .option(\"--consumer <name>\", \"server-side cursor name (default <hostname>-<profile>)\")\n .option(\"--cwd <dir>\", \"directory --exec commands run in (default: a private dir in the state dir; must be yours and not group/world-writable)\")\n .option(\"--insecure\", \"allow plain http to a non-loopback, non-Tailscale daemon (loud warning)\")\n .option(\"--save\", \"save these settings to the profile\");\n}\n\nexport function registerListenCommands(program: Command): void {\n program\n .command(\"connect\")\n .description(\"pair this machine's agent with an agentinbox daemon (approve the code in the console)\")\n .argument(\"<daemon-url>\", \"the agentinbox daemon, e.g. https://mail.example.com or http://100.101.102.103:8425\")\n .option(\"--name <name>\", 'how the agent appears in the console (default \"<Tool> on <hostname>\")')\n .option(\"--tool <tool>\", `the agent's tool: ${TOOLS.join(\", \")}; tailors the next steps`)\n .option(\"--account <email>\", \"mailbox to request access to (repeatable; the approver decides)\", collect)\n .option(\"--capabilities <list>\", \"capabilities to request, e.g. read,draft (the approver decides)\")\n .option(\"--reason <text>\", \"why the agent needs access (shown to the approver)\")\n .option(\"--setup-mcp\", \"also register the MCP server in the chosen tool (the key is never shown)\")\n .option(\"--profile <name>\", `profile to save the key in (default \"${DEFAULT_PROFILE}\")`)\n .option(\"--consumer <name>\", \"listener cursor name (default <hostname>-<profile>)\")\n .option(\"--log <file>\", \"also write the output to this file (created 0600; for running connect in the background)\")\n .option(\"--insecure\", \"allow plain http to a non-loopback, non-Tailscale daemon (loud warning)\")\n .option(\"--force\", \"re-pair an existing profile (keeps its listener settings)\")\n .addHelpText(\n \"after\",\n `\nExit codes: 0 approved, 2 denied, 3 expired, 4 already consumed, 130 cancelled, 1 error.\n\nExample:\n ${NPX} connect https://mail.example.com --tool claude-code --setup-mcp`,\n )\n .action(async (url: string, flags: ConnectFlags) => {\n const { runConnect } = await import(\"./connect.ts\");\n process.exit(await runConnect(url, flags, { handleSignals: true }));\n });\n\n profileOption(program.command(\"setup-mcp\").description(\"register the agentinbox MCP server in your agent's tool, without showing the key\"))\n .option(\"--tool <tool>\", `${TOOLS.filter((t) => t !== \"other\").join(\", \")} (default: the tool given to connect)`)\n .action(async (flags: { tool?: string; profile?: string }) => {\n const { parseTool, printMcpResult } = await import(\"./connect.ts\");\n const { loadProfile } = await import(\"./profile.ts\");\n const { setupMcp, mcpTarget } = await import(\"./mcpsetup.ts\");\n const { profilePath } = await import(\"./paths.ts\");\n const name = flags.profile ?? DEFAULT_PROFILE;\n const profile = loadProfile(name);\n const tool = flags.tool ? parseTool(flags.tool) : (profile.tool ?? \"other\");\n if (tool === \"other\") throw new Error(`Which tool? Pass --tool ${TOOLS.filter((t) => t !== \"other\").join(\"|\")}.`);\n const { writeClaudeMcpConfig } = await import(\"./mcpheader.ts\");\n writeClaudeMcpConfig(name, `${profile.url}/mcp`);\n const r = setupMcp(tool, mcpTarget(name, profile), profilePath(name));\n printMcpResult(r);\n if (!r.ok) process.exitCode = 1;\n });\n\n profileOption(\n program.command(\"mcp-header\").description(\"print the agent's Authorization header as JSON for an MCP client's header helper (refuses to print to a terminal)\"),\n ).action(async (flags: { profile?: string }) => {\n const { printMcpHeader } = await import(\"./mcpheader.ts\");\n process.exitCode = printMcpHeader(flags.profile ?? DEFAULT_PROFILE);\n });\n\n const listen = listenFlags(\n program\n .command(\"listen\")\n .description(\"wake a local agent on new mail: hold an outbound stream to the daemon and run a command or POST to localhost per event\"),\n )\n .option(\"--dry-run\", \"print matching events without running anything (uses a throwaway <consumer>-dry-run cursor)\")\n .option(\"--from-start\", \"a NEW consumer starts at the oldest stored event (no effect once it exists; backfill with --consumer <name>-backfill)\")\n .option(\"--print-recipe <tool>\", `print the ready-made --exec command for ${TOOLS.join(\", \")} (with this machine's paths) and exit`)\n .addHelpText(\"after\", recipesHelp())\n .action(async (flags: ListenFlags & { printRecipe?: string }) => {\n if (flags.printRecipe !== undefined) {\n const { parseTool } = await import(\"./connect.ts\");\n const { recipe } = await import(\"./recipes.ts\");\n process.stdout.write(`${recipe(parseTool(flags.printRecipe), flags.profile ?? DEFAULT_PROFILE).exec}\\n`);\n return;\n }\n const { runListener } = await import(\"./listen.ts\");\n process.exit(await runListener({ flags, handleSignals: true }));\n });\n\n profileOption(listen.command(\"status\").description(\"show the listener's connection, cursor, recent events and dead letters\")).action(async (_o: unknown, cmd: Command) => {\n const flags = all<{ profile?: string }>(cmd);\n const { printStatus } = await import(\"./admin.ts\");\n await printStatus(flags.profile ?? DEFAULT_PROFILE);\n });\n\n profileOption(listen.command(\"secret\").description(\"print the forward-mode signing secret (whsec_…) for your local receiver\"))\n .option(\"--rotate\", \"generate a new secret (restart the listener afterwards)\")\n .action(async (_o: unknown, cmd: Command) => {\n const flags = all<{ profile?: string; rotate?: boolean }>(cmd);\n const { printSecret } = await import(\"./admin.ts\");\n printSecret(flags.profile ?? DEFAULT_PROFILE, flags);\n });\n\n listenFlags(listen.command(\"dead\").description(\"list events that failed every attempt; --replay runs them again\"))\n .option(\"--replay\", \"run dead events again (successes are removed)\")\n .option(\"--id <eventId>\", \"only this event (with --replay or --clear)\")\n .option(\"--clear\", \"delete dead events without running them\")\n .option(\"--json\", \"print raw JSON lines\")\n .action(async (_o: unknown, cmd: Command) => {\n const flags = all<ListenFlags & { replay?: boolean; id?: string; clear?: boolean; json?: boolean }>(cmd);\n const admin = await import(\"./admin.ts\");\n const profile = flags.profile ?? DEFAULT_PROFILE;\n if (flags.clear) admin.clearDead(profile, flags.id);\n else if (flags.replay) {\n if ((await admin.replayDead(flags)) > 0) process.exitCode = 1;\n } else admin.printDead(profile, flags);\n });\n\n const service = listen.command(\"service\").description(\"run the listener in the background (systemd user service / launchd agent, one per profile)\");\n listenFlags(service.command(\"install\").description(\"install and start the listener service; flags are saved to the profile. Re-run to update\")).action(\n async (_o: unknown, cmd: Command) => {\n const flags = all<ListenFlags>(cmd);\n const { installListenService } = await import(\"./service.ts\");\n await installListenService(flags);\n },\n );\n profileOption(service.command(\"uninstall\").description(\"stop and remove the listener service (keeps the profile)\")).action(async (_o: unknown, cmd: Command) => {\n const flags = all<{ profile?: string }>(cmd);\n const { uninstallListenService } = await import(\"./service.ts\");\n uninstallListenService(flags.profile ?? DEFAULT_PROFILE);\n });\n profileOption(service.command(\"status\").description(\"show the service manager's status for the listener\")).action(async (_o: unknown, cmd: Command) => {\n const flags = all<{ profile?: string }>(cmd);\n const { listenServiceStatus } = await import(\"./service.ts\");\n listenServiceStatus(flags.profile ?? DEFAULT_PROFILE);\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { n as loadConfig } from "./config-v975gxv4.js";
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import { chmodSync, mkdirSync, statSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ //#region server/config.ts
6
+ const DEFAULT_PORT = 8425;
7
+ function loadConfig(input = {}, version = "0.0.0") {
8
+ const dataDir = resolve(input.dataDir ?? process.env.AGENTINBOX_DATA ?? join(homedir(), ".agentinbox"));
9
+ mkdirSync(dataDir, {
10
+ recursive: true,
11
+ mode: 448
12
+ });
13
+ restrictDir(dataDir);
14
+ return {
15
+ dataDir,
16
+ dbPath: join(dataDir, "agentinbox.db"),
17
+ masterKeyPath: join(dataDir, "master.key"),
18
+ host: input.host ?? process.env.HOST ?? "0.0.0.0",
19
+ port: input.port ?? (process.env.PORT ? Number(process.env.PORT) : 8425),
20
+ dev: input.dev ?? false,
21
+ trustProxy: process.env.TRUST_PROXY === "1",
22
+ service: process.env.AGENTINBOX_SERVICE === "1",
23
+ version,
24
+ google: {
25
+ oauthBase: process.env.AGENTINBOX_GOOGLE_OAUTH_BASE ?? "https://oauth2.googleapis.com",
26
+ authBase: process.env.AGENTINBOX_GOOGLE_AUTH_BASE ?? "https://accounts.google.com",
27
+ gmailBase: process.env.AGENTINBOX_GOOGLE_GMAIL_BASE ?? "https://gmail.googleapis.com",
28
+ pubsubBase: process.env.AGENTINBOX_GOOGLE_PUBSUB_BASE ?? "https://pubsub.googleapis.com"
29
+ }
30
+ };
31
+ }
32
+ /** An existing data dir may be group/world accessible (umask, manual mkdir): tighten it to owner-only. */
33
+ function restrictDir(dir) {
34
+ try {
35
+ if (process.platform !== "win32" && statSync(dir).mode & 63) {
36
+ chmodSync(dir, 448);
37
+ process.stderr.write(`agentinbox: restricted ${dir} to owner-only access (it was readable by other users).\n`);
38
+ }
39
+ } catch {}
40
+ }
41
+ //#endregion
42
+ export { loadConfig as n, DEFAULT_PORT as t };
43
+
44
+ //# sourceMappingURL=config-v975gxv4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-v975gxv4.js","names":[],"sources":["../server/config.ts"],"sourcesContent":["import { chmodSync, mkdirSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\n\nexport interface Config {\n dataDir: string;\n dbPath: string;\n masterKeyPath: string;\n host: string;\n port: number;\n /** Dev mode: console served by Vite on :5173, server only serves /api, /mcp, /oauth. */\n dev: boolean;\n /** Honor X-Forwarded-For / X-Forwarded-Proto (behind a reverse proxy). */\n trustProxy: boolean;\n /** Running under `agentinbox service install` (set via AGENTINBOX_SERVICE=1 in the unit/plist). */\n service: boolean;\n version: string;\n /**\n * Base URLs for Google APIs. Overridable so tests can point at test/fake-google.ts.\n * Defaults: https://oauth2.googleapis.com, https://accounts.google.com, https://gmail.googleapis.com, https://pubsub.googleapis.com\n */\n google: { oauthBase: string; authBase: string; gmailBase: string; pubsubBase: string };\n}\n\nexport interface ConfigInput {\n dataDir?: string;\n host?: string;\n port?: number;\n dev?: boolean;\n}\n\nexport const DEFAULT_PORT = 8425;\n\nexport function loadConfig(input: ConfigInput = {}, version = \"0.0.0\"): Config {\n const dataDir = resolve(input.dataDir ?? process.env.AGENTINBOX_DATA ?? join(homedir(), \".agentinbox\"));\n mkdirSync(dataDir, { recursive: true, mode: 0o700 });\n restrictDir(dataDir);\n return {\n dataDir,\n dbPath: join(dataDir, \"agentinbox.db\"),\n masterKeyPath: join(dataDir, \"master.key\"),\n host: input.host ?? process.env.HOST ?? \"0.0.0.0\",\n port: input.port ?? (process.env.PORT ? Number(process.env.PORT) : DEFAULT_PORT),\n dev: input.dev ?? false,\n trustProxy: process.env.TRUST_PROXY === \"1\",\n service: process.env.AGENTINBOX_SERVICE === \"1\",\n version,\n google: {\n oauthBase: process.env.AGENTINBOX_GOOGLE_OAUTH_BASE ?? \"https://oauth2.googleapis.com\",\n authBase: process.env.AGENTINBOX_GOOGLE_AUTH_BASE ?? \"https://accounts.google.com\",\n gmailBase: process.env.AGENTINBOX_GOOGLE_GMAIL_BASE ?? \"https://gmail.googleapis.com\",\n pubsubBase: process.env.AGENTINBOX_GOOGLE_PUBSUB_BASE ?? \"https://pubsub.googleapis.com\",\n },\n };\n}\n\n/** An existing data dir may be group/world accessible (umask, manual mkdir): tighten it to owner-only. */\nfunction restrictDir(dir: string): void {\n try {\n if (process.platform !== \"win32\" && statSync(dir).mode & 0o077) {\n chmodSync(dir, 0o700);\n process.stderr.write(`agentinbox: restricted ${dir} to owner-only access (it was readable by other users).\\n`);\n }\n } catch {\n // Not ours to change; the files inside are still created owner-only.\n }\n}\n"],"mappings":";;;;;AA+BA,MAAa,eAAe;AAE5B,SAAgB,WAAW,QAAqB,CAAC,GAAG,UAAU,SAAiB;CAC7E,MAAM,UAAU,QAAQ,MAAM,WAAW,QAAQ,IAAI,mBAAmB,KAAK,QAAQ,GAAG,aAAa,CAAC;CACtG,UAAU,SAAS;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACnD,YAAY,OAAO;CACnB,OAAO;EACL;EACA,QAAQ,KAAK,SAAS,eAAe;EACrC,eAAe,KAAK,SAAS,YAAY;EACzC,MAAM,MAAM,QAAQ,QAAQ,IAAI,QAAQ;EACxC,MAAM,MAAM,SAAS,QAAQ,IAAI,OAAO,OAAO,QAAQ,IAAI,IAAI,IAAA;EAC/D,KAAK,MAAM,OAAO;EAClB,YAAY,QAAQ,IAAI,gBAAgB;EACxC,SAAS,QAAQ,IAAI,uBAAuB;EAC5C;EACA,QAAQ;GACN,WAAW,QAAQ,IAAI,gCAAgC;GACvD,UAAU,QAAQ,IAAI,+BAA+B;GACrD,WAAW,QAAQ,IAAI,gCAAgC;GACvD,YAAY,QAAQ,IAAI,iCAAiC;EAC3D;CACF;AACF;;AAGA,SAAS,YAAY,KAAmB;CACtC,IAAI;EACF,IAAI,QAAQ,aAAa,WAAW,SAAS,GAAG,CAAC,CAAC,OAAO,IAAO;GAC9D,UAAU,KAAK,GAAK;GACpB,QAAQ,OAAO,MAAM,0BAA0B,IAAI,0DAA0D;EAC/G;CACF,QAAQ,CAER;AACF"}