Skip to content

Commit 1c9dac2

Browse files
committed
feat(cma): login时初始化agent相关的baseUrl
1 parent d6bd38a commit 1c9dac2

10 files changed

Lines changed: 156 additions & 62 deletions

File tree

packages/commands/src/commands/agent/_engine/credentials.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ let bootstrapped = false;
1111
* that loads agents.yaml. `bl` prefix is safe: agent commands ship on `bl` only.
1212
*/
1313
export const CREDENTIALS_NOTE = [
14-
"Credentials come from the env vars referenced in agents.yaml (e.g. ${DASHSCOPE_API_KEY}, ${BAILIAN_WORKSPACE_ID}).",
15-
"For the bailian provider, bl fills these from your login as a fallback: `bl auth login` (API key) and `bl config set workspace_id <id>`.",
14+
"Credentials come from the env vars referenced in agents.yaml (e.g. ${DASHSCOPE_API_KEY}, ${BAILIAN_BASE_URL}).",
15+
"For the bailian provider, bl fills these from your login as a fallback: `bl auth login --api-key <key> --agentstudio-base-url <url>`.",
1616
];
1717

1818
/**
1919
* Bridge bl's own login state into the env vars the OpenAgentPack SDK reads for
20-
* the bailian provider. bl persists `api_key` / `workspace_id` in
21-
* `~/.bailian/config.json` (via `bl auth login` / `bl config set`); mirror them
22-
* onto `DASHSCOPE_API_KEY` / `BAILIAN_WORKSPACE_ID` so users don't have to
23-
* re-declare the same credentials for `bl agent *`.
20+
* the bailian provider. bl persists `api_key` / `agentstudio_base_url` in
21+
* `~/.bailian/config.json` (via `bl auth login`); mirror them onto
22+
* `DASHSCOPE_API_KEY` / `BAILIAN_BASE_URL` so users don't have to re-declare the
23+
* same credentials for `bl agent *`. `workspace_id` is still bridged for configs
24+
* that predate the base_url flow (the SDK accepts either).
2425
*
2526
* Lowest priority: only fills a var that is still unset, so anything already in
2627
* the environment (shell export, `.env`, or `~/.agents/config.json` — which the
@@ -33,6 +34,9 @@ export function bridgeBailianCredentials(): void {
3334
if (!process.env.DASHSCOPE_API_KEY?.trim() && file.api_key) {
3435
process.env.DASHSCOPE_API_KEY = file.api_key;
3536
}
37+
if (!process.env.BAILIAN_BASE_URL?.trim() && file.agentstudio_base_url) {
38+
process.env.BAILIAN_BASE_URL = file.agentstudio_base_url;
39+
}
3640
if (!process.env.BAILIAN_WORKSPACE_ID?.trim() && file.workspace_id) {
3741
process.env.BAILIAN_WORKSPACE_ID = file.workspace_id;
3842
}

packages/commands/src/commands/agent/init.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ agents.state.json
1818
const PROVIDERS = ["bailian", "claude", "qoder", "ark", "all"] as const;
1919

2020
const PROVIDER_BLOCKS: Record<string, string> = {
21-
bailian: ` bailian:\n # bl auth login sets DASHSCOPE_API_KEY; bl config set workspace_id <id> sets BAILIAN_WORKSPACE_ID\n api_key: \${DASHSCOPE_API_KEY}\n workspace_id: \${BAILIAN_WORKSPACE_ID}`,
21+
bailian: ` bailian:\n # bl auth login --api-key <key> sets DASHSCOPE_API_KEY; --agentstudio-base-url <url> sets BAILIAN_BASE_URL\n api_key: \${DASHSCOPE_API_KEY}\n base_url: \${BAILIAN_BASE_URL}`,
2222
claude: ` claude:\n api_key: \${ANTHROPIC_API_KEY}`,
2323
qoder: ` qoder:\n api_key: \${QODER_PAT}\n gateway: "https://api.qoder.com/api/v1/cloud"`,
2424
ark: ` ark:\n api_key: \${ARK_API_KEY}`,
@@ -137,7 +137,7 @@ export default defineCommand({
137137
emitBare(`Created ${file}`);
138138
if (provider === "bailian" || provider === "all") {
139139
emitBare(
140-
"Credentials: run `bl auth login` and `bl config set workspace_id <id>`, or set DASHSCOPE_API_KEY / BAILIAN_WORKSPACE_ID.",
140+
"Credentials: run `bl auth login --api-key <key> --agentstudio-base-url <url>`, or set DASHSCOPE_API_KEY / BAILIAN_BASE_URL.",
141141
);
142142
}
143143
emitBare("Next: edit agents.yaml, then run `bl agent plan`.");

packages/commands/src/commands/auth/login.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,22 @@ export default defineCommand({
1919
usageArgs:
2020
"--api-key <key> | --console | --open-api --access-key-id <id> --access-key-secret <secret>",
2121
flags: {
22-
apiKey: { type: "string", valueHint: "<key>", description: "DashScope API key to store" },
22+
apiKey: {
23+
type: "string",
24+
valueHint: "<key>",
25+
description: "DashScope API key to store",
26+
},
2327
baseUrl: {
2428
type: "string",
2529
valueHint: "<url>",
2630
description: "DashScope API base URL (used with --api-key for validation)",
2731
},
32+
agentstudioBaseUrl: {
33+
type: "string",
34+
valueHint: "<url>",
35+
description:
36+
"Bailian AgentStudio base URL for `bl agent` commands (sets BAILIAN_BASE_URL; used with --api-key)",
37+
},
2838
console: {
2939
type: "switch",
3040
description:
@@ -65,6 +75,9 @@ export default defineCommand({
6575
if (!apiKeyMode && hasValue(f.baseUrl)) {
6676
return "Use --base-url only with --api-key";
6777
}
78+
if (!apiKeyMode && hasValue(f.agentstudioBaseUrl)) {
79+
return "Use --agentstudio-base-url only with --api-key";
80+
}
6881
if (!consoleMode && hasValue(f.consoleSite)) {
6982
return "Use --console-site only with --console";
7083
}
@@ -131,6 +144,9 @@ export default defineCommand({
131144
if (baseUrl) {
132145
await store.login({ base_url: baseUrl });
133146
}
147+
if (flags.agentstudioBaseUrl) {
148+
await store.login({ agentstudio_base_url: flags.agentstudioBaseUrl });
149+
}
134150
await validateAndPersistApiKey(deps, key, baseUrl || store.resolveBaseUrl());
135151
},
136152
});

packages/commands/src/commands/config/set.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const VALID_KEYS = [
2323
"default_speech_model",
2424
"default_omni_model",
2525
"workspace_id",
26+
"agentstudio_base_url",
2627
];
2728

2829
// Keys whose values are secrets. Their stored value must never be echoed back in
@@ -44,6 +45,7 @@ const KEY_ALIASES: Record<string, string> = {
4445
"default-speech-model": "default_speech_model",
4546
"default-omni-model": "default_omni_model",
4647
"workspace-id": "workspace_id",
48+
"agentstudio-base-url": "agentstudio_base_url",
4749
};
4850

4951
export default defineCommand({
@@ -55,10 +57,15 @@ export default defineCommand({
5557
type: "string",
5658
valueHint: "<key>",
5759
description:
58-
"Config key (base_url, output, output_dir, timeout, api_key, access_token, access_key_id, access_key_secret, default_*_model, workspace_id)",
60+
"Config key (base_url, output, output_dir, timeout, api_key, access_token, access_key_id, access_key_secret, default_*_model, workspace_id, agentstudio_base_url)",
61+
required: true,
62+
},
63+
value: {
64+
type: "string",
65+
valueHint: "<value>",
66+
description: "Value to set",
5967
required: true,
6068
},
61-
value: { type: "string", valueHint: "<value>", description: "Value to set", required: true },
6269
},
6370
exampleArgs: [
6471
"--key output --value json",
@@ -106,7 +113,9 @@ export default defineCommand({
106113
}
107114

108115
const coerced = resolvedKey === "timeout" ? Number(value) : value;
109-
await ctx.configStore.write({ [resolvedKey]: coerced } as Partial<ConfigFile>);
116+
await ctx.configStore.write({
117+
[resolvedKey]: coerced,
118+
} as Partial<ConfigFile>);
110119

111120
if (!settings.quiet) {
112121
const shown = SECRET_KEYS.has(resolvedKey) ? maskToken(String(coerced)) : coerced;

packages/commands/tests/credentials-bridge.test.ts

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,29 @@ import { expect, test } from "vite-plus/test";
55
import { bridgeBailianCredentials } from "../src/commands/agent/_engine/credentials.ts";
66

77
/**
8-
* bridgeBailianCredentials 把 ~/.bailian/config.json 的 api_key / workspace_id
9-
* 作为最低优先级兜底填入 DASHSCOPE_API_KEY / BAILIAN_WORKSPACE_ID。
10-
* 用临时 config dir + env 保存恢复隔离,验证优先级与不抛错语义。
8+
* bridgeBailianCredentials 把 ~/.bailian/config.json 的 api_key / agentstudio_base_url
9+
* / workspace_id 作为最低优先级兜底填入 DASHSCOPE_API_KEY / BAILIAN_BASE_URL /
10+
* BAILIAN_WORKSPACE_ID。用临时 config dir + env 保存恢复隔离,验证优先级与不抛错语义。
1111
*/
1212
async function inScenario(
1313
scenario: {
14-
config?: { api_key?: string; workspace_id?: string };
15-
env?: { DASHSCOPE_API_KEY?: string; BAILIAN_WORKSPACE_ID?: string };
14+
config?: {
15+
api_key?: string;
16+
workspace_id?: string;
17+
agentstudio_base_url?: string;
18+
};
19+
env?: {
20+
DASHSCOPE_API_KEY?: string;
21+
BAILIAN_WORKSPACE_ID?: string;
22+
BAILIAN_BASE_URL?: string;
23+
};
1624
},
1725
assert: () => void,
1826
): Promise<void> {
1927
const savedConfigDir = process.env.BAILIAN_CONFIG_DIR;
2028
const savedApiKey = process.env.DASHSCOPE_API_KEY;
2129
const savedWorkspace = process.env.BAILIAN_WORKSPACE_ID;
30+
const savedBaseUrl = process.env.BAILIAN_BASE_URL;
2231

2332
const dir = mkdtempSync(join(tmpdir(), "bl-cred-bridge-"));
2433
process.env.BAILIAN_CONFIG_DIR = dir;
@@ -31,13 +40,16 @@ async function inScenario(
3140
else process.env.DASHSCOPE_API_KEY = scenario.env.DASHSCOPE_API_KEY;
3241
if (scenario.env?.BAILIAN_WORKSPACE_ID === undefined) delete process.env.BAILIAN_WORKSPACE_ID;
3342
else process.env.BAILIAN_WORKSPACE_ID = scenario.env.BAILIAN_WORKSPACE_ID;
43+
if (scenario.env?.BAILIAN_BASE_URL === undefined) delete process.env.BAILIAN_BASE_URL;
44+
else process.env.BAILIAN_BASE_URL = scenario.env.BAILIAN_BASE_URL;
3445

3546
try {
3647
assert();
3748
} finally {
3849
restore("BAILIAN_CONFIG_DIR", savedConfigDir);
3950
restore("DASHSCOPE_API_KEY", savedApiKey);
4051
restore("BAILIAN_WORKSPACE_ID", savedWorkspace);
52+
restore("BAILIAN_BASE_URL", savedBaseUrl);
4153
rmSync(dir, { recursive: true, force: true });
4254
}
4355
}
@@ -51,7 +63,10 @@ test("bridge:env 已有值时不被 bl config 覆盖(最低优先级)", async ()
5163
await inScenario(
5264
{
5365
config: { api_key: "sk-from-config", workspace_id: "ws-from-config" },
54-
env: { DASHSCOPE_API_KEY: "sk-from-env", BAILIAN_WORKSPACE_ID: "ws-from-env" },
66+
env: {
67+
DASHSCOPE_API_KEY: "sk-from-env",
68+
BAILIAN_WORKSPACE_ID: "ws-from-env",
69+
},
5570
},
5671
() => {
5772
bridgeBailianCredentials();
@@ -63,7 +78,10 @@ test("bridge:env 已有值时不被 bl config 覆盖(最低优先级)", async ()
6378

6479
test("bridge:env 缺失且 bl config 有值时填充", async () => {
6580
await inScenario(
66-
{ config: { api_key: "sk-from-config", workspace_id: "ws-from-config" }, env: {} },
81+
{
82+
config: { api_key: "sk-from-config", workspace_id: "ws-from-config" },
83+
env: {},
84+
},
6785
() => {
6886
bridgeBailianCredentials();
6987
expect(process.env.DASHSCOPE_API_KEY).toBe("sk-from-config");
@@ -93,3 +111,39 @@ test("bridge:env 与 bl config 皆缺失时不抛错且不写入", async () => {
93111
expect(process.env.BAILIAN_WORKSPACE_ID).toBeUndefined();
94112
});
95113
});
114+
115+
test("bridge:agentstudio_base_url 兜底填入 BAILIAN_BASE_URL", async () => {
116+
await inScenario(
117+
{
118+
config: {
119+
api_key: "sk-from-config",
120+
agentstudio_base_url: "https://ws-x.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio",
121+
},
122+
env: {},
123+
},
124+
() => {
125+
bridgeBailianCredentials();
126+
expect(process.env.BAILIAN_BASE_URL).toBe(
127+
"https://ws-x.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio",
128+
);
129+
},
130+
);
131+
});
132+
133+
test("bridge:env 已有 BAILIAN_BASE_URL 时不被 bl config 覆盖", async () => {
134+
await inScenario(
135+
{
136+
config: {
137+
api_key: "sk-from-config",
138+
agentstudio_base_url: "https://from-config.aliyuncs.com/api/v1/agentstudio",
139+
},
140+
env: {
141+
BAILIAN_BASE_URL: "https://from-env.aliyuncs.com/api/v1/agentstudio",
142+
},
143+
},
144+
() => {
145+
bridgeBailianCredentials();
146+
expect(process.env.BAILIAN_BASE_URL).toBe("https://from-env.aliyuncs.com/api/v1/agentstudio");
147+
},
148+
);
149+
});

packages/core/src/auth/store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type AuthPersistPatch = Pick<
1818
| "access_key_id"
1919
| "access_key_secret"
2020
| "base_url"
21+
| "agentstudio_base_url"
2122
| "console_site"
2223
| "console_region"
2324
| "console_switch_agent"

packages/core/src/config/schema.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ export interface ConfigFile {
2323
/** Alibaba Cloud OpenAPI AccessKey secret from `bl auth login --open-api`. */
2424
access_key_secret?: string;
2525
base_url?: string;
26+
/**
27+
* Bailian AgentStudio API base URL for `bl agent` commands, e.g.
28+
* `https://<workspace>.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio`.
29+
* Distinct from `base_url` (the DashScope model API): the agent path bridges
30+
* this to the SDK's `BAILIAN_BASE_URL`, so a workspace_id is not required.
31+
*/
32+
agentstudio_base_url?: string;
2633
output?: "text" | "json";
2734
output_dir?: string;
2835
timeout?: number;
@@ -78,6 +85,8 @@ export function parseConfigFile(raw: unknown): ConfigFile {
7885
)
7986
out.access_key_secret = obj.openapi_access_key_secret;
8087
if (typeof obj.base_url === "string" && isHttpUrl(obj.base_url)) out.base_url = obj.base_url;
88+
if (typeof obj.agentstudio_base_url === "string" && isHttpUrl(obj.agentstudio_base_url))
89+
out.agentstudio_base_url = obj.agentstudio_base_url;
8190
if (typeof obj.output === "string" && VALID_OUTPUTS.has(obj.output))
8291
out.output = obj.output as ConfigFile["output"];
8392
if (typeof obj.output_dir === "string" && obj.output_dir.length > 0)

0 commit comments

Comments
 (0)