@botpress/adk-cli
Version:
Command-line interface for the Botpress Agent Development Kit (ADK)
680 lines (675 loc) • 25 kB
JavaScript
// @bun
import {
displayWorkspaceInfo
} from "./chunk-5fy9y5qt.js";
import {
buildAndUploadComponents,
computeEvalManifestPlan,
refreshDependencySnapshotOnce,
runProdDeployPipeline
} from "./chunk-xkcrb68a.js";
import {
adkBuild
} from "./chunk-ttvctaf1.js";
import"./chunk-wmec2w0t.js";
import {
identifyUser
} from "./chunk-6zha718h.js";
import {
ensureJsonOnlyFormat
} from "./chunk-7sfagm12.js";
import"./chunk-r72adjnh.js";
import {
telemetry_default
} from "./chunk-kwmsaz7n.js";
import"./chunk-26vqkz52.js";
import {
resolveCommandContext
} from "./chunk-3ahwp6fe.js";
import {
ADK_LOGO_LINE1,
ADK_LOGO_LINE2
} from "./chunk-9e2nksab.js";
import"./chunk-m2h26j5f.js";
import"./chunk-8gqzjqmb.js";
import"./chunk-kk3h6qaj.js";
import {
createCliLogger,
source_default
} from "./chunk-gzwt1qdr.js";
import"./chunk-nxy2ya5r.js";
import {
sanitizeErrorMessage
} from "./chunk-wzj4dc7n.js";
import {
AdkError,
PreflightChecker,
SecretsManager,
assertNoBlockingDependencies,
isAdkError,
pluralize2,
summarizeBlockingDependencies
} from "./chunk-p0hjqn4r.js";
import"./chunk-np5wcwfv.js";
import"./chunk-dq2xpa24.js";
import"./chunk-6w0knnta.js";
import"./chunk-40x04ckt.js";
import"./chunk-t76d8fxx.js";
import"./chunk-nh2akp42.js";
import"./chunk-0fdvzjbh.js";
import"./chunk-2a5b6azq.js";
import"./chunk-vay209b5.js";
import"./chunk-3xrpxgq4.js";
import"./chunk-rfm3jr1m.js";
import"./chunk-w346ejn9.js";
import"./chunk-knvm2anf.js";
import"./chunk-65h5trb5.js";
import"./chunk-s2akeqpw.js";
import"./chunk-6771vrjp.js";
import"./chunk-g8mm42v1.js";
import"./chunk-50hzjdck.js";
import"./chunk-nn2jb0x0.js";
import"./chunk-v8xvth6j.js";
import"./chunk-kkk13rcb.js";
import"./chunk-ytpp1kam.js";
import"./chunk-na956zz3.js";
import"./chunk-f4bw8q7c.js";
import"./chunk-0v8vgrns.js";
import"./chunk-54qt5g7m.js";
import {
__require
} from "./chunk-dhs2bg35.js";
// src/utils/preflight-formatter.ts
var purple = source_default.hex("#D2A6FF");
function formatValue(value) {
if (value === undefined || value === null) {
return source_default.gray("(not set)");
}
if (typeof value === "string") {
return `"${value}"`;
}
if (typeof value === "boolean") {
return value ? source_default.green("true") : source_default.red("false");
}
if (Array.isArray(value)) {
return `[${value.map((v) => formatValue(v)).join(", ")}]`;
}
if (typeof value === "object") {
return JSON.stringify(value);
}
return String(value);
}
function pushSecretWarnings(lines, warnings, env) {
const required = warnings.filter((w) => !w.optional);
const optional = warnings.filter((w) => w.optional);
const flag = env === "prod" ? " --prod" : "";
const label = env === "prod" ? " (prod)" : "";
lines.push(source_default.bold.yellow(`Missing Secrets${label}`) + `
`);
if (required.length > 0) {
lines.push(source_default.red(" Required (must be set before use):") + `
`);
for (const warning of required) {
const desc = warning.description ? `: ${warning.description}` : "";
lines.push(` \u2014 ${warning.name}${desc}`);
lines.push(` ${source_default.gray("Set with:")} ${source_default.cyan(`adk secret:set ${warning.name}${flag}`)}
`);
}
}
if (optional.length > 0) {
lines.push(source_default.yellow(" Optional (can be set later):") + `
`);
for (const warning of optional) {
const desc = warning.description ? `: ${warning.description}` : "";
lines.push(` \u2014 ${warning.name}${desc}`);
lines.push(` ${source_default.gray("Set with:")} ${source_default.cyan(`adk secret:set ${warning.name}${flag}`)}
`);
}
}
if (env === "dev") {
lines.push(source_default.gray(" For local development, export SECRET_<NAME>=... in your environment."));
}
lines.push(source_default.gray(" Never commit .env files containing secrets to version control.") + `
`);
}
class PreflightFormatter {
static format(result) {
const lines = [];
lines.push("");
lines.push(purple(` ${ADK_LOGO_LINE1} Botpress ADK`));
lines.push(purple(` ${ADK_LOGO_LINE2} Preflight Check`));
lines.push("");
lines.push(purple("Running preflight checks..."));
lines.push("");
if (result.agentConfig.length > 0) {
lines.push(source_default.bold("Agent Configuration Differences"));
lines.push(source_default.gray("(These are top-level agent settings)") + `
`);
for (const diff of result.agentConfig) {
lines.push(` agent.${diff.field}:`);
lines.push(` ${source_default.red(`- Remote: ${formatValue(diff.oldValue)}`)}`);
lines.push(` ${source_default.green(`+ Local: ${formatValue(diff.newValue)}`)}
`);
}
}
if (result.secretWarnings && result.secretWarnings.length > 0) {
pushSecretWarnings(lines, result.secretWarnings, result.env);
}
lines.push(source_default.bold("Summary of Actions") + `
`);
const agentConfig = result.agentConfig.length;
if (agentConfig > 0) {
lines.push(` \u2022 ${source_default.cyan("Agent config:")} ${pluralize2(agentConfig, "change")}`);
}
lines.push("");
return lines.join(`
`);
}
static formatPrompt() {
return source_default.bold.red(`\u2753 Apply these changes before continuing?
`) + source_default.gray(`[y] apply \xB7 [n] deploy without applying \xB7 [Esc] cancel deployment
`) + source_default.gray("Proceed? [y/N] ");
}
}
// src/utils/configuration-size-warning.ts
var CONFIGURATION_DATA_WARNING_THRESHOLD_BYTES = 9 * 1024;
function getSerializedConfigurationDataSize(config) {
return new TextEncoder().encode(JSON.stringify(config)).byteLength;
}
function getConfigurationDataSizeWarning(config) {
const serializedBytes = getSerializedConfigurationDataSize(config);
if (serializedBytes <= CONFIGURATION_DATA_WARNING_THRESHOLD_BYTES) {
return null;
}
return {
serializedBytes,
thresholdBytes: CONFIGURATION_DATA_WARNING_THRESHOLD_BYTES
};
}
function formatConfigurationDataSizeWarning(warning) {
return `
\u26A0\uFE0F Serialized configuration data is ${warning.serializedBytes.toLocaleString("en-US")} bytes.
` + ` Configurations above ${warning.thresholdBytes / 1024} KiB may exceed runtime request-header limits and make the deployed bot unresponsive.
` + " Move large values to tables, files, or another storage service before deploying.";
}
// src/commands/adk-deploy.ts
var ANSI_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
var PROGRESS_PREFIXES = ["\u25CB", "\u2714", "\u2713", "\u256D", "\u2502", "\u2570"];
function extractDeployErrorMessage(error) {
if (!(error instanceof Error)) {
return String(error);
}
const extra = error;
if (typeof extra.stderr === "string" && extra.stderr.trim()) {
const lines = extra.stderr.trim().split(`
`);
for (let i = lines.length - 1;i >= 0; i--) {
const line = lines[i].replace(ANSI_RE, "").trim();
if (line && !PROGRESS_PREFIXES.some((p) => line.startsWith(p))) {
return line;
}
}
}
return error.message;
}
function formatDeployPlan(plan, logger, options) {
const hint = options?.showConfirmHints ? " (requires --confirm-storage-changes)" : "";
const integrationVersionMismatches = plan.dependencyPlan.integrationVersionMismatches;
if (integrationVersionMismatches.length > 0) {
logger.warn(`
\u26A0\uFE0F Integration versions differ between dev and prod:`);
for (const mismatch of integrationVersionMismatches) {
logger.warn(` \u2022 ${mismatch.alias}: dev ${mismatch.devVersion} \u2192 prod ${mismatch.prodVersion}`);
}
logger.warn(" Run `adk integrations copy --from dev --to prod --dry-run` to preview, then `adk integrations copy --from dev --to prod --yes` to promote dev integrations to prod.");
}
if (plan.preflight.result.hasChanges) {
logger.info(PreflightFormatter.format(plan.preflight.result));
}
if (plan.tablePlan?.hasChanges) {
logger.info(`
\uD83D\uDCCA Tables:`, "cyan");
if (plan.tablePlan.totalCreate > 0)
logger.info(` \u2022 ${plan.tablePlan.totalCreate} to create`, "green");
if (plan.tablePlan.totalUpdate > 0)
logger.info(` \u2022 ${plan.tablePlan.totalUpdate} to update`, "cyan");
if (plan.tablePlan.totalDelete > 0)
logger.warn(` \u2022 ${plan.tablePlan.totalDelete} to delete${hint}`);
}
if (plan.kbPlan?.hasChanges) {
logger.info(`
\uD83D\uDCDA Knowledge bases:`, "cyan");
logger.info(` \u2022 ${plan.kbPlan.toSync} to sync, ${plan.kbPlan.toSkip} up to date`);
if (plan.kbPlan.orphanedSourcesToDelete > 0) {
logger.warn(` \u2022 ${plan.kbPlan.orphanedSourcesToDelete} orphaned sources to remove${hint}`);
}
}
if (plan.orphanedKBs.length > 0) {
logger.warn(`
\u26A0\uFE0F ${plan.orphanedKBs.length} remote KB(s) not defined locally:`);
for (const kb of plan.orphanedKBs) {
logger.warn(` \u2022 ${kb.name}`);
}
if (options?.showConfirmHints) {
logger.warn(` Requires --confirm-storage-changes to delete`);
}
}
if (plan.assetPlan?.hasChanges) {
logger.info(`
\uD83D\uDCC1 Assets:`, "cyan");
if (plan.assetPlan.totalCreate > 0)
logger.info(` \u2022 ${plan.assetPlan.totalCreate} to upload`, "green");
if (plan.assetPlan.totalUpdate > 0)
logger.info(` \u2022 ${plan.assetPlan.totalUpdate} to update`, "cyan");
if (plan.assetPlan.totalDelete > 0)
logger.warn(` \u2022 ${plan.assetPlan.totalDelete} to delete${hint}`);
}
const evalCount = options?.evalManifestPlan?.evalCount ?? 0;
if (evalCount > 0) {
logger.info(`
\uD83E\uDDEA Evals:`, "cyan");
logger.info(` \u2022 ${evalCount} eval manifest ${evalCount === 1 ? "entry" : "entries"} to publish`, "green");
}
}
function logPipelineStageStart(stage, logger) {
switch (stage) {
case "deploy":
logger.info(`
\uD83D\uDCE4 Deploying bot to Botpress...`, "cyan");
return;
case "manifest":
logger.info(`
\uD83E\uDDFE Publishing prod metadata...`, "cyan");
return;
case "kb-sync":
logger.info(`
\uD83D\uDCDA Syncing knowledge bases...`, "cyan");
return;
case "tables":
logger.info(`
\uD83D\uDCCA Syncing tables...`, "cyan");
return;
case "assets":
logger.info(`
\uD83D\uDCC1 Syncing assets...`, "cyan");
return;
case "eval-manifest":
logger.info(`
\uD83E\uDDEA Publishing eval manifest...`, "cyan");
return;
case "preflight":
return;
}
}
function logPipelineStageComplete(stage, detail, logger, deployment) {
switch (stage) {
case "deploy":
logger.info(`
\u2705 Agent deployed successfully!`, "green");
logger.info(`\uD83C\uDF10 Your agent is now live on Botpress`, "gray");
logger.info(`\uD83E\uDD16 Bot ID: ${deployment.botId}`, "gray");
logger.info(`\uD83C\uDFE2 Workspace ID: ${deployment.workspaceId}`, "gray");
return;
case "manifest":
if (typeof detail?.agentMapSnapshotWarning === "string") {
logger.warn(`\u26A0\uFE0F Failed to publish Agent Map metadata: ${detail.agentMapSnapshotWarning}`);
logger.warn(" Redeploy again to retry publishing Agent Map metadata.");
}
logger.info("\u2705 Prod metadata published", "green");
return;
case "kb-sync":
logger.info(` \u2705 Knowledge bases synced${formatCountDetail(detail, ["synced", "skipped"])}`, "green");
return;
case "tables":
logger.info(` \u2705 Tables synced${formatCountDetail(detail, ["created", "updated", "deleted", "skipped"])}`, "green");
return;
case "assets":
logger.info(` \u2705 Assets synced${formatCountDetail(detail, ["created", "updated", "deleted", "skipped"])}`, "green");
return;
case "eval-manifest": {
const uploaded = typeof detail?.uploaded === "number" ? detail.uploaded : 0;
if (uploaded > 0) {
logger.info(`\u2705 Eval manifest uploaded (${uploaded} eval${uploaded === 1 ? "" : "s"})`, "green");
} else {
logger.info(" No evals to publish", "gray");
}
return;
}
case "preflight":
return;
}
}
function logPipelineStageError(stage, error, logger, event) {
const message = error instanceof Error ? error.message : String(error);
if (stage === "manifest" && event?.nonFatal) {
logger.warn(`\u26A0\uFE0F Failed to publish prod metadata: ${message}`);
logger.warn(" The bot was deployed. Run deploy again to retry publishing prod metadata.");
return;
}
if (stage === "deploy") {
logger.error(`\u274C Failed to deploy bot: ${message}`);
return;
}
if (stage === "preflight") {
logger.error(`\u274C Failed to apply config changes: ${message}`);
return;
}
if (stage === "eval-manifest") {
logger.error(`\u274C Failed to publish eval manifest: ${message}`);
return;
}
const label = stage === "kb-sync" ? "knowledge bases" : stage;
logger.error(`\u274C Failed to sync ${label}: ${message}`);
if (stage === "tables") {
for (const failure of getTableSyncFailureDetails(event?.detail)) {
logger.error(` \u2022 ${failure.table} (${failure.operation}, ${failure.errorType}): ${failure.message}`);
}
}
}
function getTableSyncFailureDetails(detail) {
if (!Array.isArray(detail?.failures))
return [];
return detail.failures.filter((failure) => {
if (!failure || typeof failure !== "object")
return false;
const value = failure;
return typeof value.table === "string" && typeof value.operation === "string" && typeof value.errorType === "string" && typeof value.message === "string";
});
}
function formatCountDetail(detail, keys) {
if (!detail)
return "";
const parts = keys.map((key) => typeof detail[key] === "number" && detail[key] > 0 ? `${detail[key]} ${key}` : null).filter((part) => Boolean(part));
return parts.length > 0 ? `: ${parts.join(", ")}` : "";
}
async function promptUserApproval() {
const { promptApproval } = await import("./chunk-6afthdr4.js");
return promptApproval(PreflightFormatter.formatPrompt(), process.stdout);
}
async function adkDeploy(environment = "production", options = {}) {
ensureJsonOnlyFormat(options.format);
const isJson = options.format === "json";
const logger = createCliLogger({ format: options.format });
if (isJson && !options.autoApprove && !options.dryRun) {
throw new AdkError({
code: "JSON_REQUIRES_YES",
message: "--format json requires --yes or --dry-run flag",
expected: true
});
}
logger.info("\uD83D\uDE80 Deploying ADK agent to Botpress...", "blue");
logger.info(`Environment: ${environment}`, "gray");
let stage = "auth";
try {
stage = "load";
const context = await resolveCommandContext({
target: "prod",
projectLoadOptions: { adkCommand: "adk-deploy" },
require: ["project", "credentials", "workspace", "bot"]
});
const agentRoot = context.agentRoot;
const project = context.project;
const botId = context.botId;
const workspaceId = context.workspaceId;
const credentials = context.credentials;
const client = context.client;
await refreshDependencySnapshotOnce({
projectPath: agentRoot,
env: "prod",
botId,
client,
logger,
event: "dependency-refresh",
required: true
});
await refreshDependencySnapshotOnce({
projectPath: agentRoot,
env: "dev",
botId: project.agentInfo?.devId,
client,
logger,
event: "dependency-refresh",
required: false
});
await identifyUser(agentRoot).catch(() => {});
if (!isJson) {
await displayWorkspaceInfo({ context });
}
stage = "build";
logger.info(`
\uD83C\uDFD7\uFE0F Building agent project...`, "cyan");
await adkBuild({
silent: true,
adkCommand: "adk-deploy",
beforeGenerate: project.customComponents.length > 0 ? async () => {
logger.info(`
\uD83E\uDDE9 Building and uploading custom components...`, "cyan");
await buildAndUploadComponents({
agentRoot,
project,
client,
log: (msg) => logger.info(` ${msg}`, "gray")
});
} : undefined
});
stage = "config";
logger.info(`
\uD83D\uDD27 Validating configuration...`, "cyan");
let configurationData = {};
try {
const { validateAndPromptConfig } = await import("./chunk-jzzzp7t0.js");
const configResult = await validateAndPromptConfig({
projectPath: agentRoot,
isProd: true,
interactive: !isJson,
botId,
project,
credentials,
client
});
configurationData = configResult.config ?? {};
if (!configResult.valid) {
logger.error(`
\u2717 Configuration validation failed:`);
configResult.errors.forEach((err) => logger.error(` \u2022 ${err}`));
throw new AdkError({
code: "CONFIG_VALIDATION_FAILED",
message: "Configuration validation failed",
expected: true,
details: { errors: configResult.errors }
});
}
} catch (error) {
if (!(isAdkError(error) && error.code === "INVALID_CONFIG_SCHEMA")) {
throw error;
}
const { bot } = await client.getBot({ id: botId });
configurationData = bot.configuration?.data ?? {};
}
const configurationDataSizeWarning = getConfigurationDataSizeWarning(configurationData);
if (configurationDataSizeWarning) {
logger.warn(formatConfigurationDataSizeWarning(configurationDataSizeWarning));
}
stage = "preflight";
logger.info("\uD83D\uDD0D Computing deploy plan...", "cyan");
const checker = new PreflightChecker(agentRoot, { credentials });
const plan = await checker.computeDeployPlan(botId);
const evalManifestPlan = await computeEvalManifestPlan(agentRoot);
if (!options.dryRun) {
assertNoBlockingDependencies(plan, { allowUnconfigured: options.allowUnconfigured });
}
const hasAnyChanges = plan.preflight.result.hasChanges || plan.tablePlan?.hasChanges || plan.kbPlan?.hasChanges || plan.orphanedKBs.length > 0 || plan.assetPlan?.hasChanges;
if (options.dryRun) {
if (isJson) {
const jsonPlan = {
preflight: {
hasChanges: plan.preflight.result.hasChanges,
agentConfig: plan.preflight.result.agentConfig,
secretWarnings: plan.preflight.result.secretWarnings
},
tables: plan.tablePlan ? {
hasChanges: plan.tablePlan.hasChanges,
totalCreate: plan.tablePlan.totalCreate,
totalUpdate: plan.tablePlan.totalUpdate,
totalDelete: plan.tablePlan.totalDelete
} : null,
knowledgeBases: plan.kbPlan ? {
hasChanges: plan.kbPlan.hasChanges,
toSync: plan.kbPlan.toSync,
toSkip: plan.kbPlan.toSkip,
orphanedKBs: plan.orphanedKBs.map((kb) => kb.name)
} : null,
assets: plan.assetPlan ? {
hasChanges: plan.assetPlan.hasChanges,
totalCreate: plan.assetPlan.totalCreate,
totalUpdate: plan.assetPlan.totalUpdate,
totalDelete: plan.assetPlan.totalDelete
} : null,
dependencies: {
blocking: summarizeBlockingDependencies(plan.dependencyPlan.blocking),
integrationVersionMismatches: plan.dependencyPlan.integrationVersionMismatches
},
evalManifest: { evalCount: evalManifestPlan.evalCount },
hasDestructiveStorageChanges: plan.hasDestructiveStorageChanges
};
logger.info("").result(jsonPlan);
} else {
formatDeployPlan(plan, logger, { showConfirmHints: true, evalManifestPlan });
if (plan.hasDestructiveStorageChanges) {
logger.warn(`
Destructive storage changes require: --confirm-storage-changes`);
}
logger.info(`
\uD83D\uDD0D Dry run \u2014 no changes applied.`);
}
return;
}
let interactivelyApproved = false;
let planApproved = true;
if (hasAnyChanges) {
formatDeployPlan(plan, logger, { evalManifestPlan });
if (options.autoApprove) {
if (plan.hasDestructiveStorageChanges && !options.confirmStorageChanges) {
logger.error(`
\u2717 Destructive storage changes require explicit confirmation:`);
logger.error(` --confirm-storage-changes`);
throw new AdkError({
code: "STORAGE_CONFIRM_REQUIRED",
message: "Destructive storage changes require --confirm-storage-changes",
expected: true
});
}
logger.info(`
\u2713 Auto-approved. Applying changes...
`, "green");
} else {
const choice = await promptUserApproval();
if (choice === "cancel") {
logger.error(`
\u2717 Deployment cancelled by user.
`);
return;
}
if (choice === "yes") {
interactivelyApproved = true;
} else {
planApproved = false;
logger.warn(`
\u26A0\uFE0F Plan updates declined \u2014 deploying the bot code without applying these changes.`);
logger.warn(" Your bot may not work as expected until the plan updates are applied.");
logger.warn(" Re-run `adk deploy` and approve the plan to apply them.\n");
}
}
} else {
formatDeployPlan(plan, logger, { evalManifestPlan });
if (evalManifestPlan.evalCount > 0)
logger.info("");
logger.info(`\u2713 No resource changes detected. Proceeding...
`, "gray");
}
stage = "secrets";
const declaredSecrets = project.config?.secrets ?? {};
const secretsManager = new SecretsManager(project.path);
const prodSecrets = await secretsManager.getAll("prod", declaredSecrets);
const missingRequired = Object.entries(declaredSecrets).filter(([name, def]) => !def.optional && !(name in prodSecrets)).map(([name]) => name);
if (missingRequired.length > 0) {
throw new AdkError({
code: "MISSING_SECRETS",
message: `Missing required prod secrets: ${missingRequired.join(", ")}
Set them with: ${missingRequired.map((n) => `adk secret:set ${n} <value> --prod`).join(", ")}`,
expected: true
});
}
const confirmStorage = interactivelyApproved || (options.confirmStorageChanges ?? false);
let deployTelemetryTracked = false;
const deployResult = await runProdDeployPipeline({
agentRoot,
project,
plan,
botId,
workspaceId,
credentials,
secrets: Object.keys(prodSecrets).length > 0 ? prodSecrets : undefined,
client,
confirmStorageChanges: confirmStorage,
applyPlanUpdates: planApproved,
evalManifestPlan,
callbacks: {
onStageStart: (nextStage) => {
stage = nextStage;
logPipelineStageStart(nextStage, logger);
},
onStageComplete: (completedStage, detail) => {
logPipelineStageComplete(completedStage, detail, logger, { botId, workspaceId });
if (completedStage === "deploy" && !deployTelemetryTracked) {
deployTelemetryTracked = true;
telemetry_default.track("deploy", {
success: true,
botId,
workspaceId,
environment,
autoApproved: options.autoApprove,
planDeclined: hasAnyChanges ? !planApproved : undefined,
primitive_count: project.actions.length + project.workflows.length + project.conversations.length + project.triggers.length + project.tables.length
});
}
},
onStageError: (failedStage, error, event) => {
logPipelineStageError(failedStage, error, logger, event);
},
onDeployCommand: (deployCommand) => {
if (!isJson) {
deployCommand.on("stdout", (data) => logger.stdout(data));
deployCommand.on("stderr", (data) => logger.stderr(data));
}
},
onPreflightProgress: (msg) => logger.info(`\uD83D\uDD04 ${msg}`, "cyan"),
onPreflightSuccess: (msg) => logger.info(`\u2705 ${msg}`, "green"),
onPreflightError: (msg) => logger.error(`\u274C ${msg}`)
}
});
if (!planApproved) {
logger.warn("\n\u26A0\uFE0F Plan updates were not applied \u2014 run `adk deploy` again and approve to sync your config, tables, KBs, and assets.");
}
logger.info("\uD83C\uDF89 Deployment complete.").result({
success: deployResult.success,
botId,
workspaceId,
environment,
...deployResult.tableFailures ? { tableFailures: deployResult.tableFailures } : {}
});
} catch (error) {
if (error instanceof Error) {
const annotated = error;
const errorStage = annotated.stage ?? stage;
annotated.stage = errorStage;
if (errorStage === "deploy") {
annotated.detail = sanitizeErrorMessage(extractDeployErrorMessage(error));
}
}
throw error;
}
}
export {
adkDeploy
};