2026-05-28 18:37:07 +08:00
|
|
|
|
# 鉴权扩展
|
|
|
|
|
|
|
|
|
|
|
|
## 触发条件
|
|
|
|
|
|
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- 增加新的鉴权域或 token 来源(env / config / flag / 文件)
|
|
|
|
|
|
- 调整 API Key / Console token 解析优先级
|
|
|
|
|
|
- 改 `bl auth login` / `auth status` / `auth logout` 流程
|
|
|
|
|
|
- 改 runtime 对 command `auth` 的 gating 或 credential 注入
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
|
|
|
|
|
## 鉴权链路
|
|
|
|
|
|
|
|
|
|
|
|
```
|
2026-07-06 21:50:50 +08:00
|
|
|
|
argv flags ─┐
|
|
|
|
|
|
env var ──┼─ buildSources(flags) ─┐
|
|
|
|
|
|
config ──┘ │
|
|
|
|
|
|
├─ buildSettings(sources) → ctx.settings
|
|
|
|
|
|
│
|
|
|
|
|
|
├─ resolveApiKey(sources) → model-domain Client
|
2026-07-09 15:03:48 +08:00
|
|
|
|
├─ resolveConsole(sources) → console-domain Client
|
|
|
|
|
|
└─ resolveOpenApi(sources) → OpenAPI Client
|
2026-07-06 21:50:50 +08:00
|
|
|
|
|
|
|
|
|
|
defineCommand({ auth }) → runtime/authStage → ctx.client → command.run(ctx)
|
2026-05-28 18:37:07 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-06 21:50:50 +08:00
|
|
|
|
当前 command 鉴权域(`AuthRequirement`):
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `apiKey` — DashScope / OpenAI-compatible 模型域,用 API key 与 model base URL
|
2026-08-07 15:27:46 +08:00
|
|
|
|
- `console` — Bailian Console Gateway,用 console access token + region/site/switchAgent;`workspace_id` 是独立的 Settings 作用域,不属于 credential
|
2026-07-09 15:03:48 +08:00
|
|
|
|
- `openapi` — 阿里云 OpenAPI 签名域,用 AccessKey ID/Secret 调用 Token Plan 等 OpenAPI
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `none` — 本地命令、登录/配置类命令、无需 credential 的命令
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-09 15:03:48 +08:00
|
|
|
|
### 多凭证并存
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-09 15:03:48 +08:00
|
|
|
|
`~/.bailian/config.json` 可同时保存 `api_key`、`access_token` 与 `access_key_*`。登录任一种方式不得删除另一种:
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-08-28 13:10:56 +08:00
|
|
|
|
- `bl auth login --api-key ...` 更新 `api_key`;显式 `base_url` 会一并写入,所选命名 Profile 若命中内置套餐预设(当前为 `token-plan`),则在尚未保存 `base_url` 时补写预设地址,并把该预设的默认模型物化写入。API Key 落盘成功后,`api_key_capabilities` 保留已有项并追加当前 preset 中缺少的项,不自动删除任何已有能力;无 preset 的自定义 Profile 不做合并。登录仍不得删除其他鉴权域的凭证
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `bl auth login --console` 只更新 `access_token` 以及回调携带的 console 作用域字段
|
2026-08-07 15:27:46 +08:00
|
|
|
|
- `bl auth login --open-api ...` 更新 `access_key_id` / `access_key_secret`,同时会调用 OpenAPI 生成 CLI `access_token` 并一并写入;即一次 `--open-api` 登录同时产生 `openapi` 与 `console` 域凭证
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `bl auth logout --console` 只清 `access_token`
|
2026-07-16 11:05:59 +08:00
|
|
|
|
- `bl auth logout --open-api` 只清 `access_key_id` / `access_key_secret` / `security_token`
|
2026-07-17 15:57:23 +08:00
|
|
|
|
- `bl auth logout` 清 `api_key` + `base_url` + `access_token` + `access_key_*`
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-06 21:50:50 +08:00
|
|
|
|
解析分工:
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `resolveApiKey()` — `auth: "apiKey"` 命令;优先级 `--api-key` > `DASHSCOPE_API_KEY` > config `api_key`
|
2026-07-23 19:31:02 +08:00
|
|
|
|
- `resolveModelBaseUrl()` — model base URL;优先级 `--base-url` > `DASHSCOPE_BASE_URL` > config `base_url` > `REGIONS.cn`,返回前统一归一化为 URL origin(仅保留协议、host 和显式端口,去除 path、query、fragment)
|
2026-08-26 13:42:27 +08:00
|
|
|
|
- `--config` 只选择 config 文件 block,不提升该 block 的字段优先级。对 `auth: "apiKey"` 命令,runtime 会先按叶子命令路径检查所选 Profile 的 `api_key_capabilities`:
|
|
|
|
|
|
- `--api-key` / `--base-url` 或 `DASHSCOPE_API_KEY` / `DASHSCOPE_BASE_URL` 任一显式连接覆盖存在时,完全跳过自动降级,继续走统一的 flag > env > selected config file > 默认值
|
2026-08-26 17:10:00 +08:00
|
|
|
|
- 配置文件显式声明 `api_key_capabilities` 后,命中能力时保留所选 Profile,未命中时仅把 file-backed `api_key` / `base_url` 来源切到顶层 `default`,其他 Settings 仍来自所选 Profile
|
2026-08-28 13:10:56 +08:00
|
|
|
|
- 字段缺失时不启用降级,包括命中内置套餐预设的 Profile;preset 只在 API Key 登录落盘成功后物化写入,升级 preset 需要重新登录
|
2026-08-26 13:42:27 +08:00
|
|
|
|
- fallback 反馈写 stderr:text 模式输出本地化句子,`--output json` 输出两空格缩进的多行 `warning` 对象;若后续鉴权失败,warning 与多行 `error` 对象以空行分隔,stdout 仍只保留命令结果
|
2026-08-28 13:10:56 +08:00
|
|
|
|
- 显式 `auth login --config <name>` 在凭证落盘成功后自动激活目标 Profile;未传
|
2026-07-17 09:59:14 +08:00
|
|
|
|
`--config` 时继续写当前激活项,失败和 dry-run 不切换
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `resolveConsole()` — `auth: "console"` 命令;当前 token 来自 config `access_token`,region/site/switchAgent 来自 flag > config > 默认
|
2026-07-09 15:03:48 +08:00
|
|
|
|
- `resolveOpenApi()` — `auth: "openapi"` 命令;优先级 `--access-key-id/--access-key-secret` > `ALIBABA_CLOUD_ACCESS_KEY_ID/ALIBABA_CLOUD_ACCESS_KEY_SECRET` > config `access_key_*`。兼容读取旧字段 `openapi_access_key_*`,新写入只写短字段
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `describeAuthState()` — `auth status` / banner / telemetry 使用的只读快照
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-13 16:05:08 +08:00
|
|
|
|
命令不要直接解析 token、env 或 config。业务请求统一走 `ctx.client`;登录/配置命令通过 `ctx.authStore` / `ctx.configStore` 的窄接口操作落盘。
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-27 19:51:56 +08:00
|
|
|
|
### 例外:agent 命令的分层鉴权与 SDK 凭证内存注入
|
2026-07-22 13:40:28 +08:00
|
|
|
|
|
2026-07-27 21:33:36 +08:00
|
|
|
|
`bl managed-agent *` 按调用链分两层:
|
2026-07-24 14:50:30 +08:00
|
|
|
|
|
2026-07-27 21:33:36 +08:00
|
|
|
|
- **离线命令** — `init`、`validate`、`state list/show/rm`:`auth: "none"`,只读写本地文件,无需登录;引擎侧传 `credentials: "none"` 跳过凭证断言
|
2026-09-02 19:23:40 +08:00
|
|
|
|
- **联网命令** — `plan`、`apply`、`destroy`、`state import`、`skill-list`、全部 `session *`:统一声明 `auth: "apiKey"` 硬门禁,authStage 经 `resolveApiKey(sources)` 解析 Bailian 凭证(flag > env > active profile config),缺失报统一 AUTH;引擎层再断言 Bailian key 非空。例外:`plan --no-refresh` / `plan --dry-run` 传 `credentials: "none"` 并强制 `refresh: false`(不联网、不回写 state,不查 provider key),其中 `--dry-run` 连登录也不要求(authStage 的 dry-run 豁免),`--no-refresh` 仍需登录。
|
|
|
|
|
|
|
|
|
|
|
|
`bl managed-agent` 是 Bailian-only 产品入口:命令不暴露 `--provider`,`init` 只生成 `providers.bailian`,所有远端调用固定传 `provider: "bailian"`。`resolveAgentProjectConfig` 在创建 SDK runtime 前通过 `assertBailianOnlyProviders` 拒绝包含非 Bailian Provider 的手写配置;共享 `@openagentpack/sdk` 仍可保留多 Provider 能力。
|
2026-07-24 14:50:30 +08:00
|
|
|
|
|
2026-07-27 19:51:56 +08:00
|
|
|
|
凭证不以真实值写入 `process.env`,而是经 `packages/commands/src/commands/managed-agent/_engine/` 的**内存注入管道**(`resolveAgentProjectConfig`)注入 SDK,管道五步:
|
2026-07-24 14:50:30 +08:00
|
|
|
|
|
2026-09-02 19:23:40 +08:00
|
|
|
|
1. `prepareProviderEnv()` — 调用 SDK 的凭证 bootstrap,再把凭证类 env(`CREDENTIAL_ENV_KEYS`,含兼容别名)中仍为 undefined 的项占位为 `""`,使 agents.yaml 插值阶段能够完成并由 CLI 输出明确的 Bailian-only 配置错误
|
|
|
|
|
|
2. `resolveProjectConfig` — 完成插值;随后 `normalizeInterpolatedProviderBlocks()` 把插值为空导致的 YAML `null` 归一为 `""`,避免空 key 在 SDK zod 层提前报 "received null"
|
2026-07-27 19:51:56 +08:00
|
|
|
|
3. `injectProviderCredentials()` — 用 `ctx.client.exportApiCredential()`(lint 限定 `managed-agent/_engine/**` 可用)覆写内存 config 对象的 bailian 块:有凭证时 `api_key` 无条件覆写;`base_url`(拼 `/api/v1/agentstudio` 后缀,无凭证时用 client 默认域名补齐以满足 schema)/`workspace_id`(取 `settings.workspaceId`)仅在引用且为空时填充
|
|
|
|
|
|
4. `scrubCredentialEnv()` — 从 `process.env` 删除全部凭证变量(真实凭证此后只存于 config 对象 → provider adapter 实例内存,不驻留 env / 不被子进程继承)
|
2026-09-02 19:23:40 +08:00
|
|
|
|
5. `assertBailianOnlyProviders(providers)` — 拒绝非 Bailian Provider;随后 `assertProviderCredentials(providers)` 在 Bailian `api_key` 为空时给出 CLI 权威 `AUTH` 错误和登录 hint;离线命令传 `credentials: "none"` 跳过 key 断言,但仍执行 Bailian-only 配置校验
|
2026-07-27 19:51:56 +08:00
|
|
|
|
|
2026-09-02 19:23:40 +08:00
|
|
|
|
禁止命令层直接 `readConfigFile` 裸读凭证;Bailian 字段以 CLI 鉴权链为唯一信源。SDK bootstrap 期间读取到的兼容凭证变量也会在配置解析后统一清扫。
|
2026-07-22 13:40:28 +08:00
|
|
|
|
|
2026-05-28 18:37:07 +08:00
|
|
|
|
## 必查清单
|
|
|
|
|
|
|
|
|
|
|
|
### A. core 层(类型 + 解析)
|
|
|
|
|
|
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- [ ] `packages/core/src/types/command.ts`:
|
|
|
|
|
|
- 如新增鉴权域,扩展 `AuthRequirement`
|
|
|
|
|
|
- 更新 `credentialFlagDefs()` 暴露该域可见的 flag
|
|
|
|
|
|
- 必要时新增 `*_AUTH_FLAGS`
|
2026-08-07 15:27:46 +08:00
|
|
|
|
- `workspace_id` 是作用域字段而非 credential,不要把它放进 `ConsoleCredential`;读取方式按命令 `auth` 域区分:
|
|
|
|
|
|
- `auth: "console"` 命令通过 `CONSOLE_AUTH_FLAGS` 自动获得 `--workspace-id`,由 `buildSettings()` 解析到 `settings.workspaceId`,命令统一从 `settings.workspaceId` 读取
|
|
|
|
|
|
- `auth: "apiKey"`/`"openapi"`/`"none"` 命令如需 `--workspace-id`,必须自声明 flag;因它不会进入 credential/global flags,命令从 `ctx.flags.workspaceId` 读取(可回退到 `settings.workspaceId`)
|
2026-05-28 18:37:07 +08:00
|
|
|
|
- [ ] `packages/core/src/auth/types.ts`:
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- 新增 credential 类型 / source / scope 字段
|
2026-05-28 18:37:07 +08:00
|
|
|
|
- [ ] `packages/core/src/auth/resolver.ts`:
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- 新增或调整 resolver,保持优先级注释清晰
|
2026-07-09 17:31:49 +08:00
|
|
|
|
- 新增/调整 resolver hint 时保持产品无关,不要新增 `bl` / `kscli` 硬编码;当前遗留的 `bl auth login` hint 如被触碰,迁到 runtime `enhanceHint`
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- [ ] `packages/core/src/auth/store.ts`:
|
|
|
|
|
|
- 如果新方式需要持久化,扩展 `AuthStore` / `AuthPersistPatch`
|
2026-05-28 18:37:07 +08:00
|
|
|
|
- [ ] `packages/core/src/config/schema.ts`:
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `ConfigFile` 加 disk 字段(snake_case)
|
|
|
|
|
|
- `Settings` 加运行时字段(如果命令需要读取)
|
2026-05-28 18:37:07 +08:00
|
|
|
|
- [ ] `packages/core/src/config/loader.ts`:
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- `buildSources()` / `buildSettings()` 把 flag/env/file 读到正确层
|
|
|
|
|
|
|
|
|
|
|
|
### B. runtime 层
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] `packages/runtime/src/create-cli.ts`:
|
|
|
|
|
|
- parse flags 时纳入新的全局/凭证域 flag
|
|
|
|
|
|
- `globalFlags` 与 `ownFlags` 分流正确
|
|
|
|
|
|
- [ ] `packages/runtime/src/middleware.ts:authStage`:
|
|
|
|
|
|
- 根据 `command.auth` 解析 credential 并注入 `ctx.client`
|
|
|
|
|
|
- `settings.dryRun` 下是否允许缺 credential 的策略明确
|
|
|
|
|
|
- [ ] `packages/runtime/src/error-handler.ts`:
|
|
|
|
|
|
- AUTH hint 增强使用 `binName`,不要硬编码 `bl`
|
|
|
|
|
|
- URL 从 `packages/runtime/src/urls.ts` import
|
|
|
|
|
|
|
|
|
|
|
|
### C. command 层
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] `packages/commands/src/commands/auth/login.ts`:
|
|
|
|
|
|
- 新增/调整登录 flag 与流程
|
2026-07-13 16:05:08 +08:00
|
|
|
|
- 持久化只走 `ctx.authStore.login(...)`
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- [ ] `packages/commands/src/commands/auth/status.ts`:
|
2026-07-09 15:03:48 +08:00
|
|
|
|
- 分别显示 model / console / openapi 鉴权状态,并 mask token
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- [ ] `packages/commands/src/commands/auth/logout.ts`:
|
|
|
|
|
|
- 清理范围与双凭证并存规则一致
|
|
|
|
|
|
- [ ] 新的业务命令设置正确 `auth`:
|
|
|
|
|
|
- 模型域请求 → `auth: "apiKey"`
|
|
|
|
|
|
- Console Gateway → `auth: "console"`
|
2026-07-09 15:03:48 +08:00
|
|
|
|
- 阿里云 OpenAPI 请求 → `auth: "openapi"`
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- 本地/登录/配置 → `auth: "none"`
|
|
|
|
|
|
|
|
|
|
|
|
### D. 用户面文档
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-06-08 18:38:15 +08:00
|
|
|
|
- [ ] `README.md` / `README.zh.md` "Authentication" 段落
|
2026-08-04 15:31:07 +08:00
|
|
|
|
- [ ] 各 `skills/<skill>/reference/` 通过 `pnpm run sync:skill-assets` 重建
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-06 21:50:50 +08:00
|
|
|
|
### E. 测试
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
|
|
|
|
|
- [ ] `packages/cli/tests/e2e/auth.e2e.test.ts` 增加新方式的 happy / failure 路径
|
|
|
|
|
|
- [ ] mask token 的输出格式不变(避免泄漏)
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- [ ] 如调整 resolver 优先级,补 core/runtime 单测覆盖 flag > env > file
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
|
|
|
|
|
## 完成后自查
|
|
|
|
|
|
|
2026-08-07 15:27:46 +08:00
|
|
|
|
本仓库同时存在 `bl`(packages/cli) 与 `kscli`(packages/kscli) 两个入口,二者共享 core/runtime 鉴权链路,但暴露的命令不同。如果改动会影响两个入口共用的命令或错误提示,再分别验证它们各自实际暴露的路径;不要假设 `kscli` 也有 `bl auth *` 命令。
|
|
|
|
|
|
|
2026-05-28 18:37:07 +08:00
|
|
|
|
```sh
|
|
|
|
|
|
# 各种凭证组合
|
2026-07-09 17:31:49 +08:00
|
|
|
|
unset DASHSCOPE_API_KEY ALIBABA_CLOUD_ACCESS_KEY_ID ALIBABA_CLOUD_ACCESS_KEY_SECRET
|
2026-07-09 19:39:13 +08:00
|
|
|
|
HOME=/tmp/empty pnpm -F bailian-cli exec tsx src/main.ts auth status
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
2026-07-09 15:03:48 +08:00
|
|
|
|
# flag 注入(凭证域 flag 只在对应业务命令可见,auth status 不接收)
|
2026-07-09 19:39:13 +08:00
|
|
|
|
pnpm -F bailian-cli exec tsx src/main.ts text chat --message hi --api-key sk-xxx --dry-run
|
|
|
|
|
|
pnpm -F bailian-cli exec tsx src/main.ts token-plan list-seats --access-key-id ak-xxx --access-key-secret sec-xxx --dry-run
|
|
|
|
|
|
pnpm -F bailian-cli exec tsx src/main.ts auth login --open-api --access-key-id ak-xxx --access-key-secret sec-xxx --dry-run
|
2026-05-28 18:37:07 +08:00
|
|
|
|
|
|
|
|
|
|
# env 注入
|
2026-07-09 19:39:13 +08:00
|
|
|
|
DASHSCOPE_API_KEY=sk-xxx pnpm -F bailian-cli exec tsx src/main.ts auth status
|
|
|
|
|
|
ALIBABA_CLOUD_ACCESS_KEY_ID=ak-xxx ALIBABA_CLOUD_ACCESS_KEY_SECRET=sec-xxx pnpm -F bailian-cli exec tsx src/main.ts auth status
|
2026-07-06 21:50:50 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Console 登录/网关相关改动:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
2026-07-09 19:39:13 +08:00
|
|
|
|
pnpm -F bailian-cli exec tsx src/main.ts auth login --console
|
2026-08-07 15:27:46 +08:00
|
|
|
|
pnpm -F bailian-cli exec tsx src/main.ts usage stats --dry-run --output json --workspace-id ws-xxx
|
2026-05-28 18:37:07 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-08-07 15:27:46 +08:00
|
|
|
|
注意:`usage stats --dry-run` 仍会先校验 workspace,必须传入 `--workspace-id`(或 `BAILIAN_WORKSPACE_ID` / config `workspace_id`)。
|
|
|
|
|
|
|
2026-05-28 18:37:07 +08:00
|
|
|
|
## 常见漏点
|
|
|
|
|
|
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- ✗ 加了新 token 来源但忘了改 resolver 优先级,实际不生效
|
|
|
|
|
|
- ✗ `ConfigFile` / `Settings` 加字段但 `parseConfigFile` 或 `buildSettings` 没读
|
|
|
|
|
|
- ✗ `auth login` 写成功但 `auth status` 不识别(两边走的 storage path 不一致)
|
2026-05-28 18:37:07 +08:00
|
|
|
|
- ✗ token mask 显示完整 token,日志泄漏
|
2026-07-06 21:50:50 +08:00
|
|
|
|
- ✗ `auth: "console"` 命令误用 `apiKey` 域,config 只有 API key 时会把 `sk-...` 发到网关
|
2026-07-09 17:31:49 +08:00
|
|
|
|
- ✗ 新增 core resolver hint 时写死产品命令,导致 `kscli` 等入口提示错误
|