Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/agents/telemetry-change.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
- `channel` 与 `t1` 是当前固定口径
- `User-Agent` 是独立标识:`bl` 为 `bailian-cli/<version>`,`kscli` 为 `knowledge-studio-cli/<version>`
- `X-Dashscope-Service: bailian-cli` 与 source-config 一起由 `trackingHeaders()` 注入;凡走该函数的百炼 / DashScope 请求(含 MCP 注册与 JSON-RPC)都会带上。OSS、结果文件下载、npm / Skill 仍不发送。
- **AIGC auto MCP 例外**:AIGC 命令解析出的模型为保留值 `auto` 时,经 `withAigcMcpSession` 调用的 4 个生成类 MCP server(TextGenerateImage / VideoGenerate / TextGenerateSpeech / SpeechGenerateText)把 `X-Dashscope-Service` 覆写为 `bailian-cli-call`(常量 `MCP_CALL_SERVICE`),供网关单独归因 CLI 发起的 MCP 生成流量;此时 `source-config` 的 `channel` 仍是 `bailian-cli`,只有 Service 头变。其它 MCP 命令(`search web`、`mcp tools`、`mcp call`)与所有 REST 请求保持 `bailian-cli`。覆写经 `connectBailianMcp(serverCode, urlOverride?, serviceSource?)` → `connectBailianMcpWithFallback` → `SdkMcpAdapter` → `trackingHeaders(identity, service = CHANNEL)` 逐层透传,未传时回落默认 `CHANNEL`。

source-config 只用于百炼 / DashScope API 侧消费,不发送到通用网络传输:

Expand Down
101 changes: 100 additions & 1 deletion packages/commands/src/commands/image/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ import {
ASYNC_FLAG,
CONCURRENT_FLAG,
redactDataUri,
bailianMcpPath,
AIGC_MCP_SERVER,
AIGC_MCP_TOOL,
buildImageEditArgs,
parseAigcImageResults,
} from "bailian-cli-core";
import { poll } from "bailian-cli-runtime";
import { downloadFile } from "bailian-cli-runtime";
Expand All @@ -30,6 +35,15 @@ import { emitResult, emitBare } from "bailian-cli-runtime";
import { resolveImageSize } from "bailian-cli-runtime";
import { join } from "path";
import { BOOL_FLAG_PROMPT_EXTEND_CLI_TRUE, BOOL_FLAG_WATERMARK } from "bailian-cli-runtime";
import {
isAutoModel,
assertAutoMcpAvailable,
assertMcpCompatible,
withAigcMcpSession,
resolveAutoImageInput,
mcpSingleImageOnlyError,
type Localize,
} from "../shared/aigc-mcp.ts";

const EDIT_FLAGS = {
image: {
Expand Down Expand Up @@ -181,6 +195,14 @@ export default defineCommand({
"zh-CN": '--image ./photo.png --prompt "将背景替换为海滩" --watermark false',
},
],
notes: [
{
"en-US":
'Model "auto" (via --model auto or a config default model of "auto") routes to the official Bailian MCP with a reduced parameter set; MCP-incompatible flags fail fast with a usage error instead of falling back to REST. Auto edit accepts a single --image only.',
"zh-CN":
'模型 "auto"(通过 --model auto 或将配置默认模型设为 "auto")改走百炼官方 MCP,参数面更小;MCP 不支持的参数会直接报用法错误,而不回退 REST。auto 编辑仅支持单个 --image。',
},
],
async run(ctx) {
const { settings, flags } = ctx;
// Normalize --image to string array (supports both single and repeated flags)
Expand All @@ -192,7 +214,20 @@ export default defineCommand({
}
const prompt = flags.prompt;

const model = flags.model || settings.defaultImageModel || "qwen-image-3.0";
const resolvedModel = flags.model || settings.defaultImageModel;
if (isAutoModel(resolvedModel)) {
await handleAutoMcpMode(
ctx.client,
ctx.localize,
settings,
flags,
rawImages,
detectOutputFormat(settings.output),
);
return;
}

const model = resolvedModel || "qwen-image-3.0";
const route = resolveImageEditApi(model);

// Auto-upload local files (resolve all images in parallel)
Expand Down Expand Up @@ -329,6 +364,70 @@ export default defineCommand({
},
});

async function handleAutoMcpMode(
client: Client,
localize: Localize,
settings: Settings,
flags: EditFlags,
rawImages: string[],
format: OutputFormat,
): Promise<void> {
assertAutoMcpAvailable(client, localize);
assertMcpCompatible("imageEdit", flags, localize);

// bailian_image_edit takes a single source image; bailian_image_fusion is out
// of scope, so multi-image merge is rejected in auto mode.
if (rawImages.length > 1) {
throw mcpSingleImageOnlyError(localize);
}
const source = rawImages[0];
if (!source) {
throw new BailianError("Provide one --image to edit.", ExitCode.USAGE);
}

const imageUrl = resolveAutoImageInput(source);
const concurrent = getConcurrency(flags);
const args = buildImageEditArgs({
imageUrl,
prompt: flags.prompt,
negativePrompt: flags.negativePrompt || undefined,
});

if (settings.dryRun) {
emitResult(
{
server: AIGC_MCP_SERVER.image,
url: client.url(bailianMcpPath(AIGC_MCP_SERVER.image)),
tool: AIGC_MCP_TOOL.imageEdit,
arguments: { ...args, image_url: redactDataUri(imageUrl) },
mode: "mcp",
concurrent,
},
format,
);
return;
}

if (!settings.quiet) {
process.stderr.write("[Model: auto] [Mode: mcp] [Images: 1]\n");
}

// One shared MCP session; --concurrent fans out parallel tools/call on it.
await withAigcMcpSession(client, AIGC_MCP_SERVER.image, localize, async (callTool) => {
const perRequest = await runConcurrent(concurrent, settings, async () => {
const inner = await callTool(AIGC_MCP_TOOL.imageEdit, args);
return parseAigcImageResults(inner);
});

const imageUrls = perRequest.flat();
if (imageUrls.length === 0) {
throw new BailianError("Edit completed but no images returned.", ExitCode.GENERAL);
}

await saveImages(imageUrls, flags, settings, format);
});
}

async function handleSyncMode(
client: Client,
settings: Settings,
Expand Down
91 changes: 90 additions & 1 deletion packages/commands/src/commands/image/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,26 @@ import {
resolveImageGenerateApi,
ASYNC_FLAG,
CONCURRENT_FLAG,
bailianMcpPath,
AIGC_MCP_SERVER,
AIGC_MCP_TOOL,
buildImageGenArgs,
parseAigcImageResults,
} from "bailian-cli-core";
import { poll } from "bailian-cli-runtime";
import { downloadFile } from "bailian-cli-runtime";
import { runConcurrent, downloadParallel, getConcurrency } from "bailian-cli-runtime";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { resolveImageSize } from "bailian-cli-runtime";
import { BOOL_FLAG_PROMPT_EXTEND_IMAGE_GENERATE, BOOL_FLAG_WATERMARK } from "bailian-cli-runtime";
import {
isAutoModel,
assertAutoMcpAvailable,
assertMcpCompatible,
resolveAutoImageSize,
withAigcMcpSession,
type Localize,
} from "../shared/aigc-mcp.ts";

import { join } from "path";

Expand Down Expand Up @@ -167,11 +180,31 @@ export default defineCommand({
"zh-CN": '--prompt "产品摄影" --n 2 --concurrent 3 # 并行生成 6 张图片',
},
],
notes: [
{
"en-US":
'Model "auto" (via --model auto or a config default model of "auto") routes to the official Bailian MCP with a reduced parameter set; MCP-incompatible flags fail fast with a usage error instead of falling back to REST.',
"zh-CN":
'模型 "auto"(通过 --model auto 或将配置默认模型设为 "auto")改走百炼官方 MCP,参数面更小;MCP 不支持的参数会直接报用法错误,而不回退 REST。',
},
],
async run(ctx) {
const { settings, flags } = ctx;
const prompt = flags.prompt;

const model = flags.model || settings.defaultImageModel || "qwen-image-3.0";
const resolvedModel = flags.model || settings.defaultImageModel;
if (isAutoModel(resolvedModel)) {
await handleAutoMcpMode(
ctx.client,
ctx.localize,
settings,
flags,
detectOutputFormat(settings.output),
);
return;
}

const model = resolvedModel || "qwen-image-3.0";
const route = resolveImageGenerateApi(model);
const defaultSize = "1:1";
const sizeInput = flags.size || defaultSize;
Expand Down Expand Up @@ -238,6 +271,62 @@ export default defineCommand({
},
});

// ---- Auto mode: Bailian AIGC MCP (bailian_image_gen) ----

async function handleAutoMcpMode(
client: Client,
localize: Localize,
settings: Settings,
flags: GenerateFlags,
format: string,
): Promise<void> {
assertAutoMcpAvailable(client, localize);
assertMcpCompatible("imageGen", flags, localize);

const concurrent = getConcurrency(flags);
const args = buildImageGenArgs({
prompt: flags.prompt,
size: resolveAutoImageSize(flags.size, localize),
n: flags.n,
negativePrompt: flags.negativePrompt || undefined,
promptExtend: flags.promptExtend,
});

if (settings.dryRun) {
emitResult(
{
server: AIGC_MCP_SERVER.image,
url: client.url(bailianMcpPath(AIGC_MCP_SERVER.image)),
tool: AIGC_MCP_TOOL.imageGen,
arguments: args,
mode: "mcp",
concurrent,
},
format as OutputFormat,
);
return;
}

if (!settings.quiet) {
process.stderr.write("[Model: auto] [Mode: mcp]\n");
}

// One shared MCP session; --concurrent fans out parallel tools/call on it.
await withAigcMcpSession(client, AIGC_MCP_SERVER.image, localize, async (callTool) => {
const perRequest = await runConcurrent(concurrent, settings, async () => {
const inner = await callTool(AIGC_MCP_TOOL.imageGen, args);
return parseAigcImageResults(inner);
});

const imageUrls = perRequest.flat();
if (imageUrls.length === 0) {
throw new BailianError("Generation completed but no images returned.", ExitCode.GENERAL);
}

await saveImages(imageUrls, flags, settings, format);
});
}

// ---- Sync mode: qwen-image / wan2.7-image / z-image ----

async function handleSyncMode(
Expand Down
11 changes: 10 additions & 1 deletion packages/commands/src/commands/search/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
detectOutputFormat,
mcpWebSearchPath,
type FlagsDef,
type McpConnectedClient,
} from "bailian-cli-core";
import { createSpinner, emitResult } from "bailian-cli-runtime";
import { rethrowWithWebSearchActivateHint } from "./web-activate-hint.ts";
Expand Down Expand Up @@ -66,13 +67,18 @@ export default defineCommand({
return;
}

let client: McpConnectedClient | undefined;
try {
const client = ctx.client.mcp(mcpWebSearchPath());
client = ctx.client.mcp(mcpWebSearchPath());
await client.initialize();
const tools = await client.listTools();
emitResult({ tools }, format);
} catch (error) {
rethrowWithWebSearchActivateHint(error);
} finally {
// The SDK transport keeps a live SSE stream / pending sockets open;
// without closing it the process never exits after emitting results.
client?.close?.();
}
return;
}
Expand Down Expand Up @@ -152,6 +158,9 @@ export default defineCommand({
} catch (error) {
spinner.stop("Failed.");
rethrowWithWebSearchActivateHint(error);
} finally {
// Close on both paths: an open transport stream keeps the process alive.
client.close?.();
}
},
});
Loading
Loading