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.
- package/dist/{admin-UKJE67mn.js → admin-5555baeh.js} +5 -5
- package/dist/{admin-UKJE67mn.js.map → admin-5555baeh.js.map} +1 -1
- package/dist/attachment-parser.js +43 -29
- package/dist/{cli-C9lES1ug.js → cli-Wp2ejVBT.js} +2 -2
- package/dist/{cli-C9lES1ug.js.map → cli-Wp2ejVBT.js.map} +1 -1
- package/dist/cli.js +8 -8
- package/dist/{commands-BKr8Ejx0.js → commands-CgmkYCYG.js} +2 -2
- package/dist/{commands-BKr8Ejx0.js.map → commands-CgmkYCYG.js.map} +1 -1
- package/dist/{commands-Bo6-7SBs.js → commands-DAd3ICss.js} +16 -16
- package/dist/{commands-Bo6-7SBs.js.map → commands-DAd3ICss.js.map} +1 -1
- package/dist/{connect-xUMBNHSZ.js → connect-CNECpz7N.js} +7 -7
- package/dist/{connect-xUMBNHSZ.js.map → connect-CNECpz7N.js.map} +1 -1
- package/dist/{hermes-Dic9rVz6.js → hermes-B_YoOADz.js} +3 -3
- package/dist/{hermes-Dic9rVz6.js.map → hermes-B_YoOADz.js.map} +1 -1
- package/dist/hermes-BwsRDFyI.js +3 -0
- package/dist/{import-cL9vj0jq.js → import-y1bjWoGq.js} +3 -3
- package/dist/{import-cL9vj0jq.js.map → import-y1bjWoGq.js.map} +1 -1
- package/dist/listen-B6TV_qUM.js +2 -0
- package/dist/{listen-C642JHIc.js → listen-DLZVpzxM.js} +4 -4
- package/dist/{listen-C642JHIc.js.map → listen-DLZVpzxM.js.map} +1 -1
- package/dist/{mcpheader-D94qF32t.js → mcpheader-C3EhpA7D.js} +58 -8
- package/dist/mcpheader-C3EhpA7D.js.map +1 -0
- package/dist/mcpheader-DOwvJTUJ.js +2 -0
- package/dist/{mcpsetup-xVCFyAEU.js → mcpsetup-ChBN1058.js} +3 -3
- package/dist/{mcpsetup-xVCFyAEU.js.map → mcpsetup-ChBN1058.js.map} +1 -1
- package/dist/mcpsetup-sfobhJnl.js +2 -0
- package/dist/{own-dirs-D61k1PPA.js → own-dirs-BEu3fcm3.js} +2 -2
- package/dist/{own-dirs-D61k1PPA.js.map → own-dirs-BEu3fcm3.js.map} +1 -1
- package/dist/recipes-BOhG7jcd.js +3 -0
- package/dist/{recipes-V8_-S1TM.js → recipes-BsCIHrrP.js} +2 -2
- package/dist/{recipes-V8_-S1TM.js.map → recipes-BsCIHrrP.js.map} +1 -1
- package/dist/{run-C0fpnYAb.js → run-zakbVgQR.js} +41 -11
- package/dist/run-zakbVgQR.js.map +1 -0
- package/dist/{server-DTxOHzZJ.js → server-0wxSpx6s.js} +15 -5
- package/dist/server-0wxSpx6s.js.map +1 -0
- package/dist/{service-CaLi0ECi.js → service-B-VzLRzc.js} +1 -1
- package/dist/{service-D5AMNljC.js → service-CjiEwfyC.js} +21 -276
- package/dist/service-CjiEwfyC.js.map +1 -0
- package/dist/{service-C2nRdQnH.js → service-Cp8j8469.js} +1 -1
- package/dist/{service-C-bf-vFa.js → service-e8sBNMSS.js} +6 -6
- package/dist/{service-C-bf-vFa.js.map → service-e8sBNMSS.js.map} +1 -1
- package/dist/service-node-BT7957Z9.js +289 -0
- package/dist/service-node-BT7957Z9.js.map +1 -0
- package/dist/{settings-CDJkgV6P.js → settings-BgxlT1bP.js} +2 -2
- package/dist/{settings-CDJkgV6P.js.map → settings-BgxlT1bP.js.map} +1 -1
- package/dist/settings-fUNWD1cQ.js +2 -0
- package/dist/skill/SKILL.md +3 -2
- package/dist/{smoke-Db1MSKwT.js → smoke-ZqEGwDQl.js} +64 -13
- package/dist/{smoke-Db1MSKwT.js.map → smoke-ZqEGwDQl.js.map} +1 -1
- package/dist/{spawn-CAmF_QFI.js → spawn-Dk6A4Bz4.js} +28 -4
- package/dist/spawn-Dk6A4Bz4.js.map +1 -0
- package/package.json +1 -1
- package/dist/hermes-DcrI-TtP.js +0 -3
- package/dist/listen-CcZUPf3p.js +0 -2
- package/dist/mcpheader-CK2dhg-I.js +0 -2
- package/dist/mcpheader-D94qF32t.js.map +0 -1
- package/dist/mcpsetup-C9mGhinf.js +0 -2
- package/dist/recipes-CwyZRyXM.js +0 -3
- package/dist/run-C0fpnYAb.js.map +0 -1
- package/dist/server-DTxOHzZJ.js.map +0 -1
- package/dist/service-D5AMNljC.js.map +0 -1
- package/dist/settings-DlwSOO5Y.js +0 -2
- 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-
|
|
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-
|
|
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-
|
|
9
|
-
import { r as installedServiceLabel } from "./service-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
141
|
+
const { registerListenCommands } = await import("./commands-DAd3ICss.js");
|
|
142
142
|
registerListenCommands(program);
|
|
143
|
-
const { registerConnectionsCommands } = await import("./commands-
|
|
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-
|
|
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-
|
|
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-
|
|
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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"}
|