Skip to content

Commit 7bc4851

Browse files
committed
fix(files): let Token Plan keys reach explicit upload proxies
1 parent b364d96 commit 7bc4851

3 files changed

Lines changed: 45 additions & 24 deletions

File tree

‎docs/agents/url-change.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ Prime 视频命令通过 `Client.uploadFile` / `resolveImageInput` 的 `defaultB
3535
复用 `resolvePrimeEndpoint` 推导 model Workspace origin;上传策略与模型提交均保持该
3636
Workspace origin。显式配置的 model origin 仍优先;已有媒体 URL 不触发上传或求值上传默认地址。
3737

38-
官方临时文件上传当前只支持 `cn-beijing` 与 `ap-southeast-1`;其他已知官方地域与
39-
Token Plan 在发送 API Key 前失败。显式自定义代理视为自身路由的权威来源,保持同源并由
40-
代理/服务决定能力。任何路径都不得回退公共站或跨 origin 重试;不支持时用户可改用服务
41-
可访问的 HTTPS 媒体 URL。服务端错误保留 message/code/request ID/raw response,并在
42-
message 的结构化上下文中展示本次有效上传 endpoint。
38+
官方临时文件上传当前只支持 `cn-beijing` 与 `ap-southeast-1`。Token Plan 主机,以及这些官方
39+
endpoint 上的 `sk-sp-` 密钥,在发送 API Key 前失败。显式自定义代理视为自身路由的权威来源,
40+
保持同源且不按密钥前缀拦截,由代理/服务决定能力。任何路径都不得回退公共站或跨 origin 重试;
41+
不支持时用户可改用服务可访问的 HTTPS 媒体 URL。服务端错误保留 message/code/request ID/raw
42+
response,并在 message 的结构化上下文中展示本次有效上传 endpoint。
4343

4444
## 必查清单
4545

‎packages/core/src/files/upload.ts‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,39 @@ function unsupportedTemporaryUpload(target: string): BailianError {
2424
);
2525
}
2626

27-
/** Keep the configured origin when supported; reject known unsupported official regions. */
27+
/** Reject Token Plan keys only after the endpoint is recognized as official. */
28+
function rejectOfficialTokenPlanKey(apiKey: string): void {
29+
if (getApiKeyLoginKind(apiKey) === "token-plan") {
30+
throw unsupportedTemporaryUpload("Token Plan");
31+
}
32+
}
33+
34+
/** Keep supported official origins; reject known unsupported official regions. Custom proxies skip key-prefix checks. */
2835
function resolveUploadBaseUrl(baseUrl: string, apiKey: string): string {
2936
const normalizedBaseUrl = normalizeModelBaseUrl(baseUrl);
3037
const hostname = new URL(normalizedBaseUrl).hostname.toLowerCase();
3138

32-
if (
33-
getApiKeyLoginKind(apiKey) === "token-plan" ||
34-
/^token-plan\.[a-z0-9-]+\.maas\.aliyuncs\.com$/.test(hostname)
35-
) {
39+
if (/^token-plan\.[a-z0-9-]+\.maas\.aliyuncs\.com$/.test(hostname)) {
3640
throw unsupportedTemporaryUpload("Token Plan");
3741
}
3842

39-
if (hostname === new URL(REGIONS.cn).hostname) return normalizedBaseUrl;
40-
if (hostname === new URL(REGIONS.intl).hostname) return normalizedBaseUrl;
43+
if (hostname === new URL(REGIONS.cn).hostname || hostname === new URL(REGIONS.intl).hostname) {
44+
rejectOfficialTokenPlanKey(apiKey);
45+
return normalizedBaseUrl;
46+
}
4147
if (hostname === new URL(REGIONS.us).hostname) {
4248
throw unsupportedTemporaryUpload(hostname);
4349
}
4450

4551
const maasMatch = /^[^.]+\.([a-z0-9-]+)\.maas\.aliyuncs\.com$/.exec(hostname);
4652
const cloudRegion = maasMatch?.[1];
4753
if (cloudRegion === "cn-beijing" || cloudRegion === "ap-southeast-1") {
54+
rejectOfficialTokenPlanKey(apiKey);
4855
return normalizedBaseUrl;
4956
}
5057
if (cloudRegion) throw unsupportedTemporaryUpload(cloudRegion);
5158

52-
// Explicit custom proxies are authoritative for their own upload routing.
59+
// Explicit custom proxies decide temporary-upload support themselves; do not gate on key prefix.
5360
return normalizedBaseUrl;
5461
}
5562

‎packages/core/tests/file-upload.test.ts‎

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,11 @@ function makeClient(baseUrl: string, token = "sk-synthetic-profile-key"): Client
6060
});
6161
}
6262

63-
function expectSuccessfulUpload(uploadOrigin: string): void {
63+
function expectSuccessfulUpload(uploadOrigin: string, token = "sk-synthetic-profile-key"): void {
6464
expect(fetchMock).toHaveBeenCalledTimes(2);
6565
const [policyUrl, policyOptions] = fetchMock.mock.calls[0]!;
6666
expect(policyUrl).toBe(`${uploadOrigin}/api/v1/uploads?action=getPolicy&model=wan3.0-video`);
67-
expect(new Headers(policyOptions?.headers).get("Authorization")).toBe(
68-
"Bearer sk-synthetic-profile-key",
69-
);
67+
expect(new Headers(policyOptions?.headers).get("Authorization")).toBe(`Bearer ${token}`);
7068
const [uploadUrl, uploadOptions] = fetchMock.mock.calls[1]!;
7169
expect(uploadUrl).toBe(policy.upload_host);
7270
expect(uploadOptions).toBeDefined();
@@ -131,14 +129,30 @@ describe("regional temporary file uploads", () => {
131129
expect(fetchMock).not.toHaveBeenCalled();
132130
});
133131

134-
test("rejects Token Plan keys before requesting a policy", async () => {
132+
test.each([
133+
REGIONS.cn,
134+
REGIONS.intl,
135+
"https://ws-example.cn-beijing.maas.aliyuncs.com",
136+
"https://ws-example.ap-southeast-1.maas.aliyuncs.com",
137+
])(
138+
"rejects Token Plan keys on official endpoint %s before requesting a policy",
139+
async (baseUrl) => {
140+
await expect(
141+
makeClient(baseUrl, "sk-sp-synthetic-plan-key").uploadFile(fixturePath, "wan3.0-video"),
142+
).rejects.toMatchObject({
143+
exitCode: ExitCode.USAGE,
144+
message: expect.stringContaining("Token Plan"),
145+
});
146+
expect(fetchMock).not.toHaveBeenCalled();
147+
},
148+
);
149+
150+
test("lets a Token Plan key reach an explicit custom proxy", async () => {
151+
const proxy = "https://proxy.example.test";
135152
await expect(
136-
makeClient(REGIONS.cn, "sk-sp-synthetic-plan-key").uploadFile(fixturePath, "wan3.0-video"),
137-
).rejects.toMatchObject({
138-
exitCode: ExitCode.USAGE,
139-
message: expect.stringContaining("Token Plan"),
140-
});
141-
expect(fetchMock).not.toHaveBeenCalled();
153+
makeClient(proxy, "sk-sp-synthetic-plan-key").uploadFile(fixturePath, "wan3.0-video"),
154+
).resolves.toBe("oss://temporary/sample.mp4");
155+
expectSuccessfulUpload(proxy, "sk-sp-synthetic-plan-key");
142156
});
143157

144158
test("preserves upload-policy service errors without retrying another region", async () => {

0 commit comments

Comments
 (0)