mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
feat(agent): 非bailian provider也走鉴权逻辑
This commit is contained in:
@@ -55,18 +55,18 @@ defineCommand({ auth }) → runtime/authStage → ctx.client → command.run(ctx
|
||||
|
||||
### 例外:agent 命令的分层鉴权与 SDK 凭证内存注入
|
||||
|
||||
`bl managed-agent *` 按调用链分两层,不再全命令硬门禁:
|
||||
`bl managed-agent *` 按调用链分两层:
|
||||
|
||||
- **离线命令** — `init`、`validate`、`state list/show/rm`:`auth: "none"`,只读写本地文件,无需登录;引擎侧传 `credentials: "none"` 跳过凭证断言(`plan --no-refresh` 与 `plan --dry-run` 同样传 `"none"` 并强制 `refresh: false`:不联网、不回写 state)
|
||||
- **provider-aware 命令** — `plan`(默认)、`apply`、`destroy`、`state import`、`skill-list`、全部 `session *`:仍声明 `auth: "apiKey"` 但加 `authOptional: true` —— authStage 照常经 `resolveApiKey(sources)` 解析 bailian 凭证(flag > env > active profile config)并注入 `ctx.client`,但缺失不在 authStage 抛;真正的门禁在引擎层 `assertProviderCredentials`,只校验本次运行涉及的 provider(`CredentialScope`:`--provider` / state 地址里的 provider / 配置默认 provider 链)。配了四个 provider 只跑 claude 时,缺 bailian key 不阻塞。
|
||||
- **离线命令** — `init`、`validate`、`state list/show/rm`:`auth: "none"`,只读写本地文件,无需登录;引擎侧传 `credentials: "none"` 跳过凭证断言
|
||||
- **联网命令** — `plan`、`apply`、`destroy`、`state import`、`skill-list`、全部 `session *`:统一声明 `auth: "apiKey"` 硬门禁 —— 无论目标 provider 是谁,authStage 都经 `resolveApiKey(sources)` 解析 bailian 凭证(flag > env > active profile config),缺失报统一 AUTH;引擎层 `assertProviderCredentials` 再对 agents.yaml 里**全部已声明 provider** 的空 key 拦截并给 provider 专属 hint。例外:`plan --no-refresh` / `plan --dry-run` 传 `credentials: "none"` 并强制 `refresh: false`(不联网、不回写 state,不查 provider key),其中 `--dry-run` 连登录也不要求(authStage 的 dry-run 豁免),`--no-refresh` 仍需登录。
|
||||
|
||||
凭证不以真实值写入 `process.env`,而是经 `packages/commands/src/commands/managed-agent/_engine/` 的**内存注入管道**(`resolveAgentProjectConfig`)注入 SDK,管道五步:
|
||||
|
||||
1. `prepareProviderEnv()` — 先 `bootstrapRuntimeCredentialsSync()`(SDK 把 `.env` / `~/.agents/config.json` 灌进 env,服务 claude/ark/qoder 等非 bailian provider),再把全部凭证类 env(`CREDENTIAL_ENV_KEYS`,含别名)中仍为 undefined 的占位为 `""`,使 agents.yaml 插值不因缺变量抛错
|
||||
2. `resolveProjectConfig` — 插值发生:bailian 插值拿到占位空串,claude/ark 拿到真实 env 值;随后 `normalizeInterpolatedProviderBlocks()` 把插值为空导致的 YAML `null` 归一为 `""`(避免范围外 provider 在 SDK zod 层报 "received null")
|
||||
2. `resolveProjectConfig` — 插值发生:bailian 插值拿到占位空串,claude/ark 拿到真实 env 值;随后 `normalizeInterpolatedProviderBlocks()` 把插值为空导致的 YAML `null` 归一为 `""`(避免离线命令下空 key 在 SDK zod 层报 "received null")
|
||||
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 / 不被子进程继承)
|
||||
5. `assertProviderCredentials(providers, required)` — 按 `CredentialScope` 算出的 `required` 范围校验:范围内 provider 的 `api_key` 为空 → CLI 权威 `AUTH` 错误 + provider 专属 hint(取代 SDK 原始插值/zod 报错);范围外 provider 允许空 key
|
||||
5. `assertProviderCredentials(providers)` — 任一已声明 provider 的 `api_key` 为空 → CLI 权威 `AUTH` 错误 + provider 专属 hint(取代 SDK 原始插值/zod 报错);离线命令传 `credentials: "none"` 整体跳过
|
||||
|
||||
`bl auth login` 仅管理 bailian(DashScope)凭证;claude/ark/qoder 的 key 从 env(shell / `.env` / `~/.agents/config.json`)经插值进入 config 对象,同样被清扫。禁止命令层直接 `readConfigFile` 裸读凭证;bailian 字段以 CLI 鉴权链为唯一信源。
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
version: "1"
|
||||
|
||||
providers:
|
||||
bailian:
|
||||
# bl auth login --api-key <key> sets DASHSCOPE_API_KEY; --agentstudio-base-url <url> sets BAILIAN_BASE_URL
|
||||
api_key: ${DASHSCOPE_API_KEY}
|
||||
base_url: ${BAILIAN_BASE_URL}
|
||||
|
||||
defaults:
|
||||
provider: bailian
|
||||
|
||||
environments:
|
||||
dev:
|
||||
config:
|
||||
type: cloud
|
||||
networking:
|
||||
type: unrestricted
|
||||
|
||||
agents:
|
||||
assistant:
|
||||
description: "General-purpose assistant"
|
||||
model: qwen3.7-max
|
||||
instructions: |
|
||||
You are a helpful assistant.
|
||||
environment: dev
|
||||
tools:
|
||||
builtin: [bash, read, glob, grep]
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
injectProviderCredentials,
|
||||
normalizeInterpolatedProviderBlocks,
|
||||
prepareProviderEnv,
|
||||
resolveTargetProviderNames,
|
||||
scrubCredentialEnv,
|
||||
} from "./credentials.ts";
|
||||
import { loadFileState } from "./file-state-manager.ts";
|
||||
@@ -20,15 +19,12 @@ import { type HostContext, installSdkTransport } from "./transport.ts";
|
||||
export { CREDENTIALS_NOTE, OFFLINE_NOTE } from "./credentials.ts";
|
||||
|
||||
/**
|
||||
* Which providers this run requires a non-empty key for:
|
||||
* - "targets" (default) — the run's target providers per the config's
|
||||
* default provider chain (mirrors the SDK's plan/apply targeting)
|
||||
* Whether this run requires provider keys:
|
||||
* - "all" (default) — online command: every provider declared in agents.yaml
|
||||
* must have a non-empty key after injection
|
||||
* - "none" — offline command (local config/state only), skip the check
|
||||
* - "all" — every configured provider (`--provider all`)
|
||||
* - any other name — the run was narrowed to that provider
|
||||
* (`--provider <name>` / a provider-qualified state address)
|
||||
*/
|
||||
export type CredentialScope = "targets" | "none" | "all" | (string & {});
|
||||
export type CredentialScope = "all" | "none";
|
||||
|
||||
interface AgentConfigOptions {
|
||||
resolveEnv?: boolean;
|
||||
@@ -46,8 +42,8 @@ interface AgentConfigOptions {
|
||||
* 3. override the bailian block with the CLI auth chain's credential (in-memory)
|
||||
* 4. scrub all credential vars from process.env (real values now live only in
|
||||
* the config object → provider adapters, never the environment)
|
||||
* 5. fail with a CLI-authoritative AUTH error if a provider within this run's
|
||||
* {@link CredentialScope} has an empty key (offline commands pass "none")
|
||||
* 5. fail with a CLI-authoritative AUTH error if any provider's key is empty
|
||||
* (offline commands pass `credentials: "none"` to skip the check)
|
||||
*/
|
||||
export async function resolveAgentProjectConfig(
|
||||
host: CredentialHost,
|
||||
@@ -59,16 +55,8 @@ export async function resolveAgentProjectConfig(
|
||||
normalizeInterpolatedProviderBlocks(resolved.config.providers);
|
||||
injectProviderCredentials(resolved.config.providers, host);
|
||||
scrubCredentialEnv();
|
||||
const scope = options.credentials ?? "targets";
|
||||
if (scope !== "none") {
|
||||
assertProviderCredentials(
|
||||
resolved.config.providers,
|
||||
scope === "targets"
|
||||
? resolveTargetProviderNames(resolved.config)
|
||||
: scope === "all"
|
||||
? Object.keys(resolved.config.providers)
|
||||
: [scope],
|
||||
);
|
||||
if ((options.credentials ?? "all") !== "none") {
|
||||
assertProviderCredentials(resolved.config.providers);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ export interface CredentialHost {
|
||||
export const CREDENTIALS_NOTE = [
|
||||
"Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).",
|
||||
"Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.",
|
||||
"Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.",
|
||||
"Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.",
|
||||
];
|
||||
|
||||
@@ -89,10 +88,10 @@ export function prepareProviderEnv(): void {
|
||||
* `base_url` carries {@link AGENTSTUDIO_API_PATH} because the SDK appends resource
|
||||
* paths onto it verbatim; a value already ending in the suffix is left as-is.
|
||||
* It is filled even without a credential — `client.baseUrl` is readable
|
||||
* credential-less (defaults to the CLI's model-domain base URL) — so offline /
|
||||
* out-of-scope runs still satisfy the SDK's "workspace_id or base_url" schema.
|
||||
* With no credential the `api_key` is left untouched: an in-scope empty key is
|
||||
* rejected by {@link assertProviderCredentials}, out-of-scope ones may stay empty.
|
||||
* credential-less (defaults to the CLI's model-domain base URL) — so offline
|
||||
* commands (which skip the credential assert) still satisfy the SDK's
|
||||
* "workspace_id or base_url" schema. With no credential the `api_key` is left
|
||||
* untouched: online commands reject it via {@link assertProviderCredentials}.
|
||||
*/
|
||||
export function injectProviderCredentials(
|
||||
providers: Record<string, unknown>,
|
||||
@@ -133,10 +132,11 @@ export function scrubCredentialEnv(): void {
|
||||
/**
|
||||
* The SDK interpolates `${VAR}` into the raw YAML text, so an empty env var
|
||||
* leaves `api_key:` with nothing after it — YAML parses that as null. Normalize
|
||||
* every null provider field back to "" so the pipeline stays uniform: an empty
|
||||
* api_key is caught by {@link assertProviderCredentials} when the provider is
|
||||
* in scope, and out-of-scope blocks still satisfy the SDK's string schemas
|
||||
* instead of failing zod with "received null" before the run even starts.
|
||||
* every null provider field back to "" so the pipeline stays uniform: for
|
||||
* online commands an empty api_key is caught by {@link
|
||||
* assertProviderCredentials}; for offline commands (which skip the assert) the
|
||||
* blocks still satisfy the SDK's string schemas instead of failing zod with
|
||||
* "received null" before the run even starts.
|
||||
*/
|
||||
export function normalizeInterpolatedProviderBlocks(providers: Record<string, unknown>): void {
|
||||
for (const raw of Object.values(providers)) {
|
||||
@@ -149,37 +149,15 @@ export function normalizeInterpolatedProviderBlocks(providers: Record<string, un
|
||||
}
|
||||
|
||||
/**
|
||||
* The providers a run targets when no explicit `--provider` narrows it: the
|
||||
* config's default provider, or every configured provider when the default is
|
||||
* absent or "all". Mirrors the SDK's config-based `resolveTargetProviders`
|
||||
* (not exported from the SDK's public surface).
|
||||
*/
|
||||
export function resolveTargetProviderNames(config: {
|
||||
providers: Record<string, unknown>;
|
||||
defaults?: { provider?: string };
|
||||
}): string[] {
|
||||
const defaultProvider = config.defaults?.provider;
|
||||
if (!defaultProvider || defaultProvider === "all") return Object.keys(config.providers);
|
||||
return [defaultProvider];
|
||||
}
|
||||
|
||||
/**
|
||||
* After injection, fail with a CLI-authoritative AUTH error if a required
|
||||
* After injection, fail with a CLI-authoritative AUTH error if any configured
|
||||
* provider's `api_key` resolved empty (missing env var, or no bl login for
|
||||
* bailian). Replaces the SDK's raw `Environment variable '...' is not set` /
|
||||
* zod config error with a clean message plus a provider-specific hint.
|
||||
* `required` limits the check to the providers this run actually involves
|
||||
* (← --provider / state address / config default chain); providers outside
|
||||
* that scope may keep empty keys — a project stays runnable per provider.
|
||||
* Names without a matching config block are skipped: "provider not
|
||||
* configured" is the engine's error to raise, not a credential problem.
|
||||
* zod config error with a clean message plus a provider-specific hint. Validates
|
||||
* every declared provider, so a project is only runnable once all its providers'
|
||||
* keys are available; offline commands skip the check entirely.
|
||||
*/
|
||||
export function assertProviderCredentials(
|
||||
providers: Record<string, unknown>,
|
||||
required?: readonly string[],
|
||||
): void {
|
||||
for (const name of required ?? Object.keys(providers)) {
|
||||
const raw = providers[name];
|
||||
export function assertProviderCredentials(providers: Record<string, unknown>): void {
|
||||
for (const [name, raw] of Object.entries(providers)) {
|
||||
if (!raw || typeof raw !== "object") continue;
|
||||
const block = raw as Record<string, unknown>;
|
||||
if (!("api_key" in block)) continue;
|
||||
|
||||
@@ -46,8 +46,6 @@ const APPLY_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Apply planned changes to create/update/delete agent resources",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the providers this apply targets need credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "[--file <path>] [--provider <name>] [--yes] [--concurrency <n>]",
|
||||
flags: APPLY_FLAGS,
|
||||
exampleArgs: ["--yes", "--provider bailian --yes"],
|
||||
@@ -75,9 +73,7 @@ export default defineCommand({
|
||||
|
||||
const planned = await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
assertProviderConfigured(runtime, flags.provider);
|
||||
return planProjectContext(runtime, {
|
||||
provider: flags.provider,
|
||||
|
||||
@@ -31,8 +31,6 @@ const DESTROY_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Destroy all managed agent resources tracked in state",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the run's target providers need credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "[--file <path>] [--yes] [--cascade]",
|
||||
flags: DESTROY_FLAGS,
|
||||
exampleArgs: ["--yes", "--yes --cascade"],
|
||||
@@ -56,9 +54,7 @@ export default defineCommand({
|
||||
|
||||
const planned = await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
return planDestroyProjectContext(runtime);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -41,16 +41,12 @@ const PLAN_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Show what changes would be applied to agent infrastructure",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: --no-refresh / --dry-run plan fully offline; a
|
||||
// refreshing run only needs credentials for the providers it targets
|
||||
// (see CredentialScope).
|
||||
authOptional: true,
|
||||
usageArgs: "[--file <path>] [--provider <name>] [--no-refresh] [--refresh-only]",
|
||||
flags: PLAN_FLAGS,
|
||||
exampleArgs: ["", "--provider bailian", "--no-refresh"],
|
||||
notes: [
|
||||
...CREDENTIALS_NOTE,
|
||||
"--no-refresh and --dry-run plan offline from local config and state: no credentials, no remote requests, no state writes.",
|
||||
"--no-refresh and --dry-run plan offline from local config and state: no remote requests, no state writes, provider keys are not checked.",
|
||||
],
|
||||
async run(ctx) {
|
||||
const { settings, flags } = ctx;
|
||||
@@ -58,12 +54,14 @@ export default defineCommand({
|
||||
const file = flags.file ?? "agents.yaml";
|
||||
// Offline mode never talks to a provider and never saves refreshed state:
|
||||
// --no-refresh by explicit request, --dry-run by contract (read-only run).
|
||||
// Provider keys are skipped then; the bl login gate (auth: "apiKey") still
|
||||
// applies except under --dry-run (authStage's dry-run exemption).
|
||||
const offline = Boolean(flags.noRefresh) || settings.dryRun;
|
||||
|
||||
const planned = await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: offline ? "none" : (flags.provider ?? "targets"),
|
||||
credentials: offline ? "none" : "all",
|
||||
});
|
||||
assertProviderConfigured(runtime, flags.provider);
|
||||
return planProjectContext(runtime, {
|
||||
|
||||
@@ -43,8 +43,6 @@ const SESSION_CREATE_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Create a new session for an agent",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the session's provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "[--agent <name>] [--environment <name>] [--title <title>] [--file <path>]",
|
||||
flags: SESSION_CREATE_FLAGS,
|
||||
exampleArgs: ["", "--agent assistant", "--agent assistant --title 'debug run'"],
|
||||
@@ -74,9 +72,7 @@ export default defineCommand({
|
||||
|
||||
const run = await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
return createSessionForAgent(runtime, {
|
||||
agent: flags.agent,
|
||||
provider: flags.provider,
|
||||
|
||||
@@ -27,8 +27,6 @@ const SESSION_DELETE_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Delete a session",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the session's provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "--session-id <id> [--provider <name>] [--file <path>]",
|
||||
flags: SESSION_DELETE_FLAGS,
|
||||
exampleArgs: ["--session-id sess_abc123"],
|
||||
@@ -52,9 +50,7 @@ export default defineCommand({
|
||||
|
||||
await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
await deleteSession(runtime, flags.sessionId, flags.provider);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -38,8 +38,6 @@ const SESSION_EVENTS_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "List event history for a session",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the session's provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "--session-id <id> [--limit <n>] [--all] [--file <path>]",
|
||||
flags: SESSION_EVENTS_FLAGS,
|
||||
exampleArgs: ["--session-id sess_abc123", "--session-id sess_abc123 --all"],
|
||||
@@ -51,9 +49,7 @@ export default defineCommand({
|
||||
|
||||
const { items: events, hasMore } = await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
return fetchAllPages(async (page) => {
|
||||
const result = await listSessionEvents(runtime, flags.sessionId, {
|
||||
provider: flags.provider,
|
||||
|
||||
@@ -27,8 +27,6 @@ const SESSION_GET_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Get details of a session",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the session's provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "--session-id <id> [--provider <name>] [--file <path>]",
|
||||
flags: SESSION_GET_FLAGS,
|
||||
exampleArgs: ["--session-id sess_abc123"],
|
||||
@@ -40,9 +38,7 @@ export default defineCommand({
|
||||
|
||||
const session = await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
return getSession(runtime, flags.sessionId, flags.provider);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -31,8 +31,6 @@ const SESSION_LIST_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "List sessions from the provider",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the session's provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "[--agent <name>] [--all] [--provider <name>] [--file <path>]",
|
||||
flags: SESSION_LIST_FLAGS,
|
||||
exampleArgs: ["", "--agent assistant", "--all"],
|
||||
@@ -44,9 +42,7 @@ export default defineCommand({
|
||||
|
||||
const { items: summaries, hasMore } = await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
return fetchAllPages(async (page) => {
|
||||
const result = await listSessionSummaries(runtime, {
|
||||
agent: flags.agent,
|
||||
|
||||
@@ -57,8 +57,6 @@ const SESSION_RUN_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Create a session, send a message, and stream the response",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the session's provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "--prompt <text> [--agent <name>] [--no-stream] [--file <path>]",
|
||||
flags: SESSION_RUN_FLAGS,
|
||||
exampleArgs: ['--prompt "hello"', '--agent assistant --prompt "summarize this repo"'],
|
||||
@@ -103,9 +101,7 @@ export default defineCommand({
|
||||
|
||||
await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
if (flags.noStream) {
|
||||
const run = await startSessionRunPolling(runtime, flags.prompt, runOptions);
|
||||
if (!asJson) process.stderr.write(`Session created: ${run.session.id}\n`);
|
||||
|
||||
@@ -38,8 +38,6 @@ const SESSION_SEND_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Send a message to an existing session and stream the response",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the session's provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "--session-id <id> --message <text> [--no-stream] [--file <path>]",
|
||||
flags: SESSION_SEND_FLAGS,
|
||||
exampleArgs: ['--session-id sess_abc123 --message "continue"'],
|
||||
@@ -68,9 +66,7 @@ export default defineCommand({
|
||||
|
||||
await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
if (flags.noStream) {
|
||||
const result = await sendSessionMessagePolling(runtime, flags.sessionId, flags.message, {
|
||||
provider: flags.provider,
|
||||
|
||||
@@ -30,8 +30,6 @@ const SKILL_LIST_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "List skills from the provider's skill catalog",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the resolved catalog provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs: "[--source custom|official|all] [--provider <name>] [--file <path>]",
|
||||
flags: SKILL_LIST_FLAGS,
|
||||
exampleArgs: [
|
||||
@@ -58,9 +56,7 @@ export default defineCommand({
|
||||
|
||||
const skills = await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: flags.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
if (source !== "all") {
|
||||
return listSkills(runtime, { provider: flags.provider, source });
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ const STATE_IMPORT_FLAGS = {
|
||||
export default defineCommand({
|
||||
description: "Import an existing remote resource into agents state",
|
||||
auth: "apiKey",
|
||||
// Provider-aware gate: only the address's provider needs credentials.
|
||||
authOptional: true,
|
||||
usageArgs:
|
||||
"--address <provider.type.name> --remote-id <id> [--resource-version <n>] [--file <path>]",
|
||||
flags: STATE_IMPORT_FLAGS,
|
||||
@@ -64,13 +62,11 @@ export default defineCommand({
|
||||
|
||||
await withAgentErrors(() =>
|
||||
withStdoutProtected(async () => {
|
||||
// Parse first: the address names the one provider this import touches.
|
||||
// Parse first so a malformed address fails fast, before any config I/O.
|
||||
const parsed = parseStateAddress(flags.address, {
|
||||
requireProvider: true,
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file, {
|
||||
credentials: parsed.provider ?? "targets",
|
||||
});
|
||||
const runtime = await buildAgentRuntime(ctx, file);
|
||||
await importResource(runtime, parsed, flags.remoteId, {
|
||||
resourceVersion: flags.resourceVersion,
|
||||
});
|
||||
|
||||
@@ -13,15 +13,14 @@ import {
|
||||
type CredentialHost,
|
||||
injectProviderCredentials,
|
||||
prepareProviderEnv,
|
||||
resolveTargetProviderNames,
|
||||
scrubCredentialEnv,
|
||||
} from "../src/commands/managed-agent/_engine/credentials.ts";
|
||||
|
||||
/**
|
||||
* 凭证内存注入管道:injectProviderCredentials 把 authStage 解析进 Client 的凭证
|
||||
* 权威覆写 bailian 配置块(不落 env),scrubCredentialEnv 清空所有凭证 env,
|
||||
* assertProviderCredentials 按本次运行涉及的 provider 范围对空 key 给 CLI 权威
|
||||
* AUTH 错误。用快照隔离凭证 env。
|
||||
* assertProviderCredentials 对任一已声明 provider 的空 key 给 CLI 权威 AUTH
|
||||
* 错误(离线命令跳过断言)。用快照隔离凭证 env。
|
||||
*/
|
||||
const TRACKED_ENV = [
|
||||
"DASHSCOPE_API_KEY",
|
||||
@@ -192,33 +191,6 @@ test("assert:bailian key 为空(dry-run/未登录)抛 AUTH 且 hint 指向 bl au
|
||||
expect(err.hint).toContain("bl auth login");
|
||||
});
|
||||
|
||||
test("assert:required 限定范围后,范围外 provider 的空 key 不拦截", () => {
|
||||
const providers = {
|
||||
bailian: { api_key: "" },
|
||||
claude: { api_key: "sk-ant" },
|
||||
};
|
||||
// 本次只涉及 claude(如 --provider claude):bailian 未登录不应阻塞
|
||||
expect(() => assertProviderCredentials(providers, ["claude"])).not.toThrow();
|
||||
// 反向:范围内的空 key 仍拦截
|
||||
expect(() => assertProviderCredentials(providers, ["bailian"])).toThrow();
|
||||
});
|
||||
|
||||
test("assert:required 里未配置的 provider 名被跳过(由引擎报未配置错误)", () => {
|
||||
expect(() => assertProviderCredentials({ bailian: { api_key: "" } }, ["qoder"])).not.toThrow();
|
||||
});
|
||||
|
||||
test("targets:默认 provider 链镜像 SDK —— default 为单个时只涉及它,缺失/all 时为全部", () => {
|
||||
const providers = { bailian: {}, claude: {} };
|
||||
expect(resolveTargetProviderNames({ providers, defaults: { provider: "claude" } })).toEqual([
|
||||
"claude",
|
||||
]);
|
||||
expect(resolveTargetProviderNames({ providers })).toEqual(["bailian", "claude"]);
|
||||
expect(resolveTargetProviderNames({ providers, defaults: { provider: "all" } })).toEqual([
|
||||
"bailian",
|
||||
"claude",
|
||||
]);
|
||||
});
|
||||
|
||||
test("scrub:所有凭证 env 变量被删除", () => {
|
||||
process.env.DASHSCOPE_API_KEY = "x";
|
||||
process.env.ANTHROPIC_API_KEY = "y";
|
||||
|
||||
@@ -9,8 +9,8 @@ import { MANAGED_AGENT_ROUTES } from "./topic-routes.ts";
|
||||
/**
|
||||
* managed-agent 凭证链 e2e:验证 bl 自有配置体系(config 写入 / 命名 Profile /
|
||||
* logout)与错误映射如何流入 SDK 引擎。全部离线:凭证门禁用 `managed-agent plan`
|
||||
* 验证(provider-aware:空 state 不发网络请求,但仍按目标 provider 校验凭证);
|
||||
* `validate` / `state list` / `plan --no-refresh` 属离线命令,无凭证也必须可用。
|
||||
* 验证(空 state 不发网络请求,但 auth: "apiKey" 硬门禁 + 引擎全量 provider key
|
||||
* 断言照常生效);`validate` / `state list` 属离线命令,无凭证也必须可用。
|
||||
* 配置一律通过 BAILIAN_CONFIG_DIR 指向临时目录,绝不触碰真实用户配置。
|
||||
*/
|
||||
|
||||
@@ -47,7 +47,7 @@ function validateArgs(file: string): string[] {
|
||||
return ["managed-agent", "validate", "--file", file, "--quiet"];
|
||||
}
|
||||
|
||||
/** plan 是凭证门禁命令:空 state 下不发网络,但仍按目标 provider 校验凭证。 */
|
||||
/** plan 是凭证门禁命令:空 state 下不发网络,但 authStage + 引擎断言照常生效。 */
|
||||
function planArgs(file: string): string[] {
|
||||
return ["managed-agent", "plan", "--file", file, "--quiet"];
|
||||
}
|
||||
@@ -184,7 +184,7 @@ describe("e2e: managed-agent 凭证链(config 写入 / Profile / logout / 错
|
||||
});
|
||||
});
|
||||
|
||||
describe("e2e: managed-agent 鉴权分层(离线命令免登录 / provider-aware 按需校验)", () => {
|
||||
describe("e2e: managed-agent 鉴权分层(离线命令免登录 / 联网命令统一 apiKey 门禁)", () => {
|
||||
test("validate 无任何凭证也离线通过 (0)", async () => {
|
||||
const env = makeConfigEnv({});
|
||||
const { stderr, exitCode } = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
|
||||
@@ -203,17 +203,33 @@ describe("e2e: managed-agent 鉴权分层(离线命令免登录 / provider-awa
|
||||
expect(Array.isArray(data.resources)).toBe(true);
|
||||
});
|
||||
|
||||
test("plan --no-refresh 无任何凭证也离线通过 (0)", async () => {
|
||||
test("plan --no-refresh 无登录时仍被 apiKey 硬门禁拦住 (3)", async () => {
|
||||
const env = makeConfigEnv({});
|
||||
const { stderr, exitCode } = await runCommandE2e(
|
||||
ROUTES,
|
||||
[...planArgs(AGENTS_YAML), "--no-refresh"],
|
||||
env,
|
||||
);
|
||||
expect(exitCode).toBe(3);
|
||||
expect(stderr).toMatch(/auth login|API key/i);
|
||||
});
|
||||
|
||||
test("已登录 bailian 时,多 provider 配置下 plan --no-refresh 离线通过,不查其他 provider key (0)", async () => {
|
||||
const env = {
|
||||
...makeConfigEnv({ api_key: "sk-e2e-no-refresh" }),
|
||||
...isolatedAgentsConfigEnv(),
|
||||
ANTHROPIC_API_KEY: "",
|
||||
CLAUDE_API_KEY: "",
|
||||
};
|
||||
const { stderr, exitCode } = await runCommandE2e(
|
||||
ROUTES,
|
||||
[...planArgs(AGENTS_YAML_MULTI), "--no-refresh"],
|
||||
env,
|
||||
);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
});
|
||||
|
||||
test("多 provider 下 plan --provider claude 只需 claude 凭证,bailian 未登录不阻塞 (0)", async () => {
|
||||
test("统一登录门禁:只配 claude key 未登录 bailian 时,plan --provider claude 仍报 AUTH (3)", async () => {
|
||||
const env = {
|
||||
...makeConfigEnv({}),
|
||||
...isolatedAgentsConfigEnv(),
|
||||
@@ -225,10 +241,11 @@ describe("e2e: managed-agent 鉴权分层(离线命令免登录 / provider-awa
|
||||
[...planArgs(AGENTS_YAML_MULTI), "--provider", "claude"],
|
||||
env,
|
||||
);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
expect(exitCode).toBe(3);
|
||||
expect(stderr).toMatch(/auth login|API key/i);
|
||||
});
|
||||
|
||||
test("plan --provider claude 缺 claude key 时报 AUTH (3),hint 指向 ANTHROPIC_API_KEY", async () => {
|
||||
test("已登录但缺 claude key 时,全量断言拦住并给 ANTHROPIC_API_KEY hint (3)", async () => {
|
||||
const env = {
|
||||
...makeConfigEnv({ api_key: "sk-e2e-bailian-present" }),
|
||||
...isolatedAgentsConfigEnv(),
|
||||
|
||||
@@ -6,7 +6,7 @@ import { MANAGED_AGENT_ROUTES } from "./topic-routes.ts";
|
||||
* managed-agent:help / 缺参不依赖密钥;所有 mutation 命令的 --dry-run
|
||||
* 必须在构建 SDK runtime(凭证注入 / 联网 / 写盘)之前短路,因此同样不需要密钥。
|
||||
* 鉴权分层:离线命令(init/validate/state list|show|rm)auth: "none";联网命令
|
||||
* provider-aware,只校验本次涉及的 provider(见 managed-agent-auth-chain e2e)。
|
||||
* 统一 auth: "apiKey" 硬门禁(见 managed-agent-auth-chain e2e)。
|
||||
* 真实集成(apply/destroy/session 流程)依赖工作区内的 agents.yaml 与远端资源,
|
||||
* 属批量场景,暂仅覆盖 dry-run 契约。
|
||||
*/
|
||||
@@ -69,17 +69,21 @@ describe("e2e: managed-agent", () => {
|
||||
});
|
||||
|
||||
test("managed-agent skill-list --source all 通过参数校验(缺配置文件时才失败)", async () => {
|
||||
// provider-aware 鉴权不再前置硬门禁:无需注入假 key,命令在配置加载阶段
|
||||
// 因文件缺失短路,不产生任何网络请求。
|
||||
const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [
|
||||
"managed-agent",
|
||||
"skill-list",
|
||||
"--source",
|
||||
"all",
|
||||
"--file",
|
||||
"agents.e2e-missing.yaml",
|
||||
"--quiet",
|
||||
]);
|
||||
// auth: "apiKey" 的凭证解析先于 run() 执行;注入假 key 让用例不依赖环境凭证,
|
||||
// 命令仍会在配置加载阶段因文件缺失短路,不产生任何网络请求。
|
||||
const { stderr, exitCode } = await runCommandE2e(
|
||||
MANAGED_AGENT_ROUTES,
|
||||
[
|
||||
"managed-agent",
|
||||
"skill-list",
|
||||
"--source",
|
||||
"all",
|
||||
"--file",
|
||||
"agents.e2e-missing.yaml",
|
||||
"--quiet",
|
||||
],
|
||||
{ DASHSCOPE_API_KEY: "sk-e2e-skill-list" },
|
||||
);
|
||||
// all 是合法值:不应报 --source 用法错误,而是走到配置加载后因文件缺失退出
|
||||
expect(exitCode).toBe(2);
|
||||
expect(stderr).not.toMatch(/--source must be one of/i);
|
||||
|
||||
@@ -207,14 +207,6 @@ export interface Command<F extends FlagsDef = FlagsDef> {
|
||||
description: string;
|
||||
/** Credential this command requires. See {@link AuthRequirement}. */
|
||||
auth: AuthRequirement;
|
||||
/**
|
||||
* Soften the auth gate: authStage still resolves the `auth` domain's
|
||||
* credential into `ctx.client` when available, but a missing credential no
|
||||
* longer fails before `run`. For commands that enforce their own scoped
|
||||
* credential requirements (e.g. managed-agent commands, where a run may only
|
||||
* involve third-party providers and must not be blocked on a Bailian key).
|
||||
*/
|
||||
authOptional?: boolean;
|
||||
/** Usage line arg portion, e.g. "--prompt <text> [flags]". Manually written. */
|
||||
usageArgs?: string;
|
||||
/** Example arg strings (without the `<bin> <path>` prefix). */
|
||||
|
||||
@@ -75,8 +75,6 @@ export function compose(stack: Middleware[]): (ctx: RunContext) => Promise<void>
|
||||
* Bake the credential for the command's declared `auth` into `ctx.client`, and
|
||||
* gate: no credential → throw before the command runs. dry-run 例外:凭证解析失败
|
||||
* 不抛(dry-run 只打印请求,无需凭证;console 的 dry-run 展示读 settings.console*)。
|
||||
* `authOptional` 例外:凭证可用则注入,缺失不在此处抛 —— 命令自行按实际涉及范围
|
||||
* 校验(如 managed-agent 只校验本次运行涉及的 provider)。
|
||||
* `auth: "none"` commands keep a credential-less client.
|
||||
*/
|
||||
export const authStage: Middleware = async (ctx, next) => {
|
||||
@@ -86,13 +84,12 @@ export const authStage: Middleware = async (ctx, next) => {
|
||||
settings,
|
||||
baseUrl: resolveModelBaseUrl(sources),
|
||||
};
|
||||
const tolerateMissing = settings.dryRun || command.authOptional === true;
|
||||
if (command.auth === "apiKey") {
|
||||
let cred: ApiKeyCredential | undefined;
|
||||
try {
|
||||
cred = resolveApiKey(sources);
|
||||
} catch (err) {
|
||||
if (!tolerateMissing) throw err;
|
||||
if (!settings.dryRun) throw err;
|
||||
}
|
||||
ctx.client = new Client({ ...base, apiCred: cred });
|
||||
if (cred) maybeShowStatusBar(settings, cred.token, cred);
|
||||
@@ -101,7 +98,7 @@ export const authStage: Middleware = async (ctx, next) => {
|
||||
try {
|
||||
cred = resolveConsole(sources);
|
||||
} catch (err) {
|
||||
if (!tolerateMissing) throw err;
|
||||
if (!settings.dryRun) throw err;
|
||||
}
|
||||
if (cred) ctx.client = new Client({ ...base, consoleCred: cred });
|
||||
} else if (command.auth === "openapi") {
|
||||
@@ -109,7 +106,7 @@ export const authStage: Middleware = async (ctx, next) => {
|
||||
try {
|
||||
cred = resolveOpenApi(sources);
|
||||
} catch (err) {
|
||||
if (!tolerateMissing) throw err;
|
||||
if (!settings.dryRun) throw err;
|
||||
}
|
||||
ctx.client = new Client({ ...base, openApiCred: cred });
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ Index: [index.md](index.md)
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
@@ -88,7 +87,6 @@ bl managed-agent apply --provider bailian --yes
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
@@ -155,9 +153,8 @@ bl managed-agent init --provider all
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
- --no-refresh and --dry-run plan offline from local config and state: no credentials, no remote requests, no state writes.
|
||||
- --no-refresh and --dry-run plan offline from local config and state: no remote requests, no state writes, provider keys are not checked.
|
||||
|
||||
#### Examples
|
||||
|
||||
@@ -199,7 +196,6 @@ bl managed-agent plan --no-refresh
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
@@ -238,7 +234,6 @@ bl managed-agent session create --agent assistant --title 'debug run'
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
@@ -271,7 +266,6 @@ bl managed-agent session delete --session-id sess_abc123
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
@@ -306,7 +300,6 @@ bl managed-agent session events --session-id sess_abc123 --all
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
@@ -338,7 +331,6 @@ bl managed-agent session get --session-id sess_abc123
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
@@ -383,7 +375,6 @@ bl managed-agent session list --all
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
- --output json emits one envelope: { session_id, provider, agent, events } — read session_id to chain `session send/get/events/delete`.
|
||||
|
||||
@@ -421,7 +412,6 @@ bl managed-agent session run --agent assistant --prompt "summarize this repo"
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
@@ -452,7 +442,6 @@ bl managed-agent session send --session-id sess_abc123 --message "continue"
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
- Providers without a skill listing API (e.g. ark) return an empty list.
|
||||
- For agent-driven skill selection, use `--source all --output json`: one call returns both catalogs with per-skill `source` and `description` fields to pick from.
|
||||
@@ -499,7 +488,6 @@ bl managed-agent skill-list --source custom --provider bailian
|
||||
|
||||
- Bailian credentials come from bl's auth chain: --api-key > DASHSCOPE_API_KEY > `bl auth login` (active config profile).
|
||||
- Other providers read the env vars referenced in agents.yaml (e.g. ${ANTHROPIC_API_KEY}), including .env and ~/.agents/config.json.
|
||||
- Only the providers this run involves (--provider, or the config's default provider chain) need credentials; other configured providers are not checked.
|
||||
- Resolved credentials are injected into the SDK in-memory and cleared from the environment; they never persist in process env.
|
||||
|
||||
#### Examples
|
||||
|
||||
Reference in New Issue
Block a user