UNPKG

@botpress/adk-cli

Version:

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

315 lines (312 loc) 11.7 kB
// @bun import { buildDevServerHeaders } from "./chunk-ndxsgd72.js"; import { resolveCommandContext } from "./chunk-3ahwp6fe.js"; import { readConsolePort } from "./chunk-sgj6770p.js"; import { findAgentRootOrFail } from "./chunk-kk3h6qaj.js"; import { source_default } from "./chunk-gzwt1qdr.js"; import { AgentProject, TableManager } from "./chunk-p0hjqn4r.js"; // src/status/index.ts var ADK_DEV_CONSOLE_PORTS = Array.from({ length: 10 }, (_, index) => 3001 + index); var HEALTH_TIMEOUT_MS = 500; var CONFIG_TIMEOUT_MS = 1000; function getDefinitionName(value) { return typeof value === "string" && value.length > 0 ? value : undefined; } function getPrimitiveDescription(value) { return typeof value === "string" ? value : undefined; } function buildPrimitiveDetail(name, path, description) { const detail = { name, path }; if (description !== undefined) { detail.description = description; } return detail; } function getPrimitiveFallbackName(ref, primitiveType) { if (ref.export !== "default") { return ref.export; } if (ref.path === "<adk:builtin>") { return `${primitiveType}:builtin`; } return `${primitiveType}:${ref.path}`; } function getConversationChannelName(channel) { if (typeof channel === "string") { return channel; } if (Array.isArray(channel) && channel.length > 0) { return channel.join(", "); } return "unknown-channel"; } function getConversationName(ref) { const channelName = getConversationChannelName(ref.definition.channel); return ref.export === "default" ? `conversation @ ${channelName}` : `${ref.export} @ ${channelName}`; } function mapPrimitiveReferences(refs, primitiveType, getName = (ref) => getDefinitionName(ref.definition.name) ?? getPrimitiveFallbackName(ref, primitiveType)) { return refs.map((ref) => buildPrimitiveDetail(getName(ref), ref.path, getPrimitiveDescription(ref.definition.description))); } function mapToolReferences(tools) { return tools.map((tool) => buildPrimitiveDetail(getDefinitionName(tool.definition.name) ?? getPrimitiveFallbackName(tool, "tool"), tool.path, getPrimitiveDescription(tool.definition.description))); } function summarizePrimitiveDetails(details) { return { count: details.length, details }; } function getProjectTools(project) { return project.tools ?? []; } function summarizeActions(project) { const details = mapPrimitiveReferences(project.actions, "action"); return { count: details.length, details, names: details.map((detail) => detail.name) }; } function getIntegrationSchema(integration) { const definition = integration.definition; if (!definition) { return; } const configurationType = integration.configurationType; if (configurationType && definition.configurations?.[configurationType]?.schema) { return definition.configurations[configurationType].schema; } if (definition.configurations?.default?.schema) { return definition.configurations.default.schema; } return definition.configuration?.schema; } function getMissingConfigFields(schema, config) { if (!schema || !Array.isArray(schema.required)) { return []; } return schema.required.filter((key) => typeof key === "string").filter((key) => { const property = schema.properties?.[key]; if (property?.default !== undefined) { return false; } return config?.[key] === undefined; }); } function getProjectIntegrations(project) { return project.integrations; } function summarizeIntegrations(project) { return getProjectIntegrations(project).map((integration) => { const missing = getMissingConfigFields(getIntegrationSchema(integration), integration.config); const status = missing.length > 0 ? "unconfigured" : "configured"; return { alias: integration.alias, name: integration.definition?.name ?? integration.ref.name, version: integration.ref.version, status, ...missing.length > 0 ? { missing } : {} }; }); } function getAdkDevConsolePorts(persistedPort = readConsolePort()) { const ports = persistedPort === null ? ADK_DEV_CONSOLE_PORTS : [persistedPort, ...ADK_DEV_CONSOLE_PORTS]; return [...new Set(ports)]; } async function fetchJson(url, timeoutMs, headers = {}, fetchImpl = fetch) { try { const response = await fetchImpl(url, { headers, signal: AbortSignal.timeout(timeoutMs) }); if (!response.ok) { return null; } return await response.json(); } catch { return null; } } async function findMatchingAdkDevConsole(projectPath, options = {}) { const headers = options.headers ?? buildDevServerHeaders(projectPath); const ports = options.ports ?? getAdkDevConsolePorts(); const fetchImpl = options.fetch ?? fetch; const matches = await Promise.all(ports.map(async (port) => { const url = `http://localhost:${port}`; const health = await fetchJson(`${url}/api/health`, HEALTH_TIMEOUT_MS, headers, fetchImpl); if (!health || health.agentPath !== projectPath) { return null; } const config = await fetchJson(`${url}/api/config`, CONFIG_TIMEOUT_MS, headers, fetchImpl) ?? {}; return { port, url, health, config }; })); return matches.find((match) => match !== null) ?? null; } async function summarizeSupportingTables(project, botId, options) { if (!botId || project.tables.length === 0) { return; } try { const context = await resolveCommandContext({ cwd: options.cwd, target: options.target, require: ["project", "credentials", "workspace"], createClient: false }); const tableManager = new TableManager({ project, botId, credentials: context.credentials }); const syncPlan = await tableManager.createSyncPlan(); const items = syncPlan.items.filter((item) => item.localTable && item.operation !== "delete").map((item) => ({ name: item.localTable.name, status: item.operation === "none" ? "synced" : "out_of_sync", operation: item.operation, reason: item.reason })); return { synced: items.filter((item) => item.status === "synced").length, outOfSync: items.filter((item) => item.status === "out_of_sync").length, items }; } catch { return; } } async function collectProjectStatus(options = {}) { const agentRoot = await findAgentRootOrFail(options.cwd ?? process.cwd()); const project = await AgentProject.load(agentRoot, { noCache: options.noCache, offline: options.offline }); const adkDevConsole = await findMatchingAdkDevConsole(project.path); const devBot = adkDevConsole?.config.devBot; const actionSummary = summarizeActions(project); const status = { project: { name: project.config?.name ?? "unknown", path: project.path }, primitives: { actions: actionSummary, tools: summarizePrimitiveDetails(mapToolReferences(getProjectTools(project))), workflows: summarizePrimitiveDetails(mapPrimitiveReferences(project.workflows, "workflow")), conversations: summarizePrimitiveDetails(mapPrimitiveReferences(project.conversations, "conversation", getConversationName)), triggers: summarizePrimitiveDetails(mapPrimitiveReferences(project.triggers, "trigger")), tables: summarizePrimitiveDetails(mapPrimitiveReferences(project.tables, "table")), knowledge: summarizePrimitiveDetails(mapPrimitiveReferences(project.knowledge, "knowledge")) }, integrations: summarizeIntegrations(project), devServer: { running: Boolean(devBot?.running), port: devBot?.running ? devBot.port ?? null : null, botId: devBot?.running ? devBot.botId ?? null : null, url: devBot?.running ? devBot.url ?? null : null }, adkDevConsole: adkDevConsole ? { running: true, port: adkDevConsole.port, url: adkDevConsole.url } : { running: false, port: null, url: null } }; const supportingTables = await summarizeSupportingTables(project, status.devServer.botId ?? project.agentInfo?.devId ?? project.agentInfo?.botId ?? null, { cwd: options.cwd ?? process.cwd(), target: status.devServer.botId || project.agentInfo?.devId ? "dev" : "prod" }); if (supportingTables && supportingTables.items.length > 0) { status.supportingTables = supportingTables; } return status; } function renderServerLine(label, server) { if (!server.running) { return `${source_default.bold(label)} ${source_default.yellow("stopped")}`; } const details = [server.url]; if (server.botId) { details.push(`bot ${server.botId}`); } return `${source_default.bold(label)} ${source_default.green("running")} ${source_default.gray(details.filter(Boolean).join(" \u2022 "))}`; } function renderPrimitiveSection(title, summary) { if (summary.details.length === 0) { return [source_default.bold(`${title} (${summary.count})`), source_default.gray(" (none)")]; } return [ source_default.bold(`${title} (${summary.count})`), ...summary.details.flatMap((detail) => { const lines = [` ${detail.name}`, source_default.gray(` path: ${detail.path}`)]; if (detail.description) { lines.push(source_default.gray(` description: ${detail.description}`)); } return lines; }) ]; } function renderProjectStatus(status) { const primitiveSummary = [ `actions ${status.primitives.actions.count}`, `tools ${status.primitives.tools.count}`, `workflows ${status.primitives.workflows.count}`, `conversations ${status.primitives.conversations.count}`, `triggers ${status.primitives.triggers.count}`, `tables ${status.primitives.tables.count}`, `knowledge ${status.primitives.knowledge.count}` ].join(source_default.gray(" \u2022 ")); const integrationLines = status.integrations.length > 0 ? status.integrations.map((integration) => { const marker = integration.status === "configured" ? source_default.green("\u2713") : source_default.yellow("!"); const missing = integration.missing?.length ? ` ${source_default.gray(`missing: ${integration.missing.join(", ")}`)}` : ""; return ` ${marker} ${integration.alias} ${source_default.gray(`${integration.version}`)}${missing}`; }) : [source_default.gray(" (none)")]; const lines = [ `${source_default.bold(status.project.name)} ${source_default.gray(`(${status.project.path})`)}`, primitiveSummary, "", ...renderPrimitiveSection("Actions", status.primitives.actions), "", ...renderPrimitiveSection("Tools", status.primitives.tools), "", ...renderPrimitiveSection("Workflows", status.primitives.workflows), "", ...renderPrimitiveSection("Conversations", status.primitives.conversations), "", ...renderPrimitiveSection("Triggers", status.primitives.triggers), "", ...renderPrimitiveSection("Tables", status.primitives.tables), "", ...renderPrimitiveSection("Knowledge", status.primitives.knowledge), "", source_default.bold("Integrations"), ...integrationLines, "", renderServerLine("Dev server", status.devServer), renderServerLine("Console ", status.adkDevConsole) ]; if (status.supportingTables) { lines.push("", source_default.bold("Supporting tables")); for (const item of status.supportingTables.items) { const marker = item.status === "synced" ? source_default.green("\u2713") : source_default.yellow("!"); const operation = item.operation === "none" ? "" : ` ${source_default.gray(`(${item.operation})`)}`; lines.push(` ${marker} ${item.name}${operation}`); } } return lines.join(` `); } export { findMatchingAdkDevConsole, collectProjectStatus, renderProjectStatus };