diff --git a/docs/agents/skill-change.md b/docs/agents/skill-change.md index 4136f17ed..2adee7e30 100644 --- a/docs/agents/skill-change.md +++ b/docs/agents/skill-change.md @@ -40,6 +40,7 @@ bailian-gen bailian-finetune bailian-managed-agent bailian-web-search - [ ] **整包装齐**:安装 / 缺 skill 文案主推 `bl skill init`;升级同步用 `bl skill update`(只更新已安装,不装新 skill);业务 skill **不**声明 `companions` - [ ] **协议读取**:CRITICAL / references 可链 `../bailian-protocol/…`;若读不到 → 停止执行 `bl`,提示 `bl skill init` +- [ ] **调用归因**:协议 `SKILL.md` 放 bash + PowerShell 双模板与协议样例;每个业务 skill **只留一条**填好的样例并链回协议;其余示例 / `assets/` / `reference/` 保持裸 `bl …`。Agent 执行时再套模板;人类终端不要设这些变量 - [ ] **高风险确认**:统一由 `bailian-protocol` 定义;reference / leaf help 以 `risk: high` 明示风险,业务 skill 不得引导 Agent 自动补 `--yes`。遇到 exit code 7 / `requires_confirmation` 时停止执行并请求确认;目标或范围变化后重新确认 - [ ] **正常控制流**:`requires_confirmation` 不是 CLI bug,`assets/issue-reporting.md` 必须将 exit code 7 保持在 EXCLUDE 范围 - [ ] **软 hand-off**:兄弟业务 skill **只写 skill 名**;已安装则 Read,未安装则 `bl … --help` 或提示整包安装;**不要**把 `../bailian-gen/…` 等写成执行前提 diff --git a/docs/agents/telemetry-change.md b/docs/agents/telemetry-change.md index 8dc0e68fb..dd35b278d 100644 --- a/docs/agents/telemetry-change.md +++ b/docs/agents/telemetry-change.md @@ -95,9 +95,18 @@ source-config 只用于百炼 / DashScope API 侧消费,不发送到通用网 - `command`、`timestamp`、`durationMs`、`success` - `cliVersion`、`nodeVersion`、`os` - `authMethod` +- `callerType`(必填:`agent` 或 `unknown`);`callerType=agent` 时可选 `callerName`、`skillName`、`skillVersion` - 失败时的 `errorMessage`、`exitCode`、`httpStatus`、`requestId` - 安全 allowlist 过滤后的 `params` +调用方归因只认本次进程的 `BAILIAN_CALLER_*`(由 `readCaller` 归一化后写入事件): + +- `BAILIAN_CALLER_TYPE` 为 `agent` / `ai_agent`(大小写不敏感)→ `callerType=agent` +- 未设置、空白或其他值 → `callerType=unknown`;不要把该字段当成人工 +- `callerName` / `skillName` / `skillVersion` 仅在 `agent` 时发送;`kscli` 通常没有 skill 字段 + +新 CLI 每条进入 middleware 的命令事件都有 `callerType`。旧 CLI 没有该字段。看板只统计带 `callerType` 的事件,不能把字段缺失混进分母。`unknown` 只表示未识别为 agent,不是人工。新 CLI + 旧 skill / Agent 漏设变量时,会落进 `unknown`。`BAILIAN_CALLER_*` 是调用方声明,不是 `Identity`,不得用来猜 `bl` / `kscli`。 + 参数默认不上传,只有 `packages/core/src/telemetry/tracker.ts` 的 `PARAM_ALLOWLIST` 中字段会进入事件。不得加入 prompt、凭证、文件路径、URL、账号/租户/工作空间 ID 或其他用户内容。 `page` 仅记录有限数值页码;Webhook 等命令使用的不透明字符串游标不得进入本地日志或远端 AEM,即使字符串内容看起来是数字。 @@ -113,16 +122,16 @@ source-config 只用于百炼 / DashScope API 侧消费,不发送到通用网 AEM 映射: -| AEM 字段 | 内容 | -| ---------- | ------------------------------------------------------------------ | -| event name | 命令路径 | -| `et` | `EXP` | -| `ext` | 除 `command`、`params` 外的结构化事件字段,包含失败时的 `exitCode` | -| `c1` | allowlist 参数 | -| `c2` | `success` / `failure` | -| `c3` | HTTP status | -| `c4` | 错误文案,最多 500 字符 | -| `c5` | request ID | +| AEM 字段 | 内容 | +| ---------- | -------------------------------------------------------------------------------- | +| event name | 命令路径 | +| `et` | `EXP` | +| `ext` | 除 `command`、`params` 外的结构化事件字段,含 `callerType` 与失败时的 `exitCode` | +| `c1` | allowlist 参数 | +| `c2` | `success` / `failure` | +| `c3` | HTTP status | +| `c4` | 错误文案,最多 500 字符 | +| `c5` | request ID | 远端发送是 best-effort,不得阻塞命令或改变退出码。正常退出最多等待 1 秒,SIGINT 最多等待 500 ms。 @@ -140,6 +149,7 @@ AEM 映射: - [ ] 同时核对 `packages/core/src/client/http.ts`、`mcp.ts`、`instrumented-fetch.ts`、`client.ts` 与 `files/upload.ts` - [ ] 产品身份必须来自 `Identity`;不得从命令路径、环境变量或 `process.argv` 猜测 +- [ ] `BAILIAN_CALLER_*` 是调用方声明,不得用来猜产品身份(`bl` / `kscli`) - [ ] `bl` 与 `kscli` 必须分别验证 `binName`、`clientName`、`version` - [ ] OSS、结果文件、npm、二进制和 Skill 下载不得为了业务渠道统计新增 source-config - [ ] 改 URL / host 范围时同时执行 [URL / 渠道变更](url-change.md) 清单 @@ -148,6 +158,7 @@ AEM 映射: - [ ] 更新 `TrackingEvent`、`createTrackingEvent()` 与 `buildRemoteAemOptions()` 的字段映射 - [ ] 本地 JSONL 与远端 AEM 必须基于同一结构化事件,不能维护两套字段口径 +- [ ] 调用方字段经 `readCaller` → `TrackingDeps.caller` → 事件;仅显式 `agent` / `ai_agent` 为 `agent`,其余为 `unknown` - [ ] 成功与失败均覆盖;遥测异常必须静默且不改变业务退出码 - [ ] runtime 本地语义错误应记录 `exitCode`;新增字段默认随 AEM `ext` 上报,无需占用新的 `c1`—`c5` - [ ] 检查 `DO_NOT_TRACK=1` 与 `telemetry: false` 两个关闭入口 @@ -157,15 +168,17 @@ AEM 映射: ```sh rg -n "trackingHeaders|x-dashscope-source-config|User-Agent" packages --glob '*.ts' -rg -n "trackCommandExecution|PARAM_ALLOWLIST|buildRemoteAemOptions" packages/core packages/runtime --glob '*.ts' +rg -n "trackCommandExecution|PARAM_ALLOWLIST|buildRemoteAemOptions|readCaller" packages/core packages/runtime --glob '*.ts' vp check -vp test packages/core/tests packages/commands/tests/e2e/auth.e2e.test.ts +vp test packages/core/tests packages/runtime/tests/telemetry-stage.test.ts packages/commands/tests/e2e/auth.e2e.test.ts ``` ## 常见漏点 - ✗ 只看 AEM 命令事件,误以为它能替代网关侧请求渠道统计 - ✗ 把 `authMethod` 当成实际凭证来源;它只是命令声明的鉴权域 +- ✗ 把旧 CLI「没有 `callerType`」或「`callerType=unknown`」当成人工占比 +- ✗ 用 `BAILIAN_CALLER_*` 猜测产品身份,或根据 TTY / 安装目录反推调用方 - ✗ 新增 bypass `fetch` 后漏掉应由网关消费的 source-config,或把它发给 OSS / npm / 第三方下载地址 - ✗ 只改 `bl` 入口,导致 `kscli` 的产品名或版本标签错误 - ✗ 把帮助、版本或参数校验失败算进“全部命令”;这些路径当前没有进入 telemetry middleware diff --git a/packages/cli/tests/skill-sandbox-reference.test.ts b/packages/cli/tests/skill-sandbox-reference.test.ts index 41d181ae9..e61b0c06c 100644 --- a/packages/cli/tests/skill-sandbox-reference.test.ts +++ b/packages/cli/tests/skill-sandbox-reference.test.ts @@ -23,7 +23,8 @@ test("Sandbox is a valid standalone skill aligned with the CLI version", () => { expect(frontmatter.metadata.version).toBe(cliPackage.version); for (const link of skillMarkdown.matchAll(/\]\(([^)]+)\)/g)) { - expect(existsSync(join(sandboxSkillDirectory, link[1]))).toBe(true); + const target = link[1].split("#")[0]; + expect(existsSync(join(sandboxSkillDirectory, target)), target).toBe(true); } }); diff --git a/packages/core/src/telemetry/caller.ts b/packages/core/src/telemetry/caller.ts new file mode 100644 index 000000000..8fbde3f66 --- /dev/null +++ b/packages/core/src/telemetry/caller.ts @@ -0,0 +1,59 @@ +/** Parse BAILIAN_CALLER_*. Only an explicit agent is `agent`; everything else is `unknown`. */ + +export type CallerAttribution = + | { callerType: "unknown" } + | { + callerType: "agent"; + callerName: string; + skillName?: string; + skillVersion?: string; + }; + +const CALLER_TYPE_ENV = "BAILIAN_CALLER_TYPE"; +const CALLER_NAME_ENV = "BAILIAN_CALLER_NAME"; +const SKILL_NAME_ENV = "BAILIAN_SKILL_NAME"; +const SKILL_VERSION_ENV = "BAILIAN_SKILL_VERSION"; + +const NAME_PATTERN = /^[a-z0-9][a-z0-9.-]{0,63}$/; +const SKILL_NAME_PATTERN = /^bailian-[a-z0-9-]{1,64}$/; +const SKILL_VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z.-]{1,20})?$/; + +function readTrimmed(env: NodeJS.ProcessEnv, key: string): string | undefined { + const raw = env[key]; + if (typeof raw !== "string") return undefined; + const trimmed = raw.trim(); + return trimmed.length > 0 ? trimmed : undefined; +} + +/** Normalize host-reported name to a stable id, or `unknown`. */ +function normalizeCallerName(raw: string | undefined): string { + if (!raw) return "unknown"; + const normalized = raw.toLowerCase().replace(/[\s_]+/g, "-"); + return NAME_PATTERN.test(normalized) ? normalized : "unknown"; +} + +/** Env-only attribution; no TTY or install-path heuristics. */ +export function readCaller(env: NodeJS.ProcessEnv): CallerAttribution { + const callerType = readTrimmed(env, CALLER_TYPE_ENV)?.toLowerCase(); + if (callerType !== "agent" && callerType !== "ai_agent") { + return { callerType: "unknown" }; + } + + const skillNameRaw = readTrimmed(env, SKILL_NAME_ENV)?.toLowerCase(); + const skillName = + skillNameRaw !== undefined && SKILL_NAME_PATTERN.test(skillNameRaw) ? skillNameRaw : undefined; + const skillVersionRaw = readTrimmed(env, SKILL_VERSION_ENV); + const skillVersion = + skillName !== undefined && + skillVersionRaw !== undefined && + SKILL_VERSION_PATTERN.test(skillVersionRaw) + ? skillVersionRaw + : undefined; + + return { + callerType: "agent", + callerName: normalizeCallerName(readTrimmed(env, CALLER_NAME_ENV)), + ...(skillName !== undefined ? { skillName } : {}), + ...(skillVersion !== undefined ? { skillVersion } : {}), + }; +} diff --git a/packages/core/src/telemetry/event.ts b/packages/core/src/telemetry/event.ts index 228bf699f..bc31e60dd 100644 --- a/packages/core/src/telemetry/event.ts +++ b/packages/core/src/telemetry/event.ts @@ -1,4 +1,5 @@ import type { AuthRequirement } from "../types/command.ts"; +import type { CallerAttribution } from "./caller.ts"; export interface TrackingEvent { command: string; @@ -14,6 +15,11 @@ export interface TrackingEvent { os: string; authMethod?: AuthRequirement; params?: Record; + /** Always present on new CLI events: `agent` or `unknown`. */ + callerType: CallerAttribution["callerType"]; + callerName?: string; + skillName?: string; + skillVersion?: string; } export function createTrackingEvent(opts: { @@ -24,6 +30,7 @@ export function createTrackingEvent(opts: { cliVersion: string; authMethod?: AuthRequirement; params?: Record; + caller: CallerAttribution; }): TrackingEvent { const event: TrackingEvent = { command: opts.command, @@ -33,8 +40,15 @@ export function createTrackingEvent(opts: { cliVersion: opts.cliVersion, nodeVersion: process.version, os: process.platform, + callerType: opts.caller.callerType, }; + if (opts.caller.callerType === "agent") { + event.callerName = opts.caller.callerName; + if (opts.caller.skillName !== undefined) event.skillName = opts.caller.skillName; + if (opts.caller.skillVersion !== undefined) event.skillVersion = opts.caller.skillVersion; + } + if (opts.authMethod) { event.authMethod = opts.authMethod; } diff --git a/packages/core/src/telemetry/index.ts b/packages/core/src/telemetry/index.ts index a21f78571..e70ea5fb4 100644 --- a/packages/core/src/telemetry/index.ts +++ b/packages/core/src/telemetry/index.ts @@ -1,4 +1,7 @@ +export type { CallerAttribution } from "./caller.ts"; +export { readCaller } from "./caller.ts"; export type { TrackingEvent } from "./event.ts"; export { createTrackingEvent } from "./event.ts"; export { localSink, remoteSink, flushTelemetry } from "./sink.ts"; export { trackCommandExecution } from "./tracker.ts"; +export type { TrackingDeps } from "./tracker.ts"; diff --git a/packages/core/src/telemetry/tracker.ts b/packages/core/src/telemetry/tracker.ts index a65534835..2f161016e 100644 --- a/packages/core/src/telemetry/tracker.ts +++ b/packages/core/src/telemetry/tracker.ts @@ -1,6 +1,7 @@ import type { Identity, Settings } from "../config/schema.ts"; import { BailianError } from "../errors/base.ts"; import type { AuthRequirement } from "../types/command.ts"; +import type { CallerAttribution } from "./caller.ts"; import { createTrackingEvent } from "./event.ts"; import { localSink, remoteSink } from "./sink.ts"; @@ -89,6 +90,8 @@ export interface TrackingDeps { identity: Identity; settings: Settings; authMethod?: AuthRequirement; + /** From `readCaller(process.env)` in telemetryStage. */ + caller: CallerAttribution; } export async function trackCommandExecution( @@ -133,6 +136,7 @@ export async function trackCommandExecution( cliVersion: deps.identity.version, authMethod: deps.authMethod, params: extractParams(flags), + caller: deps.caller, }); // Fire-and-forget — never block the CLI exit or mask errors diff --git a/packages/core/tests/telemetry-caller.test.ts b/packages/core/tests/telemetry-caller.test.ts new file mode 100644 index 000000000..e949e6305 --- /dev/null +++ b/packages/core/tests/telemetry-caller.test.ts @@ -0,0 +1,142 @@ +import { expect, test } from "vite-plus/test"; +import { readCaller } from "../src/telemetry/caller.ts"; + +test.each([ + { label: "missing", env: {} }, + { label: "blank", env: { BAILIAN_CALLER_TYPE: " " } }, + { label: "explicit human", env: { BAILIAN_CALLER_TYPE: "human" } }, + { label: "illegal false", env: { BAILIAN_CALLER_TYPE: "false" } }, + { label: "illegal ai-agent", env: { BAILIAN_CALLER_TYPE: "ai-agent" } }, + { label: "illegal zero", env: { BAILIAN_CALLER_TYPE: "0" } }, +])("maps $label to unknown without agent fields", ({ env }) => { + expect(readCaller(env)).toEqual({ callerType: "unknown" }); +}); + +test.each([ + { label: "agent", type: "agent" }, + { label: "ai_agent", type: "ai_agent" }, + { label: "Agent", type: "Agent" }, + { label: "AI_AGENT", type: "AI_AGENT" }, + { label: "padded agent", type: " agent " }, +])("accepts $label as agent", ({ type }) => { + expect( + readCaller({ + BAILIAN_CALLER_TYPE: type, + BAILIAN_CALLER_NAME: "cursor", + BAILIAN_SKILL_NAME: "bailian-cli", + BAILIAN_SKILL_VERSION: "2.5.0", + }), + ).toEqual({ + callerType: "agent", + callerName: "cursor", + skillName: "bailian-cli", + skillVersion: "2.5.0", + }); +}); + +test("normalizes callerName and falls back to unknown", () => { + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "agent", + BAILIAN_CALLER_NAME: " Claude Code ", + }), + ).toEqual({ callerType: "agent", callerName: "claude-code" }); + + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "agent", + BAILIAN_CALLER_NAME: "qwen_code", + }), + ).toEqual({ callerType: "agent", callerName: "qwen-code" }); + + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "agent", + BAILIAN_CALLER_NAME: "bad/name!", + }), + ).toEqual({ callerType: "agent", callerName: "unknown" }); + + expect(readCaller({ BAILIAN_CALLER_TYPE: "agent" })).toEqual({ + callerType: "agent", + callerName: "unknown", + }); +}); + +test("drops illegal skillName and only keeps version when skillName is valid", () => { + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "agent", + BAILIAN_CALLER_NAME: "cursor", + BAILIAN_SKILL_NAME: "not-bailian", + BAILIAN_SKILL_VERSION: "2.5.0", + }), + ).toEqual({ callerType: "agent", callerName: "cursor" }); + + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "agent", + BAILIAN_CALLER_NAME: "cursor", + BAILIAN_SKILL_NAME: "bailian-cli", + BAILIAN_SKILL_VERSION: "not-a-version", + }), + ).toEqual({ + callerType: "agent", + callerName: "cursor", + skillName: "bailian-cli", + }); + + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "agent", + BAILIAN_CALLER_NAME: "cursor", + BAILIAN_SKILL_NAME: "bailian-cli", + BAILIAN_SKILL_VERSION: "2.5.0-beta.1", + }), + ).toEqual({ + callerType: "agent", + callerName: "cursor", + skillName: "bailian-cli", + skillVersion: "2.5.0-beta.1", + }); +}); + +test("lowercases skillName before validation", () => { + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "agent", + BAILIAN_CALLER_NAME: "cursor", + BAILIAN_SKILL_NAME: "Bailian-CLI", + BAILIAN_SKILL_VERSION: "2.5.0", + }), + ).toEqual({ + callerType: "agent", + callerName: "cursor", + skillName: "bailian-cli", + skillVersion: "2.5.0", + }); + + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "agent", + BAILIAN_CALLER_NAME: "cursor", + BAILIAN_SKILL_NAME: "BAILIAN-GEN", + BAILIAN_SKILL_VERSION: "2.5.0", + }), + ).toEqual({ + callerType: "agent", + callerName: "cursor", + skillName: "bailian-gen", + skillVersion: "2.5.0", + }); +}); + +test("ignores agent detail fields when callerType is not agent", () => { + expect( + readCaller({ + BAILIAN_CALLER_TYPE: "human", + BAILIAN_CALLER_NAME: "cursor", + BAILIAN_SKILL_NAME: "bailian-cli", + BAILIAN_SKILL_VERSION: "2.5.0", + }), + ).toEqual({ callerType: "unknown" }); +}); diff --git a/packages/core/tests/telemetry-tracker.test.ts b/packages/core/tests/telemetry-tracker.test.ts index 1870673ae..f36da576d 100644 --- a/packages/core/tests/telemetry-tracker.test.ts +++ b/packages/core/tests/telemetry-tracker.test.ts @@ -2,6 +2,7 @@ import { expect, test, vi } from "vite-plus/test"; 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"; const sinkMocks = vi.hoisted(() => ({ @@ -20,6 +21,14 @@ const identity: Identity = { npmPackage: "bailian-cli", }; +const unknownCaller: CallerAttribution = { callerType: "unknown" }; +const agentCaller: CallerAttribution = { + callerType: "agent", + callerName: "cursor", + skillName: "bailian-cli", + skillVersion: "2.5.0", +}; + test("records BailianError exitCode in local events and AEM ext", async () => { sinkMocks.localSink.mockClear(); sinkMocks.remoteSink.mockClear(); @@ -31,6 +40,7 @@ test("records BailianError exitCode in local events and AEM ext", async () => { identity, settings: { telemetry: true } as Settings, authMethod: "apiKey", + caller: unknownCaller, }, ["knowledge", "doc", "delete"], {}, @@ -47,9 +57,13 @@ test("records BailianError exitCode in local events and AEM ext", async () => { success: false, exitCode: 7, errorMessage: "该操作会永久删除文档。", + callerType: "unknown", }); + expect(event.callerName).toBeUndefined(); + expect(event.skillName).toBeUndefined(); + expect(event.skillVersion).toBeUndefined(); expect(buildRemoteAemOptions(event)).toMatchObject({ - ext: expect.objectContaining({ exitCode: 7 }), + ext: expect.objectContaining({ exitCode: 7, callerType: "unknown" }), }); }); @@ -67,7 +81,12 @@ test.each([ sinkMocks.remoteSink.mockClear(); await trackCommandExecution( - { identity, settings: { telemetry: true } as Settings, authMethod: "apiKey" }, + { + identity, + settings: { telemetry: true } as Settings, + authMethod: "apiKey", + caller: unknownCaller, + }, ["managed-agent", "webhook", "events"], { page, pageSize: 20 }, async () => {}, @@ -82,3 +101,60 @@ test.each([ expect(buildRemoteAemOptions(remoteEvent).c1).toEqual(expectedParams); }, ); + +test("keeps agent attribution on success and in AEM ext", async () => { + sinkMocks.localSink.mockClear(); + sinkMocks.remoteSink.mockClear(); + + await trackCommandExecution( + { + identity, + settings: { telemetry: true } as Settings, + authMethod: "apiKey", + caller: agentCaller, + }, + ["config", "show"], + {}, + async () => {}, + ); + + const event = sinkMocks.localSink.mock.calls[0]![0]; + expect(event).toMatchObject({ + success: true, + callerType: "agent", + callerName: "cursor", + skillName: "bailian-cli", + skillVersion: "2.5.0", + }); + expect(event.params).toBeUndefined(); + expect(buildRemoteAemOptions(event)).toMatchObject({ + ext: expect.objectContaining({ + callerType: "agent", + callerName: "cursor", + skillName: "bailian-cli", + skillVersion: "2.5.0", + }), + }); +}); + +test("does not call sinks when telemetry is disabled", async () => { + sinkMocks.localSink.mockClear(); + sinkMocks.remoteSink.mockClear(); + const next = vi.fn(async () => {}); + + await trackCommandExecution( + { + identity, + settings: { telemetry: false } as Settings, + authMethod: "apiKey", + caller: agentCaller, + }, + ["config", "show"], + {}, + next, + ); + + expect(next).toHaveBeenCalledOnce(); + expect(sinkMocks.localSink).not.toHaveBeenCalled(); + expect(sinkMocks.remoteSink).not.toHaveBeenCalled(); +}); diff --git a/packages/runtime/src/middleware.ts b/packages/runtime/src/middleware.ts index b44271b2a..c98e186f5 100644 --- a/packages/runtime/src/middleware.ts +++ b/packages/runtime/src/middleware.ts @@ -19,6 +19,7 @@ import { resolveApiKey, resolveConsole, resolveOpenApi, + readCaller, resolveModelBaseUrlState, selectApiKeyResolutionSources, trackCommandExecution, @@ -176,6 +177,7 @@ export const telemetryStage: Middleware = (ctx, next) => { identity: ctx.identity, settings: ctx.settings, authMethod: ctx.command.auth, + caller: readCaller(process.env), }, ctx.path, ctx.flags, diff --git a/packages/runtime/tests/telemetry-stage.test.ts b/packages/runtime/tests/telemetry-stage.test.ts new file mode 100644 index 000000000..c575441eb --- /dev/null +++ b/packages/runtime/tests/telemetry-stage.test.ts @@ -0,0 +1,87 @@ +import { afterEach, beforeEach, expect, test, vi } from "vite-plus/test"; +import type { CallerAttribution } from "bailian-cli-core"; + +const trackerMocks = vi.hoisted(() => ({ + trackCommandExecution: vi.fn( + async ( + _deps: unknown, + _path: string[], + _flags: Record, + next: () => Promise, + ) => { + await next(); + }, + ), +})); + +vi.mock("bailian-cli-core", async () => { + const actual = await vi.importActual("bailian-cli-core"); + return { + ...actual, + trackCommandExecution: trackerMocks.trackCommandExecution, + }; +}); + +import { telemetryStage, type RunContext } from "../src/middleware.ts"; + +function makeContext(): RunContext { + return { + identity: { + binName: "bl", + version: "0.0.0-test", + clientName: "bailian-cli-test", + npmPackage: "bailian-cli", + }, + path: ["config", "show"], + command: { auth: "none" }, + settings: { telemetry: true }, + flags: {}, + } as RunContext; +} + +beforeEach(() => { + trackerMocks.trackCommandExecution.mockClear(); +}); + +afterEach(() => { + vi.unstubAllEnvs(); +}); + +test("wires process.env through readCaller into trackCommandExecution", async () => { + vi.stubEnv("BAILIAN_CALLER_TYPE", "agent"); + vi.stubEnv("BAILIAN_CALLER_NAME", "cursor"); + vi.stubEnv("BAILIAN_SKILL_NAME", "bailian-cli"); + vi.stubEnv("BAILIAN_SKILL_VERSION", "2.5.0"); + const next = vi.fn(async () => {}); + + await telemetryStage(makeContext(), next); + + expect(trackerMocks.trackCommandExecution).toHaveBeenCalledOnce(); + const deps = trackerMocks.trackCommandExecution.mock.calls[0]![0] as { + caller: CallerAttribution; + authMethod: string; + }; + expect(deps.caller).toEqual({ + callerType: "agent", + callerName: "cursor", + skillName: "bailian-cli", + skillVersion: "2.5.0", + }); + expect(deps.authMethod).toBe("none"); + expect(next).toHaveBeenCalledOnce(); +}); + +test("defaults to unknown when caller env is unset", async () => { + vi.stubEnv("BAILIAN_CALLER_TYPE", undefined); + vi.stubEnv("BAILIAN_CALLER_NAME", undefined); + vi.stubEnv("BAILIAN_SKILL_NAME", undefined); + vi.stubEnv("BAILIAN_SKILL_VERSION", undefined); + const next = vi.fn(async () => {}); + + await telemetryStage(makeContext(), next); + + const deps = trackerMocks.trackCommandExecution.mock.calls[0]![0] as { + caller: CallerAttribution; + }; + expect(deps.caller).toEqual({ callerType: "unknown" }); +}); diff --git a/skills/bailian-cli/SKILL.md b/skills/bailian-cli/SKILL.md index 24a78594a..d8a7fae58 100644 --- a/skills/bailian-cli/SKILL.md +++ b/skills/bailian-cli/SKILL.md @@ -21,7 +21,13 @@ description: >- # Aliyun Model Studio CLI (`bl`) -**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Provider selection and consent, Version & updates (pre-flight checklist), Setup & auth, and CLI errors: report an issue. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** +**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Provider selection and consent, Version & updates (pre-flight checklist), Call attribution (every Agent `bl` command), Setup & auth, and CLI errors: report an issue. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** + +**Call attribution:** Examples below and in `reference/` stay bare `bl …`. When running a command, wrap it with [`bailian-protocol` → Call attribution](../bailian-protocol/SKILL.md#call-attribution-mandatory-for-every-agent-bl-command) (bash or PowerShell). One sample for this skill (`BAILIAN_SKILL_NAME=bailian-cli`; replace `codex` with this host): + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-cli BAILIAN_SKILL_VERSION=2.5.0 bl text chat --message "Write a poem about spring" +``` > **Family hub** — This skill owns Bailian resource commands and the hub `reference/` (apps, knowledge, usage, auth, config, …). > Shared protocol → [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md) (install the full family with `bl skill init`). @@ -93,7 +99,7 @@ Use this table only after the decision table in [`bailian-protocol`](../bailian- | Bailian Sandbox instance / template lifecycle | → skill `bailian-sandbox` | Fallback: `bl sandbox --help` | | Web search (model-aware routing) | → skill `bailian-web-search` | Token Plan vs MCP path + fallback; fallback: `bl search web --help` | -Flags, usage, and examples: see hub [`reference/`](reference/index.md) or `bl --help` — do not guess flags. Domain command details live in the owning skill's `reference/`. +Flags, usage, and examples: see hub [`reference/`](reference/index.md) or `bl --help` — do not guess flags. Domain command details live in the owning skill's `reference/`. Examples in `reference/` stay bare; wrap with Call attribution when running. --- diff --git a/skills/bailian-finetune/SKILL.md b/skills/bailian-finetune/SKILL.md index c00c1a514..05b0130e9 100644 --- a/skills/bailian-finetune/SKILL.md +++ b/skills/bailian-finetune/SKILL.md @@ -16,7 +16,13 @@ description: >- # Bailian fine-tuning pipeline (`bl dataset` / `bl finetune` / `bl deploy`) -**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Version & updates (pre-flight checklist), Setup & auth, and CLI errors: report an issue. Command details are authoritative in [`reference/`](reference/index.md) (dataset / finetune / deploy) and `bl --help` — do not guess flags. The whole pipeline requires an API key. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** +**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Version & updates (pre-flight checklist), Call attribution (every Agent `bl` command), Setup & auth, and CLI errors: report an issue. Command details are authoritative in [`reference/`](reference/index.md) (dataset / finetune / deploy) and `bl --help` — do not guess flags. Examples in `reference/` stay bare; wrap with Call attribution when running. The whole pipeline requires an API key. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** + +**Call attribution:** Examples below and in `reference/` stay bare `bl …`. When running a command, wrap it with [`bailian-protocol` → Call attribution](../bailian-protocol/SKILL.md#call-attribution-mandatory-for-every-agent-bl-command) (bash or PowerShell). One sample for this skill (`BAILIAN_SKILL_NAME=bailian-finetune`; replace `codex` with this host). After deploy, `bl text chat` is owned by `bailian-cli`. + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-finetune BAILIAN_SKILL_VERSION=2.5.0 bl dataset validate --file train.jsonl +``` ## End-to-end workflow (follow in order) diff --git a/skills/bailian-gen/SKILL.md b/skills/bailian-gen/SKILL.md index 102912664..278f57c80 100644 --- a/skills/bailian-gen/SKILL.md +++ b/skills/bailian-gen/SKILL.md @@ -20,7 +20,13 @@ description: >- # Bailian media generation & understanding (`bl image` / `bl video` / `bl speech` / `bl omni` / `bl vision`) -**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Provider selection and consent (one-time ask templates), Version & updates (pre-flight checklist), and CLI errors: report an issue. Command details are authoritative in [`reference/`](reference/index.md) and `bl --help` — do not guess flags. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** +**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Provider selection and consent (one-time ask templates), Version & updates (pre-flight checklist), Call attribution (every Agent `bl` command), and CLI errors: report an issue. Command details are authoritative in [`reference/`](reference/index.md) and `bl --help` — do not guess flags. Examples in `reference/` stay bare; wrap with Call attribution when running. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** + +**Call attribution:** Examples below and in `reference/` stay bare `bl …`. When running a command, wrap it with [`bailian-protocol` → Call attribution](../bailian-protocol/SKILL.md#call-attribution-mandatory-for-every-agent-bl-command) (bash or PowerShell). One sample for this skill (`BAILIAN_SKILL_NAME=bailian-gen`; replace `codex` with this host): + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-gen BAILIAN_SKILL_VERSION=2.5.0 bl image generate --prompt "A cat in space" --out-dir ./out/ +``` ## Consent (short version; full rules in bailian-protocol) @@ -54,7 +60,7 @@ Flags, usage, and examples: see [`reference/`](reference/index.md) or `bl - # Bailian managed agent IaC (`bl managed-agent`) -**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): High-risk operation confirmation, Version & updates (pre-flight checklist), and CLI errors: report an issue. Command details are authoritative in [`reference/managed-agent.md`](reference/managed-agent.md) and `bl managed-agent --help` — do not guess flags. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** +**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): High-risk operation confirmation, Version & updates (pre-flight checklist), Call attribution (every Agent `bl` command), and CLI errors: report an issue. Command details are authoritative in [`reference/managed-agent.md`](reference/managed-agent.md) and `bl managed-agent --help` — do not guess flags. Examples in `reference/` stay bare; wrap with Call attribution when running. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** + +**Call attribution:** Examples below and in `reference/` stay bare `bl …`. When running a command, wrap it with [`bailian-protocol` → Call attribution](../bailian-protocol/SKILL.md#call-attribution-mandatory-for-every-agent-bl-command) (bash or PowerShell). One sample for this skill (`BAILIAN_SKILL_NAME=bailian-managed-agent`; replace `codex` with this host): + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-managed-agent BAILIAN_SKILL_VERSION=2.5.0 bl managed-agent plan +``` ## Safety guardrail (the most important rule) diff --git a/skills/bailian-memory/SKILL.md b/skills/bailian-memory/SKILL.md index 5cb67e466..27a993c31 100644 --- a/skills/bailian-memory/SKILL.md +++ b/skills/bailian-memory/SKILL.md @@ -19,7 +19,13 @@ description: >- **读取本 Skill、检查本地启用状态与发送云端请求是三个不同步骤。** 前两步不以已经启用为前提;只有云端个人记忆读写需要启用授权。安装 Skill 不代表用户已授权上传。 -执行任何 `bl` 命令前,先读取共享协议 [bailian-protocol](../bailian-protocol/SKILL.md),遵循鉴权、版本预检和高风险确认。协议缺失不妨碍识别记忆意图或解释启用流程;但执行命令前须补齐,提示 `bl skill init`,不能静默退回内置记忆并声称本服务已工作。 +执行任何 `bl` 命令前,先读取共享协议 [bailian-protocol](../bailian-protocol/SKILL.md),遵循鉴权、版本预检、调用归因和高风险确认。协议缺失不妨碍识别记忆意图或解释启用流程;但执行命令前须补齐,提示 `bl skill init`,不能静默退回内置记忆并声称本服务已工作。 + +**Call attribution:** Examples below and in `reference/` stay bare `bl …`. When running a command, wrap it with [`bailian-protocol` → Call attribution](../bailian-protocol/SKILL.md#call-attribution-mandatory-for-every-agent-bl-command) (bash or PowerShell). One sample for this skill (`BAILIAN_SKILL_NAME=bailian-memory`; replace `codex` with this host). Fill `` / `` from `~/.bailian/personal-memory/config.json`; do not invent ids. Command names in the tables below are arguments only. + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-memory BAILIAN_SKILL_VERSION=2.5.0 bl memory list --user-id --workspace-id --page-size 1 --output json +``` ## 入口与范围 diff --git a/skills/bailian-protocol/SKILL.md b/skills/bailian-protocol/SKILL.md index dfacbb90d..4f9ae7277 100644 --- a/skills/bailian-protocol/SKILL.md +++ b/skills/bailian-protocol/SKILL.md @@ -21,7 +21,7 @@ description: >- > bl skill init > ``` > -> 子集安装请显式带上 `bailian-protocol`(例如 `bl skill add --name bailian-protocol,bailian-gen`),否则业务 skill 的相对路径 Read 会失败。 +> 子集安装请显式带上 `bailian-protocol`(例如 `bl skill add --name bailian-protocol,bailian-gen`),否则业务 skill 的相对路径 Read 会失败。Agents wrap with [Call attribution](#call-attribution-mandatory-for-every-agent-bl-command). ## Provider selection and consent @@ -72,9 +72,64 @@ After approval, treat Bailian as selected for the current task. Do not ask again - `bailian-protocol`:靠 `bl skill init` 与业务 skill 同装;CRITICAL 可用相对路径 `../bailian-protocol/…`。读不到则停止跑 `bl`,提示整包安装。 - 其它 bailian-\* 业务 skill:只按名字提及,**不要**写死 `../bailian-*/SKILL.md` 当执行前提。 +## Call attribution (mandatory for every Agent `bl` command) + +Examples in this protocol, `assets/`, business skills, and generated `reference/` show bare `bl …`. When an Agent **runs** a command, wrap that single invocation with one of the templates below. Humans must not set these variables. Anything other than an explicit agent is recorded as `unknown`. + +`--help` and `--version` skip telemetry and need no prefix. Every other `bl` does, including `bl skill init`, `bl skill update`, and `bl update`. Do not `export` the variables into the shell session. If the host accepts a one-shot env map without rewriting the command string, set the same four variables there instead. + +Pick the template that matches the **current shell**. Windows Codex defaults to PowerShell — do not paste the bash form into PowerShell. Sample host id is `codex`; replace with this host's stable id (examples: `codex`, `qwen-code`, `claude-code`, `cursor`, `qoder`). Use `unknown` only when you cannot tell. Do not borrow another product's name. Do not scan install directories. + +### bash / zsh / Git Bash / WSL + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME= BAILIAN_SKILL_NAME= BAILIAN_SKILL_VERSION= bl +``` + +Filled sample for this protocol (`bl skill init`): + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-protocol BAILIAN_SKILL_VERSION=2.5.0 bl skill init +``` + +Inside a command substitution: put the four variables inside `$(...)`. + +With `NO_COLOR` (config is owned by `bailian-cli`): + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-cli BAILIAN_SKILL_VERSION=2.5.0 NO_COLOR=1 bl config show --output text +``` + +### PowerShell (Windows Codex default) + +```powershell +$env:BAILIAN_CALLER_TYPE='agent'; $env:BAILIAN_CALLER_NAME=''; $env:BAILIAN_SKILL_NAME=''; $env:BAILIAN_SKILL_VERSION=''; bl ; $code=$LASTEXITCODE; Remove-Item Env:BAILIAN_CALLER_TYPE,Env:BAILIAN_CALLER_NAME,Env:BAILIAN_SKILL_NAME,Env:BAILIAN_SKILL_VERSION -ErrorAction SilentlyContinue; if ($code -ne 0) { exit $code } +``` + +Filled sample for this protocol: + +```powershell +$env:BAILIAN_CALLER_TYPE='agent'; $env:BAILIAN_CALLER_NAME='codex'; $env:BAILIAN_SKILL_NAME='bailian-protocol'; $env:BAILIAN_SKILL_VERSION='2.5.0'; bl skill init; $code=$LASTEXITCODE; Remove-Item Env:BAILIAN_CALLER_TYPE,Env:BAILIAN_CALLER_NAME,Env:BAILIAN_SKILL_NAME,Env:BAILIAN_SKILL_VERSION -ErrorAction SilentlyContinue; if ($code -ne 0) { exit $code } +``` + +Clear the four variables after each command so they do not linger in the Codex session. Save `$LASTEXITCODE` before `Remove-Item` and `exit` only on nonzero — a trailing successful cleanup must not mask `bl` failures (e.g. exit 7 `requires_confirmation`). + +### Owning skill + +Reuse the same four-variable shape on every later `bl`, but change `BAILIAN_SKILL_NAME` when the command belongs to a different skill; keep `BAILIAN_CALLER_NAME` as this host. Each business skill shows one filled-in sample for its own commands. + +| Task | `` | +| ------------------------------------------------------------------------ | -------------------------------------- | +| Install, `skill init` / `skill update`, `bl update`, protocol pre-flight | `bailian-protocol` | +| auth, config, text, app, knowledge, usage | `bailian-cli` | +| image / video / speech / vision / omni | `bailian-gen` | +| Other domain commands | The `bailian-*` skill currently in use | + +`BAILIAN_SKILL_VERSION`: copy `metadata.version` from that owning skill's `SKILL.md` frontmatter. The family currently shares one version; if the frontmatter differs from `2.5.0` in a sample, use the frontmatter. + ## Version & updates (after provider selection, before the first `bl` command) -**MANDATORY:** Before running any `bl` command, complete the **Agent pre-flight checklist** in [`assets/versioning.md`](assets/versioning.md). Do NOT run any `bl` command until the checklist is complete. If versions mismatch, ask the user whether to upgrade — do not proceed silently. +**MANDATORY:** Before running any `bl` command, complete the **Agent pre-flight checklist** in [`assets/versioning.md`](assets/versioning.md). Do NOT run any `bl` command until the checklist is complete. If versions mismatch, ask the user whether to upgrade — do not proceed silently. Pre-flight `bl` commands that enter middleware (for example `bl skill update` / `bl update` / `bl skill init`) must use Call attribution above; owning skill for those is `bailian-protocol`. ## Setup & auth @@ -94,7 +149,7 @@ bl text chat --message "Write a poem about spring" # explicit text-model smoke ## Color output When an agent needs plain text without ANSI color codes (for parsing, logs, or -snapshots), run the command with `NO_COLOR=1`: +snapshots), run with `NO_COLOR=1` and Call attribution (see the bash sample under [Call attribution](#call-attribution-mandatory-for-every-agent-bl-command)): ```bash NO_COLOR=1 bl config show --output text diff --git a/skills/bailian-protocol/assets/setup.md b/skills/bailian-protocol/assets/setup.md index d61e2c518..5efc09a20 100644 --- a/skills/bailian-protocol/assets/setup.md +++ b/skills/bailian-protocol/assets/setup.md @@ -7,6 +7,8 @@ Read this only when you need to install `bl`, change credentials/endpoint, or inspect config keys. Day-to-day command routing lives in the business skills (`bailian-cli` / `bailian-gen` / …). +Examples stay bare `bl …`. Agents wrap them with [Call attribution](../SKILL.md#call-attribution-mandatory-for-every-agent-bl-command): `bailian-protocol` for install / `skill init` / `skill update` / `bl update`, `bailian-cli` for auth / config, `bailian-gen` for image / video smoke tests. + --- ## Install @@ -61,10 +63,10 @@ For the exact command-to-auth mapping, read the owning skill's `reference/index. | OpenAPI AK/SK | `bl auth login --open-api --access-key-id --access-key-secret ` or Alibaba env vars | Token Plan management commands (`token-plan`) | ```bash -bl auth status # check current auth -bl auth logout # clear credentials and the model Base URL -bl auth logout --console # clear console token only -bl auth logout --open-api # clear OpenAPI AK/SK only +bl auth status +bl auth logout +bl auth logout --console +bl auth logout --open-api ``` - Get a DashScope API key: https://bailian.console.aliyun.com/cn-beijing/?spm=a2ty07.github&tab=app#/api-key @@ -179,7 +181,6 @@ Use `bl auth login --console` for the China site. Add `--console-site internatio 5. **Still unclear** — ask the user which console they use; do not assume domestic. ```bash -# Domestic bl auth login --console # International diff --git a/skills/bailian-protocol/assets/versioning.md b/skills/bailian-protocol/assets/versioning.md index 6de95f88c..b598eada0 100644 --- a/skills/bailian-protocol/assets/versioning.md +++ b/skills/bailian-protocol/assets/versioning.md @@ -3,6 +3,8 @@ > Hand-maintained. Lives in `bailian-protocol/assets/` (not auto-generated from command metadata). > Entry point: [SKILL.md → Version & updates](../SKILL.md#version--updates-after-provider-selection-before-the-first-bl-command). +Agents wrap middleware `bl` commands with [Call attribution](../SKILL.md#call-attribution-mandatory-for-every-agent-bl-command); `` here is always `bailian-protocol`. Examples stay bare. `bl --version` / `--help` need no prefix. + ## Agent pre-flight checklist (MANDATORY) **Do NOT run any `bl` command until you complete this checklist.** Run it **once per session**, before the first `bl` command. Cache the result — do not re-check before every command. @@ -18,7 +20,7 @@ ```bash bl skill update ``` - - If `bailian-protocol` or another needed skill is missing, stop and run `bl skill init`. Do not use `bl skill init` only to refresh versions — it installs every `bailian-*` skill. + - If `bailian-protocol` or another needed skill is missing, stop and run `bl skill init` (same Call attribution, `bailian-protocol`). Do not use `bl skill init` only to refresh versions — it installs every `bailian-*` skill. - Do not trust a stale `reference/` when versions mismatch — flags may be wrong. 4. Check the latest published CLI version: ```bash @@ -27,7 +29,10 @@ 5. If the installed `bl` is **older** than the latest npm version, **STOP** the current task and **ask the user** (report skill version, installed CLI version, and npm latest): > A newer version of bl is available (current: X.Y.Z, latest: A.B.C). Upgrade before continuing? - **Do NOT auto-upgrade silently** — the user decides. - - If the user agrees: run `bl update`, then continue. (`bl update` uses the detected install channel and, on success, also runs `bl skill update` to refresh already-installed skills; it does not install new skills. First-time install still uses `bl skill init`.) + - If the user agrees: run `bl update` (same Call attribution), then continue. (`bl update` uses the detected install channel and, on success, also runs `bl skill update` to refresh already-installed skills; it does not install new skills. First-time install still uses `bl skill init`.) + ```bash + bl update + ``` - If the user declines: continue with the current version and note it in the summary. - If `npm view` / `bl update` fails (offline, registry blocked, permission): continue with the current `bl` and tell the user it could not be updated. 6. Only proceed with the user's actual task after the above is resolved. diff --git a/skills/bailian-sandbox/SKILL.md b/skills/bailian-sandbox/SKILL.md index 86cdd80c1..0d190531f 100644 --- a/skills/bailian-sandbox/SKILL.md +++ b/skills/bailian-sandbox/SKILL.md @@ -15,7 +15,13 @@ description: >- # Bailian Sandbox (`bl sandbox`) -Before running `bl`, read the shared [bailian-protocol](../bailian-protocol/SKILL.md) for consent, high-risk confirmation, version checks, authentication, and error handling. If it is missing, stop execution and prompt the user to install the full family with `bl skill init`. +Before running `bl`, read the shared [bailian-protocol](../bailian-protocol/SKILL.md) for consent, high-risk confirmation, version checks, call attribution, authentication, and error handling. If it is missing, stop execution and prompt the user to install the full family with `bl skill init`. Examples in `reference/` stay bare; wrap with Call attribution when running. + +**Call attribution:** Examples below and in `reference/` stay bare `bl …`. When running a command, wrap it with [`bailian-protocol` → Call attribution](../bailian-protocol/SKILL.md#call-attribution-mandatory-for-every-agent-bl-command) (bash or PowerShell). One sample for this skill (`BAILIAN_SKILL_NAME=bailian-sandbox`; replace `codex` with this host). `bl auth` / `bl config` are owned by `bailian-cli`. + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-sandbox BAILIAN_SKILL_VERSION=2.5.0 bl sandbox list +``` ## Scope and setup diff --git a/skills/bailian-sandbox/assets/instance-connections.md b/skills/bailian-sandbox/assets/instance-connections.md index 2497cf516..081850c54 100644 --- a/skills/bailian-sandbox/assets/instance-connections.md +++ b/skills/bailian-sandbox/assets/instance-connections.md @@ -15,7 +15,7 @@ The browser paths below belong to the official browser image, not to every Sandb Reuse the requested instance. Create one only when creation is part of the user's request. Follow the shared protocol before running `bl`; use the generated command reference for lifecycle flags. -Capture the credential-bearing result in a private temporary directory: +Capture the credential-bearing result in a private temporary directory. Agents wrap the `bl` line with Call attribution (`bailian-sandbox`). ```bash umask 077 diff --git a/skills/bailian-web-search/SKILL.md b/skills/bailian-web-search/SKILL.md index 7dc93855c..44bf72e25 100644 --- a/skills/bailian-web-search/SKILL.md +++ b/skills/bailian-web-search/SKILL.md @@ -19,7 +19,13 @@ description: >- # Bailian web search routing (`bailian-web-search`) -**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Provider selection and consent, Version & updates (pre-flight checklist), Setup & auth, and CLI errors: report an issue. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** +**CRITICAL — Before executing, MUST read the shared protocol in [`../bailian-protocol/SKILL.md`](../bailian-protocol/SKILL.md): Provider selection and consent, Version & updates (pre-flight checklist), Call attribution (every Agent `bl` command), Setup & auth, and CLI errors: report an issue. If that protocol file is missing, stop and run `bl skill init`; do not guess auth/consent.** + +**Call attribution:** Examples below and in `reference/` stay bare `bl …`. When running a command, wrap it with [`bailian-protocol` → Call attribution](../bailian-protocol/SKILL.md#call-attribution-mandatory-for-every-agent-bl-command) (bash or PowerShell). One sample for this skill (`BAILIAN_SKILL_NAME=bailian-web-search`; replace `codex` with this host). `bl config` / `bl text chat` are owned by `bailian-cli`. + +```bash +BAILIAN_CALLER_TYPE=agent BAILIAN_CALLER_NAME=codex BAILIAN_SKILL_NAME=bailian-web-search BAILIAN_SKILL_VERSION=2.5.0 bl search web --query "阿里云百炼最新功能" +``` > **Scope:** model-aware routing for web search inside Agent-driven `bl` workflows — this skill owns the routing decision only. > Command flags / usage / examples: Read skill `bailian-cli` reference if installed; else `bl search web --help` / `bl text chat --help` — do not guess flags. @@ -32,7 +38,7 @@ Token Plan keys do not authorize Bailian MCP search — never route them to `bl ### Step 1 — Identify the active model identity -Run (no auth needed): +Wrap with Call attribution (`BAILIAN_SKILL_NAME=bailian-cli`). No auth needed: ```bash bl config show --output json