@botpress/adk-cli
Version:
Command-line interface for the Botpress Agent Development Kit (ADK)
315 lines (312 loc) • 11.7 kB
JavaScript
// @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 };