UNPKG

@botpress/adk-cli

Version:

Command-line interface for the Botpress Agent Development Kit (ADK)

221 lines (218 loc) 7.42 kB
// @bun import { ensureJsonOnlyFormat } from "./chunk-7sfagm12.js"; import { isAdkDevConsolePort, readConsolePort } from "./chunk-sgj6770p.js"; import { createCliLogger } from "./chunk-gzwt1qdr.js"; import { AdkError } from "./chunk-p0hjqn4r.js"; // src/commands/adk-ps.ts var FETCH_TIMEOUT_MS = 2000; async function fetchProcesses(port) { try { const res = await fetch(`http://localhost:${port}/api/processes`, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) }); if (res.ok) { const body = await res.json(); if (body && Array.isArray(body.agents)) { return body; } } else { await res.body?.cancel(); } } catch {} try { const res = await fetch(`http://localhost:${port}/api/agents`, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) }); if (!res.ok) { await res.body?.cancel(); return null; } const body = await res.json(); if (!body || !Array.isArray(body.agents)) return null; return { console: { pid: 0, port, url: `http://localhost:${port}`, mode: "unknown" }, agents: body.agents }; } catch { return null; } } function formatDuration(ms) { const seconds = Math.floor(ms / 1000); if (seconds < 60) return `${seconds}s`; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m`; const hours = Math.floor(minutes / 60); return `${hours}h${minutes % 60}m`; } function formatUptime(startedAt) { if (!startedAt) return "unknown"; return formatDuration(Date.now() - startedAt); } var DASH = "-"; function renderColumns(headers, rows) { const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length))); const lastIdx = headers.length - 1; const formatRow = (cells) => cells.map((c, i) => i === lastIdx ? c : c.padEnd(widths[i])).join(" ").trimEnd(); return [formatRow(headers), ...rows.map(formatRow)]; } function pidCell(value, isCloud) { if (isCloud) return DASH; return value != null && value > 0 ? String(value) : "unknown"; } function buildAgentRow(agent, wide) { const isCloud = agent.consoleMode === "cloud"; const statusParts = [agent.status]; if (isCloud) statusParts.push("cloud"); if (agent.outdated) statusParts.push("outdated"); const status = statusParts.join(", "); const adkPid = pidCell(agent.process?.adkPid, isCloud); const uptime = isCloud ? DASH : formatUptime(agent.process?.startedAt); if (wide) { const runtime = agent.runtimeVersion && agent.runtimeVersion !== "unknown" ? agent.runtimeVersion : DASH; const backend = isCloud ? DASH : String(agent.backendPort); const bot = isCloud ? DASH : String(agent.botPort); const botPid = pidCell(agent.process?.botPid, isCloud); return [agent.name, status, runtime, backend, bot, adkPid, botPid, uptime, agent.agentPath]; } const ports = isCloud ? DASH : `be:${agent.backendPort} bot:${agent.botPort}`; return [agent.name, status, ports, adkPid, uptime]; } function renderTable(data, opts = {}) { const lines = []; const wide = !!opts.wide; const localAgents = data.agents.filter((a) => a.consoleMode !== "cloud"); const cloudAgents = data.agents.filter((a) => a.consoleMode === "cloud"); const includeCloud = !!opts.cloud; const visibleAgents = includeCloud ? [...localAgents, ...cloudAgents] : localAgents; const modeSuffix = data.console.mode && data.console.mode !== "unknown" ? ` (${data.console.mode})` : ""; const consolePidStr = data.console.pid > 0 ? String(data.console.pid) : "unknown"; lines.push(`DevConsole ${data.console.url}${modeSuffix} pid ${consolePidStr}`); lines.push(""); const cloudSummary = includeCloud ? `, ${cloudAgents.length} cloud` : ""; lines.push(`Agents (${localAgents.length} local${cloudSummary})`); lines.push(""); if (visibleAgents.length === 0) { lines.push(" No agents connected. Start one with `adk dev`."); } else { const headers = wide ? ["NAME", "STATUS", "RUNTIME", "BACKEND", "BOT", "ADK PID", "BOT PID", "UPTIME", "PATH"] : ["NAME", "STATUS", "PORTS", "PID", "UPTIME"]; const rows = visibleAgents.map((a) => buildAgentRow(a, wide)); lines.push(...renderColumns(headers, rows).map((l) => " " + l)); } if (!includeCloud && cloudAgents.length > 0) { lines.push(""); lines.push(` (${cloudAgents.length} cloud selection${cloudAgents.length === 1 ? "" : "s"} not shown \u2014 pass --cloud to include)`); } if (!wide && localAgents.length > 0) { lines.push(""); lines.push(" (pass --wide for runtime version, both PIDs, and agent path)"); } return lines.join(` `); } async function adkPs(options = {}) { ensureJsonOnlyFormat(options.format); const logger = createCliLogger({ format: options.format }); if (options.watch && options.format === "json") { logger.fatal(new AdkError({ code: "INVALID_FLAGS", message: "--watch is not supported with --format json. Use text mode for watch output.", expected: true })); } const port = readConsolePort(); if (port === null) { if (options.format === "json") { logger.info("No DevConsole running").result({ console: null, agents: [] }); return; } logger.info("No ADK processes are running. Start one with `adk dev` or `adk dashboard`."); return; } if (!await isAdkDevConsolePort(port)) { if (options.format === "json") { logger.info(`Stale console.port (${port})`).result({ console: null, agents: [] }); return; } logger.info(`No ADK processes are running. ` + `(~/.adk/console.port points at port ${port} but nothing is listening \u2014 run \`adk kill --all\` to clear stale state.)`); return; } const render = async () => { let data; try { data = await fetchProcesses(port); } catch (err) { if (options.format === "json") { logger.fatal(new AdkError({ code: "DEVCONSOLE_UNREACHABLE", message: `Failed to reach DevConsole: ${err instanceof Error ? err.message : String(err)}`, expected: true })); } else { throw err; } return; } if (!data) { if (options.format === "json") { logger.info(`Failed to fetch processes from port ${port}`).result({ console: null, agents: [] }); return; } logger.fatal(new AdkError({ code: "DEVCONSOLE_UNREACHABLE", message: `Failed to fetch process list from DevConsole at port ${port}`, expected: true })); return; } if (options.format === "json") { logger.info(`DevConsole at port ${port}`).result(data); return; } const table = renderTable(data, options); logger.info(table).result(data); }; if (!options.watch) { await render(); return; } const intervalSeconds = typeof options.watch === "number" ? Math.max(1, options.watch) : 2; process.stdout.write("\x1B[2J\x1B[0f"); await render(); let rendering = false; const timer = setInterval(async () => { if (rendering) return; rendering = true; try { process.stdout.write("\x1B[2J\x1B[0f"); await render(); } finally { rendering = false; } }, intervalSeconds * 1000); await new Promise((resolve) => { process.once("SIGINT", () => { clearInterval(timer); resolve(); }); }); } export { fetchProcesses, renderTable, adkPs };