Skip to content

Commit 4bc43ab

Browse files
committed
Merge branch 'main' of github.com:modelstudioai/cli into fix/binary-build-env
2 parents 8e4cae8 + 1a17ac8 commit 4bc43ab

31 files changed

Lines changed: 1420 additions & 175 deletions

‎docs/agents/error-hint-change.md‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ runtime/create-cli.ts: 每次调用创建 reporter,统一捕获初始化/解
4343
↓
4444
runtime/error-handler.ts:
4545
- 服务端错误(BailianError(GENERAL)) → text 直接打 message
46-
- 内部 AUTH/USAGE/NETWORK/TIMEOUT → 走 errorHint(只 AUTH 还有增强)
46+
- 内部 AUTH/USAGE/NETWORK/TIMEOUT → 走 errorHint(AUTH 增强;另可对已知可恢复服务端错误追加 hint)
4747
- TypeError("fetch failed") → 读 err.cause.code 翻成 NETWORK
4848
- Node fs errno → 翻成 GENERAL + errno hint
4949
- reporter:JSON 错误、warning、debug 各占 stderr 一行,无空行;stdout 仅保留最终业务结果
@@ -104,8 +104,13 @@ process.exit(err.exitCode)
104104

105105
### B. runtime 增强(`errorHint`)
106106

107-
- [ ] `packages/runtime/src/error-handler.ts:errorHint`:**当前只为 internal AUTH 增强**(因为 resolver / authStage 等内部位置会发 AUTH)
107+
- [ ] `packages/runtime/src/error-handler.ts:errorHint`:**主要为 internal AUTH 增强**(resolver / authStage 等内部位置会发 AUTH)
108+
- [ ] **允许的有限扩展**:对已知可恢复的服务端错误,**追加**恢复 hint(保留原 message / exitCode)。当前仅 `AllocationQuota.FreeTierOnly`(及同义英文 message 短语)。message / apiCode 匹配**只用于追加**,不参与分类、归并或替换 message(勿与下方反面 case 3 的关键词归并混淆)
109+
- [ ] 追加时若已有 hint(如 video task id),拼接并对**完整恢复文本**去重;不要 `hint` 非空就跳过,也不要用 `"usage freetier"` 子串匹配
110+
- [ ] 命令层不得自行拼 `usage free` / `usage freetier` 恢复指引;该指引只由 runtime `errorHint` 生成
111+
- [ ] 渲染产品命令前须经能力门控(`hasCommand(path)`),不要对 `kscli` 等缺命令的产品硬拼 `usage freetier`
108112
- [ ] 命令名使用 `commandPrefix`,不要硬编码 `bl`
113+
- [ ] 恢复命令经 `recoveryCommand` 渲染;Profile 名用 `resolveRecoveryConfigName` / `Settings.recoveryConfigName`(显式 `--config default` 保留,隐式 default 不加)。API Key capability fallback 到 `default` 时,runtime 须把恢复 hint 的 profile 改写为 `default`(当前命令自身的控制台会话 hint `consoleRecoveryHint` 不改)。理由:恢复命令要跟随实际报错的 API Key 所属 Profile——此时报错(如 `AllocationQuota.FreeTierOnly`)来自 `default` 的 API Key 所属账号,免费额度恢复虽是 Console 命令,也须钉 `default`;钉回原命名 Profile 可能用另一账号的 Console token 静默改错账号的计费设置。`default` 缺 Console token 时报 AUTH 并给出 `auth login --console --config default`,可恢复且账号正确。不要拆成 api-key / console 两套 Profile 名
109114
- [ ] URL 必须是 `import { API_KEY_PAGE } from "./urls.ts"`
110115

111116
### C. command / runtime 直接抛错

‎docs/agents/output-contract.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ OpenAgentPack SDK 没有 logger 注入接口:仅在 SDK 调用上下文内,
8282
- [ ] JSON 诊断每条一行;stdout 为单个业务 JSON 文档,stderr 单独解析
8383
- [ ] speech synthesize 的原始音频仅在 text 流式模式下直接写 stdout;JSON 流式需 --out 文件并返回元数据,未指定文件时在请求和文件操作前报用法错误;dry-run 仍允许 JSON 预览。--list-voices 的 JSON 返回音色条目与已有文档链接,内置列表未知时 voices/total 为 null
8484
- [ ] speech recognize 的同步 JSON 返回完整服务响应;异步完成后返回原始任务响应并附加 transcriptions 数组,保留失败子任务和 request_id。text 保留转写、说话人和批量分隔;--out 仍保存原有转写文件结构,下载及写盘失败不提前输出最终结果;部分子任务失败不改变原有退出码
85-
- [ ] usage freetier 的启用 JSON 保留原始响应数组;停用 JSON 返回 `{ processed: [{ model, result }], skipped: [model] }`,跳过项仍不发起停用请求。失败与超时用 warning 提示,保留原有退出策略,不把部分失败丢出结果。
85+
- [ ] usage freetier 的启用 JSON 保留原始响应数组(含零模型时的 `[]`);停用 JSON 返回 `{ processed: [{ model, result }], skipped: [model] }`(含空对象),跳过项仍不发起停用请求。按模型串行修改:业务失败 / 超时 / 未在 successModels(或 successCount)确认成功时 fail-fast,不再请求后续模型;HTTP/网络异常先输出已确认进度并用 warning 记录进度,再原样抛出原始错误(不二次包装,保留 cause.code / ERROR_STACK)。失败与超时先用 warning 提示并保留业务结果,再抛摘要:仅轮询超时 → `TIMEOUT(5)`(文案用「超时未确认」);服务端拒绝 / 未确认成功 → `GENERAL(1)`;成功文案(Enabled / Disabled)仅在该模型操作成功时输出
8686
- [ ] Token Plan 创建密钥的完整响应和一次性明文仍属于业务结果,quiet 不裁剪;不得通过 warning/debug 泄露明文。
8787
- [ ] 原始流与原生交互仅限上表;Command Pack API 1 由宿主缓存并提交一次结果,不再直接转发 stdout
8888
- [ ] 每个调用使用独立 reporter,不共享结果状态、不替换全局 stdout/stderr;SDK console 桥接只能读取调用上下文,不缓存上一个 host

‎docs/agents/telemetry-change.md‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,18 @@ source-config 只用于百炼 / DashScope API 侧消费,不发送到通用网
116116

117117
`page` 仅记录有限数值页码;Webhook 等命令使用的不透明字符串游标不得进入本地日志或远端 AEM,即使字符串内容看起来是数字。
118118

119-
事件同时写入两处:
119+
事件写入两处:
120120

121-
1. 本地 `~/.bailian/telemetry.jsonl`:权限 `0600`,超过 5 MB 后重建
121+
1. 本地 `~/.bailian/telemetry.jsonl`:权限 `0600`,超过 5 MB 后重建;每行附带 `uid`(只读已有文件,不创建)
122122
2. AEM:`pid=bailian-cli-node`,源码运行自动使用 `env=dev`,npm 安装或编译二进制使用 `env=prod`
123123

124124
编译二进制的 `env=prod` 由 `BAILIAN_COMPILED=1` 决定,并且必须先于 `NODE_ENV`。`bun build --compile`(当前 pin 1.3.14)会把点访问 `process.env.NODE_ENV` 内联成构建机的值,未设置时是 `"development"`;`tools/release/lib/binary-compile.mjs` 同时 `--define process.env.NODE_ENV="production"`。这两处读取必须写成 `process.env.*`,包进别的变量后 `--define` 匹配不到。
125125

126-
底层 Node tracker 还会附加公共设备字段:OS 类型/版本、Node 应用名与版本、平台,以及由本机网络标识计算的 MD5 `device_id`。
126+
旁路状态:`~/.bailian/telemetry-uid`(遵循 `BAILIAN_CONFIG_DIR`)由 `ensureUid` 在首次远端上报时生成,不是事件落盘目标。
127+
128+
底层 Node tracker 还会附加公共设备字段:OS 类型/版本、Node 应用名与版本、平台,以及内置的 `device_id`(本机网卡 MAC 的 MD5,保持不变)。稳定匿名 install id 写入 AEM 的 `uid`:来自 `~/.bailian/telemetry-uid`,首次远端上报时生成随机 UUID v4,以 0600 权限原子发布;生成失败时不上报 `uid`。遥测关闭时不生成该文件。本地 `telemetry.jsonl` 每行附带同名 `uid`。注意:这不是账号用户 ID,只是上报到 AEM `uid` 字段的匿名标识;与 Tracker 内置 `device_id` 无关。
129+
130+
`ensureUid` 无锁(对齐 qwen-code `InstallationManager`):稳态只读;缺失或损坏时同目录 temp 写满后 `rename` 发布(内容原子替换;并发首次创建时后写覆盖先写,磁盘最终为一个 UUID)。失败则省略 `uid`,不阻塞业务退出。
127131

128132
从 2.8.2 起,每条命令事件通过 `commandPrefix` 显式记录调用入口,三种入口仍共用 `pid=bailian-cli-node`:
129133

@@ -182,6 +186,8 @@ AEM 映射:
182186
- [ ] 成功与失败均覆盖;遥测异常必须静默且不改变业务退出码
183187
- [ ] runtime 本地语义错误应记录 `exitCode`;新增字段默认随 AEM `ext` 上报,无需占用新的 `c1`—`c5`
184188
- [ ] 检查 `DO_NOT_TRACK=1` 与 `telemetry: false` 两个关闭入口
189+
- [ ] `remoteSink` 从调用到 `remoteSendEvent` 入队之间不得出现 `await`(flush 只排空已入队事件)
190+
- [ ] 测试中调用 `remoteSink` 的用例必须使用 `enterIsolatedHome()`,避免写入真实配置目录
185191
- [ ] 遥测发送的 DNS/HTTP/响应体必须受可取消预算控制;flush 超时必须取消在途操作,不能只停止等待,也不能修改全局 DNS/dispatcher 或强制退出宿主
186192
- [ ] 退出生命周期变更保留真实子进程回归:挂起响应头/响应体、DNS 预查询和关闭遥测;检查实际退出及进程仍存活时的连接释放,不以 flush Promise 返回代替验收
187193
- [ ] 错误字段不得额外拼接 token、请求体、prompt 或本地路径
@@ -192,7 +198,7 @@ AEM 映射:
192198
rg -n "trackingHeaders|x-dashscope-source-config|User-Agent" packages --glob '*.ts'
193199
rg -n "trackCommandExecution|PARAM_ALLOWLIST|buildRemoteAemOptions|readCaller" packages/core packages/runtime --glob '*.ts'
194200
vp check
195-
vp test packages/core/tests packages/runtime/tests/telemetry-stage.test.ts packages/commands/tests/e2e/auth.e2e.test.ts
201+
vp test packages/core/tests/telemetry-uid.test.ts packages/core/tests/telemetry-sink.test.ts packages/core/tests/telemetry-tracker.test.ts packages/core/tests/telemetry-send.test.ts packages/runtime/tests/telemetry-stage.test.ts packages/commands/tests/e2e/auth.e2e.test.ts
196202
```
197203

198204
## 常见漏点

‎packages/cli/README.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,20 @@ Knowledge bases accrue running-time charges from creation, even without queries.
139139

140140
Keep the returned IDs and recovery record. Closing the CLI or a later failure does not stop billing; follow the reported cleanup actions to delete unused knowledge bases. See the [Knowledge Studio quick start](packages/kscli/README.md#quick-start) for retrieval and cleanup. The `bailian-knowledge` Skill is available through `bl skill init`.
141141

142+
### Console dataset and deployment queries
143+
144+
```bash
145+
bl dataset groups --page 1 --page-size 10 --output json
146+
bl dataset version --dataset-group-id <group-id> --output json
147+
bl dataset version --dataset-id <version-id> --include-status --output json
148+
bl file url --file-key <console-file-key> --output json
149+
bl deploy instances --page 1 --page-size 10 --output json
150+
```
151+
152+
Use `dataset groups` (`--name` optionally filters on the server) to obtain `records[].datasetGroupId`, then `dataset version --dataset-group-id <group-id>` to get `{result:[...]}` with `result[].datasetId` and its optional `fileId`. Use `dataset version --dataset-id <version-id>` for details; `--include-status` is available only in detail mode. Exactly one of `--dataset-group-id` and `--dataset-id` is required; they cannot be combined. `fileId` is not `datasetId` and must not be passed as a version ID. Groups fetch one page with one embedded version preview per group; `version --dataset-group-id` does not promise exhaustive results or expose pagination. The API Key / `file_id` semantics of `dataset list` are unchanged.
153+
154+
These read-only commands use Console authentication. An optional `--workspace-id` resolves the workspace's Console identity before querying; resolution failure stops the request. `dataset version --dataset-id` keeps metadata and version/task state separate and does not infer validation success or training readiness. `file url` only resolves a download address. `deploy instances` returns one page of independent services (`records` is the list, `items` the total), not capacity instances. The API-key `dataset get --file-id` and `deploy list` commands are unchanged. Add `--dry-run` to preview without business requests; framework update checks/telemetry follow their settings.
155+
142156
### RAG audio and video
143157

144158
`bl knowledge doc import --index-id <id> --doc-id <fileId>` imports an existing file into a knowledge base; `doc list --details` shows per-file processing settings. Media support includes parser options, streaming uploads, and media references in search/chunk output. Chat JSON separates the final answer from phases, tools, docs, usage, and raw events; quiet mode hides progress and informational messages while preserving the final result, including every JSON field. See the [command guide](docs/knowledge/knowledge-cli-guide.md).

‎packages/cli/README.zh.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@ bl knowledge init --workspace-id <workspace-id> --dry-run
138138

139139
保留返回的 ID 和恢复记录。退出 CLI 或后续步骤失败不会停止计费;按返回的清理动作删除不再需要的知识库。检索及清理示例见 [Knowledge Studio 快速开始](packages/kscli/README.zh.md#快速开始)。知识库 Skill `bailian-knowledge` 通过 `bl skill init` 安装。
140140

141+
### Console 数据集与部署查询
142+
143+
```bash
144+
bl dataset groups --page 1 --page-size 10 --output json
145+
bl dataset version --dataset-group-id <组ID> --output json
146+
bl dataset version --dataset-id <版本ID> --include-status --output json
147+
bl file url --file-key <Console文件键> --output json
148+
bl deploy instances --page 1 --page-size 10 --output json
149+
```
150+
151+
先用 `dataset groups`(可加 `--name` 服务端筛选)获取 `records[].datasetGroupId`,再用 `dataset version --dataset-group-id <组ID>` 获取 `{result:[...]}`,其中包含 `result[].datasetId` 及可能返回的 `fileId`。查询详情用 `dataset version --dataset-id <版本ID>`,`--include-status` 仅详情模式可用。`--dataset-group-id` 与 `--dataset-id` 必选一个且不能同传。`fileId` 不是 `datasetId`,不能作为版本 ID 传入。组列表仅取一页,每组附带一条版本预览;`version --dataset-group-id` 不承诺返回全部版本,也不提供分页选项。原有 `dataset list` 的 API Key / `file_id` 语义保持不变。
152+
153+
以上只读命令使用 Console 鉴权。可选 `--workspace-id` 会在查询前解析对应空间的 Console 身份,解析失败即停止。`dataset version --dataset-id` 分开保留元信息与版本/任务状态,不推导校验成功或训练就绪;`file url` 仅解析下载地址;`deploy instances` 返回独立部署服务的单页数据(`records` 为列表、`items` 为总数),不是容量实例。原有 API Key 域的 `dataset get --file-id` 与 `deploy list` 保持不变。添加 `--dry-run` 可预览且不发送业务请求;框架更新检查和埋点遵循其配置。
154+
141155
### RAG 音视频接入
142156

143157
`bl knowledge doc import --index-id <id> --doc-id <fileId>` 将历史文件导入已有库;`doc list --details` 查看文件级处理参数。支持音视频 parser、流式上传,以及检索/切片中的媒体来源展示。问答 JSON 将最终 answer 与 phases/tools/docs/usage/events 分开保存,quiet 只隐藏进度和过程提示,保留最终结果及完整 JSON 字段。参阅 [命令指南](docs/knowledge/knowledge-cli-guide.md)。

‎packages/commands/src/commands/config/shared.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const UI_MODEL_DEFAULTS: Record<string, string> = {
8080
default_reference_to_video_model: "wan3.0-video",
8181
default_speech_model: "cosyvoice-v3-flash",
8282
default_speech_recognition_model: "fun-asr",
83-
default_omni_model: "qwen3.5-omni-plus",
83+
default_omni_model: "qwen3.8-omni-flash",
8484
};
8585

8686
/** One selectable model plus a short note on where the CLI uses it. */
@@ -131,7 +131,8 @@ export const UI_MODEL_CATALOG: Record<string, ModelOption[]> = {
131131
{ id: "qwen-audio-3.0-asr-flash", role: "Token Plan speech/recognize (ASR)" },
132132
],
133133
default_omni_model: [
134-
{ id: "qwen3.5-omni-plus", role: "omni/chat default" },
134+
{ id: "qwen3.8-omni-flash", role: "omni/chat default · --text-only" },
135+
{ id: "qwen3.5-omni-plus", role: "omni/chat · audio output path (without --text-only)" },
135136
{ id: "qwen3-vl-plus", role: "vision/describe · multimodal input" },
136137
],
137138
};

‎packages/commands/src/commands/knowledge/chunk-delete.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,12 @@ export default defineCommand({
103103
deletedCount += batchIds.length;
104104
completedBatches.push(batchIds);
105105
} catch (error) {
106-
const original = normalizeError(error, formatCommand(ctx.identity), ctx.localize);
106+
const original = normalizeError(
107+
error,
108+
formatCommand(ctx.identity),
109+
ctx.localize,
110+
ctx.settings.recoveryConfigName,
111+
);
107112
const failedBatch = completedBatches.length + 1;
108113
const progress = [
109114
ctx.localize({

‎packages/commands/src/commands/omni/chat.ts‎

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,10 @@ export default defineCommand({
141141
type: "string",
142142
valueHint: "<model>",
143143
description: {
144-
"en-US": "Model ID (default: qwen3.5-omni-plus)",
145-
"zh-CN": "模型 ID(默认:qwen3.5-omni-plus)",
144+
"en-US":
145+
"Model ID (default: qwen3.8-omni-flash; when --model is omitted and --text-only is absent, auto-switches to qwen3.5-omni-plus for audio output)",
146+
"zh-CN":
147+
"模型 ID(默认:qwen3.8-omni-flash;省略 --model 且未带 --text-only 时,自动切换为支持音频输出的 qwen3.5-omni-plus)",
146148
},
147149
},
148150
system: {
@@ -275,12 +277,26 @@ export default defineCommand({
275277
// --- Parse messages ---
276278
const userMessages = flags.message ?? [];
277279

278-
const model = flags.model || settings.defaultOmniModel || "qwen3.5-omni-plus";
280+
// Default to flash; switch to plus when --model is omitted and --text-only is absent (flash has no audio out).
281+
const textOnly = flags.textOnly === true;
282+
let model = flags.model || settings.defaultOmniModel || "qwen3.8-omni-flash";
283+
if (!flags.model && !textOnly && model === "qwen3.8-omni-flash") {
284+
model = "qwen3.5-omni-plus";
285+
}
279286
const voice = flags.voice || "Tina";
280287
const audioFormat = flags.audioFormat || "wav";
281-
const textOnly = flags.textOnly === true;
282288
const format = settings.output;
283289

290+
// Explicit flash + audio path will be rejected by the server; warn locally without overriding --model.
291+
if (flags.model === "qwen3.8-omni-flash" && !textOnly) {
292+
ctx.output.warning({
293+
"en-US":
294+
"qwen3.8-omni-flash does not support audio output. Add --text-only, or omit --model to auto-switch to qwen3.5-omni-plus.",
295+
"zh-CN":
296+
"qwen3.8-omni-flash 不支持音频输出。请添加 --text-only,或省略 --model 以自动切换为 qwen3.5-omni-plus。",
297+
});
298+
}
299+
284300
// --- Build messages array ---
285301
const allMessages: ChatMessage[] = [];
286302
if (flags.system) {

0 commit comments

Comments
 (0)