Skip to content

feat(auth): add Singapore Token Plan region - #7280

Merged
wenshao merged 6 commits into
QwenLM:mainfrom
han-dreamer:feat/token-plan-singapore-region
Jul 21, 2026
Merged

feat(auth): add Singapore Token Plan region#7280
wenshao merged 6 commits into
QwenLM:mainfrom
han-dreamer:feat/token-plan-singapore-region

Conversation

@han-dreamer

@han-dreamer han-dreamer commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Adds China and Singapore (International) region choices to the Alibaba ModelStudio Token Plan provider used by /auth, while keeping the existing China endpoint as the backward-compatible default. It also updates the VS Code companion subscription-plan definitions so Token Plan metadata and config lookup recognize the Singapore endpoint consistently.

Why it's needed

Fixes #7252. Token Plan users with an international Alibaba Cloud setup need to use token-plan.ap-southeast-1.maas.aliyuncs.com, but the Token Plan preset only exposed the China Beijing endpoint, so /auth could not select the Singapore/International Token Plan region.

Reviewer Test Plan

How to verify

Run the focused core provider tests and confirm Token Plan defaults to the China endpoint, can resolve/build models for the Singapore endpoint, matches credentials for both registered Token Plan base URLs, and owns models from registered endpoints while preserving legacy Token Plan name-prefix ownership. Run the VS Code companion subscription-plan test and confirm Token Plan defaults to China while supporting CodingPlanRegion.GLOBAL with the Singapore endpoint and global model-name prefix. On a reliable TUI platform, run the AuthDialog Token Plan flow tests and confirm the wizard navigates Region -> API Key -> Model IDs, with Esc returning from API Key to Region and then to Token Plan selection. I also verified the Singapore endpoint responds without an API key by calling /compatible-mode/v1/models and receiving the expected 401 InvalidApiKey; this confirms the endpoint is live enough to add, but does not validate a real international Token Plan subscription.

Evidence (Before & After)

Before: Token Plan had a single fixed base URL, https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1, so no region step was shown for /auth. After: Token Plan exposes China (Beijing) and Singapore (International) region options, Region is used as the base URL step title, model display names use the regional prefix, and provider matching recognizes both Token Plan endpoints. Endpoint check: curl.exe -i --max-time 15 https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/models returned HTTP/1.1 401 Unauthorized with InvalidApiKey / No API-key provided.

Tested on

OS Status
🍏 macOS ⚠️ not tested
🪟 Windows ✅ tested
🐧 Linux ⚠️ not tested

Environment (optional)

Windows PowerShell, Node/Vitest from the repo workspace.

Risk & Scope

  • Main risk or tradeoff: The Singapore endpoint is verified to resolve and return the expected unauthenticated API error, but I do not have a valid international Token Plan subscription to validate an authenticated model-list request.
  • Not validated / out of scope: No runtime DashScope provider detection changes were made because it already handles token-plan.<region>.maas.aliyuncs.com generically.
  • Breaking changes / migration notes: None. The existing TOKEN_PLAN_BASE_URL export remains as an alias for the China endpoint, preserving the previous default behavior.

Linked Issues

Closes #7252

中文说明

What this PR does

这个 PR 为 /auth 使用的 Alibaba ModelStudio Token Plan provider 增加了中国区和新加坡(国际)区两个区域选项,同时保留现有中国区 endpoint 作为向后兼容的默认值。它也更新了 VS Code companion 的 subscription-plan 定义,使 Token Plan 的 metadata 和配置查找可以一致识别新加坡 endpoint。

Why it's needed

修复 #7252。使用阿里云国际站配置的 Token Plan 用户需要使用 token-plan.ap-southeast-1.maas.aliyuncs.com,但当前 Token Plan preset 只暴露了中国北京 endpoint,因此 /auth 里无法选择新加坡/国际 Token Plan 区域。

Reviewer Test Plan

How to verify

运行聚焦的 core provider 测试,确认 Token Plan 默认使用中国区 endpoint,可以为新加坡 endpoint resolve/build models,可以匹配两个已注册 Token Plan base URL 的 credentials,并且可以识别已注册 endpoint 下的 Token Plan models,同时保留旧版 Token Plan name-prefix ownership。运行 VS Code companion 的 subscription-plan 测试,确认 Token Plan 默认使用中国区,同时在 CodingPlanRegion.GLOBAL 下支持新加坡 endpoint 和国际区 model-name prefix。在可靠的 TUI 平台上运行 AuthDialog Token Plan flow 测试,确认 wizard 按 Region -> API Key -> Model IDs 导航,并且 Esc 会先从 API Key 回到 Region,再回到 Token Plan 选择。我还通过调用 /compatible-mode/v1/models 验证新加坡 endpoint 在没有 API key 时会返回预期的 401 InvalidApiKey;这说明 endpoint 已经在线,足以加入配置,但不代表我验证了真实的国际 Token Plan 订阅。

Evidence (Before & After)

Before:Token Plan 只有一个固定 base URL,即 https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1,所以 /auth 不会展示区域选择步骤。After:Token Plan 暴露 China (Beijing)Singapore (International) 两个区域选项,base URL 步骤标题使用 Region,模型显示名会根据区域使用不同 prefix,并且 provider matching 可以识别两个 Token Plan endpoints。Endpoint check:curl.exe -i --max-time 15 https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/models 返回了 HTTP/1.1 401 Unauthorized,响应中包含 InvalidApiKey / No API-key provided.

Tested on

OS Status
🍏 macOS ⚠️ not tested
🪟 Windows ✅ tested
🐧 Linux ⚠️ not tested

Environment (optional)

Windows PowerShell,使用仓库 workspace 里的 Node/Vitest。

Risk & Scope

  • Main risk or tradeoff: 新加坡 endpoint 已验证可以解析并返回预期的未认证 API 错误,但我没有有效的国际 Token Plan 订阅,无法验证带真实 API key 的 model-list 请求。
  • Not validated / out of scope: 没有修改 runtime DashScope provider detection,因为它已经可以通用处理 token-plan.<region>.maas.aliyuncs.com
  • Breaking changes / migration notes: 无。现有 TOKEN_PLAN_BASE_URL export 仍然保留为中国区 endpoint 的 alias,保留之前的默认行为。

Linked Issues

Closes #7252

@dtometzki dtometzki left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested bay Damian Tometzki

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

— qwen3.8-max-preview via Qwen Code /review

Comment on lines +184 to +190
expect(
tokenPlanProvider.ownsModel?.({
id: 'custom-model',
baseUrl: 'https://custom.example.com/v1',
envKey: TOKEN_PLAN_ENV_KEY,
}),
).toBe(false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The ownsModel negative case only exercises the baseUrl mismatch branch; the envKey guard is never independently tested. — Concrete cost: if a future edit removes model.envKey === TOKEN_PLAN_ENV_KEY && from ownsModel, all three existing tests still pass (both positives use the correct envKey; the sole negative fails on baseUrl). A model registered under a different provider's API key but sharing a Token Plan base URL would be incorrectly claimed by the token-plan provider.

Suggested change
expect(
tokenPlanProvider.ownsModel?.({
id: 'custom-model',
baseUrl: 'https://custom.example.com/v1',
envKey: TOKEN_PLAN_ENV_KEY,
}),
).toBe(false);
expect(
tokenPlanProvider.ownsModel?.({
id: 'custom-model',
baseUrl: 'https://custom.example.com/v1',
envKey: TOKEN_PLAN_ENV_KEY,
}),
).toBe(false);
expect(
tokenPlanProvider.ownsModel?.({
id: 'token-model',
baseUrl: TOKEN_PLAN_CHINA_BASE_URL,
envKey: 'SOME_OTHER_API_KEY',
}),
).toBe(false);

— qwen3.8-max-preview via Qwen Code /review

@QwenLM QwenLM deleted a comment from qwen-code-ci-bot Jul 20, 2026
@QwenLM QwenLM deleted a comment from qwen-code-ci-bot Jul 20, 2026

@dtometzki dtometzki left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved

@han-dreamer

Copy link
Copy Markdown
Contributor Author

Thanks, good catch. I added a separate negative case for a valid Token Plan base URL with a non-Token Plan env key, and the focused test passes.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Local verification report (Linux) — PR #7280

I built and ran this branch locally on Linux (the PR is marked "Windows only"), including a real /auth walkthrough, a compiled-build differential against the merge-base, and an upgrade simulation for existing Token Plan users.

Verdict: the feature itself is correct and cleanly implemented — but it breaks 3 existing CLI auth tests that CI cannot see. One of those 3 is a genuine behaviour regression, not just a navigation update. I'd hold the merge until those are resolved; everything else checks out.

Environment / what "BASE" means here
  • PR head 6e21220f, merge-base 102c6921, Linux x86_64, Node 22, full npm run build + bundle.
  • BASE = a compiled build of the merge-base. I verified its tokenPlanProvider block and model list match 102c6921 source exactly, so every A/B below isolates this change only.
  • All test runs are on the pristine PR tree (git status clean at 6e21220f).

✅ What works

1. The Region step is real and correctly wired. Live /auth → Alibaba ModelStudio → Token Plan:

Region step

Same path on the merge-base goes straight to API Key — the differential is clean:

Base has no region step

2. Selecting Singapore genuinely routes to ap-southeast-1. After completing the wizard, settings.json contains all 15 models on the Singapore endpoint with the Global/Intl prefix, and providerMetadata.token-plan.baseUrl is the Singapore URL. A live turn with a dummy key returns a 401 whose help link is the international domain (www.alibabacloud.com, not help.aliyun.com) — proof the request actually reached the Singapore host rather than falling back to Beijing:

Live Singapore request

I also confirmed both endpoints are live from Linux, matching your Windows check:

ap-southeast-1 → HTTP/2 401  {"code":"InvalidApiKey","message":"No API-key provided."}
cn-beijing     → HTTP/1.1 401 {"code":"InvalidApiKey","message":"No API-key provided."}

3. Existing China users are not disturbed — verified, not assumed. This was my main upgrade risk: useProviderUpdates compares a hash of the built template, so any perturbation of the China template would show every existing Token Plan user a spurious "configuration update available" prompt. It does not:

  • China model configs are deep-equal BASE vs PR, and the version hash is identical.
  • Live check: I generated a pre-PR China install using the merge-base compiled code, then opened it with the PR build — no update prompt, no re-auth, prefix preserved, and every Token Plan field in settings.json byte-identical afterwards (only $version/ui housekeeping changed).

Clean upgrade

4. Runtime provider detection needs no change — your "out of scope" note is correct. isDashScopeProvider matches token-plan.<region>.maas.aliyuncs.com generically, and I confirmed the Singapore host is detected while token-plan.evil.com still is not.

5. 42/42 compiled-build differential checks pass (region step, base-URL resolution incl. trailing-slash and unknown-URL fallback, per-region prefixes, install plan, ownership, credential matching, no collision with Coding Plan, DashScope routing):

Differential harness

6. Suites and static checks green: core providers 145/145, vscode-ide-companion services 118/118, npm run lint clean, tsc --noEmit clean, Prettier clean.


🔴 Finding 1 (blocking): 3 AuthDialog tests break, and CI structurally cannot catch it

packages/cli/src/ui/auth/AuthDialog.test.tsx is not touched by this PR and is identical on both sides, so this is a clean A/B:

BASE (102c6921) PR (6e21220f)
AuthDialog.test.tsx 1 failed | 24 passed 4 failed | 21 passed

The 1 shared failure (drives API key provider steps from endpoint options metadata) is pre-existing and fails on the merge-base too — not yours. The 3 new ones are all Token Plan.

Regression evidence

Why CI is green: AuthDialog.test.tsx:238-239

const isUnreliableTuiInputEnvironment =
  process.platform === 'win32' || process.env['CI'] === 'true';
const itWhenTuiInputReliable = isUnreliableTuiInputEnvironment ? it.skip : it;

The CI log for this PR confirms it: ✓ src/ui/auth/AuthDialog.test.tsx (25 tests | 18 skipped). All three broken tests are in the skipped set. Worth knowing generally: any Token Plan / Coding Plan wizard-flow regression is invisible to CI, so this PR's green tick is not evidence these flows still work.

1a — navigation (2 tests, expected consequence of the feature)

should submit Token Plan through the shared subscription handler and should return from Token Plan API key input to Token Plan selection assert Step 1/2 · API Key; Token Plan is now a 3-step wizard. These just need to walk the new step, mirroring the Coding Plan idiom already at line ~889:

await pressEnterAndWaitFor(stdin, lastFrame, 'Alibaba ModelStudio · Step 1/3 · Region');
// Keep the default China (Beijing) region.
await pressEnterAndWaitFor(stdin, lastFrame, 'Alibaba ModelStudio · Step 2/3 · API Key');
await typeText(stdin, 'sk-token-plan');
await pressEnterAndWaitFor(stdin, lastFrame, 'Alibaba ModelStudio · Step 3/3 · Model IDs');

For the back-navigation test, note Esc now returns API Key → Region, and a second Esc returns to the Token Plan selection. I applied this and both tests pass.

1b — ownership semantics changed (1 test — this one is a real regression)

should pre-fill the Model IDs step with previously saved custom model IDs fails fast (403 ms, reaches Step 3/3 and finds the input empty), so it is not a navigation problem. Its fixture is:

{
  id: 'my-custom-token-model',
  name: '[ModelStudio Token Plan] my-custom-token-model',
  baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',   // <- not a Token Plan preset URL
  envKey: 'BAILIAN_TOKEN_PLAN_API_KEY',
}

Moving modelNamePrefix to a function forced an explicit ownsModel, which switched the match from name prefix to a base-URL allow-list. A saved model whose baseUrl isn't one of the two preset endpoints is therefore no longer owned. Two consequences for such users:

  1. the /auth wizard no longer pre-fills their saved custom model IDs (what the test catches); and
  2. prepend-and-remove-owned no longer removes those entries on reinstall, so they linger as stale duplicates alongside freshly installed ones.

I confirmed the root cause by widening ownsModel to also accept the legacy name prefix — all 25 AuthDialog tests and all 145 core provider tests pass, including this PR's own ownsModel assertions (the custom-model case has no name, so it still correctly returns false):

ownsModel: (model) =>
  model.envKey === TOKEN_PLAN_ENV_KEY &&
  ((typeof model.baseUrl === 'string' &&
    (model.baseUrl === TOKEN_PLAN_CHINA_BASE_URL ||
      model.baseUrl === TOKEN_PLAN_GLOBAL_BASE_URL)) ||
    (typeof model.name === 'string' &&
      model.name.startsWith('[ModelStudio Token Plan]'))),

Your call which way to go — coding-plan already uses the baseUrl-only form, so deliberately accepting the narrower semantics and updating the fixture is defensible. But it should be a conscious decision recorded in the PR, not a silent side effect, since it changes behaviour for already-installed users.


🟡 Non-blocking

2. The Singapore URL value has no test coverage in core. Mutation check: I changed TOKEN_PLAN_GLOBAL_BASE_URL to the China host and all 68 core provider tests still passed — they reference the constant symbolically throughout. The literal is only asserted in the vscode-ide-companion test, which has its own separate copy of the string, so the two copies could silently drift apart. One literal assertion in the core test would close this. (For contrast, 5 of my 7 mutations — dropping the Global/Intl prefix branch, removing the SG option, dropping ownsModel, and both vscode mutations — were caught.)

3. baseUrlStepTitle: 'Region' is untested. Removing it leaves every test green, yet it's the user-visible step title the PR description highlights.

4. The PR description overstates the VS Code half. It says the change "mirrors the same region support in the VS Code companion subscription-plan definitions so both auth paths generate Token Plan model configs for the selected region." In practice getSubscriptionPlanConfig('token', …) has zero production callers — the only one is settingsWriter.ts:320, hardcoded to 'coding', and read-back is hard-gated to coding at settingsWriter.ts:633. The VS Code interactive auth QuickPick is driven by core's ALL_PROVIDERS (AuthMessageHandler.ts:167-283, reading uiLabels.baseUrlStepTitle at :273), so the Region step there comes from your core preset change, not from subscriptionPlanDefinitions.ts. The regions array is still worth keeping — it makes findSubscriptionPlanByConfig recognise the Singapore endpoint consistently with China — but the "both auth paths" wording should be corrected so future readers don't assume a second flow exists.


Summary

The core design is right, mirrors alibaba-coding-plan.ts closely, and the backward-compatibility story holds up under a real upgrade test. Please fix the three AuthDialog tests — and decide explicitly on the ownsModel semantics in 1b — and I'm happy to approve.

中文版本

本地验证报告(Linux)— PR #7280

我在 Linux 上完整构建并运行了这个分支(PR 标注为仅在 Windows 测试过),包括真实的 /auth 全流程走查、与 merge-base 的编译产物对比,以及针对存量 Token Plan 用户的升级模拟。

结论:功能本身实现正确、也很干净,但它破坏了 3 个现有的 CLI auth 测试,而 CI 在结构上无法发现这一点。其中 1 个是真实的行为回归,不只是导航步骤需要更新。 建议先解决这几点再合并;其余部分都没有问题。

环境说明: PR head 6e21220f,merge-base 102c6921,Linux x86_64,Node 22,完整 npm run build + bundle。这里的 BASE 指 merge-base 的编译产物 —— 我核对过它的 tokenPlanProvider 代码块和模型列表与 102c6921 源码完全一致,因此下面所有 A/B 对比都只隔离了本次改动。所有测试都跑在干净的 PR 工作树上(git status6e21220f 上是干净的)。

✅ 验证通过的部分

  1. Region 步骤真实可用且接线正确。 实际 /auth → Alibaba ModelStudio → Token Plan 会出现区域选择;在 merge-base 上同样路径直接进入 API Key,差分对比很干净(见上方前两张截图)。

  2. 选择新加坡确实路由到 ap-southeast-1 完成向导后 settings.json 里 15 个模型全部指向新加坡 endpoint 并带 Global/Intl 前缀,providerMetadata.token-plan.baseUrl 也是新加坡 URL。用假 key 发起真实请求返回的 401,其帮助链接是国际站域名(www.alibabacloud.com 而非 help.aliyun.com)—— 这证明请求真的打到了新加坡节点,而不是回退到北京。我也从 Linux 侧确认两个 endpoint 都在线,与你在 Windows 上的检查一致。

  3. 存量中国区用户不受影响 —— 这点我实测过,不是推断。 这是我最关注的升级风险:useProviderUpdates 比对的是模板哈希,中国区模板只要有任何扰动,所有存量 Token Plan 用户都会看到一次莫名其妙的"配置有更新"提示。结果是不会:中国区模型配置在 BASE 与 PR 之间深度相等,版本哈希完全一致。实测层面,我用 merge-base 的编译代码生成了一份"PR 之前"的中国区安装状态,再用 PR 构建打开它 —— 没有更新提示、不需要重新认证、前缀保持不变,事后 settings.json 中所有 Token Plan 字段逐字节一致(只有 $version/ui 这类杂项变化)。

  4. 运行时 provider 检测确实不需要改 —— 你"不在范围内"的判断是对的。isDashScopeProvider 已经通用匹配 token-plan.<region>.maas.aliyuncs.com,我确认新加坡主机能被识别,而 token-plan.evil.com 仍然不会被误判。

  5. 42/42 编译产物差分检查全部通过(region 步骤、base URL 解析含尾部斜杠与未知 URL 回退、各区域前缀、install plan、归属判定、凭据匹配、与 Coding Plan 不冲突、DashScope 路由)。

  6. 测试套件与静态检查全绿: core providers 145/145,vscode-ide-companion services 118/118,npm run lint 干净,tsc --noEmit 干净,Prettier 干净。

🔴 发现 1(阻塞):3 个 AuthDialog 测试被破坏,且 CI 在结构上无法发现

packages/cli/src/ui/auth/AuthDialog.test.tsx 未被本 PR 修改,两侧文件完全相同,所以这是一次干净的 A/B:BASE 是 1 失败 / 24 通过,PR 是 4 失败 / 21 通过。那 1 个共同失败(drives API key provider steps from endpoint options metadata)在 merge-base 上同样失败,属于既有问题,与你无关。新增的 3 个全部是 Token Plan 相关。

CI 为什么是绿的: AuthDialog.test.tsx:238-239 中,itWhenTuiInputReliableCI=true 时等于 it.skip。本 PR 的 CI 日志印证了这一点:✓ src/ui/auth/AuthDialog.test.tsx (25 tests | 18 skipped),三个被破坏的测试都在跳过集合里。这里有个更普遍的提醒:任何 Token Plan / Coding Plan 向导流程的回归对 CI 都是不可见的,所以本 PR 的绿勾并不能证明这些流程仍然正常。

1a — 导航(2 个测试,属于功能的预期后果): should submit Token Plan through the shared subscription handlershould return from Token Plan API key input to Token Plan selection 断言的是 Step 1/2 · API Key,而 Token Plan 现在是 3 步向导。按第 889 行附近 Coding Plan 已有的写法补上新步骤即可(注意返回逻辑:Esc 现在是 API Key → Region,再按一次 Esc 才回到 Token Plan 选择)。我本地改完后这两个测试都通过。

1b — 归属语义变化(1 个测试,这个是真实回归): should pre-fill the Model IDs step with previously saved custom model IDs快速失败(403 毫秒,已经走到 Step 3/3,只是输入框是空的),因此不是导航问题。它的 fixture 里保存的模型 baseUrlhttps://dashscope.aliyuncs.com/compatible-mode/v1,并不是 Token Plan 的预设 endpoint。

modelNamePrefix 改成函数后必须显式提供 ownsModel,这就把匹配方式从名称前缀换成了 base URL 白名单。于是 baseUrl 不在两个预设 endpoint 之列的已保存模型不再被认作本 provider 所有。对这类用户有两个后果:一是 /auth 向导不再预填他们保存的自定义模型 ID(即该测试捕获的现象);二是 prepend-and-remove-owned 不再清理这些条目,重装后它们会作为陈旧重复项与新装模型并存。

我通过把 ownsModel 放宽到同时接受旧的名称前缀,验证了这就是根因 —— 25 个 AuthDialog 测试和 145 个 core provider 测试全部通过,包括本 PR 自己的 ownsModel 断言(custom-model 那条没有 name,仍然正确返回 false)。具体写法见英文版代码块。

怎么选由你们定:coding-plan 本来就是只按 baseUrl 匹配的写法,所以有意识地接受这个更窄的语义、同时更新 fixture 也完全说得通。但这应该是一个明确记录在 PR 里的决定,而不是一个静默的副作用,因为它改变了已安装用户的行为。

🟡 非阻塞

  1. 新加坡 URL 的取值在 core 侧没有测试覆盖。 变异测试:我把 TOKEN_PLAN_GLOBAL_BASE_URL 改成中国区主机,68 个 core provider 测试依然全过 —— 因为测试全程是符号化引用该常量的。这个字面量只在 vscode-ide-companion 的测试里被断言,而那边有一份独立的字符串副本,两份副本可能悄悄不一致。在 core 测试里加一条字面量断言就能补上。(作为对照,我 7 个变异里有 5 个被抓到:去掉 Global/Intl 前缀分支、移除 SG 选项、删掉 ownsModel,以及两个 vscode 变异。)

  2. baseUrlStepTitle: 'Region' 没有测试。 把它删掉所有测试依然全绿,但它正是 PR 描述里强调的、用户可见的步骤标题。

  3. PR 描述夸大了 VS Code 那一半的作用。 描述称该改动"在 VS Code companion 的 subscription-plan 定义中同步加入同样的区域支持,确保两个认证路径都会根据所选区域生成 Token Plan model config"。实际上 getSubscriptionPlanConfig('token', …) 没有任何生产调用方 —— 唯一的调用点是 settingsWriter.ts:320,写死为 'coding',而读回路径在 settingsWriter.ts:633 也硬性限定为 coding。VS Code 交互式认证的 QuickPick 由 core 的 ALL_PROVIDERS 驱动(AuthMessageHandler.ts:167-283,在 :273 读取 uiLabels.baseUrlStepTitle),所以那里的 Region 步骤来自你的 core preset 改动,而不是 subscriptionPlanDefinitions.tsregions 数组仍然值得保留 —— 它让 findSubscriptionPlanByConfig 能像识别中国区一样识别新加坡 endpoint —— 但"两个认证路径"的措辞建议修正,以免后来的读者误以为存在第二条流程。

小结

核心设计是对的,与 alibaba-coding-plan.ts 保持了高度一致,向后兼容性在真实升级测试下也站得住。麻烦修复这三个 AuthDialog 测试,并就 1b 的 ownsModel 语义做一个明确决定,之后我很乐意 approve。

@han-dreamer

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough Linux verification. I agree with preserving the legacy Token Plan ownership semantics rather than narrowing this PR to baseUrl-only ownership. I updated ownsModel to keep the BAILIAN_TOKEN_PLAN_API_KEY guard while also accepting the existing [ModelStudio Token Plan] name prefix, updated the Token Plan AuthDialog flow tests for the new Region step, and added focused assertions for the Singapore endpoint literal and Region step title.

I also adjusted the PR description to avoid overstating the VS Code companion path. Locally verified with:

  • npx prettier --check packages/core/src/providers/presets/alibaba-token-plan.ts packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts packages/cli/src/ui/auth/AuthDialog.test.tsx
  • npx vitest run src/providers/__tests__/presets/alibaba-token-plan.test.ts src/providers/__tests__/provider-config.test.ts --reporter=verbose
  • npx vitest run src/ui/auth/AuthDialog.test.tsx -t "Token Plan" --reporter=verbose (collected successfully on Windows; the TUI tests are platform-skipped there)

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: CI still running. Reviewed.

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /review

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
_Qwen Code review request accepted. Review is queued in [workflow run](https://github.com/QwenLM/qwen-code/actions/runs/29797459529)._

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Not reviewed: coverage — could not read the agents' transcripts (no subagent transcripts at /home/github-runner/actions-runner-2/_work/_temp/qwen-home/projects/-home-github-runner-actions-runner-2--work-qwen-code-qwen-code/subagents/12707871-0a9b-4ce4-b5a1-0ca3a71c73c9 (ENOENT: no such file or directory, scandir '/home/github-runner/actions-runner-2/_work/_temp/qwen-home/projects/-home-github-runner-actions-runner-2--work-qwen-code-qwen-code/subagents/12707871-0a9b-4ce4-b5a1-0ca3a71c73c9'). The harness writes one per agent; if there are none, either no agents ran or the harness could not write them.), so this run cannot show that any of the diff was read. Not reviewed: verification — could not check that Step 4 and Step 5 ran (no subagent transcripts at /home/github-runner/actions-runner-2/_work/_temp/qwen-home/projects/-home-github-runner-actions-runner-2--work-qwen-code-qwen-code/subagents/12707871-0a9b-4ce4-b5a1-0ca3a71c73c9 (ENOENT: no such file or directory, scandir '/home/github-runner/actions-runner-2/_work/_temp/qwen-home/projects/-home-github-runner-actions-runner-2--work-qwen-code-qwen-code/subagents/12707871-0a9b-4ce4-b5a1-0ca3a71c73c9'). The harness writes one per agent; if there are none, either no agents ran or the harness could not write them.).

— qwen3.7-max via Qwen Code /review

@dtometzki

Copy link
Copy Markdown

Hi @wenshao

we need your review :-)

@wenshao

wenshao commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /review

@wenshao

wenshao commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Code Review — feat(auth): add Singapore Token Plan region (#7280)

Overview

Adds a Singapore (International) region to the Alibaba ModelStudio Token Plan provider used by /auth, keeping China (Beijing) as the backward-compatible default. The core preset switches baseUrl from a fixed string to a BaseUrlOption[] so /auth renders a Region step, makes modelNamePrefix region-aware, and adds an explicit ownsModel. The VS Code companion's subscription-plan definitions are restructured from single endpoint/documentationUrl/apiKeyUrl fields into a regions[] + defaultRegion shape so Token Plan metadata and config lookup recognize the Singapore endpoint. Closes #7252.

The change closely mirrors the existing coding-plan provider (which already ships China + Singapore), which is the right pattern to follow.

Correctness — verified ✅

I checked out the branch and ran the affected suites:

  • alibaba-token-plan.test.ts — 4/4 pass
  • provider-config.test.ts — 64/64 pass
  • subscriptionPlanDefinitions.test.ts — 2/2 pass (incl. new Singapore case)
  • AuthDialog Token Plan flow — pass, incl. the new Region → API Key → Model IDs navigation and Esc back-navigation

Key contracts hold:

  • baseUrl: BaseUrlOption[] correctly triggers shouldShowStep('baseUrl'), and the first option (China) is the default → backward compatible.
  • modelNamePrefix is now a function, so the auto-derived resolveOwnsModel returns undefined; the explicit ownsModel covers ownership, so nothing regresses.
  • TOKEN_PLAN_BASE_URL is preserved as an alias for TOKEN_PLAN_CHINA_BASE_URL → existing importers unaffected.
  • Cross-package consistency: the global model-name prefix ModelStudio Token Plan for Global/Intl matches between the core preset and the companion — so a model installed via CLI is still detected by the companion.

Note: one unrelated AuthDialog test (drives API key provider steps from endpoint options metadata) fails in my Linux/Node environment, but it fails identically on main without this PR — a pre-existing keyboard-navigation flake, not a regression from #7280.

Observations / minor points (non-blocking)

  1. ownsModel name-prefix fallback is well-justified — add a one-line comment. Unlike coding-plan (baseUrl-only), Token Plan also claims models whose name starts with [ModelStudio Token Plan]. This is meaningful: it catches legacy Token Plan installs whose stored baseUrl was the generic https://dashscope.aliyuncs.com/compatible-mode/v1 rather than the dedicated token-plan.* host (the new test covers exactly this). Since the asymmetry vs coding-plan is intentional, a short comment explaining why Token Plan needs the name fallback would keep a future reader from "simplifying" it away.

  2. Companion: Singapore Token region is detection-only, not user-selectable. The companion's only write path, writeCodingPlanConfig, hardcodes getSubscriptionPlanConfig('coding', …) — there is no Token Plan write path in the IDE. The new regions support is exercised only by findSubscriptionPlanByConfig/isSubscriptionPlanConfig (detection + filtering). This matches the PR's stated scope, but reviewers should note the actual region selection UX lives only in the CLI /auth flow.

  3. Companion usageDocumentationUrl stays China-only. documentationUrl/apiKeyUrl now resolve per-region, but usageDocumentationUrl: TOKEN_PLAN_CHINA_DOC_URL is not regionalized. Inert today (no companion selection path for Token global), but if that path is ever added, a Singapore user would get the China usage link. Cheap to future-proof by moving it per-region.

  4. Doc/endpoint URLs are duplicated across the core preset and the companion constants. This mirrors the existing coding-plan duplication, so it's consistent — just a latent drift risk to keep in mind.

Test coverage — good

New tests cover the Singapore install plan, regional model-name prefix, provider state, credential matching for both region URLs, ownsModel across both regions + legacy name-prefix + negatives, getAllProviderBaseUrls/findProviderByCredentials inclusion, the companion China-default/Global-region resolution, and the CLI Region-step + Esc navigation. The test-import restructuring (new symbols imported from relative source instead of the package name) is correct and necessary, since the package name resolves to dist/ which lags an unbuilt branch.

Security / performance

No concerns. No new credential handling or network calls in the changed code; endpoint/doc URLs are static constants; the added functions are trivial and run only at install/detection time.

Verdict

LGTM. Clean, well-tested, backward-compatible, and faithful to the established coding-plan pattern. The points above are nits, not blockers.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

modelNamePrefix: 'ModelStudio Token Plan for Global/Intl',
},
],
usageDocumentationUrl: TOKEN_PLAN_CHINA_DOC_URL,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] usageDocumentationUrl is hardcoded to the China Aliyun console URL at the plan level, so Global/International users receive the Chinese documentation link instead of the international one.

Failure scenario: A Singapore/International user configures Token Plan via VS Code. getSubscriptionPlanConfig('token', CodingPlanRegion.GLOBAL) correctly returns TOKEN_PLAN_GLOBAL_DOC_URL for documentationUrl and apiKeyUrl (both per-region), but returns TOKEN_PLAN_CHINA_DOC_URL for usageDocumentationUrl because it reads the plan-level field directly. The user clicks "usage documentation" and lands on bailian.console.aliyun.com/cn-beijing instead of the international console.

Note: the SubscriptionPlanRegionConfig interface lacks a usageDocumentationUrl field, so per-region resolution isn't structurally available yet. Consider adding it (mirroring how documentationUrl and apiKeyUrl are per-region) or falling back to the region's documentationUrl in getSubscriptionPlanConfig.

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: observed bug with solid evidence. #7334 documents a concrete reproduction (background subagent completes after the parent turn ends → the model's final background_notification_response is silently dropped, the user only ever sees the interim acknowledgement). I confirmed the root cause in the code: Session.#emitBackgroundNotificationResponse (in packages/cli acp-integration) stamps qwenDiscreteMessage: true, and the bridges previously discarded every discrete message — so the final answer was lost along with the internal notification. This is real, not theoretical.

Direction: aligned. Delivering a background agent's final reply back to the originating Channel chat is squarely within the daemon/channels feature area, which is under active development. It correctly preserves the #7223 isolation (the internal background_notification stays filtered; only background_notification_response is delivered). No direct CHANGELOG entry for this exact path, but the surrounding area is clearly relevant.

Size: cross-package (packages/channels + packages/cli), so the core gate applies. ~102 production logic lines (AcpBridge 16, ChannelBase 33, DaemonChannelBridge 15, runtime 30, start 5, daemon-worker 2, ChannelAgentBridge 1) vs. ~365 test lines. Well under every threshold; no maintainer-awareness escalation needed.

Approach: the scope feels right and the design is the obvious one — it mirrors the existing toolCall relay exactly. The bridges emit a new backgroundResponse event for the one source that matters; ChannelBase.dispatchBackgroundResponse handles the standalone (no-router) case via the existing proactive-send-then-fallback helpers; registerBackgroundResponseRelay handles the gateway/daemon (router-supplied) case. The two paths are mutually exclusive by the existing registerBridgeEvents ?? !router rule, so no double delivery, and registration is added to all four startup paths. The newest commit adds a rewritten !== true guard: when message-rewriting is enabled the rewriter appends a second copy of the response stamped _meta.rewritten: true, and without the guard the user would get the reply twice. I verified against MessageRewriteMiddleware that the original is always sent as-is, so skipping the rewritten append dedupes without ever dropping the response. Every edit is load-bearing — no drive-by refactors or churn.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:已观测到的 bug,证据充分。#7334 给出了具体复现(后台 subagent 在父请求结束后才完成 → 模型最终的 background_notification_response 被静默丢弃,用户只看到中间确认话术)。我在代码中确认了根因:Session.#emitBackgroundNotificationResponse(位于 packages/cli 的 acp-integration)带有 qwenDiscreteMessage: true,而桥接层此前会丢弃所有离散消息——于是最终答案和内部通知一起被丢掉。这是真实问题,不是理论假设。

方向:对齐。把后台 agent 的最终回复投递回发起会话的 Channel,完全属于 daemon/channels 功能领域,且该领域正在活跃开发。它正确保留了 #7223 的隔离(内部 background_notification 继续被过滤,只投递 background_notification_response)。CHANGELOG 没有这条路径的直接条目,但周边领域明显相关。

规模:跨包(packages/channels + packages/cli),触发核心门禁。约 102 行生产逻辑(AcpBridge 16、ChannelBase 33、DaemonChannelBridge 15、runtime 30、start 5、daemon-worker 2、ChannelAgentBridge 1),测试约 365 行。远低于所有阈值,无需维护者关注升级。

方案:范围合理,设计也是最自然的一种——完全复刻现有 toolCall 转发模式。桥接层只对关键的那一个 source 发出新的 backgroundResponse 事件;ChannelBase.dispatchBackgroundResponse 通过现有的「主动发送优先、否则回退」辅助方法处理 standalone(无 router)场景;registerBackgroundResponseRelay 处理 gateway/daemon(带 router)场景。两条路径由现有的 registerBridgeEvents ?? !router 规则互斥,因此不会重复投递,且注册逻辑已加入全部四个启动路径。最新一次提交增加了 rewritten !== true 守卫:当启用消息重写时,重写器会追加一份带 _meta.rewritten: true 的响应副本,若无此守卫用户会收到两次回复。我已对照 MessageRewriteMiddleware 确认原始消息总是原样发送,因此跳过重写副本只是去重,绝不会丢掉响应。每处改动都是必需的——没有顺手重构或无关变更。

进入代码审查 🔍

Qwen Code · qwen3.7-max

Reviewed at c271ff1c4040e74967fd874829943736557d9961 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: to add Singapore region to Token Plan, I would change baseUrl from a string to a BaseUrlOption[] array (mirroring Coding Plan), add a modelNamePrefix function for per-region prefixes, add ownsModel to recognize models from both endpoints (plus legacy name-prefix ownership for backward compat), update the VS Code companion subscription plan definitions, and update tests.

Comparison with the diff: the PR does exactly this. The implementation is a near-mirror of the Coding Plan provider's region support — same BaseUrlOption[] shape, same modelNamePrefix function pattern, same baseUrlStepTitle: 'Region' label. The one addition beyond the Coding Plan pattern is the legacy model.name.startsWith('[ModelStudio Token Plan]') check in ownsModel, which is necessary because Token Plan previously relied on name-prefix ownership without an explicit ownsModel function. This preserves backward compatibility for existing users' saved configs.

No critical blockers found. No AGENTS.md violations. The code is straightforward and follows established conventions.

Tests: all pass —

  • alibaba-token-plan.test.ts: 4/4 ✓ (including Singapore install plan, credential matching for both regions, ownership with legacy name-prefix and negative case)
  • provider-config.test.ts: 64/64 ✓ (including Token Plan credential matching for both region URLs, getAllProviderBaseUrls expansion)
  • subscriptionPlanDefinitions.test.ts: 2/2 ✓ (China default + Singapore region)
  • AuthDialog.test.tsx: 7 passed, 18 skipped ✓ (skipped tests are the TUI-input-reliable set, pre-existing CI skip — not caused by this PR)
  • tsc --noEmit: clean for both packages/core and packages/vscode-ide-companion

Real-Scenario Testing (tmux)

Built the PR branch (npm run build && npm run bundle) and drove the /auth flow in tmux:

Token Plan → Region step (new):

  │ Alibaba ModelStudio · Step 1/3 · Region                                                          │
  │                                                                                                  │
  │ › China (Beijing)                                                                                │
  │   https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1                             │
  │                                                                                                  │
  │   Singapore (International)                                                                      │
  │   https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1                         │

Selecting Singapore → API Key step:

  │ Alibaba ModelStudio · Step 2/3 · API Key                                                         │
  │                                                                                                  │
  │ > sk-...                                                                                         │

Esc from API Key → back to Region:

  │ Alibaba ModelStudio · Step 1/3 · Region                                                          │
  │                                                                                                  │
  │   China (Beijing)                                                                                │
  │   https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1                             │
  │                                                                                                  │
  │ › Singapore (International)                                                                      │
  │   https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1                         │

Esc from Region → back to Access Method:

  │ Alibaba ModelStudio · Access Method                                                              │
  │                                                                                                  │
  │   Coding Plan                                                                                    │
  │   For individual developers · Weekly quota included                                              │
  │                                                                                                  │
  │ › Token Plan                                                                                     │
  │   For teams and companies · Usage-based billing with dedicated endpoint                          │

China default path also verified: selecting Token Plan → China (Beijing) is the default highlight → Enter → Step 2/3 API Key. Existing China users see no disruption.

中文说明

代码审查

独立方案: 要为 Token Plan 添加新加坡区域,我会将 baseUrl 从字符串改为 BaseUrlOption[] 数组(与 Coding Plan 一致),添加 modelNamePrefix 函数实现按区域区分前缀,添加 ownsModel 以识别两个 endpoint 的模型(加上旧版名称前缀所有权以保持向后兼容),更新 VS Code companion 的订阅计划定义,并更新测试。

与 diff 对比: PR 完全这样做了。实现几乎是 Coding Plan provider 区域支持的镜像。唯一超出 Coding Plan 模式的是 ownsModel 中的旧版 model.name.startsWith('[ModelStudio Token Plan]') 检查,这是必要的,因为 Token Plan 之前依赖名称前缀所有权而没有显式的 ownsModel 函数。

未发现关键阻塞问题。未发现 AGENTS.md 违规。代码简洁,遵循既有惯例。

测试: 全部通过。tsc --noEmit 两个包均干净。

真实场景测试 (tmux)

构建了 PR 分支并在 tmux 中驱动 /auth 流程:Region 步骤正确显示中国区和新加坡区选项,选择新加坡后进入 API Key 步骤,Esc 正确回退到 Region,再 Esc 回退到 Access Method。中国区默认路径也验证通过。

Qwen Code · qwen3.7-max

Reviewed at 41b47911b01a155ca3a6d10c3f2ba15ad1d52e1d · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean implementation that mirrors the established Coding Plan region pattern; the only reservation is that the AuthDialog wizard-flow tests remain CI-invisible (pre-existing gap, not this PR's fault).

This is a textbook "extend the existing pattern" PR. My independent proposal and the actual diff are essentially the same thing — change baseUrl to BaseUrlOption[], add per-region prefix, add ownsModel with legacy compat. The author also handled the backward-compatibility edge case I would have worried about: existing Token Plan users' saved configs use the old name-prefix ownership model, and the new ownsModel explicitly preserves that via the startsWith('[ModelStudio Token Plan]') fallback. The TOKEN_PLAN_BASE_URL alias means no downstream import breaks.

The tmux run confirms the feature works end-to-end: Region step appears, both regions route correctly, Esc back-navigation walks the full chain (API Key → Region → Access Method), and China remains the default. The previous review's three broken AuthDialog tests are fixed in this head — navigation updated for the 3-step wizard, and the ownership negative case (valid Token Plan URL + wrong env key → false) is covered.

One non-blocking note: the 18 skipped AuthDialog tests (TUI-input-reliable set) mean wizard-flow regressions are structurally invisible to CI. That's a pre-existing gap worth tracking separately, not a reason to hold this PR.

LGTM, shipping it. ✅

中文说明

置信度:4/5 — 干净的实现,完全镜像了既有的 Coding Plan 区域模式;唯一的保留是 AuthDialog 向导流程测试在 CI 中不可见(已有缺口,非本 PR 造成)。

这是一个教科书式的"扩展现有模式"PR。我的独立方案和实际 diff 基本一致。作者还处理了我担心的向后兼容边界情况:现有 Token Plan 用户保存的配置使用旧的名称前缀所有权模型,新的 ownsModel 通过 startsWith('[ModelStudio Token Plan]') 回退显式保留了这一点。

tmux 运行确认功能端到端可用:Region 步骤正确显示,两个区域路由正确,Esc 回退走完整链路,中国区保持默认。之前审查中三个失败的 AuthDialog 测试已在此 head 中修复。

非阻塞备注:18 个跳过的 AuthDialog 测试意味着向导流程回归在 CI 中结构性不可见。这是值得单独跟踪的已有缺口,不是扣留本 PR 的理由。

Qwen Code · qwen3.7-max

Reviewed at 41b47911b01a155ca3a6d10c3f2ba15ad1d52e1d · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao
wenshao added this pull request to the merge queue Jul 21, 2026
Merged via the queue into QwenLM:main with commit 848eb5d Jul 21, 2026
97 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.20.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: token-plan.ap-southeast-1 is not selectable on /auth

4 participants