Skip to content

Commit 85f9fad

Browse files
committed
fix(sdk): honor base_url in bailian runtime readiness and sync placeholder
1 parent 25d7026 commit 85f9fad

4 files changed

Lines changed: 68 additions & 7 deletions

File tree

packages/sdk/src/internal/provider-config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,14 @@ export function resolveActiveProvider(): ProviderConfigProvider {
4242
/** Whether the current process env has all required fields for the active provider. */
4343
export function areRuntimeCredentialsReady(): boolean {
4444
const provider = resolveActiveProvider();
45-
return AGENTS_PROVIDER_FIELDS[provider].every((field) => process.env[field.key]?.trim());
45+
return AGENTS_PROVIDER_FIELDS[provider].every((field) => {
46+
if (process.env[field.key]?.trim()) return true;
47+
// bailian derives its endpoint from either workspace_id or base_url, so a
48+
// configured BAILIAN_BASE_URL satisfies the workspace_id slot (mirrors the
49+
// provider config schema's "at least one" rule).
50+
if (field.key === "BAILIAN_WORKSPACE_ID") return Boolean(process.env.BAILIAN_BASE_URL?.trim());
51+
return false;
52+
});
4653
}
4754

4855
function isProvider(value: string): value is ProviderConfigProvider {

packages/sdk/src/internal/providers/registry.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,14 @@ export function buildProviders(
9191
* Environment variable mappings for each provider.
9292
* Used to construct a provider adapter without a agents.yaml config file.
9393
*/
94-
const PROVIDER_ENV_VARS: Record<string, Record<string, { env: string[]; required: boolean }>> = {
94+
const PROVIDER_ENV_VARS: Record<string, Record<string, { env: string[]; required: boolean; placeholder?: boolean }>> = {
9595
bailian: {
9696
api_key: { env: ["DASHSCOPE_API_KEY", "BAILIAN_API_KEY"], required: true },
9797
// Neither workspace_id nor base_url is required on its own; the config schema
9898
// enforces "at least one" so a host can supply just base_url (BAILIAN_BASE_URL).
99+
// base_url is the preferred placeholder emitted by `agents sync`.
99100
workspace_id: { env: ["BAILIAN_WORKSPACE_ID"], required: false },
100-
base_url: { env: ["BAILIAN_BASE_URL"], required: false },
101+
base_url: { env: ["BAILIAN_BASE_URL"], required: false, placeholder: true },
101102
},
102103
qoder: {
103104
api_key: { env: ["QODER_PAT", "QODER_API_KEY"], required: true },
@@ -115,15 +116,17 @@ const PROVIDER_ENV_VARS: Record<string, Record<string, { env: string[]; required
115116

116117
/**
117118
* Build a `providers` config block for a single provider using `${ENV}`
118-
* placeholders for its required fields. Used by `agents sync` to emit a providers
119-
* block without leaking resolved secrets when the original file is unavailable.
119+
* placeholders. Emits fields that are either required for env resolution or
120+
* explicitly marked as the provider's preferred placeholder (e.g. bailian's
121+
* base_url). Used by `agents sync` to emit a providers block without leaking
122+
* resolved secrets when the original file is unavailable.
120123
*/
121124
export function placeholderProviderConfig(providerName: string): Record<string, string> {
122125
const envMap = PROVIDER_ENV_VARS[providerName];
123126
if (!envMap) return {};
124127
const out: Record<string, string> = {};
125-
for (const [field, { env, required }] of Object.entries(envMap)) {
126-
if (required && env[0]) out[field] = `\${${env[0]}}`;
128+
for (const [field, { env, required, placeholder }] of Object.entries(envMap)) {
129+
if ((required || placeholder) && env[0]) out[field] = `\${${env[0]}}`;
127130
}
128131
return out;
129132
}

packages/sdk/tests/unit/provider-config.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ describe("provider-config bootstrap", () => {
126126
}
127127
});
128128

129+
test("areRuntimeCredentialsReady accepts BAILIAN_BASE_URL in place of workspace_id", () => {
130+
const prevProvider = process.env.AGENTS_PROVIDER;
131+
const prevKey = process.env.DASHSCOPE_API_KEY;
132+
const prevWs = process.env.BAILIAN_WORKSPACE_ID;
133+
const prevBaseUrl = process.env.BAILIAN_BASE_URL;
134+
process.env.AGENTS_PROVIDER = "bailian";
135+
process.env.DASHSCOPE_API_KEY = "sk-x";
136+
delete process.env.BAILIAN_WORKSPACE_ID;
137+
process.env.BAILIAN_BASE_URL = "https://ws.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio";
138+
try {
139+
expect(areRuntimeCredentialsReady()).toBe(true);
140+
} finally {
141+
if (prevProvider === undefined) delete process.env.AGENTS_PROVIDER;
142+
else process.env.AGENTS_PROVIDER = prevProvider;
143+
if (prevKey === undefined) delete process.env.DASHSCOPE_API_KEY;
144+
else process.env.DASHSCOPE_API_KEY = prevKey;
145+
if (prevWs === undefined) delete process.env.BAILIAN_WORKSPACE_ID;
146+
else process.env.BAILIAN_WORKSPACE_ID = prevWs;
147+
if (prevBaseUrl === undefined) delete process.env.BAILIAN_BASE_URL;
148+
else process.env.BAILIAN_BASE_URL = prevBaseUrl;
149+
}
150+
});
151+
129152
test("applyProviderConfigToEnv respects force flag", () => {
130153
const prevProvider = process.env.AGENTS_PROVIDER;
131154
const prevKey = process.env.ARK_API_KEY;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { expect, test } from "bun:test";
2+
import { placeholderProviderConfig } from "../../src/internal/providers/registry.ts";
3+
4+
// Placeholder values are literal "${ENV_VAR}" tokens; assert their shape with a
5+
// regex instead of literal strings so biome's noTemplateCurlyInString rule (which
6+
// guards against accidental template placeholders) stays happy.
7+
const PLACEHOLDER_RE = /^\$\{[A-Z_]+\}$/;
8+
9+
// `agents sync` falls back to these placeholders when the source providers block
10+
// is unavailable. bailian must emit base_url (its preferred field) so the emitted
11+
// config is valid without a workspace_id.
12+
test("placeholderProviderConfig emits api_key + base_url for bailian, not workspace_id", () => {
13+
const block = placeholderProviderConfig("bailian");
14+
expect(Object.keys(block).sort()).toEqual(["api_key", "base_url"]);
15+
expect(block.workspace_id).toBeUndefined();
16+
for (const value of Object.values(block)) {
17+
expect(value).toMatch(PLACEHOLDER_RE);
18+
}
19+
});
20+
21+
test("placeholderProviderConfig emits only required fields for other providers", () => {
22+
expect(Object.keys(placeholderProviderConfig("claude"))).toEqual(["api_key"]);
23+
expect(Object.keys(placeholderProviderConfig("ark"))).toEqual(["api_key"]);
24+
});
25+
26+
test("placeholderProviderConfig returns empty for an unknown provider", () => {
27+
expect(placeholderProviderConfig("nope")).toEqual({});
28+
});

0 commit comments

Comments
 (0)