Cursor/connect tool wizard - #6277
Conversation
Enable multi-arch image publishing to ghcr.io on version tags and manual dispatch. Co-authored-by: Cursor <cursoragent@cursor.com>
Checkout the triggering commit directly instead of treating the image tag as a git ref. Co-authored-by: Cursor <cursoragent@cursor.com>
Docker requires lowercase registry paths; normalize github.repository before tagging. Co-authored-by: Cursor <cursoragent@cursor.com>
The create_manifests job needs write access to publish multi-arch tags. Co-authored-by: Cursor <cursoragent@cursor.com>
Create a token with the selected group and open CC Switch or Cherry Studio via deep link, returning the plaintext key from AddToken for one-click setup. Co-authored-by: Cursor <cursoragent@cursor.com>
Expose enable_groups_by_endpoint in pricing and narrow the wizard to Anthropic/OpenAI models for the selected protocol and group. Co-authored-by: Cursor <cursoragent@cursor.com>
WalkthroughAdds a connect-tool wizard with endpoint-aware pricing, API-key creation and client import URLs. It also adds exact redemption-key search, redemption-code export, a multi-architecture GHCR workflow, and supporting localization updates. ChangesConnect tool integration
Redemption workflows
Container publishing
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Operator
participant ConnectToolDialog
participant PricingAPI
participant ToolApplication
Operator->>ConnectToolDialog: select provider, group, model, and client
ConnectToolDialog->>PricingAPI: create API key
PricingAPI-->>ConnectToolDialog: return key data
ConnectToolDialog->>ToolApplication: open import URL
ConnectToolDialog-->>Operator: show manual key instructions
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Warning |
|
lgtm |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/docker-ghcr-custom.yml:
- Around line 17-19: Remove the workflow-level packages: write permission from
the permissions block in docker-ghcr-custom.yml, leaving only contents: read.
Preserve the existing job-level packages: write declarations in
build_single_arch and create_manifests.
- Around line 61-64: Update the actions/checkout step in the Check out workflow
action to explicitly disable credential persistence by setting
persist-credentials to false alongside fetch-depth, preventing the GITHUB_TOKEN
from being stored in .git/config.
- Around line 30-37: Remove direct GitHub expression interpolation from every
run block in the workflow, especially the tag/image handling around TAG and
IMAGE_NAME. Map required github context values, including workflow_dispatch
image_tag, repository, event name, and ref, through step-level env variables,
then reference quoted shell variables in the scripts. Update remaining steps to
use "$TAG" and "$IMAGE_NAME" instead of interpolated context or env expressions
while preserving the existing outputs and GITHUB_ENV behavior.
In `@model/redemption.go`:
- Around line 79-103: Update the redemption query construction to reuse the
existing commonKeyCol symbol from model/main.go instead of locally selecting
keyCol via a PostgreSQL check. Normalize normalizedKey to lowercase before
validating and comparing it, while preserving the existing exact-key detection
and query branches.
In
`@web/default/src/features/redemption-codes/components/redemptions-mutate-drawer.tsx`:
- Around line 96-104: Update downloadRedemptionCodes to append the created
anchor to document.body before invoking anchor.click(), then remove that same
anchor immediately afterward while preserving the existing download and URL
cleanup behavior.
In `@web/default/src/i18n/locales/ja.json`:
- Line 2503: Update the Japanese translation for the “Loading available
providers...” key to use provider terminology consistently, replacing “タイプ” with
“プロバイダー” or the intended “プロバイダータイプ” wording while preserving the existing
loading-message style.
In `@web/default/src/i18n/locales/zh-TW.json`:
- Line 121: Replace the English values for all referenced new entries in the
zh-TW locale with reviewed Traditional Chinese translations, including the
connect-tool wizard strings. Preserve interpolation tokens and punctuation, and
update the line 1439 translation to explicitly include the meaning “as a text
file.”
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 428aec85-6a0b-47ed-9c58-30f39bee1b71
📒 Files selected for processing (23)
.github/workflows/docker-ghcr-custom.ymlcontroller/token.gomodel/pricing.gomodel/redemption.gomodel/redemption_test.goweb/default/src/features/keys/api.tsweb/default/src/features/keys/components/api-keys-dialogs.tsxweb/default/src/features/keys/components/api-keys-primary-buttons.tsxweb/default/src/features/keys/components/dialogs/connect-tool-dialog.tsxweb/default/src/features/keys/lib/connect-tool.tsweb/default/src/features/keys/types.tsweb/default/src/features/pricing/types.tsweb/default/src/features/redemption-codes/components/redemptions-mutate-drawer.tsxweb/default/src/features/redemption-codes/components/redemptions-table.tsxweb/default/src/i18n/locales/_reports/_sync-report.jsonweb/default/src/i18n/locales/en.jsonweb/default/src/i18n/locales/fr.jsonweb/default/src/i18n/locales/ja.jsonweb/default/src/i18n/locales/ru.jsonweb/default/src/i18n/locales/vi.jsonweb/default/src/i18n/locales/zh-TW.jsonweb/default/src/i18n/locales/zh.jsonweb/default/src/i18n/static-keys.ts
| permissions: | ||
| contents: read | ||
| packages: write |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Scope down workflow-level permissions.
packages: write at the workflow level grants it to every job, including prepare, which only writes $GITHUB_OUTPUT. The build_single_arch and create_manifests jobs already declare packages: write at job scope, so the workflow default can be contents: read.
🔒 Proposed fix
permissions:
contents: read
- packages: write📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| permissions: | |
| contents: read | |
| packages: write | |
| permissions: | |
| contents: read |
🧰 Tools
🪛 zizmor (1.26.1)
[error] 19-19: overly broad permissions (excessive-permissions): packages: write is overly broad at the workflow level
(excessive-permissions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/docker-ghcr-custom.yml around lines 17 - 19, Remove the
workflow-level packages: write permission from the permissions block in
docker-ghcr-custom.yml, leaving only contents: read. Preserve the existing
job-level packages: write declarations in build_single_arch and
create_manifests.
Source: Linters/SAST tools
| run: | | ||
| echo "image_name=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" | ||
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | ||
| TAG="${{ github.event.inputs.image_tag }}" | ||
| else | ||
| TAG=${GITHUB_REF#refs/tags/} | ||
| fi | ||
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Avoid injecting GitHub context directly into run: shell (template injection).
${{ github.event.inputs.image_tag }} (and the other ${{ ... }} expansions in run: blocks such as Lines 68, 72, 108-109, 124-125, 137-154) is expanded into the shell before execution. A crafted workflow_dispatch input or tag ref can break out and execute arbitrary commands on the runner. Map the context to env: and reference shell variables instead.
🔒 Proposed fix (env mapping)
- id: meta
+ env:
+ REPOSITORY: ${{ github.repository }}
+ EVENT_NAME: ${{ github.event_name }}
+ IMAGE_TAG_INPUT: ${{ github.event.inputs.image_tag }}
run: |
- echo "image_name=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- TAG="${{ github.event.inputs.image_tag }}"
+ echo "image_name=$(echo "$REPOSITORY" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
+ if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
+ TAG="$IMAGE_TAG_INPUT"
else
TAG=${GITHUB_REF#refs/tags/}
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"Apply the same treatment to the remaining run: steps: they already set TAG/IMAGE_NAME into $GITHUB_ENV, so use "$TAG"/"$IMAGE_NAME" in the scripts rather than ${{ env.TAG }}/${{ env.IMAGE_NAME }}.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| run: | | |
| echo "image_name=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| TAG="${{ github.event.inputs.image_tag }}" | |
| else | |
| TAG=${GITHUB_REF#refs/tags/} | |
| fi | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | |
| env: | |
| REPOSITORY: ${{ github.repository }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| IMAGE_TAG_INPUT: ${{ github.event.inputs.image_tag }} | |
| run: | | |
| echo "image_name=$(echo "$REPOSITORY" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| TAG="$IMAGE_TAG_INPUT" | |
| else | |
| TAG=${GITHUB_REF#refs/tags/} | |
| fi | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" |
🧰 Tools
🪛 zizmor (1.26.1)
[error] 33-33: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/docker-ghcr-custom.yml around lines 30 - 37, Remove direct
GitHub expression interpolation from every run block in the workflow, especially
the tag/image handling around TAG and IMAGE_NAME. Map required github context
values, including workflow_dispatch image_tag, repository, event name, and ref,
through step-level env variables, then reference quoted shell variables in the
scripts. Update remaining steps to use "$TAG" and "$IMAGE_NAME" instead of
interpolated context or env expressions while preserving the existing outputs
and GITHUB_ENV behavior.
Source: Linters/SAST tools
| - name: Check out | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Disable credential persistence on checkout.
This checkout runs on a job that logs into GHCR and pushes images; leaving persist-credentials at its default writes the GITHUB_TOKEN into .git/config, where later steps could exfiltrate it. Set persist-credentials: false.
🔒 Proposed fix
- name: Check out
uses: actions/checkout@v4
with:
fetch-depth: 0
+ persist-credentials: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Check out | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check out | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false |
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 61-64: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/docker-ghcr-custom.yml around lines 61 - 64, Update the
actions/checkout step in the Check out workflow action to explicitly disable
credential persistence by setting persist-credentials to false alongside
fetch-depth, preventing the GITHUB_TOKEN from being stored in .git/config.
Source: Linters/SAST tools
| keyCol := "`key`" | ||
| if common.UsingMainDatabase(common.DatabaseTypePostgreSQL) { | ||
| keyCol = `"key"` | ||
| } | ||
| normalizedKey := strings.ReplaceAll(strings.TrimSpace(keyword), "-", "") | ||
| exactKeyMatch := len(normalizedKey) == 32 | ||
| if exactKeyMatch { | ||
| _, decodeErr := hex.DecodeString(normalizedKey) | ||
| exactKeyMatch = decodeErr == nil | ||
| } | ||
|
|
||
| if id, err := strconv.Atoi(keyword); err == nil { | ||
| query = query.Where("id = ? OR name LIKE ?", id, keyword+"%") | ||
| if exactKeyMatch { | ||
| query = query.Where( | ||
| fmt.Sprintf("id = ? OR name LIKE ? OR %s = ?", keyCol), | ||
| id, keyword+"%", normalizedKey, | ||
| ) | ||
| } else { | ||
| query = query.Where("id = ? OR name LIKE ?", id, keyword+"%") | ||
| } | ||
| } else if exactKeyMatch { | ||
| query = query.Where( | ||
| fmt.Sprintf("name LIKE ? OR %s = ?", keyCol), | ||
| keyword+"%", normalizedKey, | ||
| ) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Use commonKeyCol and normalize key casing for robust matching.
As per coding guidelines, use the commonKeyCol variable from model/main.go to handle database-specific column quoting for reserved words, rather than manually duplicating the DatabaseTypePostgreSQL check.
Additionally, consider converting the normalized key to lowercase. PostgreSQL's exact match is case-sensitive, so if a user searches using an uppercase hex key, the match might fail depending on how it's stored.
♻️ Proposed refactor
- keyCol := "`key`"
- if common.UsingMainDatabase(common.DatabaseTypePostgreSQL) {
- keyCol = `"key"`
- }
- normalizedKey := strings.ReplaceAll(strings.TrimSpace(keyword), "-", "")
+ normalizedKey := strings.ToLower(strings.ReplaceAll(strings.TrimSpace(keyword), "-", ""))
exactKeyMatch := len(normalizedKey) == 32
if exactKeyMatch {
_, decodeErr := hex.DecodeString(normalizedKey)
exactKeyMatch = decodeErr == nil
}
if id, err := strconv.Atoi(keyword); err == nil {
if exactKeyMatch {
query = query.Where(
- fmt.Sprintf("id = ? OR name LIKE ? OR %s = ?", keyCol),
+ fmt.Sprintf("id = ? OR name LIKE ? OR %s = ?", commonKeyCol),
id, keyword+"%", normalizedKey,
)
} else {
query = query.Where("id = ? OR name LIKE ?", id, keyword+"%")
}
} else if exactKeyMatch {
query = query.Where(
- fmt.Sprintf("name LIKE ? OR %s = ?", keyCol),
+ fmt.Sprintf("name LIKE ? OR %s = ?", commonKeyCol),
keyword+"%", normalizedKey,
)
} else {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| keyCol := "`key`" | |
| if common.UsingMainDatabase(common.DatabaseTypePostgreSQL) { | |
| keyCol = `"key"` | |
| } | |
| normalizedKey := strings.ReplaceAll(strings.TrimSpace(keyword), "-", "") | |
| exactKeyMatch := len(normalizedKey) == 32 | |
| if exactKeyMatch { | |
| _, decodeErr := hex.DecodeString(normalizedKey) | |
| exactKeyMatch = decodeErr == nil | |
| } | |
| if id, err := strconv.Atoi(keyword); err == nil { | |
| query = query.Where("id = ? OR name LIKE ?", id, keyword+"%") | |
| if exactKeyMatch { | |
| query = query.Where( | |
| fmt.Sprintf("id = ? OR name LIKE ? OR %s = ?", keyCol), | |
| id, keyword+"%", normalizedKey, | |
| ) | |
| } else { | |
| query = query.Where("id = ? OR name LIKE ?", id, keyword+"%") | |
| } | |
| } else if exactKeyMatch { | |
| query = query.Where( | |
| fmt.Sprintf("name LIKE ? OR %s = ?", keyCol), | |
| keyword+"%", normalizedKey, | |
| ) | |
| normalizedKey := strings.ToLower(strings.ReplaceAll(strings.TrimSpace(keyword), "-", "")) | |
| exactKeyMatch := len(normalizedKey) == 32 | |
| if exactKeyMatch { | |
| _, decodeErr := hex.DecodeString(normalizedKey) | |
| exactKeyMatch = decodeErr == nil | |
| } | |
| if id, err := strconv.Atoi(keyword); err == nil { | |
| if exactKeyMatch { | |
| query = query.Where( | |
| fmt.Sprintf("id = ? OR name LIKE ? OR %s = ?", commonKeyCol), | |
| id, keyword+"%", normalizedKey, | |
| ) | |
| } else { | |
| query = query.Where("id = ? OR name LIKE ?", id, keyword+"%") | |
| } | |
| } else if exactKeyMatch { | |
| query = query.Where( | |
| fmt.Sprintf("name LIKE ? OR %s = ?", commonKeyCol), | |
| keyword+"%", normalizedKey, | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@model/redemption.go` around lines 79 - 103, Update the redemption query
construction to reuse the existing commonKeyCol symbol from model/main.go
instead of locally selecting keyCol via a PostgreSQL check. Normalize
normalizedKey to lowercase before validating and comparing it, while preserving
the existing exact-key detection and query branches.
Source: Coding guidelines
| function downloadRedemptionCodes(keys: string[], filename: string) { | ||
| const blob = new Blob([keys.join('\n')], { type: 'text/plain;charset=utf-8' }) | ||
| const url = URL.createObjectURL(blob) | ||
| const anchor = document.createElement('a') | ||
| anchor.href = url | ||
| anchor.download = filename | ||
| anchor.click() | ||
| URL.revokeObjectURL(url) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Append the anchor element to the document body before clicking.
In some browsers, particularly Firefox, calling .click() on an unattached anchor element is silently ignored, preventing the file from downloading. Append the anchor to document.body before clicking, and remove it immediately after to ensure cross-browser compatibility.
🐛 Proposed fix
function downloadRedemptionCodes(keys: string[], filename: string) {
const blob = new Blob([keys.join('\n')], { type: 'text/plain;charset=utf-8' })
const url = URL.createObjectURL(blob)
const anchor = document.createElement('a')
anchor.href = url
anchor.download = filename
+ document.body.appendChild(anchor)
anchor.click()
+ document.body.removeChild(anchor)
URL.revokeObjectURL(url)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function downloadRedemptionCodes(keys: string[], filename: string) { | |
| const blob = new Blob([keys.join('\n')], { type: 'text/plain;charset=utf-8' }) | |
| const url = URL.createObjectURL(blob) | |
| const anchor = document.createElement('a') | |
| anchor.href = url | |
| anchor.download = filename | |
| anchor.click() | |
| URL.revokeObjectURL(url) | |
| } | |
| function downloadRedemptionCodes(keys: string[], filename: string) { | |
| const blob = new Blob([keys.join('\n')], { type: 'text/plain;charset=utf-8' }) | |
| const url = URL.createObjectURL(blob) | |
| const anchor = document.createElement('a') | |
| anchor.href = url | |
| anchor.download = filename | |
| document.body.appendChild(anchor) | |
| anchor.click() | |
| document.body.removeChild(anchor) | |
| URL.revokeObjectURL(url) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@web/default/src/features/redemption-codes/components/redemptions-mutate-drawer.tsx`
around lines 96 - 104, Update downloadRedemptionCodes to append the created
anchor to document.body before invoking anchor.click(), then remove that same
anchor immediately afterward while preserving the existing download and URL
cleanup behavior.
| "Load template...": "テンプレートをロード...", | ||
| "Loader": "ローダー", | ||
| "Loading": "読み込み中", | ||
| "Loading available providers...": "利用可能なタイプを読み込み中…", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Translate “providers” consistently.
The source key says “Loading available providers...”, but the Japanese value says “loading available types.” Use 利用可能なプロバイダーを読み込み中… (or 利用可能なプロバイダータイプを読み込み中… if that is the intended UI concept) so the translation matches the actual wizard state.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/default/src/i18n/locales/ja.json` at line 2503, Update the Japanese
translation for the “Loading available providers...” key to use provider
terminology consistently, replacing “タイプ” with “プロバイダー” or the intended
“プロバイダータイプ” wording while preserving the existing loading-message style.
| "80,443,8080": "80,443,8080", | ||
| "A billing multiplier. Lower ratios mean lower API call costs.": "收費乘數,倍率越低,API 呼叫費用越低。", | ||
| "A focused home for keys, balance, routing, and service health.": "集中展示金鑰、餘額、路由和服務健康狀態。", | ||
| "A recommended model is selected automatically. You can change it.": "A recommended model is selected automatically. You can change it.", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Localize the new zh-TW values before merging.
These entries render English strings in the Traditional Chinese locale, including most of the new connect-tool wizard. Line 1439 also omits “as a text file” from the source meaning. Replace the English values with reviewed Traditional Chinese translations while preserving interpolation and punctuation.
Also applies to: 391-391, 955-955, 988-988, 1023-1023, 1124-1124, 1136-1136, 1439-1439, 2272-2272, 2503-2503, 2897-2897, 2960-2962, 3353-3353, 3550-3550, 4028-4028
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/default/src/i18n/locales/zh-TW.json` at line 121, Replace the English
values for all referenced new entries in the zh-TW locale with reviewed
Traditional Chinese translations, including the connect-tool wizard strings.
Preserve interpolation tokens and punctuation, and update the line 1439
translation to explicitly include the meaning “as a text file.”
Source: Coding guidelines
Important
📝 变更描述 / Description
(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
(请在此粘贴截图、关键日志或测试报告,以证明变更生效)
Summary by CodeRabbit