diff --git a/packages/commands/src/commands/knowledge/doc-upload.ts b/packages/commands/src/commands/knowledge/doc-upload.ts index a3615f8..47a55f8 100644 --- a/packages/commands/src/commands/knowledge/doc-upload.ts +++ b/packages/commands/src/commands/knowledge/doc-upload.ts @@ -25,13 +25,14 @@ import { pollImportJob, withPartialSuccessHint, } from "./shared.ts"; -import { checkUploadFile } from "./upload-support.ts"; +import { checkUploadFile, expandUploadPaths } from "./upload-support.ts"; const DOC_UPLOAD_FLAGS = { file: { type: "array", valueHint: "", - description: "Local file path (repeatable). Extension and size validated before upload", + description: + "Local file or directory path (repeatable). Directories are scanned recursively; unsupported formats are skipped", required: true, }, indexId: { @@ -67,18 +68,22 @@ interface UploadedFile { } export default defineCommand({ - description: "Upload local files to the data center and optionally import into a knowledge base", + description: + "Upload local files or directories to the data center and optionally import into a knowledge base", auth: "apiKey", usageArgs: "--file [flags]", flags: DOC_UPLOAD_FLAGS, notes: [ "Pipeline: apply upload lease → PUT to OSS → register file → (with --index-id) create import job.", "Without --category-id the workspace default category is resolved automatically.", + "Directories are scanned recursively; node_modules, .git, and similar are skipped automatically.", "Multiple files are processed sequentially; on failure, already-registered file ids are listed in the error hint.", ], exampleArgs: [ "--file ./a.md --workspace-id ws-xxx", "--file ./a.md --file ./b.pdf --index-id idx-xxx --wait", + "--file ./docs/ --workspace-id ws-xxx", + "--file ./docs/ --dry-run --verbose", ], validate(flags) { if (flags.wait && !flags.indexId) return "--wait requires --index-id"; @@ -89,9 +94,20 @@ export default defineCommand({ const workspaceId = resolveWorkspaceId(ctx); const format = detectOutputFormat(settings.output); + // Expand directories into individual file paths; unsupported extensions are + // collected into `skipped` rather than throwing (directory-scan semantics) + const { files: expandedFiles, skipped } = expandUploadPaths(flags.file); + if (expandedFiles.length === 0) { + throw new BailianError( + "No supported files found", + ExitCode.USAGE, + `Supported formats: .pdf .doc .docx .ppt .pptx .xls .xlsx .csv .md .txt .html .png .jpg .jpeg .bmp .gif`, + ); + } + // Local pre-flight validation also runs in dry-run (rehearsal semantics: surface // file problems early); exceeding a soft limit only warns - const checkedFiles = flags.file.map((filePath) => { + const checkedFiles = expandedFiles.map((filePath) => { const checked = checkUploadFile(filePath); if (checked.warning) process.stderr.write(`Warning: ${checked.warning}\n`); return { filePath, sizeBytes: checked.sizeBytes }; @@ -140,7 +156,7 @@ export default defineCommand({ } as unknown, }); } - emitResult({ steps }, format); + emitResult({ steps, skipped }, format); return; } @@ -278,12 +294,25 @@ export default defineCommand({ } if (ingestionId) emitBare(`job: ${ingestionId}`); if (finalStatus) emitBare(`status: ${finalStatus}`); + // Summary line: always show counts; list skipped files only with --verbose + const summaryParts = [`Uploaded ${uploaded.length} file${uploaded.length !== 1 ? "s" : ""}`]; + if (skipped.length > 0) { + summaryParts.push(`skipped ${skipped.length} unsupported`); + } + emitBare(`\n${summaryParts.join(", ")}.`); + if (settings.verbose && skipped.length > 0) { + emitBare("Skipped files:"); + for (const skippedPath of skipped) { + emitBare(` ${basename(skippedPath)}`); + } + } return; } // An orchestration command has no single response to pass through — emit a custom stable shape emitResult( { files: uploaded.map((item) => ({ path: item.path, fileId: item.fileId })), + skipped, ...(flags.indexId ? { index_id: flags.indexId } : {}), ...(ingestionId ? { ingestion_id: ingestionId } : {}), ...(finalStatus ? { final_status: finalStatus } : {}), diff --git a/packages/commands/src/commands/knowledge/upload-support.ts b/packages/commands/src/commands/knowledge/upload-support.ts index 9539060..54df030 100644 --- a/packages/commands/src/commands/knowledge/upload-support.ts +++ b/packages/commands/src/commands/knowledge/upload-support.ts @@ -1,8 +1,8 @@ // Local pre-flight validation for file uploads (doc upload). // Default category: the lease/addFile `category` parameter accepts the literal // "default" (verified against the live API), so no listCategory resolution is needed. -import { readFileSync, statSync } from "node:fs"; -import { basename, extname } from "node:path"; +import { readFileSync, readdirSync, statSync } from "node:fs"; +import { basename, extname, join } from "node:path"; import { BailianError, ExitCode } from "bailian-cli-core"; const MB = 1024 * 1024; @@ -37,6 +37,105 @@ export const UPLOAD_FORMAT_RULES: Record = { ".html": { maxBytes: 10 * MB, enforce: "warn" }, }; +/** Returns true when the extension is in the upload format allowlist. */ +export function isSupportedExtension(filePath: string): boolean { + const extension = extname(filePath).toLowerCase(); + return extension in UPLOAD_FORMAT_RULES; +} + +/** + * Directory names skipped when expanding a directory path (recursive scan). + * Covers common tooling artifacts that should never contain user documents. + */ +const IGNORED_DIRECTORIES = new Set([ + "node_modules", + ".git", + ".svn", + ".hg", + "__pycache__", + ".venv", + "venv", + ".env", + ".tox", + "dist", + "build", + ".cache", + ".next", + ".nuxt", +]); + +export interface ExpandResult { + files: string[]; + skipped: string[]; +} + +/** + * Expand an array of paths into individual file paths. + * - Regular files are included as-is. + * - Directories are recursively scanned; files with unsupported extensions are + * collected into `skipped` instead of throwing. + * - Common tooling directories (node_modules, .git, …) are silently skipped. + * - A non-existent path throws USAGE so the user gets a clear error. + */ +export function expandUploadPaths(paths: string[]): ExpandResult { + const files: string[] = []; + const skipped: string[] = []; + + function walkDirectory(directoryPath: string): void { + let entries: import("node:fs").Dirent[]; + try { + entries = readdirSync(directoryPath, { withFileTypes: true }); + } catch (error) { + const errno = (error as { code?: string }).code ?? "unknown"; + throw new BailianError( + `Cannot read directory: ${directoryPath}`, + ExitCode.GENERAL, + `File system error (${errno}) — check the path and permissions.`, + ); + } + for (const entry of entries) { + const entryFullPath = join(directoryPath, entry.name); + if (entry.isDirectory()) { + if (!IGNORED_DIRECTORIES.has(entry.name)) { + walkDirectory(entryFullPath); + } + continue; + } + if (entry.isFile()) { + if (isSupportedExtension(entry.name)) { + files.push(entryFullPath); + } else { + skipped.push(entryFullPath); + } + } + // Symlinks: withFileTypes follows symlinks for isFile/isDirectory, + // so they are handled by the branches above. + } + } + + for (const inputPath of paths) { + let pathStat: import("node:fs").Stats; + try { + pathStat = statSync(inputPath); + } catch (error) { + const errno = (error as { code?: string }).code ?? "unknown"; + throw new BailianError( + `Cannot read path: ${inputPath}`, + ExitCode.GENERAL, + `File system error (${errno}) — check the path and permissions.`, + ); + } + if (pathStat.isDirectory()) { + walkDirectory(inputPath); + } else if (pathStat.isFile()) { + files.push(inputPath); + } + // Other types (socket, block device, etc.) are silently ignored. + } + + return { files, skipped }; +} + /** Local pre-flight check before reading the file: extension allowlist + hard/soft size limits. File I/O failure → GENERAL + errno hint. */ export function checkUploadFile(filePath: string): { sizeBytes: number; warning?: string } { const extension = extname(filePath).toLowerCase(); diff --git a/packages/commands/tests/e2e/knowledge/knowledge-doc-upload.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-doc-upload.e2e.test.ts index 744928f..795b9b7 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-doc-upload.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-doc-upload.e2e.test.ts @@ -1,4 +1,4 @@ -import { mkdtempSync, writeFileSync } from "node:fs"; +import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; @@ -104,11 +104,13 @@ describe("e2e: knowledge doc upload", () => { "json", ]); expect(exitCode, stderr).toBe(0); - const data = parseStdoutJson<{ steps: DryRunStep[] }>(stdout); + const data = parseStdoutJson<{ steps: DryRunStep[]; skipped: string[] }>(stdout); expect(data.steps).toHaveLength(3); const leaseRequest = data.steps[0]!.request as { sizeBytes?: unknown; category?: string }; expect(typeof leaseRequest.sizeBytes).toBe("string"); // gotcha: sizeBytes must be a string expect(leaseRequest.category).toBe("cate_test"); + // Single file path → no skipped entries + expect(data.skipped).toEqual([]); }); test("--dry-run 带 --index-id 输出 4 步且 job 请求含显式 sourceType", async () => { @@ -129,7 +131,7 @@ describe("e2e: knowledge doc upload", () => { "json", ]); expect(exitCode, stderr).toBe(0); - const data = parseStdoutJson<{ steps: DryRunStep[] }>(stdout); + const data = parseStdoutJson<{ steps: DryRunStep[]; skipped: string[] }>(stdout); expect(data.steps).toHaveLength(4); const jobRequest = data.steps[3]!.request as { indexId?: string; @@ -141,6 +143,56 @@ describe("e2e: knowledge doc upload", () => { expect(jobRequest.indexId).toBe("idx_test"); expect(jobRequest).not.toHaveProperty("documentIds"); }); + + test("--file dry-run 展开目录且 skipped 包含不支持的文件", async () => { + const dirFixture = mkdtempSync(join(tmpdir(), "doc-upload-dir-e2e-")); + writeFileSync(join(dirFixture, "readme.md"), "# dir fixture\n"); + writeFileSync(join(dirFixture, "data.csv"), "a,b,c"); + writeFileSync(join(dirFixture, "config.json"), "{}"); // unsupported + const subDir = join(dirFixture, "sub"); + mkdirSync(subDir); + writeFileSync(join(subDir, "notes.txt"), "hello"); + + const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_UPLOAD_ROUTES, [ + "knowledge", + "doc", + "upload", + "--file", + dirFixture, + "--category-id", + "cate_test", + "--workspace-id", + "ws_test", + "--dry-run", + "--output", + "json", + ]); + expect(exitCode, stderr).toBe(0); + const data = parseStdoutJson<{ steps: DryRunStep[]; skipped: string[] }>(stdout); + // 3 supported files × 3 steps each = 9 steps + expect(data.steps).toHaveLength(9); + // config.json is the only unsupported file + expect(data.skipped).toHaveLength(1); + expect(data.skipped[0]).toMatch(/config\.json$/); + }); + + test("--file 仅含不支持的文件报 USAGE", async () => { + const unsupportedDir = mkdtempSync(join(tmpdir(), "doc-upload-unsupported-")); + writeFileSync(join(unsupportedDir, "data.json"), "{}"); + writeFileSync(join(unsupportedDir, "script.py"), "print(1)"); + + const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_UPLOAD_ROUTES, [ + "knowledge", + "doc", + "upload", + "--file", + unsupportedDir, + "--workspace-id", + "ws_test", + ]); + expect(exitCode).toBe(2); + expect(stderr).toMatch(/No supported files found/i); + }); }); // Live write artifacts (data-center files) are cleaned up in place via the file delete command. diff --git a/packages/commands/tests/knowledge/knowledge-upload-support.test.ts b/packages/commands/tests/knowledge/knowledge-upload-support.test.ts index 7d16ef9..d12102f 100644 --- a/packages/commands/tests/knowledge/knowledge-upload-support.test.ts +++ b/packages/commands/tests/knowledge/knowledge-upload-support.test.ts @@ -1,10 +1,11 @@ -import { mkdtempSync, writeFileSync } from "node:fs"; +import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; import { ExitCode } from "bailian-cli-core"; import { checkUploadFile, + expandUploadPaths, UPLOAD_FORMAT_RULES, } from "../../src/commands/knowledge/upload-support.ts"; @@ -60,3 +61,84 @@ describe("UPLOAD_FORMAT_RULES", () => { expect(UPLOAD_FORMAT_RULES[".csv"]).toBeDefined(); // inconsistent across public docs; kept in the allowlist }); }); + +describe("expandUploadPaths", () => { + const expandFixtureDir = mkdtempSync(join(tmpdir(), "expand-upload-")); + + function setupExpandFixtures(): void { + // Root-level files + writeFileSync(join(expandFixtureDir, "readme.md"), "# root"); + writeFileSync(join(expandFixtureDir, "data.csv"), "a,b,c"); + writeFileSync(join(expandFixtureDir, "config.json"), "{}"); // unsupported + writeFileSync(join(expandFixtureDir, "script.py"), "print(1)"); // unsupported + + // Subdirectory with files + const subDir = join(expandFixtureDir, "subdir"); + mkdirSync(subDir); + writeFileSync(join(subDir, "notes.txt"), "hello"); + writeFileSync(join(subDir, "archive.zip"), "PK"); // unsupported + + // node_modules should be ignored + const nodeModulesDir = join(expandFixtureDir, "node_modules"); + mkdirSync(nodeModulesDir); + writeFileSync(join(nodeModulesDir, "index.js"), "module.exports = {}"); + + // .git should be ignored + const gitDir = join(expandFixtureDir, ".git"); + mkdirSync(gitDir); + writeFileSync(join(gitDir, "HEAD"), "ref: refs/heads/main"); + } + + setupExpandFixtures(); + + test("目录递归扫描: 支持的文件收集, 不支持的跳过, 忽略目录不进入", () => { + const result = expandUploadPaths([expandFixtureDir]); + const fileNames = result.files.map((filePath) => filePath.split("/").pop()); + const skippedNames = result.skipped.map((filePath) => filePath.split("/").pop()); + + expect(fileNames).toContain("readme.md"); + expect(fileNames).toContain("data.csv"); + expect(fileNames).toContain("notes.txt"); + // Unsupported files in root and subdir + expect(skippedNames).toContain("config.json"); + expect(skippedNames).toContain("script.py"); + expect(skippedNames).toContain("archive.zip"); + // node_modules and .git contents should NOT appear + expect(fileNames).not.toContain("index.js"); + expect(skippedNames).not.toContain("index.js"); + expect(fileNames).not.toContain("HEAD"); + expect(skippedNames).not.toContain("HEAD"); + }); + + test("单个文件路径直接返回", () => { + const filePath = join(expandFixtureDir, "readme.md"); + const result = expandUploadPaths([filePath]); + expect(result.files).toEqual([filePath]); + expect(result.skipped).toEqual([]); + }); + + test("混合文件和目录路径", () => { + const filePath = join(expandFixtureDir, "readme.md"); + const result = expandUploadPaths([filePath, expandFixtureDir]); + // readme.md appears once from the direct file, once from the directory scan + const mdCount = result.files.filter((path) => path.endsWith("readme.md")).length; + expect(mdCount).toBe(2); + }); + + test("空目录返回空数组", () => { + const emptyDir = mkdtempSync(join(tmpdir(), "empty-upload-")); + const result = expandUploadPaths([emptyDir]); + expect(result.files).toEqual([]); + expect(result.skipped).toEqual([]); + }); + + test("不存在的路径抛 GENERAL 且 hint 含 errno", () => { + try { + expandUploadPaths([join(expandFixtureDir, "nonexistent.md")]); + expect.unreachable("should throw"); + } catch (error) { + expect((error as { exitCode: number }).exitCode).toBe(ExitCode.GENERAL); + expect((error as { hint?: string }).hint).toMatch(/ENOENT/); + } + }); +}); diff --git a/skills/bailian-cli/reference/index.md b/skills/bailian-cli/reference/index.md index 293d186..dcce108 100644 --- a/skills/bailian-cli/reference/index.md +++ b/skills/bailian-cli/reference/index.md @@ -9,95 +9,95 @@ Use this index for the skill-scoped quick index and global flags. ## Quick index -| Command | Description | Detail | -| -------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------ | -| `bl advisor recommend` | Recommend the best models for your use case (intent analysis → candidate recall → LLM ranking) | [advisor.md](advisor.md) | -| `bl app call` | Call a Bailian application (agent or workflow) | [app.md](app.md) | -| `bl app list` | List Bailian applications | [app.md](app.md) | -| `bl auth generate-access-token` | Generate a CLI access token using OpenAPI AK/SK | [auth.md](auth.md) | -| `bl auth login` | Authenticate with API key, console browser login, or OpenAPI AK/SK (credentials can coexist) | [auth.md](auth.md) | -| `bl auth logout` | Clear stored credentials; full logout also clears the model Base URL | [auth.md](auth.md) | -| `bl auth status` | Show current authentication state | [auth.md](auth.md) | -| `bl config agent` | Configure a coding agent to use DashScope API | [config.md](config.md) | -| `bl config list` | List config profiles and show the active profile | [config.md](config.md) | -| `bl config set` | Set a config value | [config.md](config.md) | -| `bl config show` | Display current configuration | [config.md](config.md) | -| `bl config ui` | Open a local web UI to manage config profiles | [config.md](config.md) | -| `bl config use` | Set the active config profile | [config.md](config.md) | -| `bl console call` | Call a Bailian console API via the CLI gateway | [console.md](console.md) | -| `bl file upload` | Upload a local file to DashScope temporary storage (48h) | [file.md](file.md) | -| `bl knowledge category add` | Create a data-center category | [knowledge.md](knowledge.md) | -| `bl knowledge category delete` | Delete a data-center category | [knowledge.md](knowledge.md) | -| `bl knowledge category list` | List data-center categories | [knowledge.md](knowledge.md) | -| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) | [knowledge.md](knowledge.md) | -| `bl knowledge chunk add` | Add a chunk directly to a knowledge base | [knowledge.md](knowledge.md) | -| `bl knowledge chunk delete` | Delete chunks from a knowledge base (irreversible) | [knowledge.md](knowledge.md) | -| `bl knowledge chunk list` | List chunks in a knowledge base with content and status | [knowledge.md](knowledge.md) | -| `bl knowledge chunk update` | Update chunk content or toggle its retrieval visibility | [knowledge.md](knowledge.md) | -| `bl knowledge collection create` | Create a FILE data collection | [knowledge.md](knowledge.md) | -| `bl knowledge collection get` | Show data collection details | [knowledge.md](knowledge.md) | -| `bl knowledge create` | Create a knowledge base and import data-center files or categories | [knowledge.md](knowledge.md) | -| `bl knowledge delete` | Delete a knowledge base with all its documents and chunks | [knowledge.md](knowledge.md) | -| `bl knowledge doc delete` | Delete documents and their chunks from a knowledge base | [knowledge.md](knowledge.md) | -| `bl knowledge doc import-oss` | Batch import files from an authorized OSS bucket into the data center | [knowledge.md](knowledge.md) | -| `bl knowledge doc list` | List documents in a knowledge base with parse/index status | [knowledge.md](knowledge.md) | -| `bl knowledge doc status` | Check knowledge base import job status | [knowledge.md](knowledge.md) | -| `bl knowledge doc tag` | Batch update tags on data-center files | [knowledge.md](knowledge.md) | -| `bl knowledge doc upload` | Upload local files to the data center and optionally import into a knowledge base | [knowledge.md](knowledge.md) | -| `bl knowledge file delete` | Permanently delete a file from the data center | [knowledge.md](knowledge.md) | -| `bl knowledge file get` | Show data-center file details (size, MD5, tags, timestamps) | [knowledge.md](knowledge.md) | -| `bl knowledge file list` | List files in a data-center category | [knowledge.md](knowledge.md) | -| `bl knowledge info` | Show knowledge base configuration details | [knowledge.md](knowledge.md) | -| `bl knowledge list` | List knowledge bases in the workspace | [knowledge.md](knowledge.md) | -| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) | [knowledge.md](knowledge.md) | -| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) | [knowledge.md](knowledge.md) | -| `bl knowledge service copy` | Copy a service into a new draft (name gets a copy\_ prefix) | [knowledge.md](knowledge.md) | -| `bl knowledge service create` | Create a retrieval / Q&A service (initial status: draft, version: beta) | [knowledge.md](knowledge.md) | -| `bl knowledge service delete` | Delete a retrieval / Q&A service (soft delete, idempotent) | [knowledge.md](knowledge.md) | -| `bl knowledge service deploy` | Publish the beta draft of a service as a new version | [knowledge.md](knowledge.md) | -| `bl knowledge service get` | Show service (agent) details including per-version configuration | [knowledge.md](knowledge.md) | -| `bl knowledge service list` | List retrieval / Q&A services (agents) in the workspace | [knowledge.md](knowledge.md) | -| `bl knowledge service update` | Update service name, description or draft configuration | [knowledge.md](knowledge.md) | -| `bl knowledge stats` | Show knowledge base storage and QPS monitoring data | [knowledge.md](knowledge.md) | -| `bl knowledge update` | Update knowledge base name, description or rerank threshold | [knowledge.md](knowledge.md) | -| `bl mcp call` | Call a tool on an MCP server (tools/call) | [mcp.md](mcp.md) | -| `bl mcp list` | List MCP servers activated under your Bailian account | [mcp.md](mcp.md) | -| `bl mcp tools` | List tools exposed by an MCP server (tools/list) | [mcp.md](mcp.md) | -| `bl memory add` | Add memory from messages or custom content | [memory.md](memory.md) | -| `bl memory delete` | Delete a memory node | [memory.md](memory.md) | -| `bl memory list` | List memory nodes for a user | [memory.md](memory.md) | -| `bl memory profile create` | Create a user profile schema for memory profiling | [memory.md](memory.md) | -| `bl memory profile get` | Get user profile by schema ID and user ID | [memory.md](memory.md) | -| `bl memory search` | Search memory nodes by query or messages | [memory.md](memory.md) | -| `bl memory update` | Update a memory node content | [memory.md](memory.md) | -| `bl model list` | Browse model families or show detailed model info in the Bailian model marketplace | [model.md](model.md) | -| `bl pipeline run` | Run a pipeline workflow definition | [pipeline.md](pipeline.md) | -| `bl pipeline validate` | Validate a pipeline definition without executing | [pipeline.md](pipeline.md) | -| `bl plugin install` | Install or upgrade an allowlisted Command Pack | [plugin.md](plugin.md) | -| `bl plugin link` | Link an allowlisted local Command Pack for development | [plugin.md](plugin.md) | -| `bl plugin list` | List installed Command Packs and their load status | [plugin.md](plugin.md) | -| `bl plugin remove` | Remove an installed Command Pack | [plugin.md](plugin.md) | -| `bl quota check` | Check current usage against rate limits | [quota.md](quota.md) | -| `bl quota history` | View quota change history | [quota.md](quota.md) | -| `bl quota list` | View model RPM/TPM rate limits | [quota.md](quota.md) | -| `bl quota request` | Request a temporary quota increase | [quota.md](quota.md) | -| `bl search web` | Search the web using DashScope MCP WebSearch service | [search.md](search.md) | -| `bl skill add` | Install skills from the Bailian skill registry into local agents | [skill.md](skill.md) | -| `bl skill list` | List registry skills and diff against local installs | [skill.md](skill.md) | -| `bl skill remove` | Remove locally installed skills (registry is untouched) | [skill.md](skill.md) | -| `bl skill update` | Update installed skills to the latest registry versions | [skill.md](skill.md) | -| `bl text chat` | Send a chat completion (OpenAI compatible, DashScope) | [text.md](text.md) | -| `bl token-plan add-member` | Add a member to a Token Plan organization | [token-plan.md](token-plan.md) | -| `bl token-plan assign-seats` | Batch assign Token Plan seats to members | [token-plan.md](token-plan.md) | -| `bl token-plan create-key` | Create a Token Plan API key for a seat | [token-plan.md](token-plan.md) | -| `bl token-plan list-seats` | List Token Plan subscription seat details | [token-plan.md](token-plan.md) | -| `bl update` | Update the CLI to the latest or a specified version | [update.md](update.md) | -| `bl usage free` | Query free-tier quota for models (all models if --model is omitted) | [usage.md](usage.md) | -| `bl usage freetier` | Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable | [usage.md](usage.md) | -| `bl usage stats` | Query model usage statistics | [usage.md](usage.md) | -| `bl usage summary` | Show a unified usage summary: free-tier quota and recent usage overview | [usage.md](usage.md) | -| `bl workspace init` | Initialize Bailian workspace and activate postpaid services | [workspace.md](workspace.md) | -| `bl workspace list` | List all workspaces | [workspace.md](workspace.md) | +| Command | Description | Detail | +| -------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------ | +| `bl advisor recommend` | Recommend the best models for your use case (intent analysis → candidate recall → LLM ranking) | [advisor.md](advisor.md) | +| `bl app call` | Call a Bailian application (agent or workflow) | [app.md](app.md) | +| `bl app list` | List Bailian applications | [app.md](app.md) | +| `bl auth generate-access-token` | Generate a CLI access token using OpenAPI AK/SK | [auth.md](auth.md) | +| `bl auth login` | Authenticate with API key, console browser login, or OpenAPI AK/SK (credentials can coexist) | [auth.md](auth.md) | +| `bl auth logout` | Clear stored credentials; full logout also clears the model Base URL | [auth.md](auth.md) | +| `bl auth status` | Show current authentication state | [auth.md](auth.md) | +| `bl config agent` | Configure a coding agent to use DashScope API | [config.md](config.md) | +| `bl config list` | List config profiles and show the active profile | [config.md](config.md) | +| `bl config set` | Set a config value | [config.md](config.md) | +| `bl config show` | Display current configuration | [config.md](config.md) | +| `bl config ui` | Open a local web UI to manage config profiles | [config.md](config.md) | +| `bl config use` | Set the active config profile | [config.md](config.md) | +| `bl console call` | Call a Bailian console API via the CLI gateway | [console.md](console.md) | +| `bl file upload` | Upload a local file to DashScope temporary storage (48h) | [file.md](file.md) | +| `bl knowledge category add` | Create a data-center category | [knowledge.md](knowledge.md) | +| `bl knowledge category delete` | Delete a data-center category | [knowledge.md](knowledge.md) | +| `bl knowledge category list` | List data-center categories | [knowledge.md](knowledge.md) | +| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) | [knowledge.md](knowledge.md) | +| `bl knowledge chunk add` | Add a chunk directly to a knowledge base | [knowledge.md](knowledge.md) | +| `bl knowledge chunk delete` | Delete chunks from a knowledge base (irreversible) | [knowledge.md](knowledge.md) | +| `bl knowledge chunk list` | List chunks in a knowledge base with content and status | [knowledge.md](knowledge.md) | +| `bl knowledge chunk update` | Update chunk content or toggle its retrieval visibility | [knowledge.md](knowledge.md) | +| `bl knowledge collection create` | Create a FILE data collection | [knowledge.md](knowledge.md) | +| `bl knowledge collection get` | Show data collection details | [knowledge.md](knowledge.md) | +| `bl knowledge create` | Create a knowledge base and import data-center files or categories | [knowledge.md](knowledge.md) | +| `bl knowledge delete` | Delete a knowledge base with all its documents and chunks | [knowledge.md](knowledge.md) | +| `bl knowledge doc delete` | Delete documents and their chunks from a knowledge base | [knowledge.md](knowledge.md) | +| `bl knowledge doc import-oss` | Batch import files from an authorized OSS bucket into the data center | [knowledge.md](knowledge.md) | +| `bl knowledge doc list` | List documents in a knowledge base with parse/index status | [knowledge.md](knowledge.md) | +| `bl knowledge doc status` | Check knowledge base import job status | [knowledge.md](knowledge.md) | +| `bl knowledge doc tag` | Batch update tags on data-center files | [knowledge.md](knowledge.md) | +| `bl knowledge doc upload` | Upload local files or directories to the data center and optionally import into a knowledge base | [knowledge.md](knowledge.md) | +| `bl knowledge file delete` | Permanently delete a file from the data center | [knowledge.md](knowledge.md) | +| `bl knowledge file get` | Show data-center file details (size, MD5, tags, timestamps) | [knowledge.md](knowledge.md) | +| `bl knowledge file list` | List files in a data-center category | [knowledge.md](knowledge.md) | +| `bl knowledge info` | Show knowledge base configuration details | [knowledge.md](knowledge.md) | +| `bl knowledge list` | List knowledge bases in the workspace | [knowledge.md](knowledge.md) | +| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) | [knowledge.md](knowledge.md) | +| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) | [knowledge.md](knowledge.md) | +| `bl knowledge service copy` | Copy a service into a new draft (name gets a copy\_ prefix) | [knowledge.md](knowledge.md) | +| `bl knowledge service create` | Create a retrieval / Q&A service (initial status: draft, version: beta) | [knowledge.md](knowledge.md) | +| `bl knowledge service delete` | Delete a retrieval / Q&A service (soft delete, idempotent) | [knowledge.md](knowledge.md) | +| `bl knowledge service deploy` | Publish the beta draft of a service as a new version | [knowledge.md](knowledge.md) | +| `bl knowledge service get` | Show service (agent) details including per-version configuration | [knowledge.md](knowledge.md) | +| `bl knowledge service list` | List retrieval / Q&A services (agents) in the workspace | [knowledge.md](knowledge.md) | +| `bl knowledge service update` | Update service name, description or draft configuration | [knowledge.md](knowledge.md) | +| `bl knowledge stats` | Show knowledge base storage and QPS monitoring data | [knowledge.md](knowledge.md) | +| `bl knowledge update` | Update knowledge base name, description or rerank threshold | [knowledge.md](knowledge.md) | +| `bl mcp call` | Call a tool on an MCP server (tools/call) | [mcp.md](mcp.md) | +| `bl mcp list` | List MCP servers activated under your Bailian account | [mcp.md](mcp.md) | +| `bl mcp tools` | List tools exposed by an MCP server (tools/list) | [mcp.md](mcp.md) | +| `bl memory add` | Add memory from messages or custom content | [memory.md](memory.md) | +| `bl memory delete` | Delete a memory node | [memory.md](memory.md) | +| `bl memory list` | List memory nodes for a user | [memory.md](memory.md) | +| `bl memory profile create` | Create a user profile schema for memory profiling | [memory.md](memory.md) | +| `bl memory profile get` | Get user profile by schema ID and user ID | [memory.md](memory.md) | +| `bl memory search` | Search memory nodes by query or messages | [memory.md](memory.md) | +| `bl memory update` | Update a memory node content | [memory.md](memory.md) | +| `bl model list` | Browse model families or show detailed model info in the Bailian model marketplace | [model.md](model.md) | +| `bl pipeline run` | Run a pipeline workflow definition | [pipeline.md](pipeline.md) | +| `bl pipeline validate` | Validate a pipeline definition without executing | [pipeline.md](pipeline.md) | +| `bl plugin install` | Install or upgrade an allowlisted Command Pack | [plugin.md](plugin.md) | +| `bl plugin link` | Link an allowlisted local Command Pack for development | [plugin.md](plugin.md) | +| `bl plugin list` | List installed Command Packs and their load status | [plugin.md](plugin.md) | +| `bl plugin remove` | Remove an installed Command Pack | [plugin.md](plugin.md) | +| `bl quota check` | Check current usage against rate limits | [quota.md](quota.md) | +| `bl quota history` | View quota change history | [quota.md](quota.md) | +| `bl quota list` | View model RPM/TPM rate limits | [quota.md](quota.md) | +| `bl quota request` | Request a temporary quota increase | [quota.md](quota.md) | +| `bl search web` | Search the web using DashScope MCP WebSearch service | [search.md](search.md) | +| `bl skill add` | Install skills from the Bailian skill registry into local agents | [skill.md](skill.md) | +| `bl skill list` | List registry skills and diff against local installs | [skill.md](skill.md) | +| `bl skill remove` | Remove locally installed skills (registry is untouched) | [skill.md](skill.md) | +| `bl skill update` | Update installed skills to the latest registry versions | [skill.md](skill.md) | +| `bl text chat` | Send a chat completion (OpenAI compatible, DashScope) | [text.md](text.md) | +| `bl token-plan add-member` | Add a member to a Token Plan organization | [token-plan.md](token-plan.md) | +| `bl token-plan assign-seats` | Batch assign Token Plan seats to members | [token-plan.md](token-plan.md) | +| `bl token-plan create-key` | Create a Token Plan API key for a seat | [token-plan.md](token-plan.md) | +| `bl token-plan list-seats` | List Token Plan subscription seat details | [token-plan.md](token-plan.md) | +| `bl update` | Update the CLI to the latest or a specified version | [update.md](update.md) | +| `bl usage free` | Query free-tier quota for models (all models if --model is omitted) | [usage.md](usage.md) | +| `bl usage freetier` | Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable | [usage.md](usage.md) | +| `bl usage stats` | Query model usage statistics | [usage.md](usage.md) | +| `bl usage summary` | Show a unified usage summary: free-tier quota and recent usage overview | [usage.md](usage.md) | +| `bl workspace init` | Initialize Bailian workspace and activate postpaid services | [workspace.md](workspace.md) | +| `bl workspace list` | List all workspaces | [workspace.md](workspace.md) | ## By group diff --git a/skills/bailian-cli/reference/knowledge.md b/skills/bailian-cli/reference/knowledge.md index 531bf0d..044b684 100644 --- a/skills/bailian-cli/reference/knowledge.md +++ b/skills/bailian-cli/reference/knowledge.md @@ -7,42 +7,42 @@ Index: [index.md](index.md) ## Commands in this group -| Command | Description | -| -------------------------------- | --------------------------------------------------------------------------------- | -| `bl knowledge category add` | Create a data-center category | -| `bl knowledge category delete` | Delete a data-center category | -| `bl knowledge category list` | List data-center categories | -| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) | -| `bl knowledge chunk add` | Add a chunk directly to a knowledge base | -| `bl knowledge chunk delete` | Delete chunks from a knowledge base (irreversible) | -| `bl knowledge chunk list` | List chunks in a knowledge base with content and status | -| `bl knowledge chunk update` | Update chunk content or toggle its retrieval visibility | -| `bl knowledge collection create` | Create a FILE data collection | -| `bl knowledge collection get` | Show data collection details | -| `bl knowledge create` | Create a knowledge base and import data-center files or categories | -| `bl knowledge delete` | Delete a knowledge base with all its documents and chunks | -| `bl knowledge doc delete` | Delete documents and their chunks from a knowledge base | -| `bl knowledge doc import-oss` | Batch import files from an authorized OSS bucket into the data center | -| `bl knowledge doc list` | List documents in a knowledge base with parse/index status | -| `bl knowledge doc status` | Check knowledge base import job status | -| `bl knowledge doc tag` | Batch update tags on data-center files | -| `bl knowledge doc upload` | Upload local files to the data center and optionally import into a knowledge base | -| `bl knowledge file delete` | Permanently delete a file from the data center | -| `bl knowledge file get` | Show data-center file details (size, MD5, tags, timestamps) | -| `bl knowledge file list` | List files in a data-center category | -| `bl knowledge info` | Show knowledge base configuration details | -| `bl knowledge list` | List knowledge bases in the workspace | -| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) | -| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) | -| `bl knowledge service copy` | Copy a service into a new draft (name gets a copy\_ prefix) | -| `bl knowledge service create` | Create a retrieval / Q&A service (initial status: draft, version: beta) | -| `bl knowledge service delete` | Delete a retrieval / Q&A service (soft delete, idempotent) | -| `bl knowledge service deploy` | Publish the beta draft of a service as a new version | -| `bl knowledge service get` | Show service (agent) details including per-version configuration | -| `bl knowledge service list` | List retrieval / Q&A services (agents) in the workspace | -| `bl knowledge service update` | Update service name, description or draft configuration | -| `bl knowledge stats` | Show knowledge base storage and QPS monitoring data | -| `bl knowledge update` | Update knowledge base name, description or rerank threshold | +| Command | Description | +| -------------------------------- | ------------------------------------------------------------------------------------------------ | +| `bl knowledge category add` | Create a data-center category | +| `bl knowledge category delete` | Delete a data-center category | +| `bl knowledge category list` | List data-center categories | +| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) | +| `bl knowledge chunk add` | Add a chunk directly to a knowledge base | +| `bl knowledge chunk delete` | Delete chunks from a knowledge base (irreversible) | +| `bl knowledge chunk list` | List chunks in a knowledge base with content and status | +| `bl knowledge chunk update` | Update chunk content or toggle its retrieval visibility | +| `bl knowledge collection create` | Create a FILE data collection | +| `bl knowledge collection get` | Show data collection details | +| `bl knowledge create` | Create a knowledge base and import data-center files or categories | +| `bl knowledge delete` | Delete a knowledge base with all its documents and chunks | +| `bl knowledge doc delete` | Delete documents and their chunks from a knowledge base | +| `bl knowledge doc import-oss` | Batch import files from an authorized OSS bucket into the data center | +| `bl knowledge doc list` | List documents in a knowledge base with parse/index status | +| `bl knowledge doc status` | Check knowledge base import job status | +| `bl knowledge doc tag` | Batch update tags on data-center files | +| `bl knowledge doc upload` | Upload local files or directories to the data center and optionally import into a knowledge base | +| `bl knowledge file delete` | Permanently delete a file from the data center | +| `bl knowledge file get` | Show data-center file details (size, MD5, tags, timestamps) | +| `bl knowledge file list` | List files in a data-center category | +| `bl knowledge info` | Show knowledge base configuration details | +| `bl knowledge list` | List knowledge bases in the workspace | +| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) | +| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) | +| `bl knowledge service copy` | Copy a service into a new draft (name gets a copy\_ prefix) | +| `bl knowledge service create` | Create a retrieval / Q&A service (initial status: draft, version: beta) | +| `bl knowledge service delete` | Delete a retrieval / Q&A service (soft delete, idempotent) | +| `bl knowledge service deploy` | Publish the beta draft of a service as a new version | +| `bl knowledge service get` | Show service (agent) details including per-version configuration | +| `bl knowledge service list` | List retrieval / Q&A services (agents) in the workspace | +| `bl knowledge service update` | Update service name, description or draft configuration | +| `bl knowledge stats` | Show knowledge base storage and QPS monitoring data | +| `bl knowledge update` | Update knowledge base name, description or rerank threshold | ## Command details @@ -660,30 +660,31 @@ bl knowledge doc tag --doc-id file-a --doc-id file-b --tag final --mode overwrit ### `bl knowledge doc upload` -| Field | Value | -| --------------- | --------------------------------------------------------------------------------- | -| **Name** | `knowledge doc upload` | -| **Description** | Upload local files to the data center and optionally import into a knowledge base | -| **Usage** | `bl knowledge doc upload --file [flags]` | +| Field | Value | +| --------------- | ------------------------------------------------------------------------------------------------ | +| **Name** | `knowledge doc upload` | +| **Description** | Upload local files or directories to the data center and optionally import into a knowledge base | +| **Usage** | `bl knowledge doc upload --file [flags]` | #### Flags -| Flag | Type | Required | Description | -| --------------------------- | ------ | -------- | -------------------------------------------------------------------------- | -| `--file ` | array | yes | Local file path (repeatable). Extension and size validated before upload | -| `--index-id ` | string | no | Import into this knowledge base after registration (one job for all files) | -| `--category-id ` | string | no | Target data-center category; defaults to the workspace default category | -| `--tag ` | array | no | File tag (repeatable), applied to every uploaded file | -| `--wait` | switch | no | Poll the import job to a terminal state (needs --index-id) | -| `--poll-interval ` | number | no | Polling interval when waiting (default: 5) | -| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | -| `--api-key ` | string | no | API key | -| `--base-url ` | string | no | API base URL | +| Flag | Type | Required | Description | +| --------------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------- | +| `--file ` | array | yes | Local file or directory path (repeatable). Directories are scanned recursively; unsupported formats are skipped | +| `--index-id ` | string | no | Import into this knowledge base after registration (one job for all files) | +| `--category-id ` | string | no | Target data-center category; defaults to the workspace default category | +| `--tag ` | array | no | File tag (repeatable), applied to every uploaded file | +| `--wait` | switch | no | Poll the import job to a terminal state (needs --index-id) | +| `--poll-interval ` | number | no | Polling interval when waiting (default: 5) | +| `--workspace-id ` | string | no | Workspace ID for API endpoint URL (or set BAILIAN_WORKSPACE_ID) | +| `--api-key ` | string | no | API key | +| `--base-url ` | string | no | API base URL | #### Notes - Pipeline: apply upload lease → PUT to OSS → register file → (with --index-id) create import job. - Without --category-id the workspace default category is resolved automatically. +- Directories are scanned recursively; node_modules, .git, and similar are skipped automatically. - Multiple files are processed sequentially; on failure, already-registered file ids are listed in the error hint. #### Examples @@ -696,6 +697,14 @@ bl knowledge doc upload --file ./a.md --workspace-id ws-xxx bl knowledge doc upload --file ./a.md --file ./b.pdf --index-id idx-xxx --wait ``` +```bash +bl knowledge doc upload --file ./docs/ --workspace-id ws-xxx +``` + +```bash +bl knowledge doc upload --file ./docs/ --dry-run --verbose +``` + ### `bl knowledge file delete` | Field | Value |