From 30a07af1e64d666990592c53e081e43df995e556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E9=BA=92?= Date: Wed, 4 Nov 2026 21:28:57 +0800 Subject: [PATCH] fix(telemetry): report CLI invocation prefix --- CHANGELOG.md | 6 + CHANGELOG.zh.md | 6 + docs/agents/telemetry-change.md | 36 ++++-- packages/bailian-kb-dsh/package.json | 2 +- packages/bailian-memo-dsh/package.json | 2 +- packages/cli/package.json | 2 +- packages/commands/package.json | 2 +- packages/core/package.json | 2 +- packages/core/src/telemetry/event.ts | 8 +- packages/core/src/telemetry/tracker.ts | 2 +- packages/core/tests/telemetry-tracker.test.ts | 118 +++++++++++++++++- packages/kscli/package.json | 2 +- packages/runtime/package.json | 2 +- skills/bailian-cli/SKILL.md | 2 +- skills/bailian-finetune/SKILL.md | 2 +- skills/bailian-gen/SKILL.md | 2 +- skills/bailian-knowledge/SKILL.md | 2 +- skills/bailian-managed-agent/SKILL.md | 2 +- skills/bailian-memory/SKILL.md | 2 +- skills/bailian-protocol/SKILL.md | 2 +- skills/bailian-sandbox/SKILL.md | 2 +- skills/bailian-web-search/SKILL.md | 2 +- 22 files changed, 177 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77e282ed..9e0e635d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and [中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md) +## [2.8.2] - 2026-11-04 + +### Internal + +- Improve CLI usage statistics. + ## [2.8.1] - 2026-11-04 ### Added diff --git a/CHANGELOG.zh.md b/CHANGELOG.zh.md index 6397fca0..23d4e16e 100644 --- a/CHANGELOG.zh.md +++ b/CHANGELOG.zh.md @@ -6,6 +6,12 @@ [English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md) +## [2.8.2] - 2026-11-04 + +### 内部维护 + +- 完善 CLI 使用统计。 + ## [2.8.1] - 2026-11-04 ### 新增 diff --git a/docs/agents/telemetry-change.md b/docs/agents/telemetry-change.md index 642a6d0a..f9b4a03d 100644 --- a/docs/agents/telemetry-change.md +++ b/docs/agents/telemetry-change.md @@ -95,6 +95,7 @@ source-config 只用于百炼 / DashScope API 侧消费,不发送到通用网 - `command`、`timestamp`、`durationMs`、`success` - `cliVersion`、`nodeVersion`、`os` +- `commandPrefix`(调用入口,来自 `Identity`) - `authMethod` - `callerType`(必填:`agent` 或 `unknown`);`callerType=agent` 时可选 `callerName`、`skillName`、`skillVersion` - 失败时的 `errorMessage`、`exitCode`、`httpStatus`、`requestId` @@ -119,20 +120,32 @@ source-config 只用于百炼 / DashScope API 侧消费,不发送到通用网 底层 Node tracker 还会附加公共设备字段:OS 类型/版本、Node 应用名与版本、平台,以及由本机网络标识计算的 MD5 `device_id`。 -当前 AEM 事件没有 `binName` 或 `clientName` 产品维度,并且 `bl`、`kscli` 共用 `pid=bailian-cli-node`。两边相同路径的 `config show`、`config set`、`update` 无法仅凭当前事件稳定区分产品;Knowledge 命令虽然因路径映射不同而表现为 `knowledge chat` 与 `chat`,也不应把命令路径当作长期产品标识。后端 source-config 的 `t2` 已能区分 `bl/kscli`,但这个维度尚未进入 AEM 客户端事件。 +从 2.8.2 起,每条命令事件通过 `commandPrefix` 显式记录调用入口,三种入口仍共用 `pid=bailian-cli-node`: + +| 调用入口 | `commandPrefix` | +| ----------- | --------------- | +| `bl` | `bl` | +| `kscli` | `kscli` | +| `aliyun bl` | `aliyun bl` | + +`commandPrefix` 使用 `formatCommand(identity)` 从结构化的 `commandInvocation` 生成,未设置时回落到 `binName`。Aliyun 兼容入口由产品入口识别并注入 `Identity.commandInvocation = { bin: "aliyun", path: ["bl"] }`;telemetry 不读取环境变量或 argv 猜测来源,也不采用旧的显示覆盖字段。命令路径保持不变:如三种入口的 `config show` 仍以 `config show` 为事件名,按 `commandPrefix` 区分入口。调用方 `callerType` / `callerName` 是独立维度。 + +该字段同时进入本地 JSONL 与远端 AEM `ext`,成功与失败均携带。旧版本缺少入口字段的事件保留为入口未知,不能自动归为 `bl`,也不能靠命令路径可靠补回。后端 source-config 和 User-Agent 的既有口径不变。 + +`createTrackingEvent()` 必须传入 `identity`,统一从中读取 `cliVersion` 并生成 `commandPrefix`。`trackCommandExecution()` 传入产品 `Identity`,因此本版 CLI 命令事件始终包含 `commandPrefix`。 AEM 映射: -| AEM 字段 | 内容 | -| ---------- | -------------------------------------------------------------------------------- | -| event name | 命令路径 | -| `et` | `EXP` | -| `ext` | 除 `command`、`params` 外的结构化事件字段,含 `callerType` 与失败时的 `exitCode` | -| `c1` | allowlist 参数 | -| `c2` | `success` / `failure` | -| `c3` | HTTP status | -| `c4` | 错误文案,最多 500 字符 | -| `c5` | request ID | +| AEM 字段 | 内容 | +| ---------- | ------------------------------------------------------------------------------------------------- | +| event name | 命令路径 | +| `et` | `EXP` | +| `ext` | 除 `command`、`params` 外的结构化事件字段,含 `commandPrefix`、`callerType` 与失败时的 `exitCode` | +| `c1` | allowlist 参数 | +| `c2` | `success` / `failure` | +| `c3` | HTTP status | +| `c4` | 错误文案,最多 500 字符 | +| `c5` | request ID | 远端发送是 best-effort,不得阻塞命令或改变退出码。正常退出最多等待 1 秒,SIGINT 最多等待 500 ms。 @@ -159,6 +172,7 @@ AEM 映射: - [ ] 更新 `TrackingEvent`、`createTrackingEvent()` 与 `buildRemoteAemOptions()` 的字段映射 - [ ] 本地 JSONL 与远端 AEM 必须基于同一结构化事件,不能维护两套字段口径 +- [ ] 三种入口分别核对 `commandPrefix`;共享路径也必须可区分,旧数据缺字段保留为未知 - [ ] 调用方字段经 `readCaller` → `TrackingDeps.caller` → 事件;仅显式 `agent` / `ai_agent` 为 `agent`,其余为 `unknown` - [ ] 成功与失败均覆盖;遥测异常必须静默且不改变业务退出码 - [ ] runtime 本地语义错误应记录 `exitCode`;新增字段默认随 AEM `ext` 上报,无需占用新的 `c1`—`c5` diff --git a/packages/bailian-kb-dsh/package.json b/packages/bailian-kb-dsh/package.json index c72845b5..10d54760 100644 --- a/packages/bailian-kb-dsh/package.json +++ b/packages/bailian-kb-dsh/package.json @@ -1,6 +1,6 @@ { "name": "bailian-kb-dsh", - "version": "2.8.1", + "version": "2.8.2", "description": "Bailian knowledge-base tools for DeepSeek Harness: kb_search and kb_chat over the DashScope RAG API, plus the bl CLI management skill.", "keywords": [ "alibaba-cloud", diff --git a/packages/bailian-memo-dsh/package.json b/packages/bailian-memo-dsh/package.json index 95d3feaa..2c72f8a5 100644 --- a/packages/bailian-memo-dsh/package.json +++ b/packages/bailian-memo-dsh/package.json @@ -1,6 +1,6 @@ { "name": "bailian-memo-dsh", - "version": "2.8.1", + "version": "2.8.2", "description": "Bailian personal memory for DeepSeek Harness: automatic recall, silent curation, and user-controlled remember/forget.", "keywords": [ "alibaba-cloud", diff --git a/packages/cli/package.json b/packages/cli/package.json index 57695876..6af93671 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli", - "version": "2.8.1", + "version": "2.8.2", "description": "CLI for Aliyun Model Studio (DashScope) AI Platform.", "keywords": [ "agent", diff --git a/packages/commands/package.json b/packages/commands/package.json index f0d22521..5b405a40 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-commands", - "version": "2.8.1", + "version": "2.8.2", "description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.", "homepage": "https://bailian.console.aliyun.com/cli", "bugs": { diff --git a/packages/core/package.json b/packages/core/package.json index 8bf5dac1..beb5c49b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-core", - "version": "2.8.1", + "version": "2.8.2", "description": "Core SDK for bailian-cli. See https://www.npmjs.com/package/bailian-cli for usage.", "homepage": "https://bailian.console.aliyun.com/cli", "bugs": { diff --git a/packages/core/src/telemetry/event.ts b/packages/core/src/telemetry/event.ts index bc31e60d..0691ba09 100644 --- a/packages/core/src/telemetry/event.ts +++ b/packages/core/src/telemetry/event.ts @@ -1,3 +1,5 @@ +import type { Identity } from "../config/schema.ts"; +import { formatCommand } from "../output/command.ts"; import type { AuthRequirement } from "../types/command.ts"; import type { CallerAttribution } from "./caller.ts"; @@ -11,6 +13,7 @@ export interface TrackingEvent { httpStatus?: number; requestId?: string; cliVersion: string; + commandPrefix: string; nodeVersion: string; os: string; authMethod?: AuthRequirement; @@ -27,7 +30,7 @@ export function createTrackingEvent(opts: { durationMs: number; success: boolean; error?: { message?: string; exitCode?: number; httpStatus?: number; requestId?: string }; - cliVersion: string; + identity: Identity; authMethod?: AuthRequirement; params?: Record; caller: CallerAttribution; @@ -37,7 +40,8 @@ export function createTrackingEvent(opts: { timestamp: new Date().toISOString(), durationMs: opts.durationMs, success: opts.success, - cliVersion: opts.cliVersion, + cliVersion: opts.identity.version, + commandPrefix: formatCommand(opts.identity), nodeVersion: process.version, os: process.platform, callerType: opts.caller.callerType, diff --git a/packages/core/src/telemetry/tracker.ts b/packages/core/src/telemetry/tracker.ts index 2f161016..787cc7c5 100644 --- a/packages/core/src/telemetry/tracker.ts +++ b/packages/core/src/telemetry/tracker.ts @@ -133,7 +133,7 @@ export async function trackCommandExecution( durationMs, success, error: success ? undefined : { message: errorMessage, exitCode, httpStatus, requestId }, - cliVersion: deps.identity.version, + identity: deps.identity, authMethod: deps.authMethod, params: extractParams(flags), caller: deps.caller, diff --git a/packages/core/tests/telemetry-tracker.test.ts b/packages/core/tests/telemetry-tracker.test.ts index f36da576..8d7d01ac 100644 --- a/packages/core/tests/telemetry-tracker.test.ts +++ b/packages/core/tests/telemetry-tracker.test.ts @@ -3,7 +3,11 @@ import type { Identity, Settings } from "../src/config/schema.ts"; import { BailianError } from "../src/errors/base.ts"; import { ExitCode } from "../src/errors/codes.ts"; import type { CallerAttribution } from "../src/telemetry/caller.ts"; -import { buildRemoteAemOptions, type TrackingEvent } from "../src/telemetry/event.ts"; +import { + buildRemoteAemOptions, + createTrackingEvent, + type TrackingEvent, +} from "../src/telemetry/event.ts"; const sinkMocks = vi.hoisted(() => ({ localSink: vi.fn<(event: TrackingEvent) => Promise>(async () => {}), @@ -29,6 +33,118 @@ const agentCaller: CallerAttribution = { skillVersion: "2.5.0", }; +test("derives event version and prefix from identity", () => { + const event = createTrackingEvent({ + command: "config show", + durationMs: 1, + success: true, + identity: { ...identity, version: "identity-version" }, + caller: unknownCaller, + }); + expect(event).toMatchObject({ + command: "config show", + cliVersion: "identity-version", + commandPrefix: "bl", + }); + expect(buildRemoteAemOptions(event)).toMatchObject({ + ext: { cliVersion: "identity-version", commandPrefix: "bl" }, + }); +}); + +const entryIdentities = [ + { + label: "bl", + identity: { ...identity, clientName: "bailian-cli" }, + expected: { commandPrefix: "bl" }, + }, + { + label: "kscli", + identity: { + ...identity, + binName: "kscli", + clientName: "knowledge-studio-cli", + npmPackage: "knowledge-studio-cli", + }, + expected: { commandPrefix: "kscli" }, + }, + { + label: "aliyun bl", + identity: { + ...identity, + commandInvocation: { bin: "aliyun", path: ["bl"] }, + clientName: "bailian-cli", + }, + expected: { commandPrefix: "aliyun bl" }, + }, +]; + +test.each( + entryIdentities.flatMap((entry) => [true, false].map((success) => ({ ...entry, success }))), +)("preserves $label entry in local and remote events (success=$success)", async (entry) => { + sinkMocks.localSink.mockClear(); + sinkMocks.remoteSink.mockClear(); + const error = new BailianError("Example failure", ExitCode.GENERAL); + const execution = trackCommandExecution( + { + identity: entry.identity, + settings: { telemetry: true } as Settings, + authMethod: "none", + caller: agentCaller, + }, + ["config", "show"], + { commandPrefix: "untrusted flag", clientName: "untrusted flag" }, + async () => { + if (!entry.success) throw error; + }, + ); + if (entry.success) await execution; + else await expect(execution).rejects.toBe(error); + + expect(sinkMocks.localSink).toHaveBeenCalledOnce(); + expect(sinkMocks.remoteSink).toHaveBeenCalledOnce(); + const localEvent = sinkMocks.localSink.mock.calls[0]![0]; + const remoteEvent = sinkMocks.remoteSink.mock.calls[0]![0]; + expect(localEvent).toMatchObject({ + ...entry.expected, + command: "config show", + cliVersion: "0.0.0-test", + success: entry.success, + callerType: "agent", + callerName: "cursor", + }); + expect(localEvent.params).toBeUndefined(); + expect(remoteEvent).toEqual(localEvent); + expect(buildRemoteAemOptions(remoteEvent)).toMatchObject({ + ext: entry.expected, + c2: entry.success ? "success" : "failure", + }); + for (const field of ["binName", "clientName"]) { + expect(localEvent).not.toHaveProperty(field); + expect(buildRemoteAemOptions(remoteEvent).ext).not.toHaveProperty(field); + } +}); + +test("ignores obsolete display overrides when deriving the entry", async () => { + sinkMocks.localSink.mockClear(); + sinkMocks.remoteSink.mockClear(); + const legacyIdentity = { ...identity, commandPrefix: "untrusted legacy override" }; + await trackCommandExecution( + { + identity: legacyIdentity, + settings: { telemetry: true } as Settings, + caller: unknownCaller, + }, + ["config", "show"], + {}, + async () => {}, + ); + const event = sinkMocks.localSink.mock.calls[0]![0]; + expect(event).toMatchObject({ commandPrefix: "bl" }); + expect(buildRemoteAemOptions(event)).toMatchObject({ + ext: { commandPrefix: "bl" }, + }); +}); + test("records BailianError exitCode in local events and AEM ext", async () => { sinkMocks.localSink.mockClear(); sinkMocks.remoteSink.mockClear(); diff --git a/packages/kscli/package.json b/packages/kscli/package.json index 98341b19..799612b5 100644 --- a/packages/kscli/package.json +++ b/packages/kscli/package.json @@ -1,6 +1,6 @@ { "name": "knowledge-studio-cli", - "version": "2.8.1", + "version": "2.8.2", "description": "Lightweight RAG CLI for Aliyun Model Studio — focused on knowledge-base retrieval.", "keywords": [ "alibaba-cloud", diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 1dfbdf57..f137b046 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-runtime", - "version": "2.8.1", + "version": "2.8.2", "description": "Runtime framework for bailian-cli (createCli, registry, args, output, pipeline). See https://www.npmjs.com/package/bailian-cli for usage.", "homepage": "https://bailian.console.aliyun.com/cli", "bugs": { diff --git a/skills/bailian-cli/SKILL.md b/skills/bailian-cli/SKILL.md index b38099a4..350f101b 100644 --- a/skills/bailian-cli/SKILL.md +++ b/skills/bailian-cli/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-cli metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-finetune/SKILL.md b/skills/bailian-finetune/SKILL.md index 06d276e5..81fc32fb 100644 --- a/skills/bailian-finetune/SKILL.md +++ b/skills/bailian-finetune/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-finetune metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-gen/SKILL.md b/skills/bailian-gen/SKILL.md index 9036e5a4..e79d9bc9 100644 --- a/skills/bailian-gen/SKILL.md +++ b/skills/bailian-gen/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-gen metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-knowledge/SKILL.md b/skills/bailian-knowledge/SKILL.md index f788cd06..677c8da3 100644 --- a/skills/bailian-knowledge/SKILL.md +++ b/skills/bailian-knowledge/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-knowledge metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-managed-agent/SKILL.md b/skills/bailian-managed-agent/SKILL.md index a220abd9..df291f26 100644 --- a/skills/bailian-managed-agent/SKILL.md +++ b/skills/bailian-managed-agent/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-managed-agent metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-memory/SKILL.md b/skills/bailian-memory/SKILL.md index 8d79a973..f9a5b9cc 100644 --- a/skills/bailian-memory/SKILL.md +++ b/skills/bailian-memory/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-memory metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-protocol/SKILL.md b/skills/bailian-protocol/SKILL.md index 31015f2b..896754ef 100644 --- a/skills/bailian-protocol/SKILL.md +++ b/skills/bailian-protocol/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-protocol metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-sandbox/SKILL.md b/skills/bailian-sandbox/SKILL.md index 5995e743..ac5cd047 100644 --- a/skills/bailian-sandbox/SKILL.md +++ b/skills/bailian-sandbox/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-sandbox metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-web-search/SKILL.md b/skills/bailian-web-search/SKILL.md index 042a6bab..4793367a 100644 --- a/skills/bailian-web-search/SKILL.md +++ b/skills/bailian-web-search/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-web-search metadata: - version: "2.8.1" + version: "2.8.2" requires: bins: ["bl"] description: >-