From 69c1fb9f085aa8f5d3b6125419087f56dc4230db Mon Sep 17 00:00:00 2001 From: chenanran555 Date: Mon, 24 Aug 2026 20:15:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(managed-agent):=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=89=98=E7=AE=A1=20Agent=20=E5=85=A8=E9=9D=A2=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=BD=E4=BB=A4=E9=9B=86=E5=8F=8A=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 扩展 CLI 命令,新增托管 Agent 相关全部操作命令包括能力、Agent、环境、技能、保管库、部署、会话及文件管理 - 实现 Agent 列表、详情、版本、搜索等功能 - 实现环境列表、详情及搜索功能 - 实现部署列表、详情、搜索、运行、暂停与恢复功能 - 实现会话的搜索、更新、归档及事件操作功能 - 实现文件上传、列表、下载、删除及搜索功能 - 添加托管 Agent API 操作能力展示命令 - 统一异常处理及输出格式支持,支持 JSON 与表格展示 - 完善分页、查询参数及权限验证逻辑,支持按需筛选与翻页 - 新增输入输出文件支持与控制台保护机制,提升命令行体验和稳定性 --- packages/cli/src/commands.ts | 75 + .../managed-agent/_engine/api-helpers.ts | 201 +++ .../managed-agent/_engine/output-file.ts | 68 + .../managed-agent/_engine/pagination.ts | 3 +- .../src/commands/managed-agent/agent.ts | 236 +++ .../commands/managed-agent/capabilities.ts | 49 + .../src/commands/managed-agent/deployment.ts | 471 +++++ .../src/commands/managed-agent/environment.ts | 174 ++ .../src/commands/managed-agent/file.ts | 327 ++++ .../commands/managed-agent/session-debug.ts | 317 ++++ .../commands/managed-agent/session-delete.ts | 22 +- .../commands/managed-agent/session-event.ts | 249 +++ .../commands/managed-agent/session-events.ts | 100 +- .../commands/managed-agent/session-list.ts | 76 +- .../managed-agent/session-management.ts | 246 +++ .../src/commands/managed-agent/skill-list.ts | 117 +- .../src/commands/managed-agent/skill.ts | 363 ++++ .../src/commands/managed-agent/vault.ts | 165 ++ packages/commands/src/index.ts | 55 + .../tests/e2e/managed-agent.e2e.test.ts | 79 + packages/commands/tests/e2e/topic-routes.ts | 40 + .../tests/managed-agent-api-helpers.test.ts | 41 + skills/bailian-managed-agent/SKILL.md | 42 +- .../bailian-managed-agent/reference/index.md | 82 +- .../reference/managed-agent.md | 1594 ++++++++++++++++- 25 files changed, 4854 insertions(+), 338 deletions(-) create mode 100644 packages/commands/src/commands/managed-agent/_engine/api-helpers.ts create mode 100644 packages/commands/src/commands/managed-agent/_engine/output-file.ts create mode 100644 packages/commands/src/commands/managed-agent/agent.ts create mode 100644 packages/commands/src/commands/managed-agent/capabilities.ts create mode 100644 packages/commands/src/commands/managed-agent/deployment.ts create mode 100644 packages/commands/src/commands/managed-agent/environment.ts create mode 100644 packages/commands/src/commands/managed-agent/file.ts create mode 100644 packages/commands/src/commands/managed-agent/session-debug.ts create mode 100644 packages/commands/src/commands/managed-agent/session-event.ts create mode 100644 packages/commands/src/commands/managed-agent/session-management.ts create mode 100644 packages/commands/src/commands/managed-agent/skill.ts create mode 100644 packages/commands/src/commands/managed-agent/vault.ts create mode 100644 packages/commands/tests/managed-agent-api-helpers.test.ts diff --git a/packages/cli/src/commands.ts b/packages/cli/src/commands.ts index 2742293..f5cff6b 100644 --- a/packages/cli/src/commands.ts +++ b/packages/cli/src/commands.ts @@ -151,6 +151,43 @@ import { managedAgentSessionSend, managedAgentSessionEvents, managedAgentSkillList, + managedAgentCapabilities, + managedAgentAgentList, + managedAgentAgentGet, + managedAgentAgentSearch, + managedAgentAgentVersions, + managedAgentEnvironmentList, + managedAgentEnvironmentGet, + managedAgentEnvironmentSearch, + managedAgentSkillGet, + managedAgentSkillSearch, + managedAgentSkillVersions, + managedAgentSkillDownload, + managedAgentVaultList, + managedAgentVaultGet, + managedAgentVaultSearch, + managedAgentDeploymentList, + managedAgentDeploymentGet, + managedAgentDeploymentSearch, + managedAgentDeploymentRunsList, + managedAgentDeploymentRunsGet, + managedAgentDeploymentRun, + managedAgentDeploymentPause, + managedAgentDeploymentUnpause, + managedAgentSessionSearch, + managedAgentSessionUpdate, + managedAgentSessionArchive, + managedAgentSessionEventList, + managedAgentSessionEventSend, + managedAgentSessionEventStream, + managedAgentSessionDebug, + managedAgentSessionExport, + managedAgentFileUpload, + managedAgentFileList, + managedAgentFileGet, + managedAgentFileSearch, + managedAgentFileDownload, + managedAgentFileDelete, } from "bailian-cli-commands"; // Full bailian-cli product: every command, exposed under the `bl` binary. @@ -312,6 +349,44 @@ export const commands: Record = { "managed-agent session send": managedAgentSessionSend, "managed-agent session events": managedAgentSessionEvents, "managed-agent skill-list": managedAgentSkillList, + "managed-agent capabilities": managedAgentCapabilities, + "managed-agent agent list": managedAgentAgentList, + "managed-agent agent get": managedAgentAgentGet, + "managed-agent agent search": managedAgentAgentSearch, + "managed-agent agent versions": managedAgentAgentVersions, + "managed-agent environment list": managedAgentEnvironmentList, + "managed-agent environment get": managedAgentEnvironmentGet, + "managed-agent environment search": managedAgentEnvironmentSearch, + "managed-agent skill list": managedAgentSkillList, + "managed-agent skill get": managedAgentSkillGet, + "managed-agent skill search": managedAgentSkillSearch, + "managed-agent skill versions": managedAgentSkillVersions, + "managed-agent skill download": managedAgentSkillDownload, + "managed-agent vault list": managedAgentVaultList, + "managed-agent vault get": managedAgentVaultGet, + "managed-agent vault search": managedAgentVaultSearch, + "managed-agent deployment list": managedAgentDeploymentList, + "managed-agent deployment get": managedAgentDeploymentGet, + "managed-agent deployment search": managedAgentDeploymentSearch, + "managed-agent deployment runs list": managedAgentDeploymentRunsList, + "managed-agent deployment runs get": managedAgentDeploymentRunsGet, + "managed-agent deployment run": managedAgentDeploymentRun, + "managed-agent deployment pause": managedAgentDeploymentPause, + "managed-agent deployment unpause": managedAgentDeploymentUnpause, + "managed-agent session search": managedAgentSessionSearch, + "managed-agent session update": managedAgentSessionUpdate, + "managed-agent session archive": managedAgentSessionArchive, + "managed-agent session event list": managedAgentSessionEventList, + "managed-agent session event send": managedAgentSessionEventSend, + "managed-agent session event stream": managedAgentSessionEventStream, + "managed-agent session debug": managedAgentSessionDebug, + "managed-agent session export": managedAgentSessionExport, + "managed-agent file upload": managedAgentFileUpload, + "managed-agent file list": managedAgentFileList, + "managed-agent file get": managedAgentFileGet, + "managed-agent file search": managedAgentFileSearch, + "managed-agent file download": managedAgentFileDownload, + "managed-agent file delete": managedAgentFileDelete, }; /** diff --git a/packages/commands/src/commands/managed-agent/_engine/api-helpers.ts b/packages/commands/src/commands/managed-agent/_engine/api-helpers.ts new file mode 100644 index 0000000..5fce1c7 --- /dev/null +++ b/packages/commands/src/commands/managed-agent/_engine/api-helpers.ts @@ -0,0 +1,201 @@ +import type { FlagsDef } from "bailian-cli-core"; +import { emitBare, emitResult, formatTable } from "bailian-cli-runtime"; +import type { PagedResult } from "./pagination.ts"; + +export const API_TARGET_FLAGS = { + file: { + type: "string", + valueHint: "", + description: { + "en-US": "Config file path (default: agents.yaml)", + "zh-CN": "配置文件路径(默认:agents.yaml)", + }, + }, + provider: { + type: "string", + valueHint: "", + description: { "en-US": "Target provider", "zh-CN": "目标 Provider" }, + }, +} satisfies FlagsDef; + +export const CURSOR_FLAGS = { + limit: { + type: "number", + valueHint: "", + description: { + "en-US": "Page size (1-100)", + "zh-CN": "单页数量(1-100)", + }, + }, + page: { + type: "string", + valueHint: "", + description: { + "en-US": "Opaque page cursor returned by a previous request", + "zh-CN": "上一次请求返回的不透明分页 Cursor", + }, + }, + all: { + type: "switch", + description: { + "en-US": "Fetch all pages by following opaque cursors", + "zh-CN": "跟随不透明 Cursor 获取全部分页", + }, + }, +} satisfies FlagsDef; + +export const SEARCH_FLAGS = { + query: { + type: "string", + valueHint: "", + required: true, + description: { + "en-US": "Case-insensitive text to find in IDs, names, and descriptions", + "zh-CN": "在 ID、名称和描述中进行不区分大小写的文本搜索", + }, + }, + pageLimit: { + type: "number", + valueHint: "", + description: { + "en-US": "Maximum pages to scan for client-side search (default: 10)", + "zh-CN": "客户端搜索最多扫描的页数(默认:10)", + }, + }, +} satisfies FlagsDef; + +export const INCLUDE_ARCHIVED_FLAG = { + includeArchived: { + type: "switch", + description: { + "en-US": "Include archived resources", + "zh-CN": "包含已归档资源", + }, + }, +} satisfies FlagsDef; + +export function validateLimitAndPageLimit(flags: { + limit?: number; + pageLimit?: number; +}): string | undefined { + if ( + flags.limit !== undefined && + (!Number.isInteger(flags.limit) || flags.limit < 1 || flags.limit > 100) + ) { + return "--limit must be an integer between 1 and 100."; + } + if ( + flags.pageLimit !== undefined && + (!Number.isInteger(flags.pageLimit) || flags.pageLimit < 1 || flags.pageLimit > 100) + ) { + return "--page-limit must be an integer between 1 and 100."; + } + return undefined; +} + +export function splitCommaSeparated(value?: string): string[] | undefined { + if (!value) return undefined; + const values = value + .split(",") + .map((entry) => entry.trim()) + .filter(Boolean); + return values.length > 0 ? values : undefined; +} + +export function matchesQuery(query: string, ...values: unknown[]): boolean { + const normalized = query.trim().toLocaleLowerCase(); + if (!normalized) return true; + return values.some((value) => { + if (value === undefined || value === null) return false; + const text = typeof value === "string" ? value : JSON.stringify(value); + return text.toLocaleLowerCase().includes(normalized); + }); +} + +export interface SearchResult extends PagedResult { + scannedPages: number; + truncated: boolean; +} + +export async function searchCursorPages( + fetchPage: (page?: string) => Promise>, + matches: (item: T) => boolean, + pageLimit = 10, +): Promise> { + const items: T[] = []; + let page: string | undefined; + let hasMore = false; + let nextPage: string | undefined; + let scannedPages = 0; + + do { + const result = await fetchPage(page); + scannedPages += 1; + items.push(...result.items.filter(matches)); + hasMore = result.hasMore; + nextPage = result.nextPage; + page = result.nextPage; + } while (hasMore && page && scannedPages < pageLimit); + + return { + items, + hasMore, + nextPage, + scannedPages, + truncated: Boolean(hasMore && nextPage), + }; +} + +export function emitCollection(options: { + format: "json" | "text"; + key: string; + items: T[]; + headers: string[]; + rows: string[][]; + hasMore?: boolean; + nextPage?: string; + truncated?: boolean; + scannedPages?: number; + emptyMessage?: string; +}): void { + const { + format, + key, + items, + headers, + rows, + hasMore = false, + nextPage, + truncated, + scannedPages, + emptyMessage = "No resources found.", + } = options; + if (format === "json") { + emitResult( + { + [key]: items, + has_more: hasMore, + next_page: nextPage, + ...(truncated === undefined ? {} : { truncated }), + ...(scannedPages === undefined ? {} : { scanned_pages: scannedPages }), + }, + format, + ); + return; + } + if (items.length === 0) { + emitBare(emptyMessage); + return; + } + for (const line of formatTable(headers, rows)) emitBare(line); + emitBare(`\nTotal: ${items.length}`); + if (truncated) emitBare("Search stopped at --page-limit; more pages remain."); + else if (hasMore) + emitBare(`More results are available.${nextPage ? ` Next page: ${nextPage}` : ""}`); +} + +export function displayValue(value: unknown, maxLength = 40): string { + if (value === undefined || value === null || value === "") return "-"; + const text = typeof value === "string" ? value : JSON.stringify(value); + return text.length > maxLength ? `${text.slice(0, maxLength - 3)}...` : text; +} diff --git a/packages/commands/src/commands/managed-agent/_engine/output-file.ts b/packages/commands/src/commands/managed-agent/_engine/output-file.ts new file mode 100644 index 0000000..6271a75 --- /dev/null +++ b/packages/commands/src/commands/managed-agent/_engine/output-file.ts @@ -0,0 +1,68 @@ +import { randomUUID } from "node:crypto"; +import { link, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises"; +import { basename, dirname, resolve } from "node:path"; +import { BailianError, ExitCode } from "bailian-cli-core"; + +export async function readInputFile(path: string): Promise { + return new Uint8Array(await readFile(resolve(path))); +} + +export async function readJsonInput(argument: string): Promise { + const source = argument.startsWith("@") + ? await readFile(resolve(argument.slice(1)), "utf8") + : argument; + try { + return JSON.parse(source) as unknown; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new BailianError(`Invalid JSON input: ${message}`, ExitCode.USAGE); + } +} + +export async function writeOutputFile( + outputPath: string, + content: Uint8Array, + force = false, +): Promise { + const destination = resolve(outputPath); + const parent = dirname(destination); + await mkdir(parent, { recursive: true }); + const temporary = `${destination}.${randomUUID()}.tmp`; + await writeFile(temporary, content, { flag: "wx" }); + try { + if (force) { + await rename(temporary, destination); + } else { + try { + await link(temporary, destination); + } catch (error) { + const code = (error as NodeJS.ErrnoException).code; + if (code === "EEXIST") { + throw new BailianError( + `Output file already exists: ${destination}`, + ExitCode.USAGE, + "Choose another --output-file path or re-run with --force.", + ); + } + throw error; + } + await unlink(temporary); + } + return destination; + } catch (error) { + await unlink(temporary).catch(() => undefined); + throw error; + } +} + +export function inferMimeType(path: string): string { + const filename = basename(path).toLocaleLowerCase(); + if (filename.endsWith(".json")) return "application/json"; + if (filename.endsWith(".md")) return "text/markdown"; + if (filename.endsWith(".txt")) return "text/plain"; + if (filename.endsWith(".pdf")) return "application/pdf"; + if (filename.endsWith(".png")) return "image/png"; + if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) return "image/jpeg"; + if (filename.endsWith(".zip")) return "application/zip"; + return "application/octet-stream"; +} diff --git a/packages/commands/src/commands/managed-agent/_engine/pagination.ts b/packages/commands/src/commands/managed-agent/_engine/pagination.ts index df2fd93..23fe98b 100644 --- a/packages/commands/src/commands/managed-agent/_engine/pagination.ts +++ b/packages/commands/src/commands/managed-agent/_engine/pagination.ts @@ -8,8 +8,9 @@ export interface PagedResult { export async function fetchAllPages( fetchPage: (page?: string) => Promise>, all?: boolean, + initialPage?: string, ): Promise> { - const first = await fetchPage(); + const first = await fetchPage(initialPage); const items = [...first.items]; let hasMore = first.hasMore; let nextPage = first.nextPage; diff --git a/packages/commands/src/commands/managed-agent/agent.ts b/packages/commands/src/commands/managed-agent/agent.ts new file mode 100644 index 0000000..9289dee --- /dev/null +++ b/packages/commands/src/commands/managed-agent/agent.ts @@ -0,0 +1,236 @@ +import type { CloudAgent } from "@openagentpack/sdk"; +import { getRemoteAgent, listRemoteAgents, listRemoteAgentVersions } from "@openagentpack/sdk"; +import { defineCommand, detectOutputFormat } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { + API_TARGET_FLAGS, + CURSOR_FLAGS, + displayValue, + emitCollection, + INCLUDE_ARCHIVED_FLAG, + matchesQuery, + SEARCH_FLAGS, + searchCursorPages, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { fetchAllPages } from "./_engine/pagination.ts"; + +const AGENT_LIST_FLAGS = { + ...API_TARGET_FLAGS, + ...CURSOR_FLAGS, + ...INCLUDE_ARCHIVED_FLAG, +}; + +const AGENT_SEARCH_FLAGS = { + ...API_TARGET_FLAGS, + limit: CURSOR_FLAGS.limit, + ...SEARCH_FLAGS, + ...INCLUDE_ARCHIVED_FLAG, +}; + +const AGENT_GET_FLAGS = { + ...API_TARGET_FLAGS, + agentId: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Agent ID", "zh-CN": "Agent ID" }, + }, + agentVersion: { + type: "number", + valueHint: "", + description: { "en-US": "Specific agent version", "zh-CN": "指定 Agent 版本" }, + }, +} as const; + +const AGENT_VERSIONS_FLAGS = { + ...API_TARGET_FLAGS, + ...CURSOR_FLAGS, + agentId: AGENT_GET_FLAGS.agentId, +}; + +function agentRows(agents: CloudAgent[]): string[][] { + return agents.map((agent) => [ + agent.id, + displayValue(agent.name), + displayValue(agent.version), + displayValue(agent.type), + displayValue(agent.updated_at), + ]); +} + +export const managedAgentAgentList = defineCommand({ + description: { "en-US": "List Managed Agents", "zh-CN": "列出托管 Agent" }, + auth: "apiKey", + usageArgs: "[--limit ] [--page ] [--all] [--include-archived] [--file ]", + flags: AGENT_LIST_FLAGS, + exampleArgs: ["", "--limit 50", "--all --include-archived --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteAgents(runtime, { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + page, + include_archived: ctx.flags.includeArchived, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "agents", + items: result.items, + headers: ["ID", "NAME", "VERSION", "TYPE", "UPDATED"], + rows: agentRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No agents found.", + }); + }, +}); + +export const managedAgentAgentGet = defineCommand({ + description: { "en-US": "Get a Managed Agent", "zh-CN": "获取托管 Agent 详情" }, + auth: "apiKey", + usageArgs: "--agent-id [--agent-version ] [--file ]", + flags: AGENT_GET_FLAGS, + exampleArgs: ["--agent-id agent_abc", "--agent-id agent_abc --agent-version 3 --output json"], + notes: CREDENTIALS_NOTE, + validate: (flags) => + flags.agentVersion !== undefined && + (!Number.isInteger(flags.agentVersion) || flags.agentVersion < 1) + ? "--agent-version must be a positive integer." + : undefined, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const agent = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return getRemoteAgent(runtime, ctx.flags.agentId, { + provider: ctx.flags.provider, + version: ctx.flags.agentVersion, + }); + }), + ); + if (format === "json") { + emitResult(agent, format); + return; + } + emitBare(`ID: ${agent.id}`); + emitBare(`Name: ${displayValue(agent.name)}`); + emitBare(`Description: ${displayValue(agent.description, 120)}`); + emitBare(`Version: ${displayValue(agent.version)}`); + emitBare(`Type: ${displayValue(agent.type)}`); + emitBare(`Created: ${displayValue(agent.created_at)}`); + emitBare(`Updated: ${displayValue(agent.updated_at)}`); + }, +}); + +export const managedAgentAgentSearch = defineCommand({ + description: { "en-US": "Search Managed Agents", "zh-CN": "搜索托管 Agent" }, + auth: "apiKey", + usageArgs: "--query [--limit ] [--page-limit ] [--include-archived]", + flags: AGENT_SEARCH_FLAGS, + exampleArgs: ["--query assistant", "--query code --page-limit 20 --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return searchCursorPages( + async (page) => { + const response = await listRemoteAgents(runtime, { + provider: ctx.flags.provider, + limit: ctx.flags.limit ?? 100, + page, + include_archived: ctx.flags.includeArchived, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + (agent) => matchesQuery(ctx.flags.query, agent.id, agent.name, agent.description), + ctx.flags.pageLimit, + ); + }), + ); + emitCollection({ + format, + key: "agents", + items: result.items, + headers: ["ID", "NAME", "VERSION", "TYPE", "UPDATED"], + rows: agentRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + truncated: result.truncated, + scannedPages: result.scannedPages, + emptyMessage: "No matching agents found.", + }); + }, +}); + +export const managedAgentAgentVersions = defineCommand({ + description: { "en-US": "List Managed Agent versions", "zh-CN": "列出托管 Agent 版本" }, + auth: "apiKey", + usageArgs: "--agent-id [--limit ] [--page ] [--all]", + flags: AGENT_VERSIONS_FLAGS, + exampleArgs: ["--agent-id agent_abc", "--agent-id agent_abc --all --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteAgentVersions(runtime, ctx.flags.agentId, { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + page, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "versions", + items: result.items, + headers: ["ID", "NAME", "VERSION", "TYPE", "UPDATED"], + rows: agentRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No agent versions found.", + }); + }, +}); diff --git a/packages/commands/src/commands/managed-agent/capabilities.ts b/packages/commands/src/commands/managed-agent/capabilities.ts new file mode 100644 index 0000000..77e3167 --- /dev/null +++ b/packages/commands/src/commands/managed-agent/capabilities.ts @@ -0,0 +1,49 @@ +import { getManagedAgentProviderCapabilities } from "@openagentpack/sdk"; +import { defineCommand, detectOutputFormat } from "bailian-cli-core"; +import { emitBare, emitResult, formatTable } from "bailian-cli-runtime"; + +const FLAGS = { + provider: { + type: "string", + valueHint: "", + description: { + "en-US": "Provider to inspect (default: bailian)", + "zh-CN": "要检查的 Provider(默认:bailian)", + }, + }, +} as const; + +export default defineCommand({ + description: { + "en-US": "Show operation-level Managed Agents API capabilities", + "zh-CN": "显示 Managed Agents API 的操作级能力", + }, + auth: "none", + usageArgs: "[--provider ]", + flags: FLAGS, + exampleArgs: ["", "--provider bailian --output json"], + notes: [ + { + "en-US": + "Capabilities distinguish public Managed Agents APIs from client-side compositions and unsupported resources.", + "zh-CN": "Capabilities 会区分公开 Managed Agents API、客户端组合能力和不支持的资源。", + }, + ], + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const capabilities = getManagedAgentProviderCapabilities(ctx.flags.provider ?? "bailian"); + if (format === "json") { + emitResult(capabilities, format); + return; + } + const rows = Object.entries(capabilities.operations).map(([operation, capability]) => [ + operation, + capability.supported ? "yes" : "no", + capability.auth ?? "-", + capability.reason ?? "-", + ]); + for (const line of formatTable(["OPERATION", "SUPPORTED", "AUTH", "REASON"], rows)) { + emitBare(line); + } + }, +}); diff --git a/packages/commands/src/commands/managed-agent/deployment.ts b/packages/commands/src/commands/managed-agent/deployment.ts new file mode 100644 index 0000000..f74cc22 --- /dev/null +++ b/packages/commands/src/commands/managed-agent/deployment.ts @@ -0,0 +1,471 @@ +import type { DeploymentInfo, DeploymentRunInfo, ProjectRuntimeContext } from "@openagentpack/sdk"; +import { + getRemoteDeployment, + getRemoteDeploymentRun, + listRemoteDeploymentRuns, + listRemoteDeployments, + runRemoteDeployment, + setRemoteDeploymentPaused, + UserError, +} from "@openagentpack/sdk"; +import { BailianError, defineCommand, detectOutputFormat, ExitCode } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { + API_TARGET_FLAGS, + CURSOR_FLAGS, + displayValue, + emitCollection, + INCLUDE_ARCHIVED_FLAG, + SEARCH_FLAGS, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { fetchAllPages } from "./_engine/pagination.ts"; + +const DEPLOYMENT_ID_FLAG = { + deploymentId: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Deployment ID", "zh-CN": "Deployment ID" }, + }, +} as const; + +const LIST_FILTER_FLAGS = { + agentId: { + type: "string", + valueHint: "", + description: { "en-US": "Filter by agent ID", "zh-CN": "按 Agent ID 筛选" }, + }, + status: { + type: "string", + valueHint: "", + choices: ["active", "paused"] as const, + description: { "en-US": "Filter by deployment status", "zh-CN": "按 Deployment 状态筛选" }, + }, + ...INCLUDE_ARCHIVED_FLAG, + createdAtGte: { + type: "string", + valueHint: "", + description: { + "en-US": "Created at or after this timestamp", + "zh-CN": "创建时间不早于该时间戳", + }, + }, + createdAtLte: { + type: "string", + valueHint: "", + description: { + "en-US": "Created at or before this timestamp", + "zh-CN": "创建时间不晚于该时间戳", + }, + }, +} as const; + +const LIST_FLAGS = { ...API_TARGET_FLAGS, ...CURSOR_FLAGS, ...LIST_FILTER_FLAGS }; +const GET_FLAGS = { ...API_TARGET_FLAGS, ...DEPLOYMENT_ID_FLAG }; +const SEARCH_RESOURCE_FLAGS = { + ...API_TARGET_FLAGS, + ...CURSOR_FLAGS, + query: SEARCH_FLAGS.query, + ...LIST_FILTER_FLAGS, +}; +const RUN_LIST_FLAGS = { ...API_TARGET_FLAGS, ...DEPLOYMENT_ID_FLAG, ...CURSOR_FLAGS }; +const RUN_GET_FLAGS = { + ...API_TARGET_FLAGS, + runId: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Deployment run ID", "zh-CN": "Deployment Run ID" }, + }, +} as const; +const ACTION_TARGET_FLAGS = { + ...API_TARGET_FLAGS, + deployment: { + type: "string", + valueHint: "", + description: { + "en-US": "Logical deployment name in agents.yaml/state", + "zh-CN": "agents.yaml/State 中的逻辑 Deployment 名称", + }, + }, + deploymentId: { + type: "string", + valueHint: "", + description: { "en-US": "Direct deployment ID", "zh-CN": "直接指定 Deployment ID" }, + }, +} as const; +const RUN_ACTION_FLAGS = { + ...ACTION_TARGET_FLAGS, + yes: { + type: "switch", + description: { "en-US": "Confirm deployment run", "zh-CN": "确认运行 Deployment" }, + }, +} as const; + +function validateActionTarget(flags: { + deployment?: string; + deploymentId?: string; +}): string | undefined { + if (Boolean(flags.deployment) === Boolean(flags.deploymentId)) { + return "Provide exactly one of --deployment or --deployment-id."; + } + return undefined; +} + +function deploymentRows(deployments: DeploymentInfo[]): string[][] { + return deployments.map((deployment) => [ + displayValue(deployment.id), + deployment.status, + displayValue(deployment.schedule?.expression), + displayValue(deployment.paused_reason?.type), + ]); +} + +function runRows(runs: DeploymentRunInfo[]): string[][] { + return runs.map((run) => [ + run.id, + displayValue(run.deployment_id), + displayValue(run.session_id), + displayValue(run.status), + displayValue(run.created_at), + ]); +} + +async function resolveDeploymentTarget( + runtime: ProjectRuntimeContext, + options: { provider?: string; deployment?: string; deploymentId?: string }, +): Promise { + if (options.deploymentId) return options.deploymentId; + const configuredProviders = Array.from(runtime.providers.keys()); + const provider = + options.provider ?? + (configuredProviders.length === 1 + ? configuredProviders[0] + : (() => { + throw new UserError("Multiple providers configured. Use --provider to specify one."); + })()); + const state = runtime.state.getResource({ + provider, + type: "deployment", + name: options.deployment!, + }); + if (!state?.remote_id) { + throw new UserError( + `Deployment '${options.deployment}' is not tracked in state. Use --deployment-id or run managed-agent apply/import first.`, + ); + } + return state.remote_id; +} + +export const managedAgentDeploymentList = defineCommand({ + description: { "en-US": "List Managed Agent deployments", "zh-CN": "列出托管 Agent Deployment" }, + auth: "apiKey", + usageArgs: "[--agent-id ] [--status active|paused] [--limit ] [--page ] [--all]", + flags: LIST_FLAGS, + exampleArgs: ["", "--status active --all --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteDeployments(runtime, { + provider: ctx.flags.provider, + agent_id: ctx.flags.agentId, + status: ctx.flags.status, + include_archived: ctx.flags.includeArchived, + created_at_gte: ctx.flags.createdAtGte, + created_at_lte: ctx.flags.createdAtLte, + limit: ctx.flags.limit, + page, + }); + return { + items: response.deployments, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "deployments", + items: result.items, + headers: ["ID", "STATUS", "SCHEDULE", "PAUSED REASON"], + rows: deploymentRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No deployments found.", + }); + }, +}); + +export const managedAgentDeploymentGet = defineCommand({ + description: { + "en-US": "Get a Managed Agent deployment", + "zh-CN": "获取托管 Agent Deployment 详情", + }, + auth: "apiKey", + usageArgs: "--deployment-id ", + flags: GET_FLAGS, + exampleArgs: ["--deployment-id dep_abc"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const deployment = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return getRemoteDeployment(runtime, ctx.flags.deploymentId, { + provider: ctx.flags.provider, + }); + }), + ); + if (format === "json") emitResult(deployment, format); + else { + emitBare(`ID: ${displayValue(deployment.id)}`); + emitBare(`Status: ${deployment.status}`); + emitBare(`Schedule:${displayValue(deployment.schedule?.expression)}`); + emitBare(`Paused: ${displayValue(deployment.paused_reason)}`); + } + }, +}); + +export const managedAgentDeploymentSearch = defineCommand({ + description: { + "en-US": "Search Managed Agent deployments", + "zh-CN": "搜索托管 Agent Deployment", + }, + auth: "apiKey", + usageArgs: "--query [--limit ] [--page ] [--all]", + flags: SEARCH_RESOURCE_FLAGS, + exampleArgs: ["--query report", "--query nightly --all --output json"], + notes: [ + ...CREDENTIALS_NOTE, + { + "en-US": "Deployment search maps --query to the provider's server-side keyword parameter.", + "zh-CN": "Deployment 搜索会把 --query 映射为 Provider 服务端 keyword 参数。", + }, + ], + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteDeployments(runtime, { + provider: ctx.flags.provider, + keyword: ctx.flags.query, + agent_id: ctx.flags.agentId, + status: ctx.flags.status, + include_archived: ctx.flags.includeArchived, + created_at_gte: ctx.flags.createdAtGte, + created_at_lte: ctx.flags.createdAtLte, + limit: ctx.flags.limit, + page, + }); + return { + items: response.deployments, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "deployments", + items: result.items, + headers: ["ID", "STATUS", "SCHEDULE", "PAUSED REASON"], + rows: deploymentRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No matching deployments found.", + }); + }, +}); + +export const managedAgentDeploymentRunsList = defineCommand({ + description: { + "en-US": "List runs for a Managed Agent deployment", + "zh-CN": "列出托管 Agent Deployment Run", + }, + auth: "apiKey", + usageArgs: "--deployment-id [--limit ] [--page ] [--all]", + flags: RUN_LIST_FLAGS, + exampleArgs: ["--deployment-id dep_abc", "--deployment-id dep_abc --all --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteDeploymentRuns(runtime, ctx.flags.deploymentId, { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + page, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "runs", + items: result.items, + headers: ["ID", "DEPLOYMENT", "SESSION", "STATUS", "CREATED"], + rows: runRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No deployment runs found.", + }); + }, +}); + +export const managedAgentDeploymentRunsGet = defineCommand({ + description: { + "en-US": "Get a Managed Agent deployment run", + "zh-CN": "获取托管 Agent Deployment Run 详情", + }, + auth: "apiKey", + usageArgs: "--run-id ", + flags: RUN_GET_FLAGS, + exampleArgs: ["--run-id run_abc"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const run = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return getRemoteDeploymentRun(runtime, ctx.flags.runId, { provider: ctx.flags.provider }); + }), + ); + if (format === "json") emitResult(run, format); + else { + emitBare(`ID: ${run.id}`); + emitBare(`Deployment: ${displayValue(run.deployment_id)}`); + emitBare(`Session: ${displayValue(run.session_id)}`); + emitBare(`Status: ${displayValue(run.status)}`); + emitBare(`Created: ${displayValue(run.created_at)}`); + emitBare(`Error: ${displayValue(run.error)}`); + } + }, +}); + +export const managedAgentDeploymentRun = defineCommand({ + description: { + "en-US": "Run a Managed Agent deployment now", + "zh-CN": "立即运行托管 Agent Deployment", + }, + auth: "apiKey", + usageArgs: "(--deployment | --deployment-id ) --yes", + flags: RUN_ACTION_FLAGS, + exampleArgs: ["--deployment daily-report --dry-run", "--deployment-id dep_abc --yes"], + notes: CREDENTIALS_NOTE, + validate: validateActionTarget, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + if (ctx.settings.dryRun) { + emitResult( + { + would_run_deployment: ctx.flags.deploymentId ?? ctx.flags.deployment, + target_kind: ctx.flags.deploymentId ? "id" : "state_name", + }, + format, + ); + return; + } + if (!ctx.flags.yes) { + throw new BailianError( + "Refusing to run the deployment without confirmation.", + ExitCode.USAGE, + "Re-run with --yes or preview with --dry-run.", + ); + } + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + const deploymentId = await resolveDeploymentTarget(runtime, ctx.flags); + return { + deploymentId, + run: await runRemoteDeployment(runtime, deploymentId, { provider: ctx.flags.provider }), + }; + }), + ); + if (format === "json") + emitResult({ deployment_id: result.deploymentId, ...result.run }, format); + else emitBare(`Deployment ${result.deploymentId} started. Run: ${result.run.run_id ?? "-"}`); + }, +}); + +function createPauseCommand(paused: boolean) { + return defineCommand({ + description: paused + ? { "en-US": "Pause a Managed Agent deployment", "zh-CN": "暂停托管 Agent Deployment" } + : { "en-US": "Unpause a Managed Agent deployment", "zh-CN": "恢复托管 Agent Deployment" }, + auth: "apiKey", + usageArgs: "(--deployment | --deployment-id )", + flags: ACTION_TARGET_FLAGS, + exampleArgs: [`--deployment daily-report --dry-run`, `--deployment-id dep_abc`], + notes: CREDENTIALS_NOTE, + validate: validateActionTarget, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + if (ctx.settings.dryRun) { + emitResult( + { + [paused ? "would_pause_deployment" : "would_unpause_deployment"]: + ctx.flags.deploymentId ?? ctx.flags.deployment, + target_kind: ctx.flags.deploymentId ? "id" : "state_name", + }, + format, + ); + return; + } + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + const deploymentId = await resolveDeploymentTarget(runtime, ctx.flags); + return { + deploymentId, + deployment: await setRemoteDeploymentPaused(runtime, deploymentId, paused, { + provider: ctx.flags.provider, + }), + }; + }), + ); + if (format === "json") { + emitResult({ deployment_id: result.deploymentId, deployment: result.deployment }, format); + } else { + emitBare(`Deployment ${result.deploymentId} ${paused ? "paused" : "unpaused"}.`); + } + }, + }); +} + +export const managedAgentDeploymentPause = createPauseCommand(true); +export const managedAgentDeploymentUnpause = createPauseCommand(false); diff --git a/packages/commands/src/commands/managed-agent/environment.ts b/packages/commands/src/commands/managed-agent/environment.ts new file mode 100644 index 0000000..8ef2caa --- /dev/null +++ b/packages/commands/src/commands/managed-agent/environment.ts @@ -0,0 +1,174 @@ +import type { CloudEnvironment } from "@openagentpack/sdk"; +import { getRemoteEnvironment, listRemoteEnvironments } from "@openagentpack/sdk"; +import { defineCommand, detectOutputFormat } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { + API_TARGET_FLAGS, + CURSOR_FLAGS, + displayValue, + emitCollection, + INCLUDE_ARCHIVED_FLAG, + matchesQuery, + SEARCH_FLAGS, + searchCursorPages, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { fetchAllPages } from "./_engine/pagination.ts"; + +const LIST_FLAGS = { ...API_TARGET_FLAGS, ...CURSOR_FLAGS, ...INCLUDE_ARCHIVED_FLAG }; +const SEARCH_RESOURCE_FLAGS = { + ...API_TARGET_FLAGS, + limit: CURSOR_FLAGS.limit, + ...SEARCH_FLAGS, + ...INCLUDE_ARCHIVED_FLAG, +}; +const GET_FLAGS = { + ...API_TARGET_FLAGS, + environmentId: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Environment ID", "zh-CN": "Environment ID" }, + }, +} as const; + +function environmentRows(environments: CloudEnvironment[]): string[][] { + return environments.map((environment) => [ + environment.id, + displayValue(environment.name), + displayValue(environment.scope), + displayValue(environment.version), + displayValue(environment.updated_at), + ]); +} + +export const managedAgentEnvironmentList = defineCommand({ + description: { "en-US": "List Managed Agent environments", "zh-CN": "列出托管 Agent 环境" }, + auth: "apiKey", + usageArgs: "[--limit ] [--page ] [--all] [--include-archived]", + flags: LIST_FLAGS, + exampleArgs: ["", "--all --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteEnvironments(runtime, { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + page, + include_archived: ctx.flags.includeArchived, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "environments", + items: result.items, + headers: ["ID", "NAME", "SCOPE", "VERSION", "UPDATED"], + rows: environmentRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No environments found.", + }); + }, +}); + +export const managedAgentEnvironmentGet = defineCommand({ + description: { "en-US": "Get a Managed Agent environment", "zh-CN": "获取托管 Agent 环境详情" }, + auth: "apiKey", + usageArgs: "--environment-id ", + flags: GET_FLAGS, + exampleArgs: ["--environment-id env_abc"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const environment = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return getRemoteEnvironment(runtime, ctx.flags.environmentId, { + provider: ctx.flags.provider, + }); + }), + ); + if (format === "json") { + emitResult(environment, format); + return; + } + emitBare(`ID: ${environment.id}`); + emitBare(`Name: ${displayValue(environment.name)}`); + emitBare(`Description: ${displayValue(environment.description, 120)}`); + emitBare(`Scope: ${displayValue(environment.scope)}`); + emitBare(`Version: ${displayValue(environment.version)}`); + emitBare(`Updated: ${displayValue(environment.updated_at)}`); + }, +}); + +export const managedAgentEnvironmentSearch = defineCommand({ + description: { "en-US": "Search Managed Agent environments", "zh-CN": "搜索托管 Agent 环境" }, + auth: "apiKey", + usageArgs: "--query [--limit ] [--page-limit ] [--include-archived]", + flags: SEARCH_RESOURCE_FLAGS, + exampleArgs: ["--query sandbox", "--query production --page-limit 20 --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return searchCursorPages( + async (page) => { + const response = await listRemoteEnvironments(runtime, { + provider: ctx.flags.provider, + limit: ctx.flags.limit ?? 100, + page, + include_archived: ctx.flags.includeArchived, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + (environment) => + matchesQuery( + ctx.flags.query, + environment.id, + environment.name, + environment.description, + ), + ctx.flags.pageLimit, + ); + }), + ); + emitCollection({ + format, + key: "environments", + items: result.items, + headers: ["ID", "NAME", "SCOPE", "VERSION", "UPDATED"], + rows: environmentRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + truncated: result.truncated, + scannedPages: result.scannedPages, + emptyMessage: "No matching environments found.", + }); + }, +}); diff --git a/packages/commands/src/commands/managed-agent/file.ts b/packages/commands/src/commands/managed-agent/file.ts new file mode 100644 index 0000000..0e3037b --- /dev/null +++ b/packages/commands/src/commands/managed-agent/file.ts @@ -0,0 +1,327 @@ +import { basename } from "node:path"; +import type { ProviderFileInfo } from "@openagentpack/sdk"; +import { + deleteFile, + downloadRemoteFile, + getFileInfo, + listRemoteFiles, + uploadFile, +} from "@openagentpack/sdk"; +import { BailianError, defineCommand, detectOutputFormat, ExitCode } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { + API_TARGET_FLAGS, + CURSOR_FLAGS, + displayValue, + emitCollection, + matchesQuery, + SEARCH_FLAGS, + searchCursorPages, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { inferMimeType, readInputFile, writeOutputFile } from "./_engine/output-file.ts"; +import { fetchAllPages } from "./_engine/pagination.ts"; + +const FILE_ID_FLAG = { + fileId: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Remote file ID", "zh-CN": "远端 File ID" }, + }, +} as const; + +const SCOPE_ID_FLAG = { + scopeId: { + type: "string", + valueHint: "", + description: { "en-US": "Filter by scope ID", "zh-CN": "按 Scope ID 筛选" }, + }, +} as const; + +const UPLOAD_FLAGS = { + ...API_TARGET_FLAGS, + path: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Local file path", "zh-CN": "本地文件路径" }, + }, + filename: { + type: "string", + valueHint: "", + description: { "en-US": "Remote filename override", "zh-CN": "覆盖远端文件名" }, + }, + mimeType: { + type: "string", + valueHint: "", + description: { "en-US": "MIME type override", "zh-CN": "覆盖 MIME 类型" }, + }, + purpose: { + type: "string", + valueHint: "", + description: { "en-US": "Provider upload purpose", "zh-CN": "Provider 上传用途" }, + }, +} as const; + +const LIST_FLAGS = { ...API_TARGET_FLAGS, ...CURSOR_FLAGS, ...SCOPE_ID_FLAG }; +const SEARCH_RESOURCE_FLAGS = { + ...API_TARGET_FLAGS, + limit: CURSOR_FLAGS.limit, + ...SEARCH_FLAGS, + ...SCOPE_ID_FLAG, +}; +const GET_FLAGS = { ...API_TARGET_FLAGS, ...FILE_ID_FLAG }; +const DOWNLOAD_FLAGS = { + ...GET_FLAGS, + outputFile: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Destination path", "zh-CN": "目标路径" }, + }, + force: { + type: "switch", + description: { "en-US": "Overwrite an existing output file", "zh-CN": "覆盖已存在的输出文件" }, + }, +} as const; +const DELETE_FLAGS = { + ...GET_FLAGS, + yes: { + type: "switch", + description: { "en-US": "Confirm permanent file deletion", "zh-CN": "确认永久删除文件" }, + }, +} as const; + +function fileRows(files: ProviderFileInfo[]): string[][] { + return files.map((file) => [ + file.id, + displayValue(file.filename), + displayValue(file.status), + String(file.size_bytes), + displayValue(file.scope?.id), + displayValue(file.created_at), + ]); +} + +export const managedAgentFileUpload = defineCommand({ + description: { "en-US": "Upload a Managed Agent file", "zh-CN": "上传托管 Agent 文件" }, + auth: "apiKey", + usageArgs: "--path [--filename ] [--mime-type ] [--purpose ]", + flags: UPLOAD_FLAGS, + exampleArgs: ["--path ./report.pdf", "--path ./data.json --purpose assistants"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + if (ctx.settings.dryRun) { + emitResult( + { + would_upload_file: ctx.flags.path, + filename: ctx.flags.filename ?? basename(ctx.flags.path), + mime_type: ctx.flags.mimeType ?? inferMimeType(ctx.flags.path), + purpose: ctx.flags.purpose, + }, + format, + ); + return; + } + const content = await readInputFile(ctx.flags.path); + const file = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return uploadFile(runtime, content, ctx.flags.filename ?? basename(ctx.flags.path), { + provider: ctx.flags.provider, + mimeType: ctx.flags.mimeType ?? inferMimeType(ctx.flags.path), + purpose: ctx.flags.purpose, + }); + }), + ); + if (format === "json") emitResult(file, format); + else emitBare(`File uploaded: ${file.id} (${file.filename})`); + }, +}); + +export const managedAgentFileList = defineCommand({ + description: { "en-US": "List Managed Agent files", "zh-CN": "列出托管 Agent 文件" }, + auth: "apiKey", + usageArgs: "[--scope-id ] [--limit ] [--page ] [--all]", + flags: LIST_FLAGS, + exampleArgs: ["", "--scope-id sess_abc --all --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteFiles(runtime, { + provider: ctx.flags.provider, + scope_id: ctx.flags.scopeId, + limit: ctx.flags.limit, + page, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "files", + items: result.items, + headers: ["ID", "FILENAME", "STATUS", "BYTES", "SCOPE", "CREATED"], + rows: fileRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No files found.", + }); + }, +}); + +export const managedAgentFileGet = defineCommand({ + description: { "en-US": "Get Managed Agent file metadata", "zh-CN": "获取托管 Agent 文件元数据" }, + auth: "apiKey", + usageArgs: "--file-id ", + flags: GET_FLAGS, + exampleArgs: ["--file-id file_abc"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const file = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return getFileInfo(runtime, ctx.flags.fileId, { provider: ctx.flags.provider }); + }), + ); + if (format === "json") emitResult(file, format); + else { + emitBare(`ID: ${file.id}`); + emitBare(`Filename: ${file.filename}`); + emitBare(`MIME: ${file.mime_type}`); + emitBare(`Bytes: ${file.size_bytes}`); + emitBare(`Status: ${displayValue(file.status)}`); + emitBare(`Scope: ${displayValue(file.scope?.id)}`); + } + }, +}); + +export const managedAgentFileSearch = defineCommand({ + description: { "en-US": "Search Managed Agent files", "zh-CN": "搜索托管 Agent 文件" }, + auth: "apiKey", + usageArgs: "--query [--scope-id ] [--limit ] [--page-limit ]", + flags: SEARCH_RESOURCE_FLAGS, + exampleArgs: ["--query report", "--query pdf --scope-id sess_abc --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return searchCursorPages( + async (page) => { + const response = await listRemoteFiles(runtime, { + provider: ctx.flags.provider, + scope_id: ctx.flags.scopeId, + limit: ctx.flags.limit ?? 100, + page, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + (file) => matchesQuery(ctx.flags.query, file.id, file.filename, file.mime_type), + ctx.flags.pageLimit, + ); + }), + ); + emitCollection({ + format, + key: "files", + items: result.items, + headers: ["ID", "FILENAME", "STATUS", "BYTES", "SCOPE", "CREATED"], + rows: fileRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + truncated: result.truncated, + scannedPages: result.scannedPages, + emptyMessage: "No matching files found.", + }); + }, +}); + +export const managedAgentFileDownload = defineCommand({ + description: { + "en-US": "Download Managed Agent file content", + "zh-CN": "下载托管 Agent 文件内容", + }, + auth: "apiKey", + usageArgs: "--file-id --output-file [--force]", + flags: DOWNLOAD_FLAGS, + exampleArgs: ["--file-id file_abc --output-file ./artifact.pdf"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + if (ctx.settings.dryRun) { + emitResult( + { would_download_file: ctx.flags.fileId, output_file: ctx.flags.outputFile }, + format, + ); + return; + } + const content = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return downloadRemoteFile(runtime, ctx.flags.fileId, { provider: ctx.flags.provider }); + }), + ); + const outputFile = await writeOutputFile(ctx.flags.outputFile, content, ctx.flags.force); + if (format === "json") + emitResult({ downloaded: ctx.flags.fileId, output_file: outputFile }, format); + else emitBare(`File downloaded to ${outputFile}`); + }, +}); + +export const managedAgentFileDelete = defineCommand({ + description: { "en-US": "Delete a Managed Agent file", "zh-CN": "删除托管 Agent 文件" }, + auth: "apiKey", + usageArgs: "--file-id --yes", + flags: DELETE_FLAGS, + exampleArgs: ["--file-id file_abc --dry-run", "--file-id file_abc --yes"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + if (ctx.settings.dryRun) { + emitResult({ would_delete_file: ctx.flags.fileId }, format); + return; + } + if (!ctx.flags.yes) { + throw new BailianError( + `Refusing to delete file ${ctx.flags.fileId} without confirmation.`, + ExitCode.USAGE, + "Re-run with --yes or preview with --dry-run.", + ); + } + await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + await deleteFile(runtime, ctx.flags.fileId, { provider: ctx.flags.provider }); + }), + ); + if (format === "json") emitResult({ deleted: ctx.flags.fileId }, format); + else emitBare(`File ${ctx.flags.fileId} deleted.`); + }, +}); diff --git a/packages/commands/src/commands/managed-agent/session-debug.ts b/packages/commands/src/commands/managed-agent/session-debug.ts new file mode 100644 index 0000000..fc6d9c1 --- /dev/null +++ b/packages/commands/src/commands/managed-agent/session-debug.ts @@ -0,0 +1,317 @@ +import { + getManagedAgentProviderCapabilities, + getSession, + listRemoteFiles, + listSessionEvents, + type ProjectRuntimeContext, + type ProviderFileInfo, + type ProviderSessionEvent, + type ProviderSessionInfo, +} from "@openagentpack/sdk"; +import { sanitizeSessionEvents } from "@openagentpack/sdk/session-events"; +import { defineCommand, detectOutputFormat } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { API_TARGET_FLAGS } from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { writeOutputFile } from "./_engine/output-file.ts"; +import { fetchAllPages } from "./_engine/pagination.ts"; + +const SESSION_ID_FLAG = { + sessionId: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Session ID", "zh-CN": "Session ID" }, + }, +} as const; + +const DEBUG_FLAGS = { ...API_TARGET_FLAGS, ...SESSION_ID_FLAG }; +const EXPORT_FLAGS = { + ...DEBUG_FLAGS, + outputFile: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Destination ZIP path", "zh-CN": "目标 ZIP 路径" }, + }, + force: { + type: "switch", + description: { "en-US": "Overwrite an existing output file", "zh-CN": "覆盖已存在的输出文件" }, + }, +} as const; + +interface SessionDiagnosticBundle { + session: ProviderSessionInfo; + events: unknown[]; + files: ProviderFileInfo[]; + capabilities: ReturnType; + errors: Array<{ component: "events" | "files"; message: string }>; +} + +function errorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} + +async function collectSessionDiagnostics( + runtime: ProjectRuntimeContext, + sessionId: string, + provider?: string, +): Promise { + // Session lookup is the identity anchor. If it fails, the aggregate is not meaningful. + const session = await getSession(runtime, sessionId, provider); + const providerName = + provider ?? + (runtime.providers.size === 1 ? Array.from(runtime.providers.keys())[0]! : "bailian"); + const errors: SessionDiagnosticBundle["errors"] = []; + let events: ProviderSessionEvent[] = []; + let files: ProviderFileInfo[] = []; + + try { + const result = await fetchAllPages(async (page) => { + const response = await listSessionEvents(runtime, sessionId, { + provider, + limit: 100, + page_token: page, + order: "asc", + }); + return { items: response.events, hasMore: response.has_more, nextPage: response.next_page }; + }, true); + events = result.items; + } catch (error) { + errors.push({ component: "events", message: errorMessage(error) }); + } + + try { + const result = await fetchAllPages(async (page) => { + const response = await listRemoteFiles(runtime, { + provider, + scope_id: sessionId, + limit: 100, + page, + }); + return { items: response.data, hasMore: response.has_more, nextPage: response.next_page }; + }, true); + files = result.items; + } catch (error) { + errors.push({ component: "files", message: errorMessage(error) }); + } + + return { + session, + events: sanitizeSessionEvents(events), + files, + capabilities: getManagedAgentProviderCapabilities(providerName), + errors, + }; +} + +const SENSITIVE_KEY = + /(api[_-]?key|access[_-]?key|secret|token|authorization|credential|password)/i; + +export function redactSensitiveValues(value: unknown): unknown { + if (Array.isArray(value)) return value.map(redactSensitiveValues); + if (!value || typeof value !== "object") return value; + return Object.fromEntries( + Object.entries(value).map(([key, entry]) => [ + key, + SENSITIVE_KEY.test(key) ? "[REDACTED]" : redactSensitiveValues(entry), + ]), + ); +} + +function jsonBytes(value: unknown): Uint8Array { + return new TextEncoder().encode(`${JSON.stringify(redactSensitiveValues(value), null, 2)}\n`); +} + +// Minimal ZIP writer using stored (uncompressed) entries. This avoids shelling out +// and keeps export deterministic across supported Node runtimes. +function crc32(bytes: Uint8Array): number { + let checksum = 0xffffffff; + for (const byte of bytes) { + checksum ^= byte; + for (let bit = 0; bit < 8; bit += 1) { + checksum = (checksum >>> 1) ^ (checksum & 1 ? 0xedb88320 : 0); + } + } + return (checksum ^ 0xffffffff) >>> 0; +} + +function concatBytes(parts: Uint8Array[]): Uint8Array { + const total = parts.reduce((sum, part) => sum + part.length, 0); + const output = new Uint8Array(total); + let offset = 0; + for (const part of parts) { + output.set(part, offset); + offset += part.length; + } + return output; +} + +function littleEndian(values: Array<[number, number]>): Uint8Array { + const size = values.reduce((sum, [, bytes]) => sum + bytes, 0); + const buffer = new ArrayBuffer(size); + const view = new DataView(buffer); + let offset = 0; + for (const [value, bytes] of values) { + if (bytes === 2) view.setUint16(offset, value, true); + else view.setUint32(offset, value, true); + offset += bytes; + } + return new Uint8Array(buffer); +} + +export function createZip(entries: Array<{ name: string; content: Uint8Array }>): Uint8Array { + const encoder = new TextEncoder(); + const localParts: Uint8Array[] = []; + const centralParts: Uint8Array[] = []; + let localOffset = 0; + for (const entry of entries) { + const name = encoder.encode(entry.name); + const checksum = crc32(entry.content); + const localHeader = littleEndian([ + [0x04034b50, 4], + [20, 2], + [0, 2], + [0, 2], + [0, 2], + [0, 2], + [checksum, 4], + [entry.content.length, 4], + [entry.content.length, 4], + [name.length, 2], + [0, 2], + ]); + const local = concatBytes([localHeader, name, entry.content]); + localParts.push(local); + const centralHeader = littleEndian([ + [0x02014b50, 4], + [20, 2], + [20, 2], + [0, 2], + [0, 2], + [0, 2], + [0, 2], + [checksum, 4], + [entry.content.length, 4], + [entry.content.length, 4], + [name.length, 2], + [0, 2], + [0, 2], + [0, 2], + [0, 2], + [0, 4], + [localOffset, 4], + ]); + centralParts.push(concatBytes([centralHeader, name])); + localOffset += local.length; + } + const central = concatBytes(centralParts); + const end = littleEndian([ + [0x06054b50, 4], + [0, 2], + [0, 2], + [entries.length, 2], + [entries.length, 2], + [central.length, 4], + [localOffset, 4], + [0, 2], + ]); + return concatBytes([...localParts, central, end]); +} + +export const managedAgentSessionDebug = defineCommand({ + description: { "en-US": "Aggregate session diagnostics", "zh-CN": "聚合 Session 诊断信息" }, + auth: "apiKey", + usageArgs: "--session-id ", + flags: DEBUG_FLAGS, + exampleArgs: ["--session-id sess_abc", "--session-id sess_abc --output json"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const bundle = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return collectSessionDiagnostics(runtime, ctx.flags.sessionId, ctx.flags.provider); + }), + ); + const redacted = redactSensitiveValues(bundle); + if (format === "json") { + emitResult(redacted, format); + return; + } + emitBare(`Session: ${bundle.session.id} (${bundle.session.status})`); + emitBare(`Events: ${bundle.events.length}`); + emitBare(`Scoped files: ${bundle.files.length}`); + emitBare(`Partial errors: ${bundle.errors.length}`); + for (const error of bundle.errors) emitBare(` ${error.component}: ${error.message}`); + }, +}); + +export const managedAgentSessionExport = defineCommand({ + description: { + "en-US": "Export session diagnostics as a ZIP", + "zh-CN": "将 Session 诊断信息导出为 ZIP", + }, + auth: "apiKey", + usageArgs: "--session-id --output-file [--force]", + flags: EXPORT_FLAGS, + exampleArgs: ["--session-id sess_abc --output-file ./session-debug.zip"], + notes: [ + ...CREDENTIALS_NOTE, + { + "en-US": + "The ZIP contains metadata only; file bodies and credential-like values are excluded/redacted.", + "zh-CN": "ZIP 仅包含元数据;不会包含文件正文,凭证类字段会被移除或脱敏。", + }, + ], + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + if (ctx.settings.dryRun) { + emitResult( + { would_export_session: ctx.flags.sessionId, output_file: ctx.flags.outputFile }, + format, + ); + return; + } + const bundle = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return collectSessionDiagnostics(runtime, ctx.flags.sessionId, ctx.flags.provider); + }), + ); + const manifest = { + schema_version: 1, + generated_at: new Date().toISOString(), + session_id: bundle.session.id, + partial: bundle.errors.length > 0, + counts: { + events: bundle.events.length, + files: bundle.files.length, + errors: bundle.errors.length, + }, + contents: ["session.json", "events.json", "files.json", "capabilities.json", "errors.json"], + note: "File bodies are not included. Credential-like fields are redacted.", + }; + const zip = createZip([ + { name: "manifest.json", content: jsonBytes(manifest) }, + { name: "session.json", content: jsonBytes(bundle.session) }, + { name: "events.json", content: jsonBytes(bundle.events) }, + { name: "files.json", content: jsonBytes(bundle.files) }, + { name: "capabilities.json", content: jsonBytes(bundle.capabilities) }, + { name: "errors.json", content: jsonBytes(bundle.errors) }, + ]); + const outputFile = await writeOutputFile(ctx.flags.outputFile, zip, ctx.flags.force); + if (format === "json") { + emitResult( + { exported: bundle.session.id, output_file: outputFile, partial: manifest.partial }, + format, + ); + } else { + emitBare( + `Session diagnostics exported to ${outputFile}${manifest.partial ? " (partial)" : ""}`, + ); + } + }, +}); diff --git a/packages/commands/src/commands/managed-agent/session-delete.ts b/packages/commands/src/commands/managed-agent/session-delete.ts index 9cbd2ee..00faa68 100644 --- a/packages/commands/src/commands/managed-agent/session-delete.ts +++ b/packages/commands/src/commands/managed-agent/session-delete.ts @@ -1,4 +1,10 @@ -import { defineCommand, detectOutputFormat, type FlagsDef } from "bailian-cli-core"; +import { + BailianError, + defineCommand, + detectOutputFormat, + ExitCode, + type FlagsDef, +} from "bailian-cli-core"; import { emitBare, emitResult } from "bailian-cli-runtime"; import { deleteSession } from "@openagentpack/sdk"; import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; @@ -25,12 +31,16 @@ const SESSION_DELETE_FLAGS = { valueHint: "", description: { "en-US": "Target provider", "zh-CN": "目标 Provider" }, }, + yes: { + type: "switch", + description: { "en-US": "Confirm permanent session deletion", "zh-CN": "确认永久删除 Session" }, + }, } satisfies FlagsDef; export default defineCommand({ description: { "en-US": "Delete a session", "zh-CN": "删除 Session" }, auth: "apiKey", - usageArgs: "--session-id [--provider ] [--file ]", + usageArgs: "--session-id --yes [--provider ] [--file ]", flags: SESSION_DELETE_FLAGS, exampleArgs: ["--session-id sess_abc123"], notes: CREDENTIALS_NOTE, @@ -51,6 +61,14 @@ export default defineCommand({ return; } + if (!flags.yes) { + throw new BailianError( + `Refusing to delete session ${flags.sessionId} without confirmation.`, + ExitCode.USAGE, + "Re-run with --yes or preview with --dry-run.", + ); + } + await withAgentErrors(() => withStdoutProtected(async () => { const runtime = await buildAgentRuntime(ctx, file); diff --git a/packages/commands/src/commands/managed-agent/session-event.ts b/packages/commands/src/commands/managed-agent/session-event.ts new file mode 100644 index 0000000..21cd255 --- /dev/null +++ b/packages/commands/src/commands/managed-agent/session-event.ts @@ -0,0 +1,249 @@ +import type { ProviderSessionEvent, SessionEventInput } from "@openagentpack/sdk"; +import { + isTerminalSessionStatus, + listSessionEvents, + sendRemoteSessionEvents, + streamSessionEvents, +} from "@openagentpack/sdk"; +import { sanitizeSessionEvents } from "@openagentpack/sdk/session-events"; +import { BailianError, defineCommand, detectOutputFormat, ExitCode } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { + API_TARGET_FLAGS, + CURSOR_FLAGS, + displayValue, + emitCollection, + splitCommaSeparated, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { readJsonInput } from "./_engine/output-file.ts"; +import { fetchAllPages } from "./_engine/pagination.ts"; + +const SESSION_ID_FLAG = { + sessionId: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Session ID", "zh-CN": "Session ID" }, + }, +} as const; + +const EVENT_LIST_FLAGS = { + ...API_TARGET_FLAGS, + ...SESSION_ID_FLAG, + ...CURSOR_FLAGS, + order: { + type: "string", + valueHint: "", + choices: ["asc", "desc"] as const, + description: { "en-US": "Event order: asc or desc", "zh-CN": "事件顺序:asc 或 desc" }, + }, + types: { + type: "string", + valueHint: "", + description: { + "en-US": "Comma-separated raw event types", + "zh-CN": "原始事件类型,多个以逗号分隔", + }, + }, + createdAtGte: { + type: "string", + valueHint: "", + description: { + "en-US": "Created at or after this timestamp", + "zh-CN": "创建时间不早于该时间戳", + }, + }, + createdAtLte: { + type: "string", + valueHint: "", + description: { + "en-US": "Created at or before this timestamp", + "zh-CN": "创建时间不晚于该时间戳", + }, + }, +} as const; + +const EVENT_SEND_FLAGS = { + ...API_TARGET_FLAGS, + ...SESSION_ID_FLAG, + event: { + type: "string", + valueHint: "", + required: true, + description: { + "en-US": "Raw event object/array as JSON or @event.json", + "zh-CN": "原始事件对象/数组 JSON,或 @event.json", + }, + }, +} as const; + +const EVENT_STREAM_FLAGS = { + ...API_TARGET_FLAGS, + ...SESSION_ID_FLAG, + afterId: { + type: "string", + valueHint: "", + description: { "en-US": "Resume after this event ID", "zh-CN": "从该 Event ID 之后继续" }, + }, +} as const; + +function eventRows(events: ProviderSessionEvent[]): string[][] { + return events.map((event) => [ + displayValue(event.id), + event.raw_type, + displayValue(event.session_thread_id), + displayValue(event.content ?? event.status ?? event.tool_name, 70), + ]); +} + +function normalizeEventInput(value: unknown): SessionEventInput[] { + const values = Array.isArray(value) ? value : [value]; + if ( + values.length === 0 || + values.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry)) + ) { + throw new BailianError( + "Event input must be a JSON object or a non-empty array of objects.", + ExitCode.USAGE, + ); + } + return values as SessionEventInput[]; +} + +export const managedAgentSessionEventList = defineCommand({ + description: { + "en-US": "List events for a Managed Agent session", + "zh-CN": "列出托管 Agent Session 事件", + }, + auth: "apiKey", + usageArgs: + "--session-id [--types ] [--order asc|desc] [--limit ] [--page ] [--all]", + flags: EVENT_LIST_FLAGS, + exampleArgs: ["--session-id sess_abc", "--session-id sess_abc --all --output json"], + notes: [ + ...CREDENTIALS_NOTE, + { + "en-US": "--types is applied client-side to each page returned by the provider.", + "zh-CN": "--types 会在客户端对 Provider 返回的每一页结果进行过滤。", + }, + ], + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listSessionEvents(runtime, ctx.flags.sessionId, { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + page_token: page, + order: ctx.flags.order, + types: splitCommaSeparated(ctx.flags.types), + created_at_gte: ctx.flags.createdAtGte, + created_at_lte: ctx.flags.createdAtLte, + }); + return { + items: response.events, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "events", + items: sanitizeSessionEvents(result.items), + headers: ["ID", "TYPE", "THREAD", "CONTENT"], + rows: eventRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No events found.", + }); + }, +}); + +export const managedAgentSessionEventSend = defineCommand({ + description: { + "en-US": "Send raw events to a Managed Agent session", + "zh-CN": "向托管 Agent Session 发送原始事件", + }, + auth: "apiKey", + usageArgs: "--session-id --event ", + flags: EVENT_SEND_FLAGS, + exampleArgs: [ + '--session-id sess_abc --event \'{"type":"message","role":"user","content":"hello"}\'', + "--session-id sess_abc --event @event.json", + ], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const events = normalizeEventInput(await readJsonInput(ctx.flags.event)); + if (ctx.settings.dryRun) { + emitResult({ would_send_events: events, session_id: ctx.flags.sessionId }, format); + return; + } + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return sendRemoteSessionEvents(runtime, ctx.flags.sessionId, events, { + provider: ctx.flags.provider, + }); + }), + ); + if (format === "json") emitResult({ session_id: ctx.flags.sessionId, ...result }, format); + else emitBare(`Sent ${events.length} event(s): ${result.event_ids.join(", ")}`); + }, +}); + +export const managedAgentSessionEventStream = defineCommand({ + description: { + "en-US": "Stream events from a Managed Agent session", + "zh-CN": "流式读取托管 Agent Session 事件", + }, + auth: "apiKey", + usageArgs: "--session-id [--after-id ]", + flags: EVENT_STREAM_FLAGS, + exampleArgs: ["--session-id sess_abc", "--session-id sess_abc --after-id evt_123 --output json"], + notes: [ + ...CREDENTIALS_NOTE, + { + "en-US": + "When the provider has no native event cursor, --after-id resumes through paginated history polling and event ID de-duplication.", + "zh-CN": + "当 Provider 不支持原生事件 Cursor 时,--after-id 会通过分页历史轮询和 Event ID 去重实现续传。", + }, + ], + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + const events: ProviderSessionEvent[] = []; + for await (const event of streamSessionEvents(runtime, ctx.flags.sessionId, { + provider: ctx.flags.provider, + after_id: ctx.flags.afterId, + })) { + events.push(event); + if (format !== "json") emitBare(JSON.stringify(sanitizeSessionEvents([event])[0])); + if (event.type === "status" && isTerminalSessionStatus(event.status)) break; + } + if (format === "json") { + emitResult( + { session_id: ctx.flags.sessionId, events: sanitizeSessionEvents(events) }, + format, + ); + } + }), + ); + }, +}); diff --git a/packages/commands/src/commands/managed-agent/session-events.ts b/packages/commands/src/commands/managed-agent/session-events.ts index 84e15a6..e41d179 100644 --- a/packages/commands/src/commands/managed-agent/session-events.ts +++ b/packages/commands/src/commands/managed-agent/session-events.ts @@ -1,98 +1,2 @@ -import { defineCommand, detectOutputFormat, type FlagsDef } from "bailian-cli-core"; -import { emitBare, emitResult, formatTable } from "bailian-cli-runtime"; -import { listSessionEvents } from "@openagentpack/sdk"; -import { sanitizeSessionEvents } from "@openagentpack/sdk/session-events"; -import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; -import { withStdoutProtected } from "./_engine/console-capture.ts"; -import { withAgentErrors } from "./_engine/errors.ts"; -import { fetchAllPages } from "./_engine/pagination.ts"; - -const SESSION_EVENTS_FLAGS = { - sessionId: { - type: "string", - valueHint: "", - description: { "en-US": "Session ID (required)", "zh-CN": "Session ID(必填)" }, - required: true, - }, - file: { - type: "string", - valueHint: "", - description: { - "en-US": "Config file path (default: agents.yaml)", - "zh-CN": "配置文件路径(默认:agents.yaml)", - }, - }, - provider: { - type: "string", - valueHint: "", - description: { "en-US": "Target provider", "zh-CN": "目标 Provider" }, - }, - limit: { - type: "number", - valueHint: "", - description: { "en-US": "Maximum number of events to fetch", "zh-CN": "要获取的最大事件数" }, - }, - all: { - type: "switch", - description: { - "en-US": "Fetch all pages by following the cursor", - "zh-CN": "跟随 Cursor 获取全部分页", - }, - }, -} satisfies FlagsDef; - -export default defineCommand({ - description: { "en-US": "List event history for a session", "zh-CN": "列出 Session 的事件历史" }, - auth: "apiKey", - usageArgs: "--session-id [--limit ] [--all] [--file ]", - flags: SESSION_EVENTS_FLAGS, - exampleArgs: ["--session-id sess_abc123", "--session-id sess_abc123 --all"], - notes: CREDENTIALS_NOTE, - async run(ctx) { - const { settings, flags } = ctx; - const format = detectOutputFormat(settings.output); - const file = flags.file ?? "agents.yaml"; - - const { items: events, hasMore } = await withAgentErrors(() => - withStdoutProtected(async () => { - const runtime = await buildAgentRuntime(ctx, file); - return fetchAllPages(async (page) => { - const result = await listSessionEvents(runtime, flags.sessionId, { - provider: flags.provider, - limit: flags.limit, - page_token: page, - }); - return { - items: result.events, - hasMore: result.has_more, - nextPage: result.next_page, - }; - }, flags.all); - }), - ); - - if (format === "json") { - emitResult({ events: sanitizeSessionEvents(events), has_more: hasMore }, format); - return; - } - if (events.length === 0) { - emitBare("No events found."); - return; - } - - const headers = ["#", "TYPE", "CONTENT"]; - const rows = events.map((event, index) => { - let preview = ""; - if (event.type === "message") preview = (event.content ?? "").slice(0, 60); - else if (event.type === "tool_use") preview = event.tool_name ?? ""; - else if (event.type === "tool_result") preview = (event.content ?? "").slice(0, 60); - else if (event.type === "status") preview = event.status ?? ""; - else if (event.type === "error") preview = (event.content ?? "").slice(0, 60); - else preview = event.raw_type; - return [String(index + 1), event.type, preview]; - }); - for (const line of formatTable(headers, rows)) emitBare(line); - emitBare(`\nTotal: ${events.length}`); - if (hasMore) emitBare("More events available. Use --all to fetch all."); - }, -}); +// Backward-compatible alias for `managed-agent session event list`. +export { managedAgentSessionEventList as default } from "./session-event.ts"; diff --git a/packages/commands/src/commands/managed-agent/session-list.ts b/packages/commands/src/commands/managed-agent/session-list.ts index a87abd0..b5a311a 100644 --- a/packages/commands/src/commands/managed-agent/session-list.ts +++ b/packages/commands/src/commands/managed-agent/session-list.ts @@ -5,6 +5,11 @@ import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts" import { withStdoutProtected } from "./_engine/console-capture.ts"; import { withAgentErrors } from "./_engine/errors.ts"; import { fetchAllPages } from "./_engine/pagination.ts"; +import { + CURSOR_FLAGS, + splitCommaSeparated, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; const SESSION_LIST_FLAGS = { file: { @@ -27,6 +32,32 @@ const SESSION_LIST_FLAGS = { "zh-CN": "跟随 Cursor 获取全部分页", }, }, + limit: CURSOR_FLAGS.limit, + page: CURSOR_FLAGS.page, + statuses: { + type: "string", + valueHint: "", + description: { + "en-US": "Comma-separated session statuses", + "zh-CN": "Session 状态,多个以逗号分隔", + }, + }, + createdAtGte: { + type: "string", + valueHint: "", + description: { + "en-US": "Created at or after this RFC 3339 timestamp", + "zh-CN": "创建时间不早于该 RFC 3339 时间戳", + }, + }, + createdAtLte: { + type: "string", + valueHint: "", + description: { + "en-US": "Created at or before this RFC 3339 timestamp", + "zh-CN": "创建时间不晚于该 RFC 3339 时间戳", + }, + }, provider: { type: "string", valueHint: "", @@ -40,36 +71,51 @@ export default defineCommand({ "zh-CN": "列出 Provider 中的 Session", }, auth: "apiKey", - usageArgs: "[--agent ] [--all] [--provider ] [--file ]", + usageArgs: "[--agent ] [--statuses ] [--limit ] [--page ] [--all]", flags: SESSION_LIST_FLAGS, exampleArgs: ["", "--agent assistant", "--all"], notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, async run(ctx) { const { settings, flags } = ctx; const format = detectOutputFormat(settings.output); const file = flags.file ?? "agents.yaml"; - const { items: summaries, hasMore } = await withAgentErrors(() => + const { + items: summaries, + hasMore, + nextPage, + } = await withAgentErrors(() => withStdoutProtected(async () => { const runtime = await buildAgentRuntime(ctx, file); - return fetchAllPages(async (page) => { - const result = await listSessionSummaries(runtime, { - agent: flags.agent, - provider: flags.provider, - filter: page ? { page } : undefined, - }); - return { - items: result.summaries, - hasMore: result.hasMore, - nextPage: result.nextPage, - }; - }, flags.all); + return fetchAllPages( + async (page) => { + const result = await listSessionSummaries(runtime, { + agent: flags.agent, + provider: flags.provider, + filter: { + page, + limit: flags.limit, + statuses: splitCommaSeparated(flags.statuses), + created_at_gte: flags.createdAtGte, + created_at_lte: flags.createdAtLte, + }, + }); + return { + items: result.summaries, + hasMore: result.hasMore, + nextPage: result.nextPage, + }; + }, + flags.all, + flags.page, + ); }), ); const sessions = summaries.map((summary) => summary.session); if (format === "json") { - emitResult({ sessions, has_more: hasMore }, format); + emitResult({ sessions, has_more: hasMore, next_page: nextPage }, format); return; } if (sessions.length === 0) { diff --git a/packages/commands/src/commands/managed-agent/session-management.ts b/packages/commands/src/commands/managed-agent/session-management.ts new file mode 100644 index 0000000..12101ad --- /dev/null +++ b/packages/commands/src/commands/managed-agent/session-management.ts @@ -0,0 +1,246 @@ +import type { ProviderSessionInfo } from "@openagentpack/sdk"; +import { + archiveRemoteSession, + listSessionSummaries, + updateRemoteSession, +} from "@openagentpack/sdk"; +import { BailianError, defineCommand, detectOutputFormat, ExitCode } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { + API_TARGET_FLAGS, + CURSOR_FLAGS, + displayValue, + emitCollection, + matchesQuery, + SEARCH_FLAGS, + searchCursorPages, + splitCommaSeparated, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { readJsonInput } from "./_engine/output-file.ts"; + +const SESSION_ID_FLAG = { + sessionId: { + type: "string", + valueHint: "", + required: true, + description: { "en-US": "Session ID", "zh-CN": "Session ID" }, + }, +} as const; + +const SEARCH_RESOURCE_FLAGS = { + ...API_TARGET_FLAGS, + limit: CURSOR_FLAGS.limit, + ...SEARCH_FLAGS, + agent: { + type: "string", + valueHint: "", + description: { + "en-US": "Filter by configured agent name", + "zh-CN": "按配置中的 Agent 名称筛选", + }, + }, + statuses: { + type: "string", + valueHint: "", + description: { + "en-US": "Comma-separated session statuses", + "zh-CN": "Session 状态,多个以逗号分隔", + }, + }, + createdAtGte: { + type: "string", + valueHint: "", + description: { + "en-US": "Created at or after this timestamp", + "zh-CN": "创建时间不早于该时间戳", + }, + }, + createdAtLte: { + type: "string", + valueHint: "", + description: { + "en-US": "Created at or before this timestamp", + "zh-CN": "创建时间不晚于该时间戳", + }, + }, +} as const; + +const UPDATE_FLAGS = { + ...API_TARGET_FLAGS, + ...SESSION_ID_FLAG, + title: { + type: "string", + valueHint: "", + description: { "en-US": "New session title", "zh-CN": "新的 Session 标题" }, + }, + metadata: { + type: "string", + valueHint: "<json|@path>", + description: { + "en-US": "String-valued metadata JSON or @file", + "zh-CN": "值为字符串的 Metadata JSON 或 @file", + }, + }, +} as const; + +const ARCHIVE_FLAGS = { + ...API_TARGET_FLAGS, + ...SESSION_ID_FLAG, + yes: { + type: "switch", + description: { "en-US": "Confirm session archive", "zh-CN": "确认归档 Session" }, + }, +} as const; + +function sessionRows(sessions: ProviderSessionInfo[]): string[][] { + return sessions.map((session) => [ + session.id, + displayValue(session.title), + displayValue(session.agent_id), + session.status, + displayValue(session.updated_at), + ]); +} + +async function parseMetadata(value?: string): Promise<Record<string, string> | undefined> { + if (!value) return undefined; + const parsed = await readJsonInput(value); + if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { + throw new BailianError("--metadata must be a JSON object.", ExitCode.USAGE); + } + for (const [key, entry] of Object.entries(parsed)) { + if (typeof entry !== "string") { + throw new BailianError(`Metadata value for '${key}' must be a string.`, ExitCode.USAGE); + } + } + return parsed as Record<string, string>; +} + +export const managedAgentSessionSearch = defineCommand({ + description: { "en-US": "Search Managed Agent sessions", "zh-CN": "搜索托管 Agent Session" }, + auth: "apiKey", + usageArgs: + "--query <text> [--agent <name>] [--statuses <statuses>] [--limit <n>] [--page-limit <n>]", + flags: SEARCH_RESOURCE_FLAGS, + exampleArgs: ["--query debug", "--query failed --statuses failed --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return searchCursorPages( + async (page) => { + const response = await listSessionSummaries(runtime, { + agent: ctx.flags.agent, + provider: ctx.flags.provider, + filter: { + page, + limit: ctx.flags.limit ?? 100, + statuses: splitCommaSeparated(ctx.flags.statuses), + created_at_gte: ctx.flags.createdAtGte, + created_at_lte: ctx.flags.createdAtLte, + }, + }); + return { + items: response.summaries, + hasMore: response.hasMore, + nextPage: response.nextPage, + }; + }, + (summary) => + matchesQuery( + ctx.flags.query, + summary.session.id, + summary.session.title, + summary.session.status, + summary.session.agent_id, + summary.agentName, + ), + ctx.flags.pageLimit, + ); + }), + ); + const sessions = result.items.map((summary) => summary.session); + emitCollection({ + format, + key: "sessions", + items: sessions, + headers: ["ID", "TITLE", "AGENT", "STATUS", "UPDATED"], + rows: sessionRows(sessions), + hasMore: result.hasMore, + nextPage: result.nextPage, + truncated: result.truncated, + scannedPages: result.scannedPages, + emptyMessage: "No matching sessions found.", + }); + }, +}); + +export const managedAgentSessionUpdate = defineCommand({ + description: { "en-US": "Update a Managed Agent session", "zh-CN": "更新托管 Agent Session" }, + auth: "apiKey", + usageArgs: "--session-id <id> [--title <title>] [--metadata <json|@path>]", + flags: UPDATE_FLAGS, + exampleArgs: [ + "--session-id sess_abc --title 'investigation'", + "--session-id sess_abc --metadata @metadata.json", + ], + notes: CREDENTIALS_NOTE, + validate: (flags) => + !flags.title && !flags.metadata ? "Provide --title or --metadata." : undefined, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const input = { title: ctx.flags.title, metadata: await parseMetadata(ctx.flags.metadata) }; + if (ctx.settings.dryRun) { + emitResult({ would_update_session: ctx.flags.sessionId, input }, format); + return; + } + const session = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return updateRemoteSession(runtime, ctx.flags.sessionId, input, { + provider: ctx.flags.provider, + }); + }), + ); + if (format === "json") emitResult(session, format); + else emitBare(`Session ${session.id} updated.`); + }, +}); + +export const managedAgentSessionArchive = defineCommand({ + description: { "en-US": "Archive a Managed Agent session", "zh-CN": "归档托管 Agent Session" }, + auth: "apiKey", + usageArgs: "--session-id <id> --yes", + flags: ARCHIVE_FLAGS, + exampleArgs: ["--session-id sess_abc --dry-run", "--session-id sess_abc --yes"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + if (ctx.settings.dryRun) { + emitResult({ would_archive_session: ctx.flags.sessionId }, format); + return; + } + if (!ctx.flags.yes) { + throw new BailianError( + `Refusing to archive session ${ctx.flags.sessionId} without confirmation.`, + ExitCode.USAGE, + "Re-run with --yes or preview with --dry-run.", + ); + } + const session = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return archiveRemoteSession(runtime, ctx.flags.sessionId, { provider: ctx.flags.provider }); + }), + ); + if (format === "json") emitResult({ archived: ctx.flags.sessionId, session }, format); + else emitBare(`Session ${ctx.flags.sessionId} archived.`); + }, +}); diff --git a/packages/commands/src/commands/managed-agent/skill-list.ts b/packages/commands/src/commands/managed-agent/skill-list.ts index f8f7727..ee42b96 100644 --- a/packages/commands/src/commands/managed-agent/skill-list.ts +++ b/packages/commands/src/commands/managed-agent/skill-list.ts @@ -1,115 +1,2 @@ -import { defineCommand, detectOutputFormat, type FlagsDef } from "bailian-cli-core"; -import { emitBare, emitResult, formatTable } from "bailian-cli-runtime"; -import { listSkills } from "@openagentpack/sdk"; -import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; -import { withStdoutProtected } from "./_engine/console-capture.ts"; -import { withAgentErrors } from "./_engine/errors.ts"; - -const SKILL_SOURCES = ["custom", "official", "all"] as const; -type SkillSource = (typeof SKILL_SOURCES)[number]; - -const SKILL_LIST_FLAGS = { - file: { - type: "string", - valueHint: "<path>", - description: { - "en-US": "Config file path (default: agents.yaml)", - "zh-CN": "配置文件路径(默认:agents.yaml)", - }, - }, - source: { - type: "string", - valueHint: "<source>", - description: { - "en-US": - "Skill catalog: custom (workspace-uploaded, default), official (built-in), or all (both catalogs in one call)", - "zh-CN": - "Skill Catalog:custom(Workspace 上传,默认)、official(内置)或 all(一次调用获取两类 Catalog)", - }, - }, - provider: { - type: "string", - valueHint: "<name>", - description: { "en-US": "Target provider", "zh-CN": "目标 Provider" }, - }, -} satisfies FlagsDef; - -export default defineCommand({ - description: { - "en-US": "List skills from the provider's skill catalog", - "zh-CN": "列出 Provider Skill Catalog 中的 Skill", - }, - auth: "apiKey", - usageArgs: "[--source custom|official|all] [--provider <name>] [--file <path>]", - flags: SKILL_LIST_FLAGS, - exampleArgs: [ - "", - "--source official", - "--source all --output json", - "--source custom --provider bailian", - ], - notes: [ - ...CREDENTIALS_NOTE, - { - "en-US": "Providers without a skill listing API (e.g. ark) return an empty list.", - "zh-CN": "没有 Skill 列表 API 的 Provider(例如 ark)会返回空列表。", - }, - { - "en-US": - "For agent-driven skill selection, use `--source all --output json`: one call returns both catalogs with per-skill `source` and `description` fields to pick from.", - "zh-CN": - "由 Agent 选择 Skill 时,请使用 `--source all --output json`:一次调用返回两类 Catalog,并为每个 Skill 提供 `source` 和 `description` 字段用于选择。", - }, - { - "en-US": - "When generating a task that needs a suitable skill, call this command to match official or custom skills before wiring them into the task.", - "zh-CN": "生成需要合适 Skill 的任务时,先调用此命令匹配官方或自定义 Skill,再将其接入任务。", - }, - ], - validate: (f) => - f.source && !SKILL_SOURCES.includes(f.source as SkillSource) - ? "--source must be one of: custom, official, all." - : undefined, - async run(ctx) { - const { settings, flags } = ctx; - const format = detectOutputFormat(settings.output); - const file = flags.file ?? "agents.yaml"; - const source = (flags.source as SkillSource | undefined) ?? "custom"; - - const skills = await withAgentErrors(() => - withStdoutProtected(async () => { - const runtime = await buildAgentRuntime(ctx, file); - if (source !== "all") { - return listSkills(runtime, { provider: flags.provider, source }); - } - // Both catalogs in one call; each entry carries its own `source` field. - const [customSkills, officialSkills] = await Promise.all([ - listSkills(runtime, { provider: flags.provider, source: "custom" }), - listSkills(runtime, { provider: flags.provider, source: "official" }), - ]); - return [...customSkills, ...officialSkills]; - }), - ); - - if (format === "json") { - emitResult({ source, skills }, format); - return; - } - if (skills.length === 0) { - emitBare(source === "all" ? "No skills found." : `No ${source} skills found.`); - return; - } - - const headers = ["ID", "NAME", "SOURCE", "STATUS", "VERSION", "CREATED"]; - const rows = skills.map((skill) => [ - skill.id, - skill.name.slice(0, 32), - skill.source, - skill.status, - skill.latest_version ?? "-", - skill.created_at ?? "-", - ]); - for (const line of formatTable(headers, rows)) emitBare(line); - emitBare(`\nTotal: ${skills.length} (${source})`); - }, -}); +// Backward-compatible alias for the API-oriented `managed-agent skill list` command. +export { managedAgentSkillList as default } from "./skill.ts"; diff --git a/packages/commands/src/commands/managed-agent/skill.ts b/packages/commands/src/commands/managed-agent/skill.ts new file mode 100644 index 0000000..7a8b90a --- /dev/null +++ b/packages/commands/src/commands/managed-agent/skill.ts @@ -0,0 +1,363 @@ +import type { ProviderSkillInfo, SkillVersionInfo } from "@openagentpack/sdk"; +import { + downloadRemoteSkill, + getRemoteSkill, + listRemoteSkills, + listRemoteSkillVersions, +} from "@openagentpack/sdk"; +import { defineCommand, detectOutputFormat } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { + API_TARGET_FLAGS, + CURSOR_FLAGS, + displayValue, + emitCollection, + matchesQuery, + SEARCH_FLAGS, + searchCursorPages, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { writeOutputFile } from "./_engine/output-file.ts"; +import { fetchAllPages } from "./_engine/pagination.ts"; + +const SKILL_SOURCES = ["custom", "official", "all"] as const; +type SkillSource = (typeof SKILL_SOURCES)[number]; + +const SOURCE_FLAG = { + source: { + type: "string", + valueHint: "<source>", + choices: SKILL_SOURCES, + description: { + "en-US": "Skill catalog: custom (default), official, or all", + "zh-CN": "Skill Catalog:custom(默认)、official 或 all", + }, + }, +} as const; + +const LIST_FLAGS = { ...API_TARGET_FLAGS, ...CURSOR_FLAGS, ...SOURCE_FLAG }; +const SEARCH_RESOURCE_FLAGS = { + ...API_TARGET_FLAGS, + limit: CURSOR_FLAGS.limit, + ...SEARCH_FLAGS, + ...SOURCE_FLAG, +}; +const GET_FLAGS = { + ...API_TARGET_FLAGS, + skillId: { + type: "string", + valueHint: "<id>", + required: true, + description: { "en-US": "Skill ID", "zh-CN": "Skill ID" }, + }, +} as const; +const VERSIONS_FLAGS = { + ...GET_FLAGS, + ...CURSOR_FLAGS, +}; +const DOWNLOAD_FLAGS = { + ...GET_FLAGS, + skillVersion: { + type: "string", + valueHint: "<version>", + required: true, + description: { "en-US": "Skill version", "zh-CN": "Skill 版本" }, + }, + outputFile: { + type: "string", + valueHint: "<path>", + required: true, + description: { "en-US": "Destination ZIP path", "zh-CN": "目标 ZIP 路径" }, + }, + force: { + type: "switch", + description: { "en-US": "Overwrite an existing output file", "zh-CN": "覆盖已存在的输出文件" }, + }, +} as const; + +function skillRows(skills: ProviderSkillInfo[]): string[][] { + return skills.map((skill) => [ + skill.id, + displayValue(skill.name), + skill.source, + skill.status, + displayValue(skill.latest_version), + displayValue(skill.updated_at ?? skill.created_at), + ]); +} + +function versionRows(versions: SkillVersionInfo[]): string[][] { + return versions.map((version) => [ + displayValue(version.version), + displayValue(version.name), + displayValue(version.type), + displayValue(version.status), + displayValue(version.updated_at ?? version.created_at), + ]); +} + +async function listOneCatalog( + runtime: Parameters<typeof listRemoteSkills>[0], + source: Exclude<SkillSource, "all">, + options: { provider?: string; limit?: number; page?: string; all?: boolean }, +) { + return fetchAllPages( + async (page) => { + const response = await listRemoteSkills(runtime, { + provider: options.provider, + source, + limit: options.limit, + page, + }); + return { items: response.data, hasMore: response.has_more, nextPage: response.next_page }; + }, + options.all, + options.page, + ); +} + +export const managedAgentSkillList = defineCommand({ + description: { "en-US": "List Managed Agent skills", "zh-CN": "列出托管 Agent Skill" }, + auth: "apiKey", + usageArgs: "[--source custom|official|all] [--limit <n>] [--page <cursor>] [--all]", + flags: LIST_FLAGS, + exampleArgs: ["", "--source official", "--source all --all --output json"], + notes: [ + ...CREDENTIALS_NOTE, + { + "en-US": + "--source all combines one page from each catalog, or every page with --all; it does not accept --page.", + "zh-CN": + "--source all 默认合并两个 Catalog 的各一页,传入 --all 时获取全部分页;该模式不接受 --page。", + }, + ], + validate: (flags) => + validateLimitAndPageLimit(flags) ?? + (flags.source === "all" && flags.page + ? "--source all cannot be combined with --page." + : undefined), + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const source = (ctx.flags.source as SkillSource | undefined) ?? "custom"; + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + if (source !== "all") { + return listOneCatalog(runtime, source, { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + page: ctx.flags.page, + all: ctx.flags.all, + }); + } + const [custom, official] = await Promise.all([ + listOneCatalog(runtime, "custom", { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + all: ctx.flags.all, + }), + listOneCatalog(runtime, "official", { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + all: ctx.flags.all, + }), + ]); + return { + items: [...custom.items, ...official.items], + hasMore: custom.hasMore || official.hasMore, + nextPage: undefined, + }; + }), + ); + emitCollection({ + format, + key: "skills", + items: result.items, + headers: ["ID", "NAME", "SOURCE", "STATUS", "VERSION", "UPDATED"], + rows: skillRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No skills found.", + }); + }, +}); + +export const managedAgentSkillGet = defineCommand({ + description: { "en-US": "Get a Managed Agent skill", "zh-CN": "获取托管 Agent Skill 详情" }, + auth: "apiKey", + usageArgs: "--skill-id <id>", + flags: GET_FLAGS, + exampleArgs: ["--skill-id skill_abc"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const skill = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return getRemoteSkill(runtime, ctx.flags.skillId, { provider: ctx.flags.provider }); + }), + ); + if (format === "json") { + emitResult(skill, format); + return; + } + emitBare(`ID: ${skill.id}`); + emitBare(`Name: ${skill.name}`); + emitBare(`Description: ${displayValue(skill.description, 120)}`); + emitBare(`Source: ${skill.source}`); + emitBare(`Status: ${skill.status}`); + emitBare(`Version: ${displayValue(skill.latest_version)}`); + }, +}); + +export const managedAgentSkillSearch = defineCommand({ + description: { "en-US": "Search Managed Agent skills", "zh-CN": "搜索托管 Agent Skill" }, + auth: "apiKey", + usageArgs: "--query <text> [--source custom|official|all] [--limit <n>] [--page-limit <n>]", + flags: SEARCH_RESOURCE_FLAGS, + exampleArgs: ["--query browser --source official", "--query report --source all --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const source = (ctx.flags.source as SkillSource | undefined) ?? "custom"; + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + const searchCatalog = (catalog: Exclude<SkillSource, "all">) => + searchCursorPages( + async (page) => { + const response = await listRemoteSkills(runtime, { + provider: ctx.flags.provider, + source: catalog, + limit: ctx.flags.limit ?? 100, + page, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + (skill) => matchesQuery(ctx.flags.query, skill.id, skill.name, skill.description), + ctx.flags.pageLimit, + ); + if (source !== "all") return searchCatalog(source); + const [custom, official] = await Promise.all([ + searchCatalog("custom"), + searchCatalog("official"), + ]); + return { + items: [...custom.items, ...official.items], + hasMore: custom.hasMore || official.hasMore, + nextPage: undefined, + scannedPages: custom.scannedPages + official.scannedPages, + truncated: custom.truncated || official.truncated, + }; + }), + ); + emitCollection({ + format, + key: "skills", + items: result.items, + headers: ["ID", "NAME", "SOURCE", "STATUS", "VERSION", "UPDATED"], + rows: skillRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + truncated: result.truncated, + scannedPages: result.scannedPages, + emptyMessage: "No matching skills found.", + }); + }, +}); + +export const managedAgentSkillVersions = defineCommand({ + description: { + "en-US": "List Managed Agent skill versions", + "zh-CN": "列出托管 Agent Skill 版本", + }, + auth: "apiKey", + usageArgs: "--skill-id <id> [--limit <n>] [--page <cursor>] [--all]", + flags: VERSIONS_FLAGS, + exampleArgs: ["--skill-id skill_abc", "--skill-id skill_abc --all --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteSkillVersions(runtime, ctx.flags.skillId, { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + page, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "versions", + items: result.items, + headers: ["VERSION", "NAME", "TYPE", "STATUS", "UPDATED"], + rows: versionRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No skill versions found.", + }); + }, +}); + +export const managedAgentSkillDownload = defineCommand({ + description: { + "en-US": "Download a Managed Agent skill version", + "zh-CN": "下载托管 Agent Skill 版本", + }, + auth: "apiKey", + usageArgs: "--skill-id <id> --skill-version <version> --output-file <path> [--force]", + flags: DOWNLOAD_FLAGS, + exampleArgs: ["--skill-id skill_abc --skill-version 3 --output-file ./skill.zip"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + if (ctx.settings.dryRun) { + emitResult( + { + would_download_skill: ctx.flags.skillId, + version: ctx.flags.skillVersion, + output_file: ctx.flags.outputFile, + }, + format, + ); + return; + } + const content = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return downloadRemoteSkill(runtime, ctx.flags.skillId, ctx.flags.skillVersion, { + provider: ctx.flags.provider, + }); + }), + ); + const outputFile = await writeOutputFile(ctx.flags.outputFile, content, ctx.flags.force); + if (format === "json") + emitResult( + { downloaded: ctx.flags.skillId, version: ctx.flags.skillVersion, output_file: outputFile }, + format, + ); + else emitBare(`Skill downloaded to ${outputFile}`); + }, +}); diff --git a/packages/commands/src/commands/managed-agent/vault.ts b/packages/commands/src/commands/managed-agent/vault.ts new file mode 100644 index 0000000..38e916f --- /dev/null +++ b/packages/commands/src/commands/managed-agent/vault.ts @@ -0,0 +1,165 @@ +import type { CloudVault } from "@openagentpack/sdk"; +import { getRemoteVault, listRemoteVaults } from "@openagentpack/sdk"; +import { defineCommand, detectOutputFormat } from "bailian-cli-core"; +import { emitBare, emitResult } from "bailian-cli-runtime"; +import { + API_TARGET_FLAGS, + CURSOR_FLAGS, + displayValue, + emitCollection, + INCLUDE_ARCHIVED_FLAG, + matchesQuery, + SEARCH_FLAGS, + searchCursorPages, + validateLimitAndPageLimit, +} from "./_engine/api-helpers.ts"; +import { buildAgentRuntime, CREDENTIALS_NOTE } from "./_engine/config-loader.ts"; +import { withStdoutProtected } from "./_engine/console-capture.ts"; +import { withAgentErrors } from "./_engine/errors.ts"; +import { fetchAllPages } from "./_engine/pagination.ts"; + +const LIST_FLAGS = { ...API_TARGET_FLAGS, ...CURSOR_FLAGS, ...INCLUDE_ARCHIVED_FLAG }; +const SEARCH_RESOURCE_FLAGS = { + ...API_TARGET_FLAGS, + limit: CURSOR_FLAGS.limit, + ...SEARCH_FLAGS, + ...INCLUDE_ARCHIVED_FLAG, +}; +const GET_FLAGS = { + ...API_TARGET_FLAGS, + vaultId: { + type: "string", + valueHint: "<id>", + required: true, + description: { "en-US": "Vault ID", "zh-CN": "Vault ID" }, + }, +} as const; + +function vaultRows(vaults: CloudVault[]): string[][] { + return vaults.map((vault) => [ + vault.id, + displayValue(vault.display_name), + displayValue(vault.type), + displayValue(vault.created_at), + displayValue(vault.updated_at), + ]); +} + +export const managedAgentVaultList = defineCommand({ + description: { "en-US": "List Managed Agent vaults", "zh-CN": "列出托管 Agent Vault" }, + auth: "apiKey", + usageArgs: "[--limit <n>] [--page <cursor>] [--all] [--include-archived]", + flags: LIST_FLAGS, + exampleArgs: ["", "--all --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return fetchAllPages( + async (page) => { + const response = await listRemoteVaults(runtime, { + provider: ctx.flags.provider, + limit: ctx.flags.limit, + page, + include_archived: ctx.flags.includeArchived, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + ctx.flags.all, + ctx.flags.page, + ); + }), + ); + emitCollection({ + format, + key: "vaults", + items: result.items, + headers: ["ID", "NAME", "TYPE", "CREATED", "UPDATED"], + rows: vaultRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + emptyMessage: "No vaults found.", + }); + }, +}); + +export const managedAgentVaultGet = defineCommand({ + description: { "en-US": "Get a Managed Agent vault", "zh-CN": "获取托管 Agent Vault 详情" }, + auth: "apiKey", + usageArgs: "--vault-id <id>", + flags: GET_FLAGS, + exampleArgs: ["--vault-id vault_abc"], + notes: CREDENTIALS_NOTE, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const vault = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return getRemoteVault(runtime, ctx.flags.vaultId, { provider: ctx.flags.provider }); + }), + ); + if (format === "json") { + emitResult(vault, format); + return; + } + emitBare(`ID: ${vault.id}`); + emitBare(`Name: ${displayValue(vault.display_name)}`); + emitBare(`Type: ${displayValue(vault.type)}`); + emitBare(`Created: ${displayValue(vault.created_at)}`); + emitBare(`Updated: ${displayValue(vault.updated_at)}`); + }, +}); + +export const managedAgentVaultSearch = defineCommand({ + description: { "en-US": "Search Managed Agent vaults", "zh-CN": "搜索托管 Agent Vault" }, + auth: "apiKey", + usageArgs: "--query <text> [--limit <n>] [--page-limit <n>] [--include-archived]", + flags: SEARCH_RESOURCE_FLAGS, + exampleArgs: ["--query github", "--query production --page-limit 20 --output json"], + notes: CREDENTIALS_NOTE, + validate: validateLimitAndPageLimit, + async run(ctx) { + const format = detectOutputFormat(ctx.settings.output); + const result = await withAgentErrors(() => + withStdoutProtected(async () => { + const runtime = await buildAgentRuntime(ctx, ctx.flags.file ?? "agents.yaml"); + return searchCursorPages( + async (page) => { + const response = await listRemoteVaults(runtime, { + provider: ctx.flags.provider, + limit: ctx.flags.limit ?? 100, + page, + include_archived: ctx.flags.includeArchived, + }); + return { + items: response.data, + hasMore: response.has_more, + nextPage: response.next_page, + }; + }, + (vault) => matchesQuery(ctx.flags.query, vault.id, vault.display_name, vault.metadata), + ctx.flags.pageLimit, + ); + }), + ); + emitCollection({ + format, + key: "vaults", + items: result.items, + headers: ["ID", "NAME", "TYPE", "CREATED", "UPDATED"], + rows: vaultRows(result.items), + hasMore: result.hasMore, + nextPage: result.nextPage, + truncated: result.truncated, + scannedPages: result.scannedPages, + emptyMessage: "No matching vaults found.", + }); + }, +}); diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 79e311e..6ec38d9 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -148,6 +148,61 @@ export { default as managedAgentSessionRun } from "./commands/managed-agent/sess export { default as managedAgentSessionSend } from "./commands/managed-agent/session-send.ts"; export { default as managedAgentSessionEvents } from "./commands/managed-agent/session-events.ts"; export { default as managedAgentSkillList } from "./commands/managed-agent/skill-list.ts"; +export { default as managedAgentCapabilities } from "./commands/managed-agent/capabilities.ts"; +export { + managedAgentAgentGet, + managedAgentAgentList, + managedAgentAgentSearch, + managedAgentAgentVersions, +} from "./commands/managed-agent/agent.ts"; +export { + managedAgentEnvironmentGet, + managedAgentEnvironmentList, + managedAgentEnvironmentSearch, +} from "./commands/managed-agent/environment.ts"; +export { + managedAgentSkillDownload, + managedAgentSkillGet, + managedAgentSkillSearch, + managedAgentSkillVersions, +} from "./commands/managed-agent/skill.ts"; +export { + managedAgentVaultGet, + managedAgentVaultList, + managedAgentVaultSearch, +} from "./commands/managed-agent/vault.ts"; +export { + managedAgentDeploymentGet, + managedAgentDeploymentList, + managedAgentDeploymentPause, + managedAgentDeploymentRun, + managedAgentDeploymentRunsGet, + managedAgentDeploymentRunsList, + managedAgentDeploymentSearch, + managedAgentDeploymentUnpause, +} from "./commands/managed-agent/deployment.ts"; +export { + managedAgentSessionArchive, + managedAgentSessionSearch, + managedAgentSessionUpdate, +} from "./commands/managed-agent/session-management.ts"; +export { + managedAgentSessionEventList, + managedAgentSessionEventSend, + managedAgentSessionEventStream, +} from "./commands/managed-agent/session-event.ts"; +export { + managedAgentSessionDebug, + managedAgentSessionExport, +} from "./commands/managed-agent/session-debug.ts"; +export { + managedAgentFileDelete, + managedAgentFileDownload, + managedAgentFileGet, + managedAgentFileList, + managedAgentFileSearch, + managedAgentFileUpload, +} from "./commands/managed-agent/file.ts"; export { default as workspaceInit } from "./commands/workspace/init.ts"; export { default as pluginInstall } from "./commands/plugin/install.ts"; export { default as pluginLink } from "./commands/plugin/link.ts"; diff --git a/packages/commands/tests/e2e/managed-agent.e2e.test.ts b/packages/commands/tests/e2e/managed-agent.e2e.test.ts index fc6b058..6bb31e6 100644 --- a/packages/commands/tests/e2e/managed-agent.e2e.test.ts +++ b/packages/commands/tests/e2e/managed-agent.e2e.test.ts @@ -199,9 +199,88 @@ describe("e2e: managed-agent", () => { expect(stderr).not.toMatch(/--source must be one of/i); expect(stderr).toMatch(/File not found.*agents\.e2e-missing\.yaml/i); }); + + test("managed-agent skill versions --help 展示 cursor 分页选项", async () => { + const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [ + "managed-agent", + "skill", + "versions", + "--help", + ]); + expect(exitCode, stderr).toBe(0); + expect(stderr).toMatch(/--skill-id|--limit|--page|--all/i); + }); }); describe("e2e: managed-agent(--dry-run 短路,不联网不写盘)", () => { + test("capabilities 明确区分 Session Event 与独立 Thread API", async () => { + const { stdout, stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [ + "managed-agent", + "capabilities", + "--output", + "json", + ]); + expect(exitCode, stderr).toBe(0); + const data = parseStdoutJson<{ + operations?: Record<string, { supported?: boolean; reason?: string }>; + }>(stdout); + expect(data.operations?.["session.event.list"]?.supported).toBe(true); + expect(data.operations?.["session_thread.list"]?.supported).toBe(false); + expect(data.operations?.["session_thread.list"]?.reason).toMatch(/no independent Thread/i); + }); + + test.each([ + ["session archive", ["session", "archive", "--session-id", "sess_e2e"]], + ["session update", ["session", "update", "--session-id", "sess_e2e", "--title", "new"]], + [ + "session event send", + [ + "session", + "event", + "send", + "--session-id", + "sess_e2e", + "--event", + '{"type":"message","content":"hello"}', + ], + ], + [ + "session export", + ["session", "export", "--session-id", "sess_e2e", "--output-file", "debug.zip"], + ], + ["file upload", ["file", "upload", "--path", "missing.txt"]], + [ + "file download", + ["file", "download", "--file-id", "file_e2e", "--output-file", "artifact.bin"], + ], + ["file delete", ["file", "delete", "--file-id", "file_e2e"]], + [ + "skill download", + [ + "skill", + "download", + "--skill-id", + "skill_e2e", + "--skill-version", + "1", + "--output-file", + "skill.zip", + ], + ], + ["deployment run", ["deployment", "run", "--deployment-id", "dep_e2e"]], + ["deployment pause", ["deployment", "pause", "--deployment-id", "dep_e2e"]], + ["deployment unpause", ["deployment", "unpause", "--deployment-id", "dep_e2e"]], + ])("%s --dry-run 在构建 SDK runtime 前短路", async (_label, commandArgs) => { + const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [ + "managed-agent", + ...commandArgs, + "--dry-run", + "--output", + "json", + ]); + expect(exitCode, stderr).toBe(0); + }); + test("init --dry-run 仅输出计划,不创建文件", async () => { const { stdout, stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [ "managed-agent", diff --git a/packages/commands/tests/e2e/topic-routes.ts b/packages/commands/tests/e2e/topic-routes.ts index fe249e8..5104072 100644 --- a/packages/commands/tests/e2e/topic-routes.ts +++ b/packages/commands/tests/e2e/topic-routes.ts @@ -194,7 +194,47 @@ export const MANAGED_AGENT_ROUTES: E2eRouteExports = { "managed-agent session delete": "managedAgentSessionDelete", "managed-agent session run": "managedAgentSessionRun", "managed-agent session send": "managedAgentSessionSend", + "managed-agent session list": "managedAgentSessionList", + "managed-agent session events": "managedAgentSessionEvents", "managed-agent skill-list": "managedAgentSkillList", + "managed-agent capabilities": "managedAgentCapabilities", + "managed-agent agent list": "managedAgentAgentList", + "managed-agent agent get": "managedAgentAgentGet", + "managed-agent agent search": "managedAgentAgentSearch", + "managed-agent agent versions": "managedAgentAgentVersions", + "managed-agent environment list": "managedAgentEnvironmentList", + "managed-agent environment get": "managedAgentEnvironmentGet", + "managed-agent environment search": "managedAgentEnvironmentSearch", + "managed-agent skill list": "managedAgentSkillList", + "managed-agent skill get": "managedAgentSkillGet", + "managed-agent skill search": "managedAgentSkillSearch", + "managed-agent skill versions": "managedAgentSkillVersions", + "managed-agent skill download": "managedAgentSkillDownload", + "managed-agent vault list": "managedAgentVaultList", + "managed-agent vault get": "managedAgentVaultGet", + "managed-agent vault search": "managedAgentVaultSearch", + "managed-agent deployment list": "managedAgentDeploymentList", + "managed-agent deployment get": "managedAgentDeploymentGet", + "managed-agent deployment search": "managedAgentDeploymentSearch", + "managed-agent deployment runs list": "managedAgentDeploymentRunsList", + "managed-agent deployment runs get": "managedAgentDeploymentRunsGet", + "managed-agent deployment run": "managedAgentDeploymentRun", + "managed-agent deployment pause": "managedAgentDeploymentPause", + "managed-agent deployment unpause": "managedAgentDeploymentUnpause", + "managed-agent session search": "managedAgentSessionSearch", + "managed-agent session update": "managedAgentSessionUpdate", + "managed-agent session archive": "managedAgentSessionArchive", + "managed-agent session event list": "managedAgentSessionEventList", + "managed-agent session event send": "managedAgentSessionEventSend", + "managed-agent session event stream": "managedAgentSessionEventStream", + "managed-agent session debug": "managedAgentSessionDebug", + "managed-agent session export": "managedAgentSessionExport", + "managed-agent file upload": "managedAgentFileUpload", + "managed-agent file list": "managedAgentFileList", + "managed-agent file get": "managedAgentFileGet", + "managed-agent file search": "managedAgentFileSearch", + "managed-agent file download": "managedAgentFileDownload", + "managed-agent file delete": "managedAgentFileDelete", }; export const KNOWLEDGE_KB_LIST_ROUTES: E2eRouteExports = { diff --git a/packages/commands/tests/managed-agent-api-helpers.test.ts b/packages/commands/tests/managed-agent-api-helpers.test.ts new file mode 100644 index 0000000..ee468ab --- /dev/null +++ b/packages/commands/tests/managed-agent-api-helpers.test.ts @@ -0,0 +1,41 @@ +import { expect, test } from "vite-plus/test"; +import { createZip, redactSensitiveValues } from "../src/commands/managed-agent/session-debug.ts"; +import { searchCursorPages } from "../src/commands/managed-agent/_engine/api-helpers.ts"; + +test("searchCursorPages preserves opaque cursors and marks truncated scans", async () => { + const cursors: Array<string | undefined> = []; + const result = await searchCursorPages( + async (page) => { + cursors.push(page); + if (!page) return { items: ["one"], hasMore: true, nextPage: "opaque-A" }; + return { items: ["two"], hasMore: true, nextPage: "opaque-B" }; + }, + (item) => item.includes("o"), + 2, + ); + expect(cursors).toEqual([undefined, "opaque-A"]); + expect(result.items).toEqual(["one", "two"]); + expect(result.truncated).toBe(true); + expect(result.nextPage).toBe("opaque-B"); +}); + +test("session export ZIP is valid-shaped and redacts credential-like values", () => { + const redacted = redactSensitiveValues({ + api_key: "secret", + nested: { authorization: "Bearer secret", normal: "kept" }, + }); + expect(redacted).toEqual({ + api_key: "[REDACTED]", + nested: { authorization: "[REDACTED]", normal: "kept" }, + }); + + const zip = createZip([ + { name: "manifest.json", content: new TextEncoder().encode('{"schema_version":1}\n') }, + { name: "events.json", content: new TextEncoder().encode("[]\n") }, + ]); + expect(Array.from(zip.slice(0, 4))).toEqual([0x50, 0x4b, 0x03, 0x04]); + const decoded = new TextDecoder().decode(zip); + expect(decoded).toContain("manifest.json"); + expect(decoded).toContain("events.json"); + expect(Array.from(zip.slice(-22, -18))).toEqual([0x50, 0x4b, 0x05, 0x06]); +}); diff --git a/skills/bailian-managed-agent/SKILL.md b/skills/bailian-managed-agent/SKILL.md index 7277707..cf50059 100644 --- a/skills/bailian-managed-agent/SKILL.md +++ b/skills/bailian-managed-agent/SKILL.md @@ -5,10 +5,10 @@ metadata: requires: bins: ["bl"] description: >- - 阿里云百炼托管 Agent 声明式基础设施入口:用户要创建agent、初始化 agents.yaml、校验或预览 agent 配置变更、 - 创建/更新/销毁百炼托管 Agent 或 Deployment、和托管 agent 对话、查会话事件历史、导入或取消跟踪远端资源时使用 - `bl managed-agent`。以 agents.yaml 为唯一事实源做 IaC:init 建脚手架、validate 离线校验、plan 预览 diff、 - apply / destroy 变更远端资源且必须带 `--yes`,务必先 plan 给用户看 diff 再让其确认。 + 阿里云百炼托管 Agent 声明式基础设施与 API 命令入口:用户要创建agent、初始化 agents.yaml、校验或预览配置变更、 + 创建/更新/销毁托管 Agent 或 Deployment,或查询 Agent/Environment/Skill/Vault/Deployment、管理 Session/Event/File、 + 运行/暂停 Deployment 时使用 `bl managed-agent`。持久资源仍以 agents.yaml 为唯一事实源做 IaC;公开 API 能力按资源透出 + list/get/search/versions/download、数据面和运行时动作命令。apply / destroy 与破坏性 API 命令必须遵守 `--yes` 门禁。 反触发:调用已上线的百炼应用/智能体走 bailian-app-call 或 `bl app`;宿主 agent 自身的记忆、技能、 子代理不走本 skill;生图生视频走 bailian-gen。 官方安装:`bl skill init`(与共享协议 bailian-protocol 同装)。 @@ -20,12 +20,16 @@ description: >- ## Safety guardrail (the most important rule) -`apply` / `destroy` **mutate remote resources** and only execute when `--yes` is passed: +`apply` / `destroy` **mutate persistent remote resources** and only execute when `--yes` is passed: -1. Always run `bl managed-agent plan` first and show the diff to the user. +1. For `agents.yaml` resource changes, always run `bl managed-agent plan` first and show the diff to the user. 2. Only after explicit user confirmation, retry `apply` / `destroy` with `--yes`. 3. Never add `--yes` on your own initiative before the user has confirmed. +API-oriented commands do not replace IaC. Agent / Environment / Skill / Vault 的持久配置仍通过 +`agents.yaml → plan → apply` 管理;命令式写操作只覆盖 Session、Event、File 和 Deployment 运行时动作。 +`session archive|delete`、`file delete`、`deployment run` 也需要先 `--dry-run`,确认后才传 `--yes`。 + ## IaC lifecycle ``` @@ -66,6 +70,32 @@ deployments: | List session event history | `bl managed-agent session events` | | Delete a session | `bl managed-agent session delete` | +规范路径是 `session event list|send|stream`;`session events` 保留为 `session event list` 的兼容别名。 +Managed Agents 的子线程通过 Event 中的 `session_thread_id` 暴露;公开 API 当前没有独立 Thread 资源 CRUD, +不要构造 `session thread list|get|archive|events` 命令。 + +## API-oriented resource commands + +| Intent | Command family | +| -------------------------------------- | ------------------------------- | ------- | ------------------------------ | --------- | --------- | ------- | +| Check exact API support/auth/reason | `bl managed-agent capabilities` | +| Discover agents and versions | `agent list | get | search | versions` | +| Discover environments | `environment list | get | search` | +| Discover skills and download a version | `skill list | get | search | versions | download` | +| Inspect vault envelopes | `vault list | get | search` | +| Inspect deployments and run history | `deployment list | get | search`, `deployment runs list | get` | +| Run or pause deployments | `deployment run | pause | unpause` | +| Manage session metadata/lifecycle | `session list | get | search | update | archive | delete` | +| Work with raw events | `session event send | list | stream` | +| Diagnose/export a session | `session debug | export` | +| Work with files | `file upload | list | get | search | download | delete` | + +- 所有 Cursor 都是不透明字符串:只回传 `next_page`,不得转换为数字页码。 +- 客户端搜索默认最多扫描 10 页;需要扩大范围时显式传 `--page-limit`。Deployment 搜索直接映射服务端 `keyword`。 +- 下载必须给出 `--output-file`;默认不覆盖已有文件,只有用户确认后才可加 `--force`。 +- `session export` 只导出诊断元数据,不含 File 正文,并会脱敏凭证类字段。 +- 公开 Managed Agents API 没有模型 Catalog,也没有 MCP OAuth Login;以 `capabilities` 返回的 unsupported 原因为准。 + ## Local state management | Intent | Command | diff --git a/skills/bailian-managed-agent/reference/index.md b/skills/bailian-managed-agent/reference/index.md index a401eac..46f2a87 100644 --- a/skills/bailian-managed-agent/reference/index.md +++ b/skills/bailian-managed-agent/reference/index.md @@ -9,31 +9,69 @@ Use this index for the skill-scoped quick index and global flags. ## Quick index -| Command | Authentication | Description | Detail | -| --------------------------------- | -------------- | ------------------------------------------------------------- | ------------------------------------ | -| `bl managed-agent apply` | API Key | Apply planned changes to create/update/delete agent resources | [managed-agent.md](managed-agent.md) | -| `bl managed-agent destroy` | API Key | Destroy all managed agent resources tracked in state | [managed-agent.md](managed-agent.md) | -| `bl managed-agent init` | No Auth | Create a new agents.yaml template | [managed-agent.md](managed-agent.md) | -| `bl managed-agent plan` | API Key | Show what changes would be applied to agent infrastructure | [managed-agent.md](managed-agent.md) | -| `bl managed-agent session create` | API Key | Create a new session for an agent | [managed-agent.md](managed-agent.md) | -| `bl managed-agent session delete` | API Key | Delete a session | [managed-agent.md](managed-agent.md) | -| `bl managed-agent session events` | API Key | List event history for a session | [managed-agent.md](managed-agent.md) | -| `bl managed-agent session get` | API Key | Get details of a session | [managed-agent.md](managed-agent.md) | -| `bl managed-agent session list` | API Key | List sessions from the provider | [managed-agent.md](managed-agent.md) | -| `bl managed-agent session run` | API Key | Create a session, send a message, and stream the response | [managed-agent.md](managed-agent.md) | -| `bl managed-agent session send` | API Key | Send a message to an existing session and stream the response | [managed-agent.md](managed-agent.md) | -| `bl managed-agent skill-list` | API Key | List skills from the provider's skill catalog | [managed-agent.md](managed-agent.md) | -| `bl managed-agent state import` | API Key | Import an existing remote resource into agents state | [managed-agent.md](managed-agent.md) | -| `bl managed-agent state list` | No Auth | List resources tracked in agents state | [managed-agent.md](managed-agent.md) | -| `bl managed-agent state rm` | No Auth | Remove a resource from state without destroying it remotely | [managed-agent.md](managed-agent.md) | -| `bl managed-agent state show` | No Auth | Show details of a resource in agents state | [managed-agent.md](managed-agent.md) | -| `bl managed-agent validate` | No Auth | Validate an agents.yaml configuration (offline) | [managed-agent.md](managed-agent.md) | +| Command | Authentication | Description | Detail | +| --------------------------------------- | -------------- | ------------------------------------------------------------- | ------------------------------------ | +| `bl managed-agent agent get` | API Key | Get a Managed Agent | [managed-agent.md](managed-agent.md) | +| `bl managed-agent agent list` | API Key | List Managed Agents | [managed-agent.md](managed-agent.md) | +| `bl managed-agent agent search` | API Key | Search Managed Agents | [managed-agent.md](managed-agent.md) | +| `bl managed-agent agent versions` | API Key | List Managed Agent versions | [managed-agent.md](managed-agent.md) | +| `bl managed-agent apply` | API Key | Apply planned changes to create/update/delete agent resources | [managed-agent.md](managed-agent.md) | +| `bl managed-agent capabilities` | No Auth | Show operation-level Managed Agents API capabilities | [managed-agent.md](managed-agent.md) | +| `bl managed-agent deployment get` | API Key | Get a Managed Agent deployment | [managed-agent.md](managed-agent.md) | +| `bl managed-agent deployment list` | API Key | List Managed Agent deployments | [managed-agent.md](managed-agent.md) | +| `bl managed-agent deployment pause` | API Key | Pause a Managed Agent deployment | [managed-agent.md](managed-agent.md) | +| `bl managed-agent deployment run` | API Key | Run a Managed Agent deployment now | [managed-agent.md](managed-agent.md) | +| `bl managed-agent deployment runs get` | API Key | Get a Managed Agent deployment run | [managed-agent.md](managed-agent.md) | +| `bl managed-agent deployment runs list` | API Key | List runs for a Managed Agent deployment | [managed-agent.md](managed-agent.md) | +| `bl managed-agent deployment search` | API Key | Search Managed Agent deployments | [managed-agent.md](managed-agent.md) | +| `bl managed-agent deployment unpause` | API Key | Unpause a Managed Agent deployment | [managed-agent.md](managed-agent.md) | +| `bl managed-agent destroy` | API Key | Destroy all managed agent resources tracked in state | [managed-agent.md](managed-agent.md) | +| `bl managed-agent environment get` | API Key | Get a Managed Agent environment | [managed-agent.md](managed-agent.md) | +| `bl managed-agent environment list` | API Key | List Managed Agent environments | [managed-agent.md](managed-agent.md) | +| `bl managed-agent environment search` | API Key | Search Managed Agent environments | [managed-agent.md](managed-agent.md) | +| `bl managed-agent file delete` | API Key | Delete a Managed Agent file | [managed-agent.md](managed-agent.md) | +| `bl managed-agent file download` | API Key | Download Managed Agent file content | [managed-agent.md](managed-agent.md) | +| `bl managed-agent file get` | API Key | Get Managed Agent file metadata | [managed-agent.md](managed-agent.md) | +| `bl managed-agent file list` | API Key | List Managed Agent files | [managed-agent.md](managed-agent.md) | +| `bl managed-agent file search` | API Key | Search Managed Agent files | [managed-agent.md](managed-agent.md) | +| `bl managed-agent file upload` | API Key | Upload a Managed Agent file | [managed-agent.md](managed-agent.md) | +| `bl managed-agent init` | No Auth | Create a new agents.yaml template | [managed-agent.md](managed-agent.md) | +| `bl managed-agent plan` | API Key | Show what changes would be applied to agent infrastructure | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session archive` | API Key | Archive a Managed Agent session | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session create` | API Key | Create a new session for an agent | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session debug` | API Key | Aggregate session diagnostics | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session delete` | API Key | Delete a session | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session event list` | API Key | List events for a Managed Agent session | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session event send` | API Key | Send raw events to a Managed Agent session | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session event stream` | API Key | Stream events from a Managed Agent session | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session events` | API Key | List events for a Managed Agent session | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session export` | API Key | Export session diagnostics as a ZIP | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session get` | API Key | Get details of a session | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session list` | API Key | List sessions from the provider | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session run` | API Key | Create a session, send a message, and stream the response | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session search` | API Key | Search Managed Agent sessions | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session send` | API Key | Send a message to an existing session and stream the response | [managed-agent.md](managed-agent.md) | +| `bl managed-agent session update` | API Key | Update a Managed Agent session | [managed-agent.md](managed-agent.md) | +| `bl managed-agent skill download` | API Key | Download a Managed Agent skill version | [managed-agent.md](managed-agent.md) | +| `bl managed-agent skill get` | API Key | Get a Managed Agent skill | [managed-agent.md](managed-agent.md) | +| `bl managed-agent skill list` | API Key | List Managed Agent skills | [managed-agent.md](managed-agent.md) | +| `bl managed-agent skill search` | API Key | Search Managed Agent skills | [managed-agent.md](managed-agent.md) | +| `bl managed-agent skill versions` | API Key | List Managed Agent skill versions | [managed-agent.md](managed-agent.md) | +| `bl managed-agent skill-list` | API Key | List Managed Agent skills | [managed-agent.md](managed-agent.md) | +| `bl managed-agent state import` | API Key | Import an existing remote resource into agents state | [managed-agent.md](managed-agent.md) | +| `bl managed-agent state list` | No Auth | List resources tracked in agents state | [managed-agent.md](managed-agent.md) | +| `bl managed-agent state rm` | No Auth | Remove a resource from state without destroying it remotely | [managed-agent.md](managed-agent.md) | +| `bl managed-agent state show` | No Auth | Show details of a resource in agents state | [managed-agent.md](managed-agent.md) | +| `bl managed-agent validate` | No Auth | Validate an agents.yaml configuration (offline) | [managed-agent.md](managed-agent.md) | +| `bl managed-agent vault get` | API Key | Get a Managed Agent vault | [managed-agent.md](managed-agent.md) | +| `bl managed-agent vault list` | API Key | List Managed Agent vaults | [managed-agent.md](managed-agent.md) | +| `bl managed-agent vault search` | API Key | Search Managed Agent vaults | [managed-agent.md](managed-agent.md) | ## By group -| Group | Commands | Reference | -| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | -| `managed-agent` | `apply`, `destroy`, `init`, `plan`, `session create`, `session delete`, `session events`, `session get`, `session list`, `session run`, `session send`, `skill-list`, `state import`, `state list`, `state rm`, `state show`, `validate` | [managed-agent.md](managed-agent.md) | +| Group | Commands | Reference | +| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | +| `managed-agent` | `agent get`, `agent list`, `agent search`, `agent versions`, `apply`, `capabilities`, `deployment get`, `deployment list`, `deployment pause`, `deployment run`, `deployment runs get`, `deployment runs list`, `deployment search`, `deployment unpause`, `destroy`, `environment get`, `environment list`, `environment search`, `file delete`, `file download`, `file get`, `file list`, `file search`, `file upload`, `init`, `plan`, `session archive`, `session create`, `session debug`, `session delete`, `session event list`, `session event send`, `session event stream`, `session events`, `session export`, `session get`, `session list`, `session run`, `session search`, `session send`, `session update`, `skill download`, `skill get`, `skill list`, `skill search`, `skill versions`, `skill-list`, `state import`, `state list`, `state rm`, `state show`, `validate`, `vault get`, `vault list`, `vault search` | [managed-agent.md](managed-agent.md) | ## Global flags diff --git a/skills/bailian-managed-agent/reference/managed-agent.md b/skills/bailian-managed-agent/reference/managed-agent.md index e3975b7..7836606 100644 --- a/skills/bailian-managed-agent/reference/managed-agent.md +++ b/skills/bailian-managed-agent/reference/managed-agent.md @@ -7,28 +7,220 @@ Index: [index.md](index.md) ## Commands in this group -| Command | Authentication | Description | -| --------------------------------- | -------------- | ------------------------------------------------------------- | -| `bl managed-agent apply` | API Key | Apply planned changes to create/update/delete agent resources | -| `bl managed-agent destroy` | API Key | Destroy all managed agent resources tracked in state | -| `bl managed-agent init` | No Auth | Create a new agents.yaml template | -| `bl managed-agent plan` | API Key | Show what changes would be applied to agent infrastructure | -| `bl managed-agent session create` | API Key | Create a new session for an agent | -| `bl managed-agent session delete` | API Key | Delete a session | -| `bl managed-agent session events` | API Key | List event history for a session | -| `bl managed-agent session get` | API Key | Get details of a session | -| `bl managed-agent session list` | API Key | List sessions from the provider | -| `bl managed-agent session run` | API Key | Create a session, send a message, and stream the response | -| `bl managed-agent session send` | API Key | Send a message to an existing session and stream the response | -| `bl managed-agent skill-list` | API Key | List skills from the provider's skill catalog | -| `bl managed-agent state import` | API Key | Import an existing remote resource into agents state | -| `bl managed-agent state list` | No Auth | List resources tracked in agents state | -| `bl managed-agent state rm` | No Auth | Remove a resource from state without destroying it remotely | -| `bl managed-agent state show` | No Auth | Show details of a resource in agents state | -| `bl managed-agent validate` | No Auth | Validate an agents.yaml configuration (offline) | +| Command | Authentication | Description | +| --------------------------------------- | -------------- | ------------------------------------------------------------- | +| `bl managed-agent agent get` | API Key | Get a Managed Agent | +| `bl managed-agent agent list` | API Key | List Managed Agents | +| `bl managed-agent agent search` | API Key | Search Managed Agents | +| `bl managed-agent agent versions` | API Key | List Managed Agent versions | +| `bl managed-agent apply` | API Key | Apply planned changes to create/update/delete agent resources | +| `bl managed-agent capabilities` | No Auth | Show operation-level Managed Agents API capabilities | +| `bl managed-agent deployment get` | API Key | Get a Managed Agent deployment | +| `bl managed-agent deployment list` | API Key | List Managed Agent deployments | +| `bl managed-agent deployment pause` | API Key | Pause a Managed Agent deployment | +| `bl managed-agent deployment run` | API Key | Run a Managed Agent deployment now | +| `bl managed-agent deployment runs get` | API Key | Get a Managed Agent deployment run | +| `bl managed-agent deployment runs list` | API Key | List runs for a Managed Agent deployment | +| `bl managed-agent deployment search` | API Key | Search Managed Agent deployments | +| `bl managed-agent deployment unpause` | API Key | Unpause a Managed Agent deployment | +| `bl managed-agent destroy` | API Key | Destroy all managed agent resources tracked in state | +| `bl managed-agent environment get` | API Key | Get a Managed Agent environment | +| `bl managed-agent environment list` | API Key | List Managed Agent environments | +| `bl managed-agent environment search` | API Key | Search Managed Agent environments | +| `bl managed-agent file delete` | API Key | Delete a Managed Agent file | +| `bl managed-agent file download` | API Key | Download Managed Agent file content | +| `bl managed-agent file get` | API Key | Get Managed Agent file metadata | +| `bl managed-agent file list` | API Key | List Managed Agent files | +| `bl managed-agent file search` | API Key | Search Managed Agent files | +| `bl managed-agent file upload` | API Key | Upload a Managed Agent file | +| `bl managed-agent init` | No Auth | Create a new agents.yaml template | +| `bl managed-agent plan` | API Key | Show what changes would be applied to agent infrastructure | +| `bl managed-agent session archive` | API Key | Archive a Managed Agent session | +| `bl managed-agent session create` | API Key | Create a new session for an agent | +| `bl managed-agent session debug` | API Key | Aggregate session diagnostics | +| `bl managed-agent session delete` | API Key | Delete a session | +| `bl managed-agent session event list` | API Key | List events for a Managed Agent session | +| `bl managed-agent session event send` | API Key | Send raw events to a Managed Agent session | +| `bl managed-agent session event stream` | API Key | Stream events from a Managed Agent session | +| `bl managed-agent session events` | API Key | List events for a Managed Agent session | +| `bl managed-agent session export` | API Key | Export session diagnostics as a ZIP | +| `bl managed-agent session get` | API Key | Get details of a session | +| `bl managed-agent session list` | API Key | List sessions from the provider | +| `bl managed-agent session run` | API Key | Create a session, send a message, and stream the response | +| `bl managed-agent session search` | API Key | Search Managed Agent sessions | +| `bl managed-agent session send` | API Key | Send a message to an existing session and stream the response | +| `bl managed-agent session update` | API Key | Update a Managed Agent session | +| `bl managed-agent skill download` | API Key | Download a Managed Agent skill version | +| `bl managed-agent skill get` | API Key | Get a Managed Agent skill | +| `bl managed-agent skill list` | API Key | List Managed Agent skills | +| `bl managed-agent skill search` | API Key | Search Managed Agent skills | +| `bl managed-agent skill versions` | API Key | List Managed Agent skill versions | +| `bl managed-agent skill-list` | API Key | List Managed Agent skills | +| `bl managed-agent state import` | API Key | Import an existing remote resource into agents state | +| `bl managed-agent state list` | No Auth | List resources tracked in agents state | +| `bl managed-agent state rm` | No Auth | Remove a resource from state without destroying it remotely | +| `bl managed-agent state show` | No Auth | Show details of a resource in agents state | +| `bl managed-agent validate` | No Auth | Validate an agents.yaml configuration (offline) | +| `bl managed-agent vault get` | API Key | Get a Managed Agent vault | +| `bl managed-agent vault list` | API Key | List Managed Agent vaults | +| `bl managed-agent vault search` | API Key | Search Managed Agent vaults | ## Command details +### `bl managed-agent agent get` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------- | +| **Name** | `managed-agent agent get` | +| **Description** | Get a Managed Agent | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent agent get --agent-id <id> [--agent-version <n>] [--file <path>]` | + +#### Flags + +| Flag | Type | Required | Description | +| --------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--agent-id <id>` | string | yes | Agent ID | +| `--agent-version <n>` | number | no | Specific agent version | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent agent get --agent-id agent_abc +``` + +```bash +bl managed-agent agent get --agent-id agent_abc --agent-version 3 --output json +``` + +### `bl managed-agent agent list` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent agent list` | +| **Description** | List Managed Agents | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent agent list [--limit <n>] [--page <cursor>] [--all] [--include-archived] [--file <path>]` | + +#### Flags + +| Flag | Type | Required | Description | +| -------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--include-archived` | switch | no | Include archived resources | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent agent list +``` + +```bash +bl managed-agent agent list --limit 50 +``` + +```bash +bl managed-agent agent list --all --include-archived --output json +``` + +### `bl managed-agent agent search` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent agent search` | +| **Description** | Search Managed Agents | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent agent search --query <text> [--limit <n>] [--page-limit <n>] [--include-archived]` | + +#### Flags + +| Flag | Type | Required | Description | +| -------------------- | ------ | -------- | ------------------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--query <text>` | string | yes | Case-insensitive text to find in IDs, names, and descriptions | +| `--page-limit <n>` | number | no | Maximum pages to scan for client-side search (default: 10) | +| `--include-archived` | switch | no | Include archived resources | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent agent search --query assistant +``` + +```bash +bl managed-agent agent search --query code --page-limit 20 --output json +``` + +### `bl managed-agent agent versions` + +| Field | Value | +| ------------------ | ----------------------------------------------------------------------------------------- | +| **Name** | `managed-agent agent versions` | +| **Description** | List Managed Agent versions | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent agent versions --agent-id <id> [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--agent-id <id>` | string | yes | Agent ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent agent versions --agent-id agent_abc +``` + +```bash +bl managed-agent agent versions --agent-id agent_abc --all --output json +``` + ### `bl managed-agent apply` | Field | Value | @@ -66,6 +258,330 @@ bl managed-agent apply --yes bl managed-agent apply --provider bailian --yes ``` +### `bl managed-agent capabilities` + +| Field | Value | +| ------------------ | ---------------------------------------------------- | +| **Name** | `managed-agent capabilities` | +| **Description** | Show operation-level Managed Agents API capabilities | +| **Authentication** | No Auth | +| **Usage** | `bl managed-agent capabilities [--provider <name>]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | -------------------------------------- | +| `--provider <name>` | string | no | Provider to inspect (default: bailian) | + +#### Notes + +- Capabilities distinguish public Managed Agents APIs from client-side compositions and unsupported resources. + +#### Examples + +```bash +bl managed-agent capabilities +``` + +```bash +bl managed-agent capabilities --provider bailian --output json +``` + +### `bl managed-agent deployment get` + +| Field | Value | +| ------------------ | ------------------------------------------------------ | +| **Name** | `managed-agent deployment get` | +| **Description** | Get a Managed Agent deployment | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent deployment get --deployment-id <id>` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--deployment-id <id>` | string | yes | Deployment ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent deployment get --deployment-id dep_abc +``` + +### `bl managed-agent deployment list` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent deployment list` | +| **Description** | List Managed Agent deployments | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent deployment list [--agent-id <id>] [--status active\|paused] [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------------------ | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--agent-id <id>` | string | no | Filter by agent ID | +| `--status <active\|paused>` | string | no | Filter by deployment status | +| `--include-archived` | switch | no | Include archived resources | +| `--created-at-gte <timestamp>` | string | no | Created at or after this timestamp | +| `--created-at-lte <timestamp>` | string | no | Created at or before this timestamp | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent deployment list +``` + +```bash +bl managed-agent deployment list --status active --all --output json +``` + +### `bl managed-agent deployment pause` + +| Field | Value | +| ------------------ | --------------------------------------------------------------------------------- | +| **Name** | `managed-agent deployment pause` | +| **Description** | Pause a Managed Agent deployment | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent deployment pause (--deployment <name> \| --deployment-id <id>)` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | -------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--deployment <name>` | string | no | Logical deployment name in agents.yaml/state | +| `--deployment-id <id>` | string | no | Direct deployment ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent deployment pause --deployment daily-report --dry-run +``` + +```bash +bl managed-agent deployment pause --deployment-id dep_abc +``` + +### `bl managed-agent deployment run` + +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------- | +| **Name** | `managed-agent deployment run` | +| **Description** | Run a Managed Agent deployment now | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent deployment run (--deployment <name> \| --deployment-id <id>) --yes` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | -------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--deployment <name>` | string | no | Logical deployment name in agents.yaml/state | +| `--deployment-id <id>` | string | no | Direct deployment ID | +| `--yes` | switch | no | Confirm deployment run | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent deployment run --deployment daily-report --dry-run +``` + +```bash +bl managed-agent deployment run --deployment-id dep_abc --yes +``` + +### `bl managed-agent deployment runs get` + +| Field | Value | +| ------------------ | ---------------------------------------------------- | +| **Name** | `managed-agent deployment runs get` | +| **Description** | Get a Managed Agent deployment run | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent deployment runs get --run-id <id>` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--run-id <id>` | string | yes | Deployment run ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent deployment runs get --run-id run_abc +``` + +### `bl managed-agent deployment runs list` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent deployment runs list` | +| **Description** | List runs for a Managed Agent deployment | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent deployment runs list --deployment-id <id> [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--deployment-id <id>` | string | yes | Deployment ID | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent deployment runs list --deployment-id dep_abc +``` + +```bash +bl managed-agent deployment runs list --deployment-id dep_abc --all --output json +``` + +### `bl managed-agent deployment search` + +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent deployment search` | +| **Description** | Search Managed Agent deployments | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent deployment search --query <text> [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------------------ | ------ | -------- | ------------------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--query <text>` | string | yes | Case-insensitive text to find in IDs, names, and descriptions | +| `--agent-id <id>` | string | no | Filter by agent ID | +| `--status <active\|paused>` | string | no | Filter by deployment status | +| `--include-archived` | switch | no | Include archived resources | +| `--created-at-gte <timestamp>` | string | no | Created at or after this timestamp | +| `--created-at-lte <timestamp>` | string | no | Created at or before this timestamp | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. +- Deployment search maps --query to the provider's server-side keyword parameter. + +#### Examples + +```bash +bl managed-agent deployment search --query report +``` + +```bash +bl managed-agent deployment search --query nightly --all --output json +``` + +### `bl managed-agent deployment unpause` + +| Field | Value | +| ------------------ | ----------------------------------------------------------------------------------- | +| **Name** | `managed-agent deployment unpause` | +| **Description** | Unpause a Managed Agent deployment | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent deployment unpause (--deployment <name> \| --deployment-id <id>)` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | -------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--deployment <name>` | string | no | Logical deployment name in agents.yaml/state | +| `--deployment-id <id>` | string | no | Direct deployment ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent deployment unpause --deployment daily-report --dry-run +``` + +```bash +bl managed-agent deployment unpause --deployment-id dep_abc +``` + ### `bl managed-agent destroy` | Field | Value | @@ -101,6 +617,327 @@ bl managed-agent destroy --yes bl managed-agent destroy --yes --cascade ``` +### `bl managed-agent environment get` + +| Field | Value | +| ------------------ | -------------------------------------------------------- | +| **Name** | `managed-agent environment get` | +| **Description** | Get a Managed Agent environment | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent environment get --environment-id <id>` | + +#### Flags + +| Flag | Type | Required | Description | +| ----------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--environment-id <id>` | string | yes | Environment ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent environment get --environment-id env_abc +``` + +### `bl managed-agent environment list` + +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------ | +| **Name** | `managed-agent environment list` | +| **Description** | List Managed Agent environments | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent environment list [--limit <n>] [--page <cursor>] [--all] [--include-archived]` | + +#### Flags + +| Flag | Type | Required | Description | +| -------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--include-archived` | switch | no | Include archived resources | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent environment list +``` + +```bash +bl managed-agent environment list --all --output json +``` + +### `bl managed-agent environment search` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent environment search` | +| **Description** | Search Managed Agent environments | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent environment search --query <text> [--limit <n>] [--page-limit <n>] [--include-archived]` | + +#### Flags + +| Flag | Type | Required | Description | +| -------------------- | ------ | -------- | ------------------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--query <text>` | string | yes | Case-insensitive text to find in IDs, names, and descriptions | +| `--page-limit <n>` | number | no | Maximum pages to scan for client-side search (default: 10) | +| `--include-archived` | switch | no | Include archived resources | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent environment search --query sandbox +``` + +```bash +bl managed-agent environment search --query production --page-limit 20 --output json +``` + +### `bl managed-agent file delete` + +| Field | Value | +| ------------------ | --------------------------------------------------- | +| **Name** | `managed-agent file delete` | +| **Description** | Delete a Managed Agent file | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent file delete --file-id <id> --yes` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--file-id <id>` | string | yes | Remote file ID | +| `--yes` | switch | no | Confirm permanent file deletion | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent file delete --file-id file_abc --dry-run +``` + +```bash +bl managed-agent file delete --file-id file_abc --yes +``` + +### `bl managed-agent file download` + +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------ | +| **Name** | `managed-agent file download` | +| **Description** | Download Managed Agent file content | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent file download --file-id <id> --output-file <path> [--force]` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--file-id <id>` | string | yes | Remote file ID | +| `--output-file <path>` | string | yes | Destination path | +| `--force` | switch | no | Overwrite an existing output file | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent file download --file-id file_abc --output-file ./artifact.pdf +``` + +### `bl managed-agent file get` + +| Field | Value | +| ------------------ | ------------------------------------------ | +| **Name** | `managed-agent file get` | +| **Description** | Get Managed Agent file metadata | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent file get --file-id <id>` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--file-id <id>` | string | yes | Remote file ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent file get --file-id file_abc +``` + +### `bl managed-agent file list` + +| Field | Value | +| ------------------ | -------------------------------------------------------------------------------------- | +| **Name** | `managed-agent file list` | +| **Description** | List Managed Agent files | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent file list [--scope-id <id>] [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--scope-id <id>` | string | no | Filter by scope ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent file list +``` + +```bash +bl managed-agent file list --scope-id sess_abc --all --output json +``` + +### `bl managed-agent file search` + +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------ | +| **Name** | `managed-agent file search` | +| **Description** | Search Managed Agent files | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent file search --query <text> [--scope-id <id>] [--limit <n>] [--page-limit <n>]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | ------------------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--query <text>` | string | yes | Case-insensitive text to find in IDs, names, and descriptions | +| `--page-limit <n>` | number | no | Maximum pages to scan for client-side search (default: 10) | +| `--scope-id <id>` | string | no | Filter by scope ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent file search --query report +``` + +```bash +bl managed-agent file search --query pdf --scope-id sess_abc --output json +``` + +### `bl managed-agent file upload` + +| Field | Value | +| ------------------ | ----------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent file upload` | +| **Description** | Upload a Managed Agent file | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent file upload --path <path> [--filename <name>] [--mime-type <type>] [--purpose <purpose>]` | + +#### Flags + +| Flag | Type | Required | Description | +| --------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--path <path>` | string | yes | Local file path | +| `--filename <name>` | string | no | Remote filename override | +| `--mime-type <type>` | string | no | MIME type override | +| `--purpose <purpose>` | string | no | Provider upload purpose | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent file upload --path ./report.pdf +``` + +```bash +bl managed-agent file upload --path ./data.json --purpose assistants +``` + ### `bl managed-agent init` | Field | Value | @@ -174,6 +1011,42 @@ bl managed-agent plan --provider bailian bl managed-agent plan --no-refresh ``` +### `bl managed-agent session archive` + +| Field | Value | +| ------------------ | ---------------------------------------------------------- | +| **Name** | `managed-agent session archive` | +| **Description** | Archive a Managed Agent session | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session archive --session-id <id> --yes` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--session-id <id>` | string | yes | Session ID | +| `--yes` | switch | no | Confirm session archive | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent session archive --session-id sess_abc --dry-run +``` + +```bash +bl managed-agent session archive --session-id sess_abc --yes +``` + ### `bl managed-agent session create` | Field | Value | @@ -217,14 +1090,49 @@ bl managed-agent session create --agent assistant bl managed-agent session create --agent assistant --title 'debug run' ``` +### `bl managed-agent session debug` + +| Field | Value | +| ------------------ | -------------------------------------------------- | +| **Name** | `managed-agent session debug` | +| **Description** | Aggregate session diagnostics | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session debug --session-id <id>` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--session-id <id>` | string | yes | Session ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent session debug --session-id sess_abc +``` + +```bash +bl managed-agent session debug --session-id sess_abc --output json +``` + ### `bl managed-agent session delete` -| Field | Value | -| ------------------ | --------------------------------------------------------------------------------------- | -| **Name** | `managed-agent session delete` | -| **Description** | Delete a session | -| **Authentication** | API Key | -| **Usage** | `bl managed-agent session delete --session-id <id> [--provider <name>] [--file <path>]` | +| Field | Value | +| ------------------ | --------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent session delete` | +| **Description** | Delete a session | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session delete --session-id <id> --yes [--provider <name>] [--file <path>]` | #### Flags @@ -233,6 +1141,7 @@ bl managed-agent session create --agent assistant --title 'debug run' | `--session-id <id>` | string | yes | Session ID (required) | | `--file <path>` | string | no | Config file path (default: agents.yaml) | | `--provider <name>` | string | no | Target provider | +| `--yes` | switch | no | Confirm permanent session deletion | | `--api-key <key>` | string | no | API key | | `--base-url <url>` | string | no | API base URL | @@ -248,26 +1157,68 @@ bl managed-agent session create --agent assistant --title 'debug run' bl managed-agent session delete --session-id sess_abc123 ``` -### `bl managed-agent session events` +### `bl managed-agent session event list` -| Field | Value | -| ------------------ | ----------------------------------------------------------------------------------------- | -| **Name** | `managed-agent session events` | -| **Description** | List event history for a session | -| **Authentication** | API Key | -| **Usage** | `bl managed-agent session events --session-id <id> [--limit <n>] [--all] [--file <path>]` | +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent session event list` | +| **Description** | List events for a Managed Agent session | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session event list --session-id <id> [--types <types>] [--order asc\|desc] [--limit <n>] [--page <cursor>] [--all]` | #### Flags -| Flag | Type | Required | Description | -| ------------------- | ------ | -------- | --------------------------------------- | -| `--session-id <id>` | string | yes | Session ID (required) | -| `--file <path>` | string | no | Config file path (default: agents.yaml) | -| `--provider <name>` | string | no | Target provider | -| `--limit <n>` | number | no | Maximum number of events to fetch | -| `--all` | switch | no | Fetch all pages by following the cursor | -| `--api-key <key>` | string | no | API key | -| `--base-url <url>` | string | no | API base URL | +| Flag | Type | Required | Description | +| ------------------------------ | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--session-id <id>` | string | yes | Session ID | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--order <asc\|desc>` | string | no | Event order: asc or desc | +| `--types <types>` | string | no | Comma-separated raw event types | +| `--created-at-gte <timestamp>` | string | no | Created at or after this timestamp | +| `--created-at-lte <timestamp>` | string | no | Created at or before this timestamp | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. +- --types is applied client-side to each page returned by the provider. + +#### Examples + +```bash +bl managed-agent session event list --session-id sess_abc +``` + +```bash +bl managed-agent session event list --session-id sess_abc --all --output json +``` + +### `bl managed-agent session event send` + +| Field | Value | +| ------------------ | ----------------------------------------------------------------------------- | +| **Name** | `managed-agent session event send` | +| **Description** | Send raw events to a Managed Agent session | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session event send --session-id <id> --event <json\|@path>` | + +#### Flags + +| Flag | Type | Required | Description | +| ----------------------- | ------ | -------- | --------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--session-id <id>` | string | yes | Session ID | +| `--event <json\|@path>` | string | yes | Raw event object/array as JSON or @event.json | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | #### Notes @@ -278,11 +1229,125 @@ bl managed-agent session delete --session-id sess_abc123 #### Examples ```bash -bl managed-agent session events --session-id sess_abc123 +bl managed-agent session event send --session-id sess_abc --event '{"type":"message","role":"user","content":"hello"}' ``` ```bash -bl managed-agent session events --session-id sess_abc123 --all +bl managed-agent session event send --session-id sess_abc --event @event.json +``` + +### `bl managed-agent session event stream` + +| Field | Value | +| ------------------ | --------------------------------------------------------------------------------- | +| **Name** | `managed-agent session event stream` | +| **Description** | Stream events from a Managed Agent session | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session event stream --session-id <id> [--after-id <event-id>]` | + +#### Flags + +| Flag | Type | Required | Description | +| ----------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--session-id <id>` | string | yes | Session ID | +| `--after-id <event-id>` | string | no | Resume after this event ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. +- When the provider has no native event cursor, --after-id resumes through paginated history polling and event ID de-duplication. + +#### Examples + +```bash +bl managed-agent session event stream --session-id sess_abc +``` + +```bash +bl managed-agent session event stream --session-id sess_abc --after-id evt_123 --output json +``` + +### `bl managed-agent session events` + +| Field | Value | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent session events` | +| **Description** | List events for a Managed Agent session | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session events --session-id <id> [--types <types>] [--order asc\|desc] [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------------------ | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--session-id <id>` | string | yes | Session ID | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--order <asc\|desc>` | string | no | Event order: asc or desc | +| `--types <types>` | string | no | Comma-separated raw event types | +| `--created-at-gte <timestamp>` | string | no | Created at or after this timestamp | +| `--created-at-lte <timestamp>` | string | no | Created at or before this timestamp | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. +- --types is applied client-side to each page returned by the provider. + +#### Examples + +```bash +bl managed-agent session events --session-id sess_abc +``` + +```bash +bl managed-agent session events --session-id sess_abc --all --output json +``` + +### `bl managed-agent session export` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------- | +| **Name** | `managed-agent session export` | +| **Description** | Export session diagnostics as a ZIP | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session export --session-id <id> --output-file <path> [--force]` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--session-id <id>` | string | yes | Session ID | +| `--output-file <path>` | string | yes | Destination ZIP path | +| `--force` | switch | no | Overwrite an existing output file | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. +- The ZIP contains metadata only; file bodies and credential-like values are excluded/redacted. + +#### Examples + +```bash +bl managed-agent session export --session-id sess_abc --output-file ./session-debug.zip ``` ### `bl managed-agent session get` @@ -318,23 +1383,28 @@ bl managed-agent session get --session-id sess_abc123 ### `bl managed-agent session list` -| Field | Value | -| ------------------ | -------------------------------------------------------------------------------------------- | -| **Name** | `managed-agent session list` | -| **Description** | List sessions from the provider | -| **Authentication** | API Key | -| **Usage** | `bl managed-agent session list [--agent <name>] [--all] [--provider <name>] [--file <path>]` | +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent session list` | +| **Description** | List sessions from the provider | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session list [--agent <name>] [--statuses <statuses>] [--limit <n>] [--page <cursor>] [--all]` | #### Flags -| Flag | Type | Required | Description | -| ------------------- | ------ | -------- | --------------------------------------- | -| `--file <path>` | string | no | Config file path (default: agents.yaml) | -| `--agent <name>` | string | no | Filter by agent name | -| `--all` | switch | no | Fetch all pages by following the cursor | -| `--provider <name>` | string | no | Target provider | -| `--api-key <key>` | string | no | API key | -| `--base-url <url>` | string | no | API base URL | +| Flag | Type | Required | Description | +| ------------------------------ | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--agent <name>` | string | no | Filter by agent name | +| `--all` | switch | no | Fetch all pages by following the cursor | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--statuses <statuses>` | string | no | Comma-separated session statuses | +| `--created-at-gte <timestamp>` | string | no | Created at or after this RFC 3339 timestamp | +| `--created-at-lte <timestamp>` | string | no | Created at or before this RFC 3339 timestamp | +| `--provider <name>` | string | no | Target provider | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | #### Notes @@ -398,6 +1468,47 @@ bl managed-agent session run --prompt "hello" bl managed-agent session run --agent assistant --prompt "summarize this repo" ``` +### `bl managed-agent session search` + +| Field | Value | +| ------------------ | -------------------------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent session search` | +| **Description** | Search Managed Agent sessions | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session search --query <text> [--agent <name>] [--statuses <statuses>] [--limit <n>] [--page-limit <n>]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------------------ | ------ | -------- | ------------------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--query <text>` | string | yes | Case-insensitive text to find in IDs, names, and descriptions | +| `--page-limit <n>` | number | no | Maximum pages to scan for client-side search (default: 10) | +| `--agent <name>` | string | no | Filter by configured agent name | +| `--statuses <statuses>` | string | no | Comma-separated session statuses | +| `--created-at-gte <timestamp>` | string | no | Created at or after this timestamp | +| `--created-at-lte <timestamp>` | string | no | Created at or before this timestamp | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent session search --query debug +``` + +```bash +bl managed-agent session search --query failed --statuses failed --output json +``` + ### `bl managed-agent session send` | Field | Value | @@ -431,33 +1542,255 @@ bl managed-agent session run --agent assistant --prompt "summarize this repo" bl managed-agent session send --session-id sess_abc123 --message "continue" ``` -### `bl managed-agent skill-list` +### `bl managed-agent session update` -| Field | Value | -| ------------------ | -------------------------------------------------------------------------------------------------- | -| **Name** | `managed-agent skill-list` | -| **Description** | List skills from the provider's skill catalog | -| **Authentication** | API Key | -| **Usage** | `bl managed-agent skill-list [--source custom\|official\|all] [--provider <name>] [--file <path>]` | +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------ | +| **Name** | `managed-agent session update` | +| **Description** | Update a Managed Agent session | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent session update --session-id <id> [--title <title>] [--metadata <json\|@path>]` | #### Flags -| Flag | Type | Required | Description | -| ------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------ | -| `--file <path>` | string | no | Config file path (default: agents.yaml) | -| `--source <source>` | string | no | Skill catalog: custom (workspace-uploaded, default), official (built-in), or all (both catalogs in one call) | -| `--provider <name>` | string | no | Target provider | -| `--api-key <key>` | string | no | API key | -| `--base-url <url>` | string | no | API base URL | +| Flag | Type | Required | Description | +| -------------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--session-id <id>` | string | yes | Session ID | +| `--title <title>` | string | no | New session title | +| `--metadata <json\|@path>` | string | no | String-valued metadata JSON or @file | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | #### Notes - Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). - Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. - Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. -- Providers without a skill listing API (e.g. ark) return an empty list. -- For agent-driven skill selection, use `--source all --output json`: one call returns both catalogs with per-skill `source` and `description` fields to pick from. -- When generating a task that needs a suitable skill, call this command to match official or custom skills before wiring them into the task. + +#### Examples + +```bash +bl managed-agent session update --session-id sess_abc --title 'investigation' +``` + +```bash +bl managed-agent session update --session-id sess_abc --metadata @metadata.json +``` + +### `bl managed-agent skill download` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent skill download` | +| **Description** | Download a Managed Agent skill version | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent skill download --skill-id <id> --skill-version <version> --output-file <path> [--force]` | + +#### Flags + +| Flag | Type | Required | Description | +| --------------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--skill-id <id>` | string | yes | Skill ID | +| `--skill-version <version>` | string | yes | Skill version | +| `--output-file <path>` | string | yes | Destination ZIP path | +| `--force` | switch | no | Overwrite an existing output file | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent skill download --skill-id skill_abc --skill-version 3 --output-file ./skill.zip +``` + +### `bl managed-agent skill get` + +| Field | Value | +| ------------------ | -------------------------------------------- | +| **Name** | `managed-agent skill get` | +| **Description** | Get a Managed Agent skill | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent skill get --skill-id <id>` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--skill-id <id>` | string | yes | Skill ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent skill get --skill-id skill_abc +``` + +### `bl managed-agent skill list` + +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------------ | +| **Name** | `managed-agent skill list` | +| **Description** | List Managed Agent skills | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent skill list [--source custom\|official\|all] [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--source <custom\|official\|all>` | string | no | Skill catalog: custom (default), official, or all | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. +- --source all combines one page from each catalog, or every page with --all; it does not accept --page. + +#### Examples + +```bash +bl managed-agent skill list +``` + +```bash +bl managed-agent skill list --source official +``` + +```bash +bl managed-agent skill list --source all --all --output json +``` + +### `bl managed-agent skill search` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent skill search` | +| **Description** | Search Managed Agent skills | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent skill search --query <text> [--source custom\|official\|all] [--limit <n>] [--page-limit <n>]` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------------------- | ------ | -------- | ------------------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--query <text>` | string | yes | Case-insensitive text to find in IDs, names, and descriptions | +| `--page-limit <n>` | number | no | Maximum pages to scan for client-side search (default: 10) | +| `--source <custom\|official\|all>` | string | no | Skill catalog: custom (default), official, or all | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent skill search --query browser --source official +``` + +```bash +bl managed-agent skill search --query report --source all --output json +``` + +### `bl managed-agent skill versions` + +| Field | Value | +| ------------------ | ----------------------------------------------------------------------------------------- | +| **Name** | `managed-agent skill versions` | +| **Description** | List Managed Agent skill versions | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent skill versions --skill-id <id> [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--skill-id <id>` | string | yes | Skill ID | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent skill versions --skill-id skill_abc +``` + +```bash +bl managed-agent skill versions --skill-id skill_abc --all --output json +``` + +### `bl managed-agent skill-list` + +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------------------ | +| **Name** | `managed-agent skill-list` | +| **Description** | List Managed Agent skills | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent skill-list [--source custom\|official\|all] [--limit <n>] [--page <cursor>] [--all]` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--source <custom\|official\|all>` | string | no | Skill catalog: custom (default), official, or all | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. +- --source all combines one page from each catalog, or every page with --all; it does not accept --page. #### Examples @@ -470,11 +1803,7 @@ bl managed-agent skill-list --source official ``` ```bash -bl managed-agent skill-list --source all --output json -``` - -```bash -bl managed-agent skill-list --source custom --provider bailian +bl managed-agent skill-list --source all --all --output json ``` ### `bl managed-agent state import` @@ -618,3 +1947,110 @@ bl managed-agent validate ```bash bl managed-agent validate --file agents.yaml ``` + +### `bl managed-agent vault get` + +| Field | Value | +| ------------------ | -------------------------------------------- | +| **Name** | `managed-agent vault get` | +| **Description** | Get a Managed Agent vault | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent vault get --vault-id <id>` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------- | ------ | -------- | --------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--vault-id <id>` | string | yes | Vault ID | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent vault get --vault-id vault_abc +``` + +### `bl managed-agent vault list` + +| Field | Value | +| ------------------ | ------------------------------------------------------------------------------------------ | +| **Name** | `managed-agent vault list` | +| **Description** | List Managed Agent vaults | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent vault list [--limit <n>] [--page <cursor>] [--all] [--include-archived]` | + +#### Flags + +| Flag | Type | Required | Description | +| -------------------- | ------ | -------- | ------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--page <cursor>` | string | no | Opaque page cursor returned by a previous request | +| `--all` | switch | no | Fetch all pages by following opaque cursors | +| `--include-archived` | switch | no | Include archived resources | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent vault list +``` + +```bash +bl managed-agent vault list --all --output json +``` + +### `bl managed-agent vault search` + +| Field | Value | +| ------------------ | ---------------------------------------------------------------------------------------------------- | +| **Name** | `managed-agent vault search` | +| **Description** | Search Managed Agent vaults | +| **Authentication** | API Key | +| **Usage** | `bl managed-agent vault search --query <text> [--limit <n>] [--page-limit <n>] [--include-archived]` | + +#### Flags + +| Flag | Type | Required | Description | +| -------------------- | ------ | -------- | ------------------------------------------------------------- | +| `--file <path>` | string | no | Config file path (default: agents.yaml) | +| `--provider <name>` | string | no | Target provider | +| `--limit <n>` | number | no | Page size (1-100) | +| `--query <text>` | string | yes | Case-insensitive text to find in IDs, names, and descriptions | +| `--page-limit <n>` | number | no | Maximum pages to scan for client-side search (default: 10) | +| `--include-archived` | switch | no | Include archived resources | +| `--api-key <key>` | string | no | API key | +| `--base-url <url>` | string | no | API base URL | + +#### Notes + +- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile). +- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json. +- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env. + +#### Examples + +```bash +bl managed-agent vault search --query github +``` + +```bash +bl managed-agent vault search --query production --page-limit 20 --output json +```