xaq-cli 0.1.0-nightly.91 → 0.1.0-nightly.93

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 (63) hide show
  1. package/dist/{admin-UKJE67mn.js → admin-5555baeh.js} +5 -5
  2. package/dist/{admin-UKJE67mn.js.map → admin-5555baeh.js.map} +1 -1
  3. package/dist/attachment-parser.js +43 -29
  4. package/dist/{cli-C9lES1ug.js → cli-Wp2ejVBT.js} +2 -2
  5. package/dist/{cli-C9lES1ug.js.map → cli-Wp2ejVBT.js.map} +1 -1
  6. package/dist/cli.js +8 -8
  7. package/dist/{commands-BKr8Ejx0.js → commands-CgmkYCYG.js} +2 -2
  8. package/dist/{commands-BKr8Ejx0.js.map → commands-CgmkYCYG.js.map} +1 -1
  9. package/dist/{commands-Bo6-7SBs.js → commands-DAd3ICss.js} +16 -16
  10. package/dist/{commands-Bo6-7SBs.js.map → commands-DAd3ICss.js.map} +1 -1
  11. package/dist/{connect-xUMBNHSZ.js → connect-CNECpz7N.js} +7 -7
  12. package/dist/{connect-xUMBNHSZ.js.map → connect-CNECpz7N.js.map} +1 -1
  13. package/dist/{hermes-Dic9rVz6.js → hermes-B_YoOADz.js} +3 -3
  14. package/dist/{hermes-Dic9rVz6.js.map → hermes-B_YoOADz.js.map} +1 -1
  15. package/dist/hermes-BwsRDFyI.js +3 -0
  16. package/dist/{import-cL9vj0jq.js → import-y1bjWoGq.js} +3 -3
  17. package/dist/{import-cL9vj0jq.js.map → import-y1bjWoGq.js.map} +1 -1
  18. package/dist/listen-B6TV_qUM.js +2 -0
  19. package/dist/{listen-C642JHIc.js → listen-DLZVpzxM.js} +4 -4
  20. package/dist/{listen-C642JHIc.js.map → listen-DLZVpzxM.js.map} +1 -1
  21. package/dist/{mcpheader-D94qF32t.js → mcpheader-C3EhpA7D.js} +58 -8
  22. package/dist/mcpheader-C3EhpA7D.js.map +1 -0
  23. package/dist/mcpheader-DOwvJTUJ.js +2 -0
  24. package/dist/{mcpsetup-xVCFyAEU.js → mcpsetup-ChBN1058.js} +3 -3
  25. package/dist/{mcpsetup-xVCFyAEU.js.map → mcpsetup-ChBN1058.js.map} +1 -1
  26. package/dist/mcpsetup-sfobhJnl.js +2 -0
  27. package/dist/{own-dirs-D61k1PPA.js → own-dirs-BEu3fcm3.js} +2 -2
  28. package/dist/{own-dirs-D61k1PPA.js.map → own-dirs-BEu3fcm3.js.map} +1 -1
  29. package/dist/recipes-BOhG7jcd.js +3 -0
  30. package/dist/{recipes-V8_-S1TM.js → recipes-BsCIHrrP.js} +2 -2
  31. package/dist/{recipes-V8_-S1TM.js.map → recipes-BsCIHrrP.js.map} +1 -1
  32. package/dist/{run-C0fpnYAb.js → run-zakbVgQR.js} +41 -11
  33. package/dist/run-zakbVgQR.js.map +1 -0
  34. package/dist/{server-DTxOHzZJ.js → server-0wxSpx6s.js} +15 -5
  35. package/dist/server-0wxSpx6s.js.map +1 -0
  36. package/dist/{service-CaLi0ECi.js → service-B-VzLRzc.js} +1 -1
  37. package/dist/{service-D5AMNljC.js → service-CjiEwfyC.js} +21 -276
  38. package/dist/service-CjiEwfyC.js.map +1 -0
  39. package/dist/{service-C2nRdQnH.js → service-Cp8j8469.js} +1 -1
  40. package/dist/{service-C-bf-vFa.js → service-e8sBNMSS.js} +6 -6
  41. package/dist/{service-C-bf-vFa.js.map → service-e8sBNMSS.js.map} +1 -1
  42. package/dist/service-node-BT7957Z9.js +289 -0
  43. package/dist/service-node-BT7957Z9.js.map +1 -0
  44. package/dist/{settings-CDJkgV6P.js → settings-BgxlT1bP.js} +2 -2
  45. package/dist/{settings-CDJkgV6P.js.map → settings-BgxlT1bP.js.map} +1 -1
  46. package/dist/settings-fUNWD1cQ.js +2 -0
  47. package/dist/skill/SKILL.md +3 -2
  48. package/dist/{smoke-Db1MSKwT.js → smoke-ZqEGwDQl.js} +64 -13
  49. package/dist/{smoke-Db1MSKwT.js.map → smoke-ZqEGwDQl.js.map} +1 -1
  50. package/dist/{spawn-CAmF_QFI.js → spawn-Dk6A4Bz4.js} +28 -4
  51. package/dist/spawn-Dk6A4Bz4.js.map +1 -0
  52. package/package.json +1 -1
  53. package/dist/hermes-DcrI-TtP.js +0 -3
  54. package/dist/listen-CcZUPf3p.js +0 -2
  55. package/dist/mcpheader-CK2dhg-I.js +0 -2
  56. package/dist/mcpheader-D94qF32t.js.map +0 -1
  57. package/dist/mcpsetup-C9mGhinf.js +0 -2
  58. package/dist/recipes-CwyZRyXM.js +0 -3
  59. package/dist/run-C0fpnYAb.js.map +0 -1
  60. package/dist/server-DTxOHzZJ.js.map +0 -1
  61. package/dist/service-D5AMNljC.js.map +0 -1
  62. package/dist/settings-DlwSOO5Y.js +0 -2
  63. package/dist/spawn-CAmF_QFI.js.map +0 -1
@@ -2,11 +2,11 @@
2
2
  import { t as NPX } from "./version-BxH7OuWD.js";
3
3
  import { i as saveProfile, n as loadProfile, v as ensurePrivateDir } from "./profile-QqEY5L3k.js";
4
4
  import { c as profilePath, i as deadPath, o as logPath, p as validProfileName } from "./paths-Bo6qtWNh.js";
5
- import { b as TransportError, f as isHermesRoute, x as checkTransport } from "./recipes-V8_-S1TM.js";
5
+ import { b as TransportError, f as isHermesRoute, x as checkTransport } from "./recipes-BsCIHrrP.js";
6
6
  import { i as safeText, r as formatDuration } from "./ui-B8Sd4Y7Y.js";
7
- import { D as ApiError, N as getSelf, b as forwardEvent, g as resolveListen, h as forwardSecret, m as describeFilter, o as routeWarning, u as DEFAULT_LISTEN_TYPES } from "./hermes-Dic9rVz6.js";
8
- import { c as readDead, d as runCommand, f as Dispatcher, i as outputLine, l as readStatus, m as hasAccess, p as describeGrants, s as pidAlive, u as writeDead } from "./listen-C642JHIc.js";
9
- import { r as installedServiceLabel } from "./service-C-bf-vFa.js";
7
+ import { D as ApiError, N as getSelf, b as forwardEvent, g as resolveListen, h as forwardSecret, m as describeFilter, o as routeWarning, u as DEFAULT_LISTEN_TYPES } from "./hermes-B_YoOADz.js";
8
+ import { c as readDead, d as runCommand, f as Dispatcher, i as outputLine, l as readStatus, m as hasAccess, p as describeGrants, s as pidAlive, u as writeDead } from "./listen-DLZVpzxM.js";
9
+ import { r as installedServiceLabel } from "./service-e8sBNMSS.js";
10
10
  import pc from "picocolors";
11
11
  //#region server/listen/admin.ts
12
12
  const out = (s = "") => process.stdout.write(`${s}\n`);
@@ -215,4 +215,4 @@ async function replayDead(flags, opts = {}) {
215
215
  //#endregion
216
216
  export { clearDead, printDead, printSecret, printStatus, replayDead };
217
217
 
218
- //# sourceMappingURL=admin-UKJE67mn.js.map
218
+ //# sourceMappingURL=admin-5555baeh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"admin-UKJE67mn.js","names":[],"sources":["../server/listen/admin.ts"],"sourcesContent":["import pc from \"picocolors\";\nimport { ApiError, getSelf } from \"./api.ts\";\nimport { describeGrants, hasAccess } from \"./grants.ts\";\nimport { Dispatcher, type Job, type Target } from \"./dispatcher.ts\";\nimport { runCommand } from \"./exec.ts\";\nimport { ensurePrivateDir } from \"./fsutil.ts\";\nimport { forwardEvent } from \"./forward.ts\";\nimport { routeWarning } from \"./hermes.ts\";\nimport { describeFilter, type ListenFlags, MAX_BATCH, resolveListen } from \"./options.ts\";\nimport { outputLine } from \"./listen.ts\";\nimport { isHermesRoute } from \"./recipes.ts\";\nimport { DEFAULT_PROFILE, deadPath, logPath, profilePath, validProfileName } from \"./paths.ts\";\nimport { loadProfile, saveProfile } from \"./profile.ts\";\nimport { DEFAULT_LISTEN_TYPES, forwardSecret } from \"./options.ts\";\nimport { installedServiceLabel } from \"./service.ts\";\nimport { type DeadEntry, pidAlive, readDead, readStatus, writeDead } from \"./state.ts\";\nimport { checkTransport, TransportError } from \"./transport.ts\";\nimport { formatDuration, safeText } from \"./ui.ts\";\nimport { NPX } from \"./version.ts\";\n\nconst out = (s = \"\") => process.stdout.write(`${s}\\n`);\nconst flagFor = (profile: string) => (profile === DEFAULT_PROFILE ? \"\" : ` --profile ${profile}`);\nconst ago = (iso: string | null | undefined) => (iso ? `${formatDuration(Date.now() - Date.parse(iso))} ago` : \"never\");\n\n// ───────────── listen secret ─────────────\n\n/** Prints the forward-mode signing secret (generating and saving it on first use). */\nexport function printSecret(profileName: string, opts: { rotate?: boolean }): void {\n const name = validProfileName(profileName);\n const profile = loadProfile(name);\n let { secret, created } = forwardSecret(profile);\n if (opts.rotate) {\n secret = forwardSecret({ ...profile, forward: undefined, forwardSecret: undefined }).secret;\n created = true;\n }\n if (created) {\n if (profile.forward) profile.forward = { ...profile.forward, secret };\n else profile.forwardSecret = secret;\n saveProfile(name, profile);\n process.stderr.write(pc.gray(`${opts.rotate ? \"Rotated\" : \"Generated\"} the forward signing secret for profile \"${name}\"${opts.rotate ? \"; restart the listener\" : \"\"}.\\n`));\n }\n // The secret alone on stdout, so `export XAQ_WEBHOOK_SECRET=$(xaq listen secret)` works.\n out(secret);\n}\n\n// ───────────── listen status ─────────────\n\nexport async function printStatus(profileName: string): Promise<void> {\n const name = validProfileName(profileName);\n const profile = loadProfile(name);\n const st = readStatus(name);\n const alive = st ? pidAlive(st.pid) && !st.stoppedAt : false;\n const dead = readDead(name);\n\n out(`${pc.bold(\"Profile\")} ${name} ${pc.gray(profilePath(name))}`);\n out(`${pc.bold(\"Agent\")} ${safeText(profile.agentName)} ${pc.gray(`(${profile.agentId})`)}`);\n out(`${pc.bold(\"Daemon\")} ${profile.url}`);\n out(`${pc.bold(\"Consumer\")} ${st?.consumer ?? profile.consumer}`);\n // What the last run used (flags included), else what the profile has saved.\n const exec = st && st.exec !== undefined ? st.exec : profile.exec;\n const forward = st && st.forward !== undefined ? st.forward : profile.forward?.url;\n const targets = [exec ? `exec ${safeText(exec, 120)}` : null, forward ? `forward ${forward}` : null].filter(Boolean);\n out(`${pc.bold(\"Runs\")} ${targets.length ? targets.join(\" · \") : pc.gray(\"nothing saved (pass --exec/--forward with --save)\")}`);\n out(\n `${pc.bold(\"Events\")} ${\n profile.types?.length\n ? profile.types.join(\", \")\n : `${DEFAULT_LISTEN_TYPES.join(\", \")} ${pc.gray(\"(default types; add --types all --save for every event)\")}`\n }`,\n );\n // The running listener's filters (flags included), else the profile's.\n let saved: ReturnType<typeof resolveListen>[\"config\"] | null = null;\n try {\n saved = resolveListen(name, profile, {}).config;\n } catch {\n // An invalid saved setting: `listen` itself reports it.\n }\n out(`${pc.bold(\"Filters\")} ${(alive && st?.filters) || (saved ? describeFilter(saved) : \"?\")}`);\n const batchSeconds = alive && st && st.batchSeconds !== undefined ? st.batchSeconds : (saved?.batchSeconds ?? 0);\n if (batchSeconds) out(`${pc.bold(\"Batch\")} ${formatDuration(batchSeconds * 1000)} window, up to ${MAX_BATCH} events per --exec run`);\n const route = forward && isHermesRoute(forward) ? routeWarning(name, forward) : null;\n if (route?.level === \"warn\") out(`${pc.bold(\"Route\")} ${pc.yellow(route.message)}`);\n out(`${pc.bold(\"Output\")} ${outputLine(name, { exec: exec ?? null, forward: forward ? { url: forward } : null }, logPath(name))}`);\n\n let svc = \"not installed\";\n if (process.platform === \"linux\" || process.platform === \"darwin\") {\n const installed = installedServiceLabel(name);\n if (installed) svc = `installed (${installed}; details: ${NPX} listen service status${flagFor(name)})`;\n }\n out(`${pc.bold(\"Service\")} ${svc}`);\n\n try {\n // Never send the key over a connection `listen` itself would refuse.\n await checkTransport(profile.url, { insecure: profile.insecure });\n const self = await getSelf(profile.url, profile.apiKey, 5000);\n out(`${pc.bold(\"Key\")} ${pc.green(\"valid\")}`);\n // The access the console actually granted, in plain words (what the agent should report to its human).\n const lines = describeGrants(self.grants, self.connections);\n out(`${pc.bold(\"Access\")} ${hasAccess(self) ? lines[0] : pc.yellow(lines[0]!)}`);\n for (const l of lines.slice(1)) out(` ${l}`);\n } catch (err) {\n const revoked = err instanceof ApiError && (err.status === 401 || err.status === 403);\n const why = err instanceof ApiError && err.status === 0 ? (err.cause as string) : err instanceof Error ? err.message.split(\"\\n\")[0] : String(err);\n if (err instanceof TransportError) out(`${pc.bold(\"Key\")} ${pc.red(`not checked: ${why}`)}`);\n else out(`${pc.bold(\"Key\")} ${revoked ? pc.red(`rejected: ${(err as Error).message}`) : pc.yellow(`not checked (${why})`)}`);\n }\n\n if (!st) {\n out(`${pc.bold(\"Listener\")} ${pc.gray(\"has not run yet\")}`);\n } else {\n const state = !alive\n ? pc.gray(`not running${st.stoppedAt ? ` (stopped ${ago(st.stoppedAt)}${st.exitReason ? `: ${safeText(st.exitReason, 160)}` : \"\"})` : \" (exited unexpectedly)\"}`)\n : st.connected\n ? pc.green(`running (pid ${st.pid}), connected ${ago(st.connectedSince)}`)\n : pc.yellow(`running (pid ${st.pid}), disconnected${st.lastDisconnect ? ` ${ago(st.lastDisconnect.at)}: ${safeText(st.lastDisconnect.reason, 160)}` : \"\"}`);\n out(`${pc.bold(\"Listener\")} ${state}`);\n out(\n `${pc.bold(\"Cursor\")} acked #${st.ackedSeq} · received #${st.receivedSeq}${alive && st.running ? ` · ${st.running} in progress` : \"\"}${st.skipped ? ` · ${st.skipped} skipped by filters` : \"\"} · last event ${ago(st.lastEventAt)}`,\n );\n if (st.recent.length) {\n out(pc.bold(\"Recent\"));\n for (const r of st.recent.slice(0, 8)) {\n const icon = r.outcome === \"succeeded\" ? pc.green(\"✓\") : r.outcome === \"dead\" ? pc.red(\"✖\") : r.outcome === \"retrying\" ? pc.yellow(\"↻\") : pc.gray(\"·\");\n out(` ${icon} #${r.seq} ${r.type} ${pc.gray(`${ago(r.at)}${r.attempts > 1 ? `, ${r.attempts} attempts` : \"\"}${r.error && r.outcome !== \"succeeded\" ? `, ${safeText(r.error, 100)}` : \"\"}`)}`);\n }\n }\n }\n out(`${pc.bold(\"Dead\")} ${dead.length ? pc.red(`${dead.length} event${dead.length > 1 ? \"s\" : \"\"}`) + pc.gray(` (inspect: ${NPX} listen dead${flagFor(name)})`) : \"0\"}`);\n out(`${pc.bold(\"Log\")} ${logPath(name)}`);\n}\n\n// ───────────── listen dead ─────────────\n\nexport function printDead(profileName: string, opts: { json?: boolean }): void {\n const name = validProfileName(profileName);\n const entries = readDead(name);\n if (opts.json) {\n for (const e of entries) out(JSON.stringify(e));\n return;\n }\n if (!entries.length) {\n out(`No dead events for profile \"${name}\".`);\n return;\n }\n for (const e of entries) {\n const m = e.payload.data.message;\n out(`${pc.red(\"✖\")} #${e.seq} ${e.type} ${pc.gray(e.id)} ${pc.gray(`died ${ago(e.deadAt)} after ${e.attempts} attempts`)}`);\n if (m) out(` from ${safeText(m.from?.email ?? \"?\", 80)} \"${safeText(m.subject, 80)}\"`);\n out(pc.gray(` ${safeText(e.error, 300)}`));\n }\n out();\n out(pc.gray(`${entries.length} dead event${entries.length > 1 ? \"s\" : \"\"} in ${deadPath(name)}.`));\n out(pc.gray(`Replay with: ${NPX} listen dead --replay${flagFor(name)} (one: --id <evt_…>; drop all: --clear)`));\n out(pc.gray(`Replay uses the profile's saved --exec/--forward; if you ran them as flags only, pass them again, e.g. --replay --exec '<command>'.`));\n}\n\nexport function clearDead(profileName: string, id?: string): void {\n const name = validProfileName(profileName);\n const entries = readDead(name);\n const keep = id ? entries.filter((e) => e.id !== id) : [];\n writeDead(name, keep);\n out(`Removed ${entries.length - keep.length} dead event${entries.length - keep.length === 1 ? \"\" : \"s\"}.`);\n}\n\n/**\n * Runs dead events again through the profile's targets (or the ones passed as flags). Successes are\n * removed from dead.jsonl; failures stay. Returns how many still failed.\n */\nexport async function replayDead(flags: ListenFlags & { id?: string }, opts: { retryDelayMs?: (n: number) => number } = {}): Promise<number> {\n const name = validProfileName(flags.profile ?? DEFAULT_PROFILE);\n const profile = loadProfile(name);\n const { config } = resolveListen(name, profile, { ...flags, save: false });\n const all = readDead(name);\n const chosen = all.filter((e) => !flags.id || e.id === flags.id);\n if (!chosen.length) {\n out(flags.id ? `No dead event with id ${flags.id}.` : `No dead events for profile \"${name}\".`);\n return 0;\n }\n const available: Target[] = [];\n if (config.exec) available.push(\"exec\");\n if (config.forward) available.push(\"forward\");\n if (!available.length) throw new Error(`Nothing to replay to: pass --recipe <tool> (claude-code, codex, cursor, hermes), --exec '<command>' and/or --forward <url> (or save them in the profile with \\`${NPX} listen --save …\\`).`);\n if (config.exec && process.platform === \"win32\") throw new Error(\"--exec isn't supported on Windows; replay with --forward instead.\");\n\n const timeoutMs = config.timeoutSeconds * 1000;\n if (config.cwdIsDefault) ensurePrivateDir(config.cwd);\n const failed = new Map<string, DeadEntry>();\n const bySeq = new Map(chosen.map((e) => [e.seq, e]));\n const dispatcher = new Dispatcher({\n targets: available,\n concurrency: config.concurrency,\n maxAttempts: config.maxAttempts,\n retryDelayMs: opts.retryDelayMs,\n run: async (job: Job, signal) => {\n const errors: string[] = [];\n if (job.remaining.has(\"exec\") && config.exec) {\n const r = await runCommand(config.exec, job.payload, { timeoutMs, signal, cwd: config.cwd });\n if (r.ok) job.remaining.delete(\"exec\");\n else errors.push(`command: ${r.error}`);\n }\n if (job.remaining.has(\"forward\") && config.forward) {\n const r = await forwardEvent(config.forward.url, config.forward.secret, job.payload, { timeoutMs: Math.min(timeoutMs, 30_000), signal, wrap: isHermesRoute(config.forward.url) });\n if (r.ok) job.remaining.delete(\"forward\");\n else errors.push(`forward: ${r.error}`);\n }\n return errors;\n },\n onAdvance: () => {},\n onDead: (job) => {\n const e = bySeq.get(job.seq)!;\n failed.set(e.id, { ...e, deadAt: new Date().toISOString(), attempts: e.attempts + job.attempts, error: job.lastError ?? \"failed\" });\n out(`${pc.red(\"✖\")} #${job.seq} still failing: ${safeText(job.lastError ?? \"failed\", 200)}`);\n },\n onFinished: (job) => {\n if (job.outcome === \"succeeded\") out(`${pc.green(\"✓\")} #${job.seq} ${job.payload.type} replayed`);\n },\n });\n out(`Replaying ${chosen.length} dead event${chosen.length > 1 ? \"s\" : \"\"} (up to ${config.maxAttempts} attempts each)…`);\n // Only the targets that never succeeded (a forward that went through isn't POSTed again).\n for (const e of [...chosen].sort((a, b) => a.seq - b.seq)) {\n const targets = e.targets?.length ? e.targets.filter((t) => available.includes(t)) : available;\n if (!targets.length) {\n out(`${pc.yellow(\"!\")} #${e.seq} needs ${e.targets.join(\" and \")}, which isn't configured now; pass --${e.targets[0]} to replay it.`);\n failed.set(e.id, e);\n continue;\n }\n dispatcher.push(e.payload, { targets });\n }\n await dispatcher.idle();\n\n const replayed = new Set(chosen.map((e) => e.id));\n // Re-read so entries added by a running listener meanwhile are kept.\n const current = readDead(name);\n writeDead(name, [...current.filter((e) => !replayed.has(e.id)), ...failed.values()]);\n out(`${chosen.length - failed.size} replayed, ${failed.size} still dead.`);\n return failed.size;\n}\n"],"mappings":";;;;;;;;;;;AAoBA,MAAM,OAAO,IAAI,OAAO,QAAQ,OAAO,MAAM,GAAG,EAAE,GAAG;AACrD,MAAM,WAAW,YAAqB,YAAA,YAA8B,KAAK,cAAc;AACvF,MAAM,OAAO,QAAoC,MAAM,GAAG,eAAe,KAAK,IAAI,IAAI,KAAK,MAAM,GAAG,CAAC,EAAE,QAAQ;;AAK/G,SAAgB,YAAY,aAAqB,MAAkC;CACjF,MAAM,OAAO,iBAAiB,WAAW;CACzC,MAAM,UAAU,YAAY,IAAI;CAChC,IAAI,EAAE,QAAQ,YAAY,cAAc,OAAO;CAC/C,IAAI,KAAK,QAAQ;EACf,SAAS,cAAc;GAAE,GAAG;GAAS,SAAS,KAAA;GAAW,eAAe,KAAA;EAAU,CAAC,CAAC,CAAC;EACrF,UAAU;CACZ;CACA,IAAI,SAAS;EACX,IAAI,QAAQ,SAAS,QAAQ,UAAU;GAAE,GAAG,QAAQ;GAAS;EAAO;OAC/D,QAAQ,gBAAgB;EAC7B,YAAY,MAAM,OAAO;EACzB,QAAQ,OAAO,MAAM,GAAG,KAAK,GAAG,KAAK,SAAS,YAAY,YAAY,2CAA2C,KAAK,GAAG,KAAK,SAAS,2BAA2B,GAAG,IAAI,CAAC;CAC5K;CAEA,IAAI,MAAM;AACZ;AAIA,eAAsB,YAAY,aAAoC;CACpE,MAAM,OAAO,iBAAiB,WAAW;CACzC,MAAM,UAAU,YAAY,IAAI;CAChC,MAAM,KAAK,WAAW,IAAI;CAC1B,MAAM,QAAQ,KAAK,SAAS,GAAG,GAAG,KAAK,CAAC,GAAG,YAAY;CACvD,MAAM,OAAO,SAAS,IAAI;CAE1B,IAAI,GAAG,GAAG,KAAK,SAAS,EAAE,KAAK,KAAK,IAAI,GAAG,KAAK,YAAY,IAAI,CAAC,GAAG;CACpE,IAAI,GAAG,GAAG,KAAK,OAAO,EAAE,OAAO,SAAS,QAAQ,SAAS,EAAE,GAAG,GAAG,KAAK,IAAI,QAAQ,QAAQ,EAAE,GAAG;CAC/F,IAAI,GAAG,GAAG,KAAK,QAAQ,EAAE,MAAM,QAAQ,KAAK;CAC5C,IAAI,GAAG,GAAG,KAAK,UAAU,EAAE,IAAI,IAAI,YAAY,QAAQ,UAAU;CAEjE,MAAM,OAAO,MAAM,GAAG,SAAS,KAAA,IAAY,GAAG,OAAO,QAAQ;CAC7D,MAAM,UAAU,MAAM,GAAG,YAAY,KAAA,IAAY,GAAG,UAAU,QAAQ,SAAS;CAC/E,MAAM,UAAU,CAAC,OAAO,QAAQ,SAAS,MAAM,GAAG,MAAM,MAAM,UAAU,WAAW,YAAY,IAAI,CAAC,CAAC,OAAO,OAAO;CACnH,IAAI,GAAG,GAAG,KAAK,MAAM,EAAE,QAAQ,QAAQ,SAAS,QAAQ,KAAK,KAAK,IAAI,GAAG,KAAK,mDAAmD,GAAG;CACpI,IACE,GAAG,GAAG,KAAK,QAAQ,EAAE,MACnB,QAAQ,OAAO,SACX,QAAQ,MAAM,KAAK,IAAI,IACvB,GAAG,qBAAqB,KAAK,IAAI,EAAE,GAAG,GAAG,KAAK,yDAAyD,KAE/G;CAEA,IAAI,QAA2D;CAC/D,IAAI;EACF,QAAQ,cAAc,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC;CAC3C,QAAQ,CAER;CACA,IAAI,GAAG,GAAG,KAAK,SAAS,EAAE,KAAM,SAAS,IAAI,YAAa,QAAQ,eAAe,KAAK,IAAI,MAAM;CAChG,MAAM,eAAe,SAAS,MAAM,GAAG,iBAAiB,KAAA,IAAY,GAAG,eAAgB,OAAO,gBAAgB;CAC9G,IAAI,cAAc,IAAI,GAAG,GAAG,KAAK,OAAO,EAAE,OAAO,eAAe,eAAe,GAAI,EAAE,wCAAkD;CACvI,MAAM,QAAQ,WAAW,cAAc,OAAO,IAAI,aAAa,MAAM,OAAO,IAAI;CAChF,IAAI,OAAO,UAAU,QAAQ,IAAI,GAAG,GAAG,KAAK,OAAO,EAAE,OAAO,GAAG,OAAO,MAAM,OAAO,GAAG;CACtF,IAAI,GAAG,GAAG,KAAK,QAAQ,EAAE,MAAM,WAAW,MAAM;EAAE,MAAM,QAAQ;EAAM,SAAS,UAAU,EAAE,KAAK,QAAQ,IAAI;CAAK,GAAG,QAAQ,IAAI,CAAC,GAAG;CAEpI,IAAI,MAAM;CACV,IAAI,QAAQ,aAAa,WAAW,QAAQ,aAAa,UAAU;EACjE,MAAM,YAAY,sBAAsB,IAAI;EAC5C,IAAI,WAAW,MAAM,cAAc,UAAU,aAAa,IAAI,wBAAwB,QAAQ,IAAI,EAAE;CACtG;CACA,IAAI,GAAG,GAAG,KAAK,SAAS,EAAE,KAAK,KAAK;CAEpC,IAAI;EAEF,MAAM,eAAe,QAAQ,KAAK,EAAE,UAAU,QAAQ,SAAS,CAAC;EAChE,MAAM,OAAO,MAAM,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,GAAI;EAC5D,IAAI,GAAG,GAAG,KAAK,KAAK,EAAE,SAAS,GAAG,MAAM,OAAO,GAAG;EAElD,MAAM,QAAQ,eAAe,KAAK,QAAQ,KAAK,WAAW;EAC1D,IAAI,GAAG,GAAG,KAAK,QAAQ,EAAE,MAAM,UAAU,IAAI,IAAI,MAAM,KAAK,GAAG,OAAO,MAAM,EAAG,GAAG;EAClF,KAAK,MAAM,KAAK,MAAM,MAAM,CAAC,GAAG,IAAI,aAAa,GAAG;CACtD,SAAS,KAAK;EACZ,MAAM,UAAU,eAAe,aAAa,IAAI,WAAW,OAAO,IAAI,WAAW;EACjF,MAAM,MAAM,eAAe,YAAY,IAAI,WAAW,IAAK,IAAI,QAAmB,eAAe,QAAQ,IAAI,QAAQ,MAAM,IAAI,CAAC,CAAC,KAAK,OAAO,GAAG;EAChJ,IAAI,eAAe,gBAAgB,IAAI,GAAG,GAAG,KAAK,KAAK,EAAE,SAAS,GAAG,IAAI,gBAAgB,KAAK,GAAG;OAC5F,IAAI,GAAG,GAAG,KAAK,KAAK,EAAE,SAAS,UAAU,GAAG,IAAI,aAAc,IAAc,SAAS,IAAI,GAAG,OAAO,gBAAgB,IAAI,EAAE,GAAG;CACnI;CAEA,IAAI,CAAC,IACH,IAAI,GAAG,GAAG,KAAK,UAAU,EAAE,IAAI,GAAG,KAAK,iBAAiB,GAAG;MACtD;EACL,MAAM,QAAQ,CAAC,QACX,GAAG,KAAK,cAAc,GAAG,YAAY,aAAa,IAAI,GAAG,SAAS,IAAI,GAAG,aAAa,KAAK,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,KAAK,0BAA0B,IAC9J,GAAG,YACD,GAAG,MAAM,gBAAgB,GAAG,IAAI,eAAe,IAAI,GAAG,cAAc,GAAG,IACvE,GAAG,OAAO,gBAAgB,GAAG,IAAI,iBAAiB,GAAG,iBAAiB,IAAI,IAAI,GAAG,eAAe,EAAE,EAAE,IAAI,SAAS,GAAG,eAAe,QAAQ,GAAG,MAAM,IAAI;EAC9J,IAAI,GAAG,GAAG,KAAK,UAAU,EAAE,IAAI,OAAO;EACtC,IACE,GAAG,GAAG,KAAK,QAAQ,EAAE,aAAa,GAAG,SAAS,eAAe,GAAG,cAAc,SAAS,GAAG,UAAU,MAAM,GAAG,QAAQ,gBAAgB,KAAK,GAAG,UAAU,MAAM,GAAG,QAAQ,uBAAuB,GAAG,gBAAgB,IAAI,GAAG,WAAW,GACtO;EACA,IAAI,GAAG,OAAO,QAAQ;GACpB,IAAI,GAAG,KAAK,QAAQ,CAAC;GACrB,KAAK,MAAM,KAAK,GAAG,OAAO,MAAM,GAAG,CAAC,GAAG;IACrC,MAAM,OAAO,EAAE,YAAY,cAAc,GAAG,MAAM,GAAG,IAAI,EAAE,YAAY,SAAS,GAAG,IAAI,GAAG,IAAI,EAAE,YAAY,aAAa,GAAG,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG;IACrJ,IAAI,KAAK,KAAK,IAAI,EAAE,IAAI,GAAG,EAAE,KAAK,GAAG,GAAG,KAAK,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE,SAAS,aAAa,KAAK,EAAE,SAAS,EAAE,YAAY,cAAc,KAAK,SAAS,EAAE,OAAO,GAAG,MAAM,IAAI,GAAG;GAC/L;EACF;CACF;CACA,IAAI,GAAG,GAAG,KAAK,MAAM,EAAE,QAAQ,KAAK,SAAS,GAAG,IAAI,GAAG,KAAK,OAAO,QAAQ,KAAK,SAAS,IAAI,MAAM,IAAI,IAAI,GAAG,KAAK,cAAc,IAAI,cAAc,QAAQ,IAAI,EAAE,EAAE,IAAI,KAAK;CAC5K,IAAI,GAAG,GAAG,KAAK,KAAK,EAAE,SAAS,QAAQ,IAAI,GAAG;AAChD;AAIA,SAAgB,UAAU,aAAqB,MAAgC;CAC7E,MAAM,OAAO,iBAAiB,WAAW;CACzC,MAAM,UAAU,SAAS,IAAI;CAC7B,IAAI,KAAK,MAAM;EACb,KAAK,MAAM,KAAK,SAAS,IAAI,KAAK,UAAU,CAAC,CAAC;EAC9C;CACF;CACA,IAAI,CAAC,QAAQ,QAAQ;EACnB,IAAI,+BAA+B,KAAK,GAAG;EAC3C;CACF;CACA,KAAK,MAAM,KAAK,SAAS;EACvB,MAAM,IAAI,EAAE,QAAQ,KAAK;EACzB,IAAI,GAAG,GAAG,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,KAAK,GAAG,GAAG,KAAK,EAAE,EAAE,EAAE,IAAI,GAAG,KAAK,QAAQ,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,UAAU,GAAG;EAC3H,IAAI,GAAG,IAAI,YAAY,SAAS,EAAE,MAAM,SAAS,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE;EACzF,IAAI,GAAG,KAAK,OAAO,SAAS,EAAE,OAAO,GAAG,GAAG,CAAC;CAC9C;CACA,IAAI;CACJ,IAAI,GAAG,KAAK,GAAG,QAAQ,OAAO,aAAa,QAAQ,SAAS,IAAI,MAAM,GAAG,MAAM,SAAS,IAAI,EAAE,EAAE,CAAC;CACjG,IAAI,GAAG,KAAK,gBAAgB,IAAI,uBAAuB,QAAQ,IAAI,EAAE,0CAA0C,CAAC;CAChH,IAAI,GAAG,KAAK,qIAAqI,CAAC;AACpJ;AAEA,SAAgB,UAAU,aAAqB,IAAmB;CAChE,MAAM,OAAO,iBAAiB,WAAW;CACzC,MAAM,UAAU,SAAS,IAAI;CAC7B,MAAM,OAAO,KAAK,QAAQ,QAAQ,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;CACxD,UAAU,MAAM,IAAI;CACpB,IAAI,WAAW,QAAQ,SAAS,KAAK,OAAO,aAAa,QAAQ,SAAS,KAAK,WAAW,IAAI,KAAK,IAAI,EAAE;AAC3G;;;;;AAMA,eAAsB,WAAW,OAAsC,OAAiD,CAAC,GAAoB;CAC3I,MAAM,OAAO,iBAAiB,MAAM,WAAA,SAA0B;CAC9D,MAAM,UAAU,YAAY,IAAI;CAChC,MAAM,EAAE,WAAW,cAAc,MAAM,SAAS;EAAE,GAAG;EAAO,MAAM;CAAM,CAAC;CAEzE,MAAM,SADM,SAAS,IACJ,CAAC,CAAC,QAAQ,MAAM,CAAC,MAAM,MAAM,EAAE,OAAO,MAAM,EAAE;CAC/D,IAAI,CAAC,OAAO,QAAQ;EAClB,IAAI,MAAM,KAAK,yBAAyB,MAAM,GAAG,KAAK,+BAA+B,KAAK,GAAG;EAC7F,OAAO;CACT;CACA,MAAM,YAAsB,CAAC;CAC7B,IAAI,OAAO,MAAM,UAAU,KAAK,MAAM;CACtC,IAAI,OAAO,SAAS,UAAU,KAAK,SAAS;CAC5C,IAAI,CAAC,UAAU,QAAQ,MAAM,IAAI,MAAM,kKAAkK,IAAI,qBAAqB;CAClO,IAAI,OAAO,QAAQ,QAAQ,aAAa,SAAS,MAAM,IAAI,MAAM,mEAAmE;CAEpI,MAAM,YAAY,OAAO,iBAAiB;CAC1C,IAAI,OAAO,cAAc,iBAAiB,OAAO,GAAG;CACpD,MAAM,yBAAS,IAAI,IAAuB;CAC1C,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;CACnD,MAAM,aAAa,IAAI,WAAW;EAChC,SAAS;EACT,aAAa,OAAO;EACpB,aAAa,OAAO;EACpB,cAAc,KAAK;EACnB,KAAK,OAAO,KAAU,WAAW;GAC/B,MAAM,SAAmB,CAAC;GAC1B,IAAI,IAAI,UAAU,IAAI,MAAM,KAAK,OAAO,MAAM;IAC5C,MAAM,IAAI,MAAM,WAAW,OAAO,MAAM,IAAI,SAAS;KAAE;KAAW;KAAQ,KAAK,OAAO;IAAI,CAAC;IAC3F,IAAI,EAAE,IAAI,IAAI,UAAU,OAAO,MAAM;SAChC,OAAO,KAAK,YAAY,EAAE,OAAO;GACxC;GACA,IAAI,IAAI,UAAU,IAAI,SAAS,KAAK,OAAO,SAAS;IAClD,MAAM,IAAI,MAAM,aAAa,OAAO,QAAQ,KAAK,OAAO,QAAQ,QAAQ,IAAI,SAAS;KAAE,WAAW,KAAK,IAAI,WAAW,GAAM;KAAG;KAAQ,MAAM,cAAc,OAAO,QAAQ,GAAG;IAAE,CAAC;IAChL,IAAI,EAAE,IAAI,IAAI,UAAU,OAAO,SAAS;SACnC,OAAO,KAAK,YAAY,EAAE,OAAO;GACxC;GACA,OAAO;EACT;EACA,iBAAiB,CAAC;EAClB,SAAS,QAAQ;GACf,MAAM,IAAI,MAAM,IAAI,IAAI,GAAG;GAC3B,OAAO,IAAI,EAAE,IAAI;IAAE,GAAG;IAAG,yBAAQ,IAAI,KAAK,EAAA,CAAE,YAAY;IAAG,UAAU,EAAE,WAAW,IAAI;IAAU,OAAO,IAAI,aAAa;GAAS,CAAC;GAClI,IAAI,GAAG,GAAG,IAAI,GAAG,EAAE,IAAI,IAAI,IAAI,kBAAkB,SAAS,IAAI,aAAa,UAAU,GAAG,GAAG;EAC7F;EACA,aAAa,QAAQ;GACnB,IAAI,IAAI,YAAY,aAAa,IAAI,GAAG,GAAG,MAAM,GAAG,EAAE,IAAI,IAAI,IAAI,GAAG,IAAI,QAAQ,KAAK,UAAU;EAClG;CACF,CAAC;CACD,IAAI,aAAa,OAAO,OAAO,aAAa,OAAO,SAAS,IAAI,MAAM,GAAG,UAAU,OAAO,YAAY,iBAAiB;CAEvH,KAAK,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG;EACzD,MAAM,UAAU,EAAE,SAAS,SAAS,EAAE,QAAQ,QAAQ,MAAM,UAAU,SAAS,CAAC,CAAC,IAAI;EACrF,IAAI,CAAC,QAAQ,QAAQ;GACnB,IAAI,GAAG,GAAG,OAAO,GAAG,EAAE,IAAI,EAAE,IAAI,SAAS,EAAE,QAAQ,KAAK,OAAO,EAAE,uCAAuC,EAAE,QAAQ,GAAG,eAAe;GACpI,OAAO,IAAI,EAAE,IAAI,CAAC;GAClB;EACF;EACA,WAAW,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC;CACxC;CACA,MAAM,WAAW,KAAK;CAEtB,MAAM,WAAW,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,EAAE,CAAC;CAEhD,MAAM,UAAU,SAAS,IAAI;CAC7B,UAAU,MAAM,CAAC,GAAG,QAAQ,QAAQ,MAAM,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,OAAO,OAAO,CAAC,CAAC;CACnF,IAAI,GAAG,OAAO,SAAS,OAAO,KAAK,aAAa,OAAO,KAAK,aAAa;CACzE,OAAO,OAAO;AAChB"}
1
+ {"version":3,"file":"admin-5555baeh.js","names":[],"sources":["../server/listen/admin.ts"],"sourcesContent":["import pc from \"picocolors\";\nimport { ApiError, getSelf } from \"./api.ts\";\nimport { describeGrants, hasAccess } from \"./grants.ts\";\nimport { Dispatcher, type Job, type Target } from \"./dispatcher.ts\";\nimport { runCommand } from \"./exec.ts\";\nimport { ensurePrivateDir } from \"./fsutil.ts\";\nimport { forwardEvent } from \"./forward.ts\";\nimport { routeWarning } from \"./hermes.ts\";\nimport { describeFilter, type ListenFlags, MAX_BATCH, resolveListen } from \"./options.ts\";\nimport { outputLine } from \"./listen.ts\";\nimport { isHermesRoute } from \"./recipes.ts\";\nimport { DEFAULT_PROFILE, deadPath, logPath, profilePath, validProfileName } from \"./paths.ts\";\nimport { loadProfile, saveProfile } from \"./profile.ts\";\nimport { DEFAULT_LISTEN_TYPES, forwardSecret } from \"./options.ts\";\nimport { installedServiceLabel } from \"./service.ts\";\nimport { type DeadEntry, pidAlive, readDead, readStatus, writeDead } from \"./state.ts\";\nimport { checkTransport, TransportError } from \"./transport.ts\";\nimport { formatDuration, safeText } from \"./ui.ts\";\nimport { NPX } from \"./version.ts\";\n\nconst out = (s = \"\") => process.stdout.write(`${s}\\n`);\nconst flagFor = (profile: string) => (profile === DEFAULT_PROFILE ? \"\" : ` --profile ${profile}`);\nconst ago = (iso: string | null | undefined) => (iso ? `${formatDuration(Date.now() - Date.parse(iso))} ago` : \"never\");\n\n// ───────────── listen secret ─────────────\n\n/** Prints the forward-mode signing secret (generating and saving it on first use). */\nexport function printSecret(profileName: string, opts: { rotate?: boolean }): void {\n const name = validProfileName(profileName);\n const profile = loadProfile(name);\n let { secret, created } = forwardSecret(profile);\n if (opts.rotate) {\n secret = forwardSecret({ ...profile, forward: undefined, forwardSecret: undefined }).secret;\n created = true;\n }\n if (created) {\n if (profile.forward) profile.forward = { ...profile.forward, secret };\n else profile.forwardSecret = secret;\n saveProfile(name, profile);\n process.stderr.write(pc.gray(`${opts.rotate ? \"Rotated\" : \"Generated\"} the forward signing secret for profile \"${name}\"${opts.rotate ? \"; restart the listener\" : \"\"}.\\n`));\n }\n // The secret alone on stdout, so `export XAQ_WEBHOOK_SECRET=$(xaq listen secret)` works.\n out(secret);\n}\n\n// ───────────── listen status ─────────────\n\nexport async function printStatus(profileName: string): Promise<void> {\n const name = validProfileName(profileName);\n const profile = loadProfile(name);\n const st = readStatus(name);\n const alive = st ? pidAlive(st.pid) && !st.stoppedAt : false;\n const dead = readDead(name);\n\n out(`${pc.bold(\"Profile\")} ${name} ${pc.gray(profilePath(name))}`);\n out(`${pc.bold(\"Agent\")} ${safeText(profile.agentName)} ${pc.gray(`(${profile.agentId})`)}`);\n out(`${pc.bold(\"Daemon\")} ${profile.url}`);\n out(`${pc.bold(\"Consumer\")} ${st?.consumer ?? profile.consumer}`);\n // What the last run used (flags included), else what the profile has saved.\n const exec = st && st.exec !== undefined ? st.exec : profile.exec;\n const forward = st && st.forward !== undefined ? st.forward : profile.forward?.url;\n const targets = [exec ? `exec ${safeText(exec, 120)}` : null, forward ? `forward ${forward}` : null].filter(Boolean);\n out(`${pc.bold(\"Runs\")} ${targets.length ? targets.join(\" · \") : pc.gray(\"nothing saved (pass --exec/--forward with --save)\")}`);\n out(\n `${pc.bold(\"Events\")} ${\n profile.types?.length\n ? profile.types.join(\", \")\n : `${DEFAULT_LISTEN_TYPES.join(\", \")} ${pc.gray(\"(default types; add --types all --save for every event)\")}`\n }`,\n );\n // The running listener's filters (flags included), else the profile's.\n let saved: ReturnType<typeof resolveListen>[\"config\"] | null = null;\n try {\n saved = resolveListen(name, profile, {}).config;\n } catch {\n // An invalid saved setting: `listen` itself reports it.\n }\n out(`${pc.bold(\"Filters\")} ${(alive && st?.filters) || (saved ? describeFilter(saved) : \"?\")}`);\n const batchSeconds = alive && st && st.batchSeconds !== undefined ? st.batchSeconds : (saved?.batchSeconds ?? 0);\n if (batchSeconds) out(`${pc.bold(\"Batch\")} ${formatDuration(batchSeconds * 1000)} window, up to ${MAX_BATCH} events per --exec run`);\n const route = forward && isHermesRoute(forward) ? routeWarning(name, forward) : null;\n if (route?.level === \"warn\") out(`${pc.bold(\"Route\")} ${pc.yellow(route.message)}`);\n out(`${pc.bold(\"Output\")} ${outputLine(name, { exec: exec ?? null, forward: forward ? { url: forward } : null }, logPath(name))}`);\n\n let svc = \"not installed\";\n if (process.platform === \"linux\" || process.platform === \"darwin\") {\n const installed = installedServiceLabel(name);\n if (installed) svc = `installed (${installed}; details: ${NPX} listen service status${flagFor(name)})`;\n }\n out(`${pc.bold(\"Service\")} ${svc}`);\n\n try {\n // Never send the key over a connection `listen` itself would refuse.\n await checkTransport(profile.url, { insecure: profile.insecure });\n const self = await getSelf(profile.url, profile.apiKey, 5000);\n out(`${pc.bold(\"Key\")} ${pc.green(\"valid\")}`);\n // The access the console actually granted, in plain words (what the agent should report to its human).\n const lines = describeGrants(self.grants, self.connections);\n out(`${pc.bold(\"Access\")} ${hasAccess(self) ? lines[0] : pc.yellow(lines[0]!)}`);\n for (const l of lines.slice(1)) out(` ${l}`);\n } catch (err) {\n const revoked = err instanceof ApiError && (err.status === 401 || err.status === 403);\n const why = err instanceof ApiError && err.status === 0 ? (err.cause as string) : err instanceof Error ? err.message.split(\"\\n\")[0] : String(err);\n if (err instanceof TransportError) out(`${pc.bold(\"Key\")} ${pc.red(`not checked: ${why}`)}`);\n else out(`${pc.bold(\"Key\")} ${revoked ? pc.red(`rejected: ${(err as Error).message}`) : pc.yellow(`not checked (${why})`)}`);\n }\n\n if (!st) {\n out(`${pc.bold(\"Listener\")} ${pc.gray(\"has not run yet\")}`);\n } else {\n const state = !alive\n ? pc.gray(`not running${st.stoppedAt ? ` (stopped ${ago(st.stoppedAt)}${st.exitReason ? `: ${safeText(st.exitReason, 160)}` : \"\"})` : \" (exited unexpectedly)\"}`)\n : st.connected\n ? pc.green(`running (pid ${st.pid}), connected ${ago(st.connectedSince)}`)\n : pc.yellow(`running (pid ${st.pid}), disconnected${st.lastDisconnect ? ` ${ago(st.lastDisconnect.at)}: ${safeText(st.lastDisconnect.reason, 160)}` : \"\"}`);\n out(`${pc.bold(\"Listener\")} ${state}`);\n out(\n `${pc.bold(\"Cursor\")} acked #${st.ackedSeq} · received #${st.receivedSeq}${alive && st.running ? ` · ${st.running} in progress` : \"\"}${st.skipped ? ` · ${st.skipped} skipped by filters` : \"\"} · last event ${ago(st.lastEventAt)}`,\n );\n if (st.recent.length) {\n out(pc.bold(\"Recent\"));\n for (const r of st.recent.slice(0, 8)) {\n const icon = r.outcome === \"succeeded\" ? pc.green(\"✓\") : r.outcome === \"dead\" ? pc.red(\"✖\") : r.outcome === \"retrying\" ? pc.yellow(\"↻\") : pc.gray(\"·\");\n out(` ${icon} #${r.seq} ${r.type} ${pc.gray(`${ago(r.at)}${r.attempts > 1 ? `, ${r.attempts} attempts` : \"\"}${r.error && r.outcome !== \"succeeded\" ? `, ${safeText(r.error, 100)}` : \"\"}`)}`);\n }\n }\n }\n out(`${pc.bold(\"Dead\")} ${dead.length ? pc.red(`${dead.length} event${dead.length > 1 ? \"s\" : \"\"}`) + pc.gray(` (inspect: ${NPX} listen dead${flagFor(name)})`) : \"0\"}`);\n out(`${pc.bold(\"Log\")} ${logPath(name)}`);\n}\n\n// ───────────── listen dead ─────────────\n\nexport function printDead(profileName: string, opts: { json?: boolean }): void {\n const name = validProfileName(profileName);\n const entries = readDead(name);\n if (opts.json) {\n for (const e of entries) out(JSON.stringify(e));\n return;\n }\n if (!entries.length) {\n out(`No dead events for profile \"${name}\".`);\n return;\n }\n for (const e of entries) {\n const m = e.payload.data.message;\n out(`${pc.red(\"✖\")} #${e.seq} ${e.type} ${pc.gray(e.id)} ${pc.gray(`died ${ago(e.deadAt)} after ${e.attempts} attempts`)}`);\n if (m) out(` from ${safeText(m.from?.email ?? \"?\", 80)} \"${safeText(m.subject, 80)}\"`);\n out(pc.gray(` ${safeText(e.error, 300)}`));\n }\n out();\n out(pc.gray(`${entries.length} dead event${entries.length > 1 ? \"s\" : \"\"} in ${deadPath(name)}.`));\n out(pc.gray(`Replay with: ${NPX} listen dead --replay${flagFor(name)} (one: --id <evt_…>; drop all: --clear)`));\n out(pc.gray(`Replay uses the profile's saved --exec/--forward; if you ran them as flags only, pass them again, e.g. --replay --exec '<command>'.`));\n}\n\nexport function clearDead(profileName: string, id?: string): void {\n const name = validProfileName(profileName);\n const entries = readDead(name);\n const keep = id ? entries.filter((e) => e.id !== id) : [];\n writeDead(name, keep);\n out(`Removed ${entries.length - keep.length} dead event${entries.length - keep.length === 1 ? \"\" : \"s\"}.`);\n}\n\n/**\n * Runs dead events again through the profile's targets (or the ones passed as flags). Successes are\n * removed from dead.jsonl; failures stay. Returns how many still failed.\n */\nexport async function replayDead(flags: ListenFlags & { id?: string }, opts: { retryDelayMs?: (n: number) => number } = {}): Promise<number> {\n const name = validProfileName(flags.profile ?? DEFAULT_PROFILE);\n const profile = loadProfile(name);\n const { config } = resolveListen(name, profile, { ...flags, save: false });\n const all = readDead(name);\n const chosen = all.filter((e) => !flags.id || e.id === flags.id);\n if (!chosen.length) {\n out(flags.id ? `No dead event with id ${flags.id}.` : `No dead events for profile \"${name}\".`);\n return 0;\n }\n const available: Target[] = [];\n if (config.exec) available.push(\"exec\");\n if (config.forward) available.push(\"forward\");\n if (!available.length) throw new Error(`Nothing to replay to: pass --recipe <tool> (claude-code, codex, cursor, hermes), --exec '<command>' and/or --forward <url> (or save them in the profile with \\`${NPX} listen --save …\\`).`);\n if (config.exec && process.platform === \"win32\") throw new Error(\"--exec isn't supported on Windows; replay with --forward instead.\");\n\n const timeoutMs = config.timeoutSeconds * 1000;\n if (config.cwdIsDefault) ensurePrivateDir(config.cwd);\n const failed = new Map<string, DeadEntry>();\n const bySeq = new Map(chosen.map((e) => [e.seq, e]));\n const dispatcher = new Dispatcher({\n targets: available,\n concurrency: config.concurrency,\n maxAttempts: config.maxAttempts,\n retryDelayMs: opts.retryDelayMs,\n run: async (job: Job, signal) => {\n const errors: string[] = [];\n if (job.remaining.has(\"exec\") && config.exec) {\n const r = await runCommand(config.exec, job.payload, { timeoutMs, signal, cwd: config.cwd });\n if (r.ok) job.remaining.delete(\"exec\");\n else errors.push(`command: ${r.error}`);\n }\n if (job.remaining.has(\"forward\") && config.forward) {\n const r = await forwardEvent(config.forward.url, config.forward.secret, job.payload, { timeoutMs: Math.min(timeoutMs, 30_000), signal, wrap: isHermesRoute(config.forward.url) });\n if (r.ok) job.remaining.delete(\"forward\");\n else errors.push(`forward: ${r.error}`);\n }\n return errors;\n },\n onAdvance: () => {},\n onDead: (job) => {\n const e = bySeq.get(job.seq)!;\n failed.set(e.id, { ...e, deadAt: new Date().toISOString(), attempts: e.attempts + job.attempts, error: job.lastError ?? \"failed\" });\n out(`${pc.red(\"✖\")} #${job.seq} still failing: ${safeText(job.lastError ?? \"failed\", 200)}`);\n },\n onFinished: (job) => {\n if (job.outcome === \"succeeded\") out(`${pc.green(\"✓\")} #${job.seq} ${job.payload.type} replayed`);\n },\n });\n out(`Replaying ${chosen.length} dead event${chosen.length > 1 ? \"s\" : \"\"} (up to ${config.maxAttempts} attempts each)…`);\n // Only the targets that never succeeded (a forward that went through isn't POSTed again).\n for (const e of [...chosen].sort((a, b) => a.seq - b.seq)) {\n const targets = e.targets?.length ? e.targets.filter((t) => available.includes(t)) : available;\n if (!targets.length) {\n out(`${pc.yellow(\"!\")} #${e.seq} needs ${e.targets.join(\" and \")}, which isn't configured now; pass --${e.targets[0]} to replay it.`);\n failed.set(e.id, e);\n continue;\n }\n dispatcher.push(e.payload, { targets });\n }\n await dispatcher.idle();\n\n const replayed = new Set(chosen.map((e) => e.id));\n // Re-read so entries added by a running listener meanwhile are kept.\n const current = readDead(name);\n writeDead(name, [...current.filter((e) => !replayed.has(e.id)), ...failed.values()]);\n out(`${chosen.length - failed.size} replayed, ${failed.size} still dead.`);\n return failed.size;\n}\n"],"mappings":"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"}
@@ -110,8 +110,14 @@ const PARSE_LIMITS = {
110
110
  /** Characters kept per cell. */
111
111
  cellChars: 2e3
112
112
  };
113
- /** A file the parser refuses to read (zip bomb, XXE, entry outside the archive, …): said in plain words. */
114
- var UnsafeDocumentError = class extends Error {};
113
+ /** A file the parser refuses to read (zip bomb, XXE, entry outside the archive, …): said in plain words, with a code. */
114
+ var UnsafeDocumentError = class extends Error {
115
+ code;
116
+ constructor(code, message) {
117
+ super(message);
118
+ this.code = code;
119
+ }
120
+ };
115
121
  //#endregion
116
122
  //#region server/mail/extract/csv.ts
117
123
  function guessDelimiter(text) {
@@ -7479,12 +7485,12 @@ const ELEMENT = 1;
7479
7485
  const TEXT = 3;
7480
7486
  const CDATA = 4;
7481
7487
  function parseXml(source) {
7482
- if (/<!DOCTYPE|<!ENTITY/i.test(source)) throw new UnsafeDocumentError("The document declares XML entities (a DOCTYPE), which Office files never do; it wasn't read.");
7488
+ if (/<!DOCTYPE|<!ENTITY/i.test(source)) throw new UnsafeDocumentError("hostile", "The document declares XML entities (a DOCTYPE), which Office files never do; it wasn't read.");
7483
7489
  let failed = null;
7484
7490
  const root = new import_lib.DOMParser({ onError: (level, msg) => {
7485
7491
  if (level !== "warning") failed ??= msg;
7486
7492
  } }).parseFromString(source, "text/xml").documentElement;
7487
- if (failed || !root) throw new UnsafeDocumentError("The document has malformed XML.");
7493
+ if (failed || !root) throw new UnsafeDocumentError("corrupt", "The document has malformed XML.");
7488
7494
  return root;
7489
7495
  }
7490
7496
  const local = (n) => n.localName ?? n.nodeName.replace(/^.*:/, "");
@@ -7568,25 +7574,25 @@ var ZipPackage = class {
7568
7574
  inflated = 0;
7569
7575
  constructor(buf) {
7570
7576
  this.buf = buf;
7571
- if (buf.length < 22) throw new UnsafeDocumentError("This isn't a valid zip package.");
7577
+ if (buf.length < 22) throw new UnsafeDocumentError("corrupt", "This isn't a valid zip package.");
7572
7578
  let eocd = -1;
7573
7579
  for (let i = buf.length - 22; i >= Math.max(0, buf.length - 22 - 65535); i--) if (buf.readUInt32LE(i) === EOCD) {
7574
7580
  eocd = i;
7575
7581
  break;
7576
7582
  }
7577
- if (eocd < 0) throw new UnsafeDocumentError("This isn't a valid zip package (no central directory).");
7583
+ if (eocd < 0) throw new UnsafeDocumentError("corrupt", "This isn't a valid zip package (no central directory).");
7578
7584
  const disk = buf.readUInt16LE(eocd + 4);
7579
7585
  const cdDisk = buf.readUInt16LE(eocd + 6);
7580
7586
  const count = buf.readUInt16LE(eocd + 10);
7581
7587
  const cdSize = buf.readUInt32LE(eocd + 12);
7582
7588
  const cdOffset = buf.readUInt32LE(eocd + 16);
7583
- if (disk !== 0 || cdDisk !== 0) throw new UnsafeDocumentError("Multi-part zip archives aren't supported.");
7584
- if (count === 65535 || cdOffset === 4294967295) throw new UnsafeDocumentError("ZIP64 archives aren't supported.");
7585
- if (count > PARSE_LIMITS.zipEntries) throw new UnsafeDocumentError(`The package has ${count} entries; at most ${PARSE_LIMITS.zipEntries} are read.`);
7586
- if (cdOffset + cdSize > eocd) throw new UnsafeDocumentError("This zip package is corrupt (central directory out of range).");
7589
+ if (disk !== 0 || cdDisk !== 0) throw new UnsafeDocumentError("unsupported", "Multi-part zip archives aren't supported.");
7590
+ if (count === 65535 || cdOffset === 4294967295) throw new UnsafeDocumentError("unsupported", "ZIP64 archives aren't supported.");
7591
+ if (count > PARSE_LIMITS.zipEntries) throw new UnsafeDocumentError("too_complex", `The package has ${count} entries; at most ${PARSE_LIMITS.zipEntries} are read.`);
7592
+ if (cdOffset + cdSize > eocd) throw new UnsafeDocumentError("corrupt", "This zip package is corrupt (central directory out of range).");
7587
7593
  let p = cdOffset;
7588
7594
  for (let i = 0; i < count; i++) {
7589
- if (p + 46 > eocd || buf.readUInt32LE(p) !== CDH) throw new UnsafeDocumentError("This zip package is corrupt (bad central directory).");
7595
+ if (p + 46 > eocd || buf.readUInt32LE(p) !== CDH) throw new UnsafeDocumentError("corrupt", "This zip package is corrupt (bad central directory).");
7590
7596
  const flags = buf.readUInt16LE(p + 8);
7591
7597
  const method = buf.readUInt16LE(p + 10);
7592
7598
  const compressedSize = buf.readUInt32LE(p + 20);
@@ -7597,10 +7603,10 @@ var ZipPackage = class {
7597
7603
  const localOffset = buf.readUInt32LE(p + 42);
7598
7604
  const name = buf.toString(flags & 2048 ? "utf8" : "latin1", p + 46, p + 46 + nameLen);
7599
7605
  p += 46 + nameLen + extraLen + commentLen;
7600
- if (flags & 1) throw new UnsafeDocumentError("Encrypted documents can't be read.");
7601
- if (compressedSize === 4294967295 || size === 4294967295 || localOffset === 4294967295) throw new UnsafeDocumentError("ZIP64 archives aren't supported.");
7602
- if (!safeEntryName(name)) throw new UnsafeDocumentError("The package has an entry with a path outside the archive; it wasn't read.");
7603
- if (this.entries.has(name)) throw new UnsafeDocumentError("The package lists the same entry twice; it wasn't read.");
7606
+ if (flags & 1) throw new UnsafeDocumentError("encrypted", "Encrypted documents can't be read.");
7607
+ if (compressedSize === 4294967295 || size === 4294967295 || localOffset === 4294967295) throw new UnsafeDocumentError("unsupported", "ZIP64 archives aren't supported.");
7608
+ if (!safeEntryName(name)) throw new UnsafeDocumentError("hostile", "The package has an entry with a path outside the archive; it wasn't read.");
7609
+ if (this.entries.has(name)) throw new UnsafeDocumentError("hostile", "The package lists the same entry twice; it wasn't read.");
7604
7610
  if (name.endsWith("/")) continue;
7605
7611
  this.entries.set(name, {
7606
7612
  name,
@@ -7622,23 +7628,23 @@ var ZipPackage = class {
7622
7628
  const e = this.entries.get(name);
7623
7629
  if (!e) return null;
7624
7630
  const b = this.buf;
7625
- if (e.localOffset + 30 > b.length || b.readUInt32LE(e.localOffset) !== LFH) throw new UnsafeDocumentError("This zip package is corrupt (bad entry header).");
7631
+ if (e.localOffset + 30 > b.length || b.readUInt32LE(e.localOffset) !== LFH) throw new UnsafeDocumentError("corrupt", "This zip package is corrupt (bad entry header).");
7626
7632
  const start = e.localOffset + 30 + b.readUInt16LE(e.localOffset + 26) + b.readUInt16LE(e.localOffset + 28);
7627
- if (start + e.compressedSize > b.length) throw new UnsafeDocumentError("This zip package is corrupt (entry out of range).");
7633
+ if (start + e.compressedSize > b.length) throw new UnsafeDocumentError("corrupt", "This zip package is corrupt (entry out of range).");
7628
7634
  const data = b.subarray(start, start + e.compressedSize);
7629
7635
  const budget = Math.min(PARSE_LIMITS.zipEntryBytes, PARSE_LIMITS.zipTotalBytes - this.inflated);
7630
- if (e.size > 1048576 && e.size / Math.max(1, e.compressedSize) > PARSE_LIMITS.zipRatio) throw new UnsafeDocumentError("The document looks like a zip bomb (it decompresses to far more than its size); it wasn't read.");
7631
- if (e.size > budget) throw new UnsafeDocumentError("The document is too large to read once decompressed.");
7636
+ if (e.size > 1048576 && e.size / Math.max(1, e.compressedSize) > PARSE_LIMITS.zipRatio) throw new UnsafeDocumentError("hostile", "The document looks like a zip bomb (it decompresses to far more than its size); it wasn't read.");
7637
+ if (e.size > budget) throw new UnsafeDocumentError("too_complex", "The document is too large to read once decompressed.");
7632
7638
  let out;
7633
7639
  if (e.method === 0) out = data;
7634
7640
  else if (e.method === 8) try {
7635
7641
  out = inflateRawSync(data, { maxOutputLength: Math.max(1, budget) });
7636
7642
  } catch (err) {
7637
- if (err.code === "ERR_BUFFER_TOO_LARGE") throw new UnsafeDocumentError("The document looks like a zip bomb (it decompresses to far more than its size); it wasn't read.");
7638
- throw new UnsafeDocumentError("This zip package is corrupt (an entry doesn't decompress).");
7643
+ if (err.code === "ERR_BUFFER_TOO_LARGE") throw new UnsafeDocumentError("hostile", "The document looks like a zip bomb (it decompresses to far more than its size); it wasn't read.");
7644
+ throw new UnsafeDocumentError("corrupt", "This zip package is corrupt (an entry doesn't decompress).");
7639
7645
  }
7640
- else throw new UnsafeDocumentError("The package uses a compression method that isn't supported.");
7641
- if (out.length > budget) throw new UnsafeDocumentError("The document is too large to read once decompressed.");
7646
+ else throw new UnsafeDocumentError("unsupported", "The package uses a compression method that isn't supported.");
7647
+ if (out.length > budget) throw new UnsafeDocumentError("too_complex", "The document is too large to read once decompressed.");
7642
7648
  this.inflated += out.length;
7643
7649
  return out;
7644
7650
  }
@@ -7684,7 +7690,7 @@ function extractDocx(input) {
7684
7690
  const pkg = new ZipPackage(input);
7685
7691
  const part = mainPart(pkg, "word/document.xml");
7686
7692
  const xml = pkg.text(part);
7687
- if (xml === null) throw new UnsafeDocumentError("The file isn't a Word document (no word/document.xml).");
7693
+ if (xml === null) throw new UnsafeDocumentError("corrupt", "The file isn't a Word document (no word/document.xml).");
7688
7694
  const rels = relationships(pkg.text(relsPath(part)));
7689
7695
  const byType = (suffix) => {
7690
7696
  for (const r of rels.values()) if (r.type.endsWith(suffix) && !r.external) return resolvePart(part, r.target);
@@ -77743,8 +77749,8 @@ async function extractPdf(input) {
77743
77749
  try {
77744
77750
  doc = await task.promise;
77745
77751
  } catch (err) {
77746
- if (err.name === "PasswordException") throw new UnsafeDocumentError("The PDF is password-protected.");
77747
- throw new UnsafeDocumentError("The file isn't a readable PDF.");
77752
+ if (err.name === "PasswordException") throw new UnsafeDocumentError("password", "The PDF is password-protected.");
77753
+ throw new UnsafeDocumentError("corrupt", "The file isn't a readable PDF.");
77748
77754
  }
77749
77755
  const totalPages = doc.numPages;
77750
77756
  const pages = [];
@@ -77808,7 +77814,7 @@ function extractPptx(input) {
77808
77814
  const pkg = new ZipPackage(input);
77809
77815
  const part = mainPart(pkg, "ppt/presentation.xml");
77810
77816
  const presXml = pkg.text(part);
77811
- if (presXml === null) throw new UnsafeDocumentError("The file isn't a PowerPoint presentation (no ppt/presentation.xml).");
77817
+ if (presXml === null) throw new UnsafeDocumentError("corrupt", "The file isn't a PowerPoint presentation (no ppt/presentation.xml).");
77812
77818
  const rels = relationships(pkg.text(relsPath(part)));
77813
77819
  const pres = parseXml(presXml);
77814
77820
  const ids = [...descendants(firstChild(pres, "sldIdLst") ?? pres, "sldId")];
@@ -77902,7 +77908,7 @@ function extractXlsx(input) {
77902
77908
  const pkg = new ZipPackage(input);
77903
77909
  const part = mainPart(pkg, "xl/workbook.xml");
77904
77910
  const wbXml = pkg.text(part);
77905
- if (wbXml === null) throw new UnsafeDocumentError("The file isn't an Excel workbook (no xl/workbook.xml).");
77911
+ if (wbXml === null) throw new UnsafeDocumentError("corrupt", "The file isn't an Excel workbook (no xl/workbook.xml).");
77906
77912
  const wb = parseXml(wbXml);
77907
77913
  const rels = relationships(pkg.text(relsPath(part)));
77908
77914
  const relOf = (suffix) => {
@@ -78064,6 +78070,7 @@ function cleanParts(r, clean) {
78064
78070
  async function run(input, task) {
78065
78071
  if (input.length > PARSE_LIMITS.inputBytes) return {
78066
78072
  ok: false,
78073
+ code: "too_complex",
78067
78074
  error: "The file is too large to read."
78068
78075
  };
78069
78076
  try {
@@ -78086,6 +78093,7 @@ async function run(input, task) {
78086
78093
  break;
78087
78094
  default: return {
78088
78095
  ok: false,
78096
+ code: "parser_error",
78089
78097
  error: "Unknown format."
78090
78098
  };
78091
78099
  }
@@ -78094,9 +78102,15 @@ async function run(input, task) {
78094
78102
  result: cleanAll(result)
78095
78103
  };
78096
78104
  } catch (err) {
78105
+ if (err instanceof UnsafeDocumentError) return {
78106
+ ok: false,
78107
+ code: err.code,
78108
+ error: err.message
78109
+ };
78097
78110
  return {
78098
78111
  ok: false,
78099
- error: err instanceof UnsafeDocumentError ? err.message : "The file couldn't be parsed."
78112
+ code: "parser_error",
78113
+ error: "The file couldn't be parsed."
78100
78114
  };
78101
78115
  }
78102
78116
  }
@@ -74,7 +74,7 @@ Example:
74
74
  const { openAdminChannel } = await import("./admin-DbJXEo2d.js");
75
75
  const { TerminalPrompter } = await import("./prompt-BEH311I7.js");
76
76
  const { connectAgentMcp } = await import("./agent-B8a-Ivyd.js");
77
- const { SmokeRun } = await import("./run-C0fpnYAb.js");
77
+ const { SmokeRun } = await import("./run-zakbVgQR.js");
78
78
  const { renderReport } = await import("./report-D2D_6E5O.js");
79
79
  if (!o.scratch) throw new Error("smoke google-workspace runs live on the connected Google account: it reads existing files and mail, and writes only into a new scratch folder and ONE draft to the account itself, which it removes again. Pass --scratch to run it.");
80
80
  const out = safeWriter(o.json ? process.stderr : process.stdout);
@@ -123,4 +123,4 @@ Example:
123
123
  //#endregion
124
124
  export { registerSmokeCommands };
125
125
 
126
- //# sourceMappingURL=cli-C9lES1ug.js.map
126
+ //# sourceMappingURL=cli-Wp2ejVBT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-C9lES1ug.js","names":[],"sources":["../server/smoke/cli.ts"],"sourcesContent":["// `xaq smoke google-workspace --scratch`: run on xaq's machine, through the daemon's admin API over the local admin\n// channel (server/connections-cli/admin.ts) and /mcp as a temporary agent. See run.ts for what it does.\n\nimport type { Command } from \"commander\";\nimport { LEGACY_PRODUCT } from \"../brand.ts\";\nimport { resolveDataDir } from \"../config.ts\";\nimport { NPX } from \"../listen/version.ts\";\nimport type { SmokeFlags } from \"./run.ts\";\n\nconst GROUP = \"MCP connections (on xaq's machine):\";\n\n/**\n * A line printer that can't kill the run: once the stream fails (EIO or EPIPE from a terminal that went away, e.g.\n * after SIGHUP), later lines are dropped instead of throwing or crashing on an unhandled 'error'.\n */\nexport function safeWriter(stream: NodeJS.WritableStream): { print: (line?: string) => void; dispose: () => void } {\n let dead = false;\n const onError = () => {\n dead = true;\n };\n stream.on(\"error\", onError);\n return {\n print(line = \"\") {\n if (dead) return;\n try {\n stream.write(`${line}\\n`, (err) => {\n if (err) dead = true;\n });\n } catch {\n dead = true;\n }\n },\n dispose: () => stream.removeListener(\"error\", onError),\n };\n}\n\nexport interface SmokeSignalOptions {\n print: (line: string) => void;\n exit: (code: number) => void;\n /** The second Ctrl-C: the quick part of cleanup (agent, attachmentDirs), bounded. */\n fastCleanup: () => Promise<void>;\n /** What may still be there, in a sentence. */\n leftovers: () => string;\n /** Upper bound on the second Ctrl-C's cleanup before quitting anyway (default 8 s). */\n quitWithinMs?: number;\n}\n\n/**\n * Signals during a run. SIGTERM and SIGHUP (a closed terminal, a stopped service) always act like the first Ctrl-C:\n * no new step starts and the run's full cleanup runs. The first Ctrl-C does the same; a second one runs the quick\n * cleanup (delete the agent, restore attachmentDirs) with a short timeout, says what may be left and quits.\n * Returns the remover.\n */\nexport function installSmokeSignals(controller: AbortController, opts: SmokeSignalOptions): () => void {\n let interrupts = 0;\n let quitting = false;\n const stop = (why: string) => {\n if (controller.signal.aborted) return;\n opts.print(`\\n${why}: stopping after the current call, then cleaning up.${why === \"Ctrl-C\" ? \" Press Ctrl-C again to quit sooner (only the agent and attachmentDirs are cleaned up then).\" : \"\"}`);\n controller.abort();\n };\n const onInt = () => {\n interrupts++;\n if (interrupts === 1) return stop(\"Ctrl-C\");\n if (quitting) return;\n quitting = true;\n opts.print(\"\\nQuitting: deleting the temporary agent and restoring attachmentDirs first…\");\n const deadline = new Promise<void>((r) => setTimeout(r, opts.quitWithinMs ?? 8000).unref?.());\n void Promise.race([opts.fastCleanup().catch(() => {}), deadline]).then(() => {\n opts.print(`Quit before the cleanup finished. ${opts.leftovers()}`);\n opts.exit(130);\n });\n };\n const onTerm = () => stop(\"SIGTERM\");\n const onHup = () => stop(\"SIGHUP\");\n process.on(\"SIGINT\", onInt);\n process.on(\"SIGTERM\", onTerm);\n process.on(\"SIGHUP\", onHup);\n return () => {\n process.removeListener(\"SIGINT\", onInt);\n process.removeListener(\"SIGTERM\", onTerm);\n process.removeListener(\"SIGHUP\", onHup);\n };\n}\n\nexport function registerSmokeCommands(program: Command): void {\n const smoke = program\n .command(\"smoke\")\n .description(\"live end-to-end checks against the connected accounts, safe by construction\")\n .summary(\"live end-to-end check of a connection\")\n .helpGroup(GROUP);\n\n smoke\n .command(\"google-workspace\")\n .description(\"check Gmail attachments and the google-workspace extension end to end on the real account: reads anything, writes only into a new scratch folder and ONE draft to itself, cleans up\")\n .option(\"--scratch\", \"required: confirms the run may create its scratch folder, its draft and its temporary agent (all removed again)\")\n .option(\"--account <email>\", \"the Gmail account (default: the only one, or the one the Workspace connection is signed in as)\")\n .option(\"--connection <slug>\", \"the google-workspace connection (default: the only one)\")\n .option(\"--json\", \"print the result table as JSON on stdout (progress goes to stderr)\")\n .option(\"-y, --yes\", \"don't ask before running\")\n .option(\"-d, --data <dir>\", `xaq's data directory (default ~/.xaq, or an existing ~/.${LEGACY_PRODUCT}; env XAQ_DATA)`)\n .addHelpText(\n \"after\",\n `\nPrints the plan first and asks (or --yes). Exit code 0 only when every step passes and nothing is left behind.\n\nExample:\n ${NPX} smoke google-workspace --scratch`,\n )\n .action(async (o: SmokeFlags & { data?: string }) => {\n const { openAdminChannel } = await import(\"../connections-cli/admin.ts\");\n const { TerminalPrompter } = await import(\"../connections-cli/prompt.ts\");\n const { connectAgentMcp } = await import(\"./agent.ts\");\n const { SmokeRun } = await import(\"./run.ts\");\n const { renderReport } = await import(\"./report.ts\");\n if (!o.scratch) {\n throw new Error(\n \"smoke google-workspace runs live on the connected Google account: it reads existing files and mail, and writes only into a new scratch folder and ONE draft to the account itself, which it removes again. Pass --scratch to run it.\",\n );\n }\n const out = safeWriter(o.json ? process.stderr : process.stdout);\n const err = o.json ? out : safeWriter(process.stderr);\n const json = o.json ? safeWriter(process.stdout) : null;\n const channel = openAdminChannel({ dataDir: resolveDataDir(o.data, { quiet: true }) });\n const prompter = new TerminalPrompter(!!o.yes, { input: process.stdin, output: o.json ? process.stderr : process.stdout });\n const controller = new AbortController();\n const run = new SmokeRun(o, {\n admin: channel,\n connectMcp: (key) => connectAgentMcp(channel.baseUrl, key),\n print: out.print,\n confirm: (q) => prompter.confirm(q, false),\n signal: controller.signal,\n });\n const remove = installSmokeSignals(controller, {\n print: (l) => err.print(l),\n fastCleanup: () => run.fastCleanup(3000),\n exit: (code) => {\n channel.close();\n process.exit(code);\n },\n leftovers: () => {\n const left = run.pendingLeftovers();\n return left.length ? `These may be left: ${left.join(\"; \")}. The next run removes the agent and the attachmentDirs entry and lists the rest.` : \"Nothing was left.\";\n },\n });\n try {\n const report = await run.run();\n if (json) json.print(JSON.stringify(report, null, 2));\n else for (const l of [\"\", ...renderReport(report)]) out.print(l);\n process.exitCode = report.ok ? 0 : report.interrupted ? 130 : 1;\n } finally {\n remove();\n prompter.close();\n channel.close();\n out.dispose();\n if (err !== out) err.dispose();\n json?.dispose();\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-Wp2ejVBT.js","names":[],"sources":["../server/smoke/cli.ts"],"sourcesContent":["// `xaq smoke google-workspace --scratch`: run on xaq's machine, through the daemon's admin API over the local admin\n// channel (server/connections-cli/admin.ts) and /mcp as a temporary agent. See run.ts for what it does.\n\nimport type { Command } from \"commander\";\nimport { LEGACY_PRODUCT } from \"../brand.ts\";\nimport { resolveDataDir } from \"../config.ts\";\nimport { NPX } from \"../listen/version.ts\";\nimport type { SmokeFlags } from \"./run.ts\";\n\nconst GROUP = \"MCP connections (on xaq's machine):\";\n\n/**\n * A line printer that can't kill the run: once the stream fails (EIO or EPIPE from a terminal that went away, e.g.\n * after SIGHUP), later lines are dropped instead of throwing or crashing on an unhandled 'error'.\n */\nexport function safeWriter(stream: NodeJS.WritableStream): { print: (line?: string) => void; dispose: () => void } {\n let dead = false;\n const onError = () => {\n dead = true;\n };\n stream.on(\"error\", onError);\n return {\n print(line = \"\") {\n if (dead) return;\n try {\n stream.write(`${line}\\n`, (err) => {\n if (err) dead = true;\n });\n } catch {\n dead = true;\n }\n },\n dispose: () => stream.removeListener(\"error\", onError),\n };\n}\n\nexport interface SmokeSignalOptions {\n print: (line: string) => void;\n exit: (code: number) => void;\n /** The second Ctrl-C: the quick part of cleanup (agent, attachmentDirs), bounded. */\n fastCleanup: () => Promise<void>;\n /** What may still be there, in a sentence. */\n leftovers: () => string;\n /** Upper bound on the second Ctrl-C's cleanup before quitting anyway (default 8 s). */\n quitWithinMs?: number;\n}\n\n/**\n * Signals during a run. SIGTERM and SIGHUP (a closed terminal, a stopped service) always act like the first Ctrl-C:\n * no new step starts and the run's full cleanup runs. The first Ctrl-C does the same; a second one runs the quick\n * cleanup (delete the agent, restore attachmentDirs) with a short timeout, says what may be left and quits.\n * Returns the remover.\n */\nexport function installSmokeSignals(controller: AbortController, opts: SmokeSignalOptions): () => void {\n let interrupts = 0;\n let quitting = false;\n const stop = (why: string) => {\n if (controller.signal.aborted) return;\n opts.print(`\\n${why}: stopping after the current call, then cleaning up.${why === \"Ctrl-C\" ? \" Press Ctrl-C again to quit sooner (only the agent and attachmentDirs are cleaned up then).\" : \"\"}`);\n controller.abort();\n };\n const onInt = () => {\n interrupts++;\n if (interrupts === 1) return stop(\"Ctrl-C\");\n if (quitting) return;\n quitting = true;\n opts.print(\"\\nQuitting: deleting the temporary agent and restoring attachmentDirs first…\");\n const deadline = new Promise<void>((r) => setTimeout(r, opts.quitWithinMs ?? 8000).unref?.());\n void Promise.race([opts.fastCleanup().catch(() => {}), deadline]).then(() => {\n opts.print(`Quit before the cleanup finished. ${opts.leftovers()}`);\n opts.exit(130);\n });\n };\n const onTerm = () => stop(\"SIGTERM\");\n const onHup = () => stop(\"SIGHUP\");\n process.on(\"SIGINT\", onInt);\n process.on(\"SIGTERM\", onTerm);\n process.on(\"SIGHUP\", onHup);\n return () => {\n process.removeListener(\"SIGINT\", onInt);\n process.removeListener(\"SIGTERM\", onTerm);\n process.removeListener(\"SIGHUP\", onHup);\n };\n}\n\nexport function registerSmokeCommands(program: Command): void {\n const smoke = program\n .command(\"smoke\")\n .description(\"live end-to-end checks against the connected accounts, safe by construction\")\n .summary(\"live end-to-end check of a connection\")\n .helpGroup(GROUP);\n\n smoke\n .command(\"google-workspace\")\n .description(\"check Gmail attachments and the google-workspace extension end to end on the real account: reads anything, writes only into a new scratch folder and ONE draft to itself, cleans up\")\n .option(\"--scratch\", \"required: confirms the run may create its scratch folder, its draft and its temporary agent (all removed again)\")\n .option(\"--account <email>\", \"the Gmail account (default: the only one, or the one the Workspace connection is signed in as)\")\n .option(\"--connection <slug>\", \"the google-workspace connection (default: the only one)\")\n .option(\"--json\", \"print the result table as JSON on stdout (progress goes to stderr)\")\n .option(\"-y, --yes\", \"don't ask before running\")\n .option(\"-d, --data <dir>\", `xaq's data directory (default ~/.xaq, or an existing ~/.${LEGACY_PRODUCT}; env XAQ_DATA)`)\n .addHelpText(\n \"after\",\n `\nPrints the plan first and asks (or --yes). Exit code 0 only when every step passes and nothing is left behind.\n\nExample:\n ${NPX} smoke google-workspace --scratch`,\n )\n .action(async (o: SmokeFlags & { data?: string }) => {\n const { openAdminChannel } = await import(\"../connections-cli/admin.ts\");\n const { TerminalPrompter } = await import(\"../connections-cli/prompt.ts\");\n const { connectAgentMcp } = await import(\"./agent.ts\");\n const { SmokeRun } = await import(\"./run.ts\");\n const { renderReport } = await import(\"./report.ts\");\n if (!o.scratch) {\n throw new Error(\n \"smoke google-workspace runs live on the connected Google account: it reads existing files and mail, and writes only into a new scratch folder and ONE draft to the account itself, which it removes again. Pass --scratch to run it.\",\n );\n }\n const out = safeWriter(o.json ? process.stderr : process.stdout);\n const err = o.json ? out : safeWriter(process.stderr);\n const json = o.json ? safeWriter(process.stdout) : null;\n const channel = openAdminChannel({ dataDir: resolveDataDir(o.data, { quiet: true }) });\n const prompter = new TerminalPrompter(!!o.yes, { input: process.stdin, output: o.json ? process.stderr : process.stdout });\n const controller = new AbortController();\n const run = new SmokeRun(o, {\n admin: channel,\n connectMcp: (key) => connectAgentMcp(channel.baseUrl, key),\n print: out.print,\n confirm: (q) => prompter.confirm(q, false),\n signal: controller.signal,\n });\n const remove = installSmokeSignals(controller, {\n print: (l) => err.print(l),\n fastCleanup: () => run.fastCleanup(3000),\n exit: (code) => {\n channel.close();\n process.exit(code);\n },\n leftovers: () => {\n const left = run.pendingLeftovers();\n return left.length ? `These may be left: ${left.join(\"; \")}. The next run removes the agent and the attachmentDirs entry and lists the rest.` : \"Nothing was left.\";\n },\n });\n try {\n const report = await run.run();\n if (json) json.print(JSON.stringify(report, null, 2));\n else for (const l of [\"\", ...renderReport(report)]) out.print(l);\n process.exitCode = report.ok ? 0 : report.interrupted ? 130 : 1;\n } finally {\n remove();\n prompter.close();\n channel.close();\n out.dispose();\n if (err !== out) err.dispose();\n json?.dispose();\n }\n });\n}\n"],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -59,7 +59,7 @@ const SERVER_GROUP = "Run xaq:";
59
59
  const AGENT_GROUP = "On an agent's machine:";
60
60
  withServerOptions(program.command("start", { isDefault: true }).description("run xaq in this terminal and print the setup link (the default command)").summary("run it in this terminal (the default command)").helpGroup(SERVER_GROUP)).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) => {
61
61
  if (cmd.args.length) throw new Error(unknownCommand(cmd.args[0]));
62
- const { runForeground } = await import("./server-DTxOHzZJ.js");
62
+ const { runForeground } = await import("./server-0wxSpx6s.js");
63
63
  await runForeground({
64
64
  ...configInput(o),
65
65
  dev: !!o.dev,
@@ -70,16 +70,16 @@ withServerOptions(program.command("start", { isDefault: true }).description("run
70
70
  const service = program.command("service").description("run xaq in the background as a systemd / launchd service, started at boot").summary("run it in the background (systemd / launchd)").helpGroup(SERVER_GROUP);
71
71
  withServerOptions(service.command("install").description("install and start the service; re-run it to change the port or update (replaces an agentinbox service, keeping its data)")).option("--node <path>", "the Node.js binary the service runs (default: this one if new enough, else the previous service's, else the newest found)").action(async (o) => {
72
72
  const { loadConfig } = await import("./config-CAqDP8xi.js");
73
- const { installService, serviceInstallInput, systemOps } = await import("./service-C2nRdQnH.js");
73
+ const { installService, serviceInstallInput, systemOps } = await import("./service-Cp8j8469.js");
74
74
  await installService(loadConfig(serviceInstallInput(configInput(o), systemOps()), version), void 0, { node: o.node });
75
75
  });
76
76
  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 ~/.xaq, or an existing ~/.agentinbox; env XAQ_DATA)").action(async (o) => {
77
- const { uninstallService } = await import("./service-C2nRdQnH.js");
77
+ const { uninstallService } = await import("./service-Cp8j8469.js");
78
78
  const { resolveDataDir } = await import("./config-CAqDP8xi.js");
79
79
  uninstallService({ dataDir: resolveDataDir(o.data, { quiet: true }) });
80
80
  });
81
81
  service.command("status").description("show service status, and check the Node.js it runs (code extensions need a recent one)").action(async () => {
82
- const { serviceStatus } = await import("./service-C2nRdQnH.js");
82
+ const { serviceStatus } = await import("./service-Cp8j8469.js");
83
83
  serviceStatus();
84
84
  });
85
85
  const skill = program.command("skill").description("the xaq agent skill (SKILL.md): teaches an agent how to use xaq's tools").summary("install the agent skill (SKILL.md)").helpGroup(AGENT_GROUP);
@@ -106,7 +106,7 @@ program.command("reset-password").description("forgot the console password? Remo
106
106
  const { loadConfig } = await import("./config-CAqDP8xi.js");
107
107
  const { Db } = await import("./db-Br2FHvQ-.js");
108
108
  const { createBus } = await import("./bus-OKIGut72.js");
109
- const { SettingsStore } = await import("./settings-DlwSOO5Y.js");
109
+ const { SettingsStore } = await import("./settings-fUNWD1cQ.js");
110
110
  const { AuditLog } = await import("./audit-hQnqfGxA.js");
111
111
  const { currentSetupToken, isClaimed, resetAdminPassword } = await import("./auth-CcvstbI5.js");
112
112
  const { runningInstance } = await import("./instance-BsILpEQ7.js");
@@ -138,13 +138,13 @@ program.command("reset-password").description("forgot the console password? Remo
138
138
  db.close();
139
139
  }
140
140
  });
141
- const { registerListenCommands } = await import("./commands-Bo6-7SBs.js");
141
+ const { registerListenCommands } = await import("./commands-DAd3ICss.js");
142
142
  registerListenCommands(program);
143
- const { registerConnectionsCommands } = await import("./commands-BKr8Ejx0.js");
143
+ const { registerConnectionsCommands } = await import("./commands-CgmkYCYG.js");
144
144
  registerConnectionsCommands(program);
145
145
  const { registerExtensionCommands } = await import("./cli-B8cuKWiS.js");
146
146
  registerExtensionCommands(program);
147
- const { registerSmokeCommands } = await import("./cli-C9lES1ug.js");
147
+ const { registerSmokeCommands } = await import("./cli-Wp2ejVBT.js");
148
148
  registerSmokeCommands(program);
149
149
  const { registerSettingsCommands } = await import("./settings-cli-DfWc-yWJ.js");
150
150
  registerSettingsCommands(program);
@@ -87,7 +87,7 @@ with the single xaq entry, after a timestamped backup. The entry keeps the name
87
87
  Examples:
88
88
  ${NPX} import --from hermes --dry-run
89
89
  ${NPX} import --from hermes`).action(async (o) => {
90
- const { runImport } = await import("./import-cL9vj0jq.js");
90
+ const { runImport } = await import("./import-y1bjWoGq.js");
91
91
  const self = await selfOrigins(dataDirOf(o));
92
92
  if (o.dryRun) {
93
93
  const { TerminalPrompter } = await import("./prompt-BEH311I7.js");
@@ -115,4 +115,4 @@ Examples:
115
115
  //#endregion
116
116
  export { registerConnectionsCommands };
117
117
 
118
- //# sourceMappingURL=commands-BKr8Ejx0.js.map
118
+ //# sourceMappingURL=commands-CgmkYCYG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands-BKr8Ejx0.js","names":[],"sources":["../server/connections-cli/commands.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { NPX } from \"../listen/version.ts\";\nimport { LEGACY_PRODUCT } from \"../brand.ts\";\nimport { resolveDataDir } from \"../config.ts\";\n\n/**\n * `connections …` and `import --from …`: run on the daemon's machine, talking to its admin API through the\n * one-time local admin session (admin.ts). Listed in --help since the console (#65) shipped; while the daemon's\n * `brokerUpstreams` setting is off they say so (CONNECTIONS_VISIBLE).\n */\nexport const CONNECTIONS_VISIBLE = true;\nconst GROUP = \"MCP connections (on xaq's machine):\";\n\nconst collect = (value: string, previous: string[] | undefined) => [...(previous ?? []), value];\n// --data, else XAQ_DATA (AGENTINBOX_DATA), else ~/.xaq or an existing pre-rename ~/.agentinbox (config.ts; rename-xaq: keep).\nconst dataDirOf = (o: { data?: string }) => resolveDataDir(o.data, { quiet: true });\nconst dataOption = (cmd: Command) => cmd.option(\"-d, --data <dir>\", `xaq's data directory (default ~/.xaq, or an existing ~/.${LEGACY_PRODUCT}; env XAQ_DATA)`);\n\nasync function withApi<T>(o: { data?: string; yes?: boolean }, run: (deps: { api: import(\"./api.ts\").ConnectionsApi; prompter: import(\"./prompt.ts\").Prompter }) => Promise<T>): Promise<T> {\n const { CLEANUP_SIGNALS, openAdminChannel } = await import(\"./admin.ts\");\n const { ConnectionsApi } = await import(\"./api.ts\");\n const { TerminalPrompter } = await import(\"./prompt.ts\");\n const channel = openAdminChannel({ dataDir: dataDirOf(o) });\n const handlers = CLEANUP_SIGNALS.map((sig) => {\n const h = () => {\n channel.close();\n process.exit(sig === \"SIGINT\" ? 130 : 128 + (sig === \"SIGTERM\" ? 15 : 1));\n };\n process.once(sig, h);\n return [sig, h] as const;\n });\n try {\n return await run({ api: new ConnectionsApi(channel), prompter: new TerminalPrompter(!!o.yes) });\n } finally {\n for (const [sig, h] of handlers) process.removeListener(sig, h);\n channel.close();\n }\n}\n\n/** This xaq's own MCP origins, so `import` never imports its own entry. */\nasync function selfOrigins(dataDir: string): Promise<string[]> {\n const { runningInstance } = await import(\"../instance.ts\");\n const { daemonOrigin } = await import(\"./admin.ts\");\n const running = runningInstance(dataDir);\n if (!running) return [];\n return [daemonOrigin(running), `http://localhost:${running.port}`, `http://127.0.0.1:${running.port}`, `http://[::1]:${running.port}`];\n}\n\nexport function registerConnectionsCommands(program: Command): void {\n const connections = program\n .command(\"connections\", { hidden: !CONNECTIONS_VISIBLE })\n .description(\"the MCP servers xaq connects agents to (Notion, T3 Code, any MCP URL, installed extensions); run on xaq's machine\")\n .summary(\"add, list, reconnect or remove connected MCP servers\")\n .helpGroup(GROUP);\n\n dataOption(\n connections\n .command(\"add\")\n .description(\"connect an installed extension (install it first: `ext add notion`) or any MCP server URL, interactively\")\n .argument(\"<integration-or-url>\", \"an installed extension (`ext add notion|t3code|whoop|brightspace` first), or an MCP server URL such as https://mcp.example.com/mcp\"),\n )\n .option(\"--url <address>\", \"t3code: your T3 Code server's address, e.g. http://100.101.102.103:3773 (asked when omitted)\")\n .option(\"--slug <name>\", \"its short name: agents see its tools as <name>__<tool>\")\n .option(\"--name <name>\", \"its display name (unique; default: the server's host, or the integration's name)\")\n .option(\"--access <level>\", \"full, ask (ask before risky actions; the default) or read\")\n .option(\"--agent <name>\", 'an agent that gets it (repeatable; default: all agents; \"all\")', collect)\n .option(\"--header <name>\", \"header for a server that wants a static key (default Authorization)\")\n .option(\"--account <email>\", \"a Google extension (google-workspace): the connected Google account it works as (asked when omitted)\")\n .option(\"--paste\", \"paste the address the browser lands on after signing in, instead of waiting for the redirect (automatic over SSH)\")\n .option(\"--allow-private <origin>\", \"allow this exact scheme://host:port on this machine or a private network without asking (--yes never does)\")\n .option(\"-y, --yes\", \"answer yes to confirmations and take the defaults (never to a private-network allow)\")\n .addHelpText(\n \"after\",\n `\nExamples:\n ${NPX} connections add notion\n ${NPX} connections add google-workspace --account you@example.com # turn on its Google APIs, then allow access\n ${NPX} connections add t3code --url http://100.101.102.103:3773 # one per T3 server; have a T3 pairing code ready\n ${NPX} connections add https://mcp.example.com/mcp --access read\n ${NPX} connections add http://127.0.0.1:8787/mcp # asks before allowing a server on this machine`,\n )\n .action(async (target: string, o: import(\"./manage.ts\").AddFlags & { data?: string }) => {\n const { addConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => addConnection(target, o, deps));\n });\n\n dataOption(connections.command(\"list\").description(\"list connections with their status and access\")).action(async (o: { data?: string }) => {\n const { listConnections } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => listConnections(deps));\n });\n\n dataOption(connections.command(\"reconnect\").description(\"sign in again (OAuth) or replace the key (header)\").argument(\"<name>\", \"the connection's name\"))\n .option(\"--paste\", \"paste the address the browser lands on (automatic over SSH)\")\n .option(\"--header <name>\", \"header connections: the header's name (default: asks, Authorization)\")\n .action(async (slug: string, o: { data?: string; paste?: boolean; header?: string }) => {\n const { reconnectConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => reconnectConnection(slug, o, deps));\n });\n\n dataOption(connections.command(\"disable\").description(\"turn a connection off at once (the kill switch); turn it back on in the console\").argument(\"<name>\", \"the connection's name\"))\n .option(\"-y, --yes\", \"don't ask\")\n .action(async (slug: string, o: { data?: string; yes?: boolean }) => {\n const { disableConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => disableConnection(slug, deps));\n });\n\n dataOption(\n connections\n .command(\"rename\")\n .description(\"change a connection's short name (agents see its tools as <name>__<tool>) or its display name\")\n .argument(\"<name>\", \"the connection's current short name (or its id)\"),\n )\n .option(\"--slug <new>\", \"the new short name: 1-24 lowercase letters, digits and dashes (calls by the old tool names stop working)\")\n .option(\"--name <display-name>\", \"a new display name\")\n .option(\"-y, --yes\", \"don't ask\")\n .action(async (target: string, o: { data?: string; yes?: boolean; slug?: string; name?: string }) => {\n const { renameConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => renameConnection(target, o, deps));\n });\n\n dataOption(connections.command(\"remove\").description(\"remove a connection and delete its credentials\").argument(\"<name>\", \"the connection's name\"))\n .option(\"-y, --yes\", \"don't ask\")\n .action(async (slug: string, o: { data?: string; yes?: boolean }) => {\n const { removeConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => removeConnection(slug, deps));\n });\n\n dataOption(\n program\n .command(\"import\", { hidden: !CONNECTIONS_VISIBLE })\n .description(\"move an agent tool's MCP servers into xaq: one connection each, credentials moved into xaq\")\n .summary(\"import MCP servers from Hermes, Claude Code, Cursor or Codex\")\n .helpGroup(GROUP)\n .requiredOption(\"--from <client>\", \"hermes, claude-code, cursor or codex\"),\n )\n .option(\"--dry-run\", \"print exactly what would be created and changed, and change nothing\")\n .option(\"--access <level>\", \"for every imported connection: full, ask (the default) or read\")\n .option(\"--agent <name>\", 'an agent that gets them (repeatable; default: all agents; \"all\")', collect)\n .option(\"--profile <name>\", \"the agent profile setup-mcp uses for the single xaq entry (default \\\"default\\\")\")\n .option(\"--allow-private <origin>\", \"allow this exact scheme://host:port on this machine or a private network without asking (repeatable; --yes never does)\", collect)\n .option(\"-y, --yes\", \"answer yes to the confirmations (scripts); never to a private-network allow or a project's .mcp.json servers\")\n .addHelpText(\n \"after\",\n `\nReads: hermes ~/.hermes/config.yaml mcp_servers (+ ~/.hermes/.env) · claude-code ~/.claude.json and ./.mcp.json ·\ncursor ~/.cursor/mcp.json · codex ~/.codex/config.toml. Static headers move into xaq (encrypted); OAuth\nservers are created needing Connect (their tokens stay in the client); local (stdio) servers aren't supported yet\nand stay where they are. Servers on this machine or a private network are created only after you confirm the exact\nhost:port. Then it offers setup-mcp --tool <client>, which replaces the entries that now work through xaq\nwith the single xaq entry, after a timestamped backup. The entry keeps the name the profile already uses (\"xaq\", or\n\"${LEGACY_PRODUCT}\" for a profile set up before the rename), so Hermes routes and cron jobs confined to it keep working.\n\nExamples:\n ${NPX} import --from hermes --dry-run\n ${NPX} import --from hermes`,\n )\n .action(async (o: import(\"./import.ts\").ImportFlags & { data?: string }) => {\n const { runImport } = await import(\"./import.ts\");\n const self = await selfOrigins(dataDirOf(o));\n if (o.dryRun) {\n // Read-only: the admin channel (when the daemon runs) only lists existing connections.\n const { TerminalPrompter } = await import(\"./prompt.ts\");\n const { openAdminChannel } = await import(\"./admin.ts\");\n const { ConnectionsApi } = await import(\"./api.ts\");\n let channel: import(\"./admin.ts\").AdminChannel | null = null;\n try {\n process.exitCode = await runImport(o, {\n prompter: new TerminalPrompter(!!o.yes),\n openApi: () => new ConnectionsApi((channel = openAdminChannel({ dataDir: dataDirOf(o) }))),\n selfOrigins: self,\n });\n } finally {\n (channel as import(\"./admin.ts\").AdminChannel | null)?.close();\n }\n return;\n }\n process.exitCode = await withApi(o, (deps) => runImport(o, { prompter: deps.prompter, openApi: () => deps.api, selfOrigins: self }));\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"commands-CgmkYCYG.js","names":[],"sources":["../server/connections-cli/commands.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { NPX } from \"../listen/version.ts\";\nimport { LEGACY_PRODUCT } from \"../brand.ts\";\nimport { resolveDataDir } from \"../config.ts\";\n\n/**\n * `connections …` and `import --from …`: run on the daemon's machine, talking to its admin API through the\n * one-time local admin session (admin.ts). Listed in --help since the console (#65) shipped; while the daemon's\n * `brokerUpstreams` setting is off they say so (CONNECTIONS_VISIBLE).\n */\nexport const CONNECTIONS_VISIBLE = true;\nconst GROUP = \"MCP connections (on xaq's machine):\";\n\nconst collect = (value: string, previous: string[] | undefined) => [...(previous ?? []), value];\n// --data, else XAQ_DATA (AGENTINBOX_DATA), else ~/.xaq or an existing pre-rename ~/.agentinbox (config.ts; rename-xaq: keep).\nconst dataDirOf = (o: { data?: string }) => resolveDataDir(o.data, { quiet: true });\nconst dataOption = (cmd: Command) => cmd.option(\"-d, --data <dir>\", `xaq's data directory (default ~/.xaq, or an existing ~/.${LEGACY_PRODUCT}; env XAQ_DATA)`);\n\nasync function withApi<T>(o: { data?: string; yes?: boolean }, run: (deps: { api: import(\"./api.ts\").ConnectionsApi; prompter: import(\"./prompt.ts\").Prompter }) => Promise<T>): Promise<T> {\n const { CLEANUP_SIGNALS, openAdminChannel } = await import(\"./admin.ts\");\n const { ConnectionsApi } = await import(\"./api.ts\");\n const { TerminalPrompter } = await import(\"./prompt.ts\");\n const channel = openAdminChannel({ dataDir: dataDirOf(o) });\n const handlers = CLEANUP_SIGNALS.map((sig) => {\n const h = () => {\n channel.close();\n process.exit(sig === \"SIGINT\" ? 130 : 128 + (sig === \"SIGTERM\" ? 15 : 1));\n };\n process.once(sig, h);\n return [sig, h] as const;\n });\n try {\n return await run({ api: new ConnectionsApi(channel), prompter: new TerminalPrompter(!!o.yes) });\n } finally {\n for (const [sig, h] of handlers) process.removeListener(sig, h);\n channel.close();\n }\n}\n\n/** This xaq's own MCP origins, so `import` never imports its own entry. */\nasync function selfOrigins(dataDir: string): Promise<string[]> {\n const { runningInstance } = await import(\"../instance.ts\");\n const { daemonOrigin } = await import(\"./admin.ts\");\n const running = runningInstance(dataDir);\n if (!running) return [];\n return [daemonOrigin(running), `http://localhost:${running.port}`, `http://127.0.0.1:${running.port}`, `http://[::1]:${running.port}`];\n}\n\nexport function registerConnectionsCommands(program: Command): void {\n const connections = program\n .command(\"connections\", { hidden: !CONNECTIONS_VISIBLE })\n .description(\"the MCP servers xaq connects agents to (Notion, T3 Code, any MCP URL, installed extensions); run on xaq's machine\")\n .summary(\"add, list, reconnect or remove connected MCP servers\")\n .helpGroup(GROUP);\n\n dataOption(\n connections\n .command(\"add\")\n .description(\"connect an installed extension (install it first: `ext add notion`) or any MCP server URL, interactively\")\n .argument(\"<integration-or-url>\", \"an installed extension (`ext add notion|t3code|whoop|brightspace` first), or an MCP server URL such as https://mcp.example.com/mcp\"),\n )\n .option(\"--url <address>\", \"t3code: your T3 Code server's address, e.g. http://100.101.102.103:3773 (asked when omitted)\")\n .option(\"--slug <name>\", \"its short name: agents see its tools as <name>__<tool>\")\n .option(\"--name <name>\", \"its display name (unique; default: the server's host, or the integration's name)\")\n .option(\"--access <level>\", \"full, ask (ask before risky actions; the default) or read\")\n .option(\"--agent <name>\", 'an agent that gets it (repeatable; default: all agents; \"all\")', collect)\n .option(\"--header <name>\", \"header for a server that wants a static key (default Authorization)\")\n .option(\"--account <email>\", \"a Google extension (google-workspace): the connected Google account it works as (asked when omitted)\")\n .option(\"--paste\", \"paste the address the browser lands on after signing in, instead of waiting for the redirect (automatic over SSH)\")\n .option(\"--allow-private <origin>\", \"allow this exact scheme://host:port on this machine or a private network without asking (--yes never does)\")\n .option(\"-y, --yes\", \"answer yes to confirmations and take the defaults (never to a private-network allow)\")\n .addHelpText(\n \"after\",\n `\nExamples:\n ${NPX} connections add notion\n ${NPX} connections add google-workspace --account you@example.com # turn on its Google APIs, then allow access\n ${NPX} connections add t3code --url http://100.101.102.103:3773 # one per T3 server; have a T3 pairing code ready\n ${NPX} connections add https://mcp.example.com/mcp --access read\n ${NPX} connections add http://127.0.0.1:8787/mcp # asks before allowing a server on this machine`,\n )\n .action(async (target: string, o: import(\"./manage.ts\").AddFlags & { data?: string }) => {\n const { addConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => addConnection(target, o, deps));\n });\n\n dataOption(connections.command(\"list\").description(\"list connections with their status and access\")).action(async (o: { data?: string }) => {\n const { listConnections } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => listConnections(deps));\n });\n\n dataOption(connections.command(\"reconnect\").description(\"sign in again (OAuth) or replace the key (header)\").argument(\"<name>\", \"the connection's name\"))\n .option(\"--paste\", \"paste the address the browser lands on (automatic over SSH)\")\n .option(\"--header <name>\", \"header connections: the header's name (default: asks, Authorization)\")\n .action(async (slug: string, o: { data?: string; paste?: boolean; header?: string }) => {\n const { reconnectConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => reconnectConnection(slug, o, deps));\n });\n\n dataOption(connections.command(\"disable\").description(\"turn a connection off at once (the kill switch); turn it back on in the console\").argument(\"<name>\", \"the connection's name\"))\n .option(\"-y, --yes\", \"don't ask\")\n .action(async (slug: string, o: { data?: string; yes?: boolean }) => {\n const { disableConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => disableConnection(slug, deps));\n });\n\n dataOption(\n connections\n .command(\"rename\")\n .description(\"change a connection's short name (agents see its tools as <name>__<tool>) or its display name\")\n .argument(\"<name>\", \"the connection's current short name (or its id)\"),\n )\n .option(\"--slug <new>\", \"the new short name: 1-24 lowercase letters, digits and dashes (calls by the old tool names stop working)\")\n .option(\"--name <display-name>\", \"a new display name\")\n .option(\"-y, --yes\", \"don't ask\")\n .action(async (target: string, o: { data?: string; yes?: boolean; slug?: string; name?: string }) => {\n const { renameConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => renameConnection(target, o, deps));\n });\n\n dataOption(connections.command(\"remove\").description(\"remove a connection and delete its credentials\").argument(\"<name>\", \"the connection's name\"))\n .option(\"-y, --yes\", \"don't ask\")\n .action(async (slug: string, o: { data?: string; yes?: boolean }) => {\n const { removeConnection } = await import(\"./manage.ts\");\n process.exitCode = await withApi(o, (deps) => removeConnection(slug, deps));\n });\n\n dataOption(\n program\n .command(\"import\", { hidden: !CONNECTIONS_VISIBLE })\n .description(\"move an agent tool's MCP servers into xaq: one connection each, credentials moved into xaq\")\n .summary(\"import MCP servers from Hermes, Claude Code, Cursor or Codex\")\n .helpGroup(GROUP)\n .requiredOption(\"--from <client>\", \"hermes, claude-code, cursor or codex\"),\n )\n .option(\"--dry-run\", \"print exactly what would be created and changed, and change nothing\")\n .option(\"--access <level>\", \"for every imported connection: full, ask (the default) or read\")\n .option(\"--agent <name>\", 'an agent that gets them (repeatable; default: all agents; \"all\")', collect)\n .option(\"--profile <name>\", \"the agent profile setup-mcp uses for the single xaq entry (default \\\"default\\\")\")\n .option(\"--allow-private <origin>\", \"allow this exact scheme://host:port on this machine or a private network without asking (repeatable; --yes never does)\", collect)\n .option(\"-y, --yes\", \"answer yes to the confirmations (scripts); never to a private-network allow or a project's .mcp.json servers\")\n .addHelpText(\n \"after\",\n `\nReads: hermes ~/.hermes/config.yaml mcp_servers (+ ~/.hermes/.env) · claude-code ~/.claude.json and ./.mcp.json ·\ncursor ~/.cursor/mcp.json · codex ~/.codex/config.toml. Static headers move into xaq (encrypted); OAuth\nservers are created needing Connect (their tokens stay in the client); local (stdio) servers aren't supported yet\nand stay where they are. Servers on this machine or a private network are created only after you confirm the exact\nhost:port. Then it offers setup-mcp --tool <client>, which replaces the entries that now work through xaq\nwith the single xaq entry, after a timestamped backup. The entry keeps the name the profile already uses (\"xaq\", or\n\"${LEGACY_PRODUCT}\" for a profile set up before the rename), so Hermes routes and cron jobs confined to it keep working.\n\nExamples:\n ${NPX} import --from hermes --dry-run\n ${NPX} import --from hermes`,\n )\n .action(async (o: import(\"./import.ts\").ImportFlags & { data?: string }) => {\n const { runImport } = await import(\"./import.ts\");\n const self = await selfOrigins(dataDirOf(o));\n if (o.dryRun) {\n // Read-only: the admin channel (when the daemon runs) only lists existing connections.\n const { TerminalPrompter } = await import(\"./prompt.ts\");\n const { openAdminChannel } = await import(\"./admin.ts\");\n const { ConnectionsApi } = await import(\"./api.ts\");\n let channel: import(\"./admin.ts\").AdminChannel | null = null;\n try {\n process.exitCode = await runImport(o, {\n prompter: new TerminalPrompter(!!o.yes),\n openApi: () => new ConnectionsApi((channel = openAdminChannel({ dataDir: dataDirOf(o) }))),\n selfOrigins: self,\n });\n } finally {\n (channel as import(\"./admin.ts\").AdminChannel | null)?.close();\n }\n return;\n }\n process.exitCode = await withApi(o, (deps) => runImport(o, { prompter: deps.prompter, openApi: () => deps.api, selfOrigins: self }));\n });\n}\n"],"mappings":"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"}