Skip to content

feat(skills): add batch upload functionality for multiple skill ZIP files#5804

Merged
Soulter merged 9 commits intoAstrBotDevs:masterfrom
whatevertogo:feat/batch-upload-skills
Mar 7, 2026
Merged

feat(skills): add batch upload functionality for multiple skill ZIP files#5804
Soulter merged 9 commits intoAstrBotDevs:masterfrom
whatevertogo:feat/batch-upload-skills

Conversation

@whatevertogo
Copy link
Contributor

@whatevertogo whatevertogo commented Mar 6, 2026

为 AstrBot Dashboard 的 Skills 管理页面添加批量上传功能,支持用户一次选择多个 ZIP 文件上传,并显示每个文件的上传结果。

Modifications / 改动点

后端改动:

  • astrbot/dashboard/routes/skills.py:新增 POST /api/skills/batch-upload 端点,循环调用现有 install_skill_from_zip() 方法,返回每个文件的成功/失败结果

前端改动:

  • dashboard/src/components/extension/SkillsSection.vue
    • 文件选择器改为支持多选 (multiple)
    • 新增拖拽上传区域
    • 新增文件列表预览,显示校验状态
    • 新增结果汇总对话框,区分成功/失败列表

国际化:

  • dashboard/src/i18n/locales/zh-CN/features/extension.json:新增批量上传相关中文文案
  • dashboard/src/i18n/locales/en-US/features/extension.json:新增批量上传相关英文文案

文件变更统计:

文件 变更类型 说明
astrbot/dashboard/routes/skills.py 修改 新增批量上传 API
dashboard/src/components/extension/SkillsSection.vue 修改 多选支持 + 拖拽上传 + 结果汇总
dashboard/src/i18n/locales/zh-CN/features/extension.json 修改 中文文案
dashboard/src/i18n/locales/en-US/features/extension.json 修改 英文文案
tests/test_dashboard.py 修改 批量上传 API 测试用例
  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

测试验证:

# Python 语法检查
python -m py_compile astrbot/dashboard/routes/skills.py
# 输出:Python syntax OK

# JSON 格式检查
python -c "import json; json.load(open('dashboard/src/i18n/locales/zh-CN/features/extension.json'))"
python -c "import json; json.load(open('dashboard/src/i18n/locales/en-US/features/extension.json'))"
# 输出:JSON OK

功能验证:

  • 选择单个文件:与原有功能一致
  • 选择多个文件:显示结果汇总,成功/失败分别展示
  • 拖拽上传:支持拖拽多个 ZIP 文件

58f4632f42ac3a1955dd947ae662ffd7

做了翻译的

Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了"验证步骤"和"运行截图"。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

在仪表板中添加批量技能上传支持,使其能够在一次操作中上传、验证并汇总多个 ZIP 文件的结果。

New Features:

  • 引入 POST /api/skills/batch-upload 接口,用于在一次请求中处理多个技能 ZIP 文件,并返回每个文件的处理结果。
  • 增强技能管理界面(Skills management UI),重新设计上传对话框,支持多文件选择、拖拽上传以及按文件显示上传状态。

Enhancements:

  • 在前端提供详细的上传结果映射,包括成功、错误、跳过和上传中等状态,并显示汇总计数。
  • 改进技能上传的用户体验,包括文件大小格式化、重复文件和文件类型校验,以及针对小屏设备的响应式布局优化。

Tests:

  • 添加后端测试,覆盖批量上传接口,包括无效文件、有效 ZIP 文件处理,以及与技能管理器和沙箱同步的集成测试。
Original summary in English

Summary by Sourcery

Add batch skill upload support to the dashboard, enabling multiple ZIP files to be uploaded, validated, and reported in a single operation.

New Features:

  • Introduce a POST /api/skills/batch-upload endpoint to process multiple skill ZIP files in one request and return per-file results.
  • Enhance the Skills management UI with a redesigned upload dialog that supports multi-file selection, drag-and-drop, and per-file status display.

Enhancements:

  • Provide detailed upload result mapping on the frontend, including success, error, skipped, and uploading states with summarized counts.
  • Improve skill upload UX with file size formatting, duplicate and file-type validation, and responsive layout tweaks for smaller screens.

Tests:

  • Add backend tests covering the batch upload endpoint, including invalid files, valid ZIP handling, and integration with the skill manager and sandbox sync.

…iles

- Implemented a new endpoint for batch uploading skills.
- Enhanced the SkillsSection component to support multiple file selection and drag-and-drop functionality.
- Updated localization files for new upload features and messages.
- Added tests to validate batch upload behavior and error handling.
Copilot AI review requested due to automatic review settings March 6, 2026 11:20
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Mar 6, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the skill management system by enabling users to upload multiple skill packages concurrently. This feature streamlines the process of adding new skills to the platform, improving efficiency and user experience. The changes span both the backend API for processing batch requests and the frontend interface for an intuitive multi-file upload and feedback mechanism.

Highlights

  • Batch Skill Upload API: A new backend API endpoint /api/skills/batch-upload has been introduced to handle the simultaneous upload of multiple skill ZIP files. This endpoint processes each file, attempts installation, and returns a consolidated result of successes and failures.
  • Enhanced Frontend Upload Experience: The frontend UI for skill uploads has been significantly revamped to support multi-file selection and drag-and-drop functionality. It now includes a detailed file list preview with validation status for each selected file and a comprehensive result summary dialog after the batch upload completes.
  • Internationalization Updates: New localization strings have been added for both English (en-US) and Simplified Chinese (zh-CN) to support the new batch upload features, including messages for drag-and-drop zones, file validation, and upload statuses.
  • Comprehensive Test Coverage: New test cases have been added to tests/test_dashboard.py to ensure the robustness of the new batch upload API, covering scenarios such as invalid file types and successful processing of valid skill archives.
Changelog
  • astrbot/dashboard/routes/skills.py
    • Added a new POST route /skills/batch-upload to handle multiple skill ZIP file uploads.
    • Implemented batch_upload_skills asynchronous function to process a list of files, save them temporarily, and attempt installation for each.
    • Included logic to track successful and failed uploads, providing detailed feedback for each file.
    • Ensured temporary files are cleaned up after processing, regardless of success or failure.
  • dashboard/src/components/extension/SkillsSection.vue
    • Updated the skill upload button to trigger a new openUploadDialog function.
    • Refactored the upload dialog to support a wider layout and persistent state during upload.
    • Introduced a drag-and-drop zone for ZIP files with visual feedback for dragging.
    • Added a hidden multiple file input for traditional file selection.
    • Implemented a dynamic list to display selected files, their sizes, validation messages, and current status (waiting, uploading, success, error, skipped).
    • Added computed properties (uploadStateCounts, hasUploadableItems) to manage and display upload summary statistics.
    • Integrated new utility functions for file size formatting, status label generation, and CSS class assignment.
    • Modified the upload logic to use the new uploadSkillBatch function, sending multiple files to the batch upload API.
    • Updated the dialog's close behavior to reset the upload state when not uploading.
    • Added responsive CSS styles for the new upload dialog elements.
  • dashboard/src/i18n/locales/en-US/features/extension.json
    • Added new localization keys for the batch upload feature, including uploadHint, structureRequirement, abilityMultiple, abilityValidate, abilitySkip, selectFiles, dropzoneTitle, dropzoneAction, dropzoneHint, fileListTitle, fileListEmpty, uploading, batchResultTitle, batchResultSummary, batchSuccessList, batchFailedList, confirm, confirmUpload, statusWaiting, statusUploading, statusSuccess, statusError, statusSkipped, summaryTotal, summaryReady, summarySuccess, summaryFailed, summarySkipped, validationReady, validationZipOnly, validationDuplicate, validationUploading, validationUploadFailed, validationUploadedAs, and validationNoResult.
  • dashboard/src/i18n/locales/zh-CN/features/extension.json
    • Added new localization keys for the batch upload feature in Simplified Chinese, mirroring the English additions for uploadHint, structureRequirement, abilityMultiple, abilityValidate, abilitySkip, selectFiles, dropzoneTitle, dropzoneAction, dropzoneHint, fileListTitle, fileListEmpty, uploading, batchResultTitle, batchResultSummary, batchSuccessList, batchFailedList, confirm, confirmUpload, statusWaiting, statusUploading, statusSuccess, statusError, statusSkipped, summaryTotal, summaryReady, summarySuccess, summaryFailed, summarySkipped, validationReady, validationZipOnly, validationDuplicate, validationUploading, validationUploadFailed, validationUploadedAs, and validationNoResult.
  • tests/test_dashboard.py
    • Imported io and zipfile modules for creating mock ZIP files in tests.
    • Imported FileStorage from werkzeug.datastructures for simulating file uploads.
    • Removed MockPluginConfig import as it was no longer needed.
    • Adjusted monkeypatching for plugin_manager.updator.update for conciseness.
    • Modified assertion message for skill installation failure to be more readable.
    • Added test_batch_upload_skills_returns_error_when_all_files_invalid to verify error handling for non-zip files.
    • Added test_batch_upload_skills_accepts_zip_files to confirm successful upload of valid zip files.
    • Added test_batch_upload_skills_accepts_valid_skill_archive to test the end-to-end process of uploading a valid skill archive and verifying its installation.
Activity
  • The author, whatevertogo, initiated this pull request to add batch upload functionality for multiple skill ZIP files.
  • The pull request description provides a detailed breakdown of backend and frontend modifications, along with internationalization updates.
  • The author included specific test verification steps for Python syntax, JSON format, and functional validation for single, multiple, and drag-and-drop uploads.
  • A comprehensive checklist was completed by the author, confirming no breaking changes, thorough testing, no new dependencies, and no malicious code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dosubot dosubot bot added the feature:plugin The bug / feature is about AstrBot plugin system. label Mar 6, 2026
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我发现了 1 个问题,并留下了一些总体反馈:

  • batch_upload_skills 中,建议使用唯一的临时文件名(例如通过 uuid4tempfile)来代替 os.path.join(temp_dir, f"batch_{filename}"),以避免当多个上传文件同名时发生冲突,并减少在路径中对用户提供文件名的依赖。
  • batch_upload_skills 中的临时文件清理逻辑既在单个文件循环内部删除文件,又在 finally 块中通过 temp_paths 再次清理;可以通过只保留一种清理机制来简化逻辑,避免重复记录和潜在的不一致。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `batch_upload_skills`, consider generating unique temp filenames (e.g., with `uuid4` or `tempfile`) instead of `os.path.join(temp_dir, f"batch_{filename}")` to avoid collisions when multiple uploads share the same filename and to reduce reliance on user-provided names in paths.
- The temp file cleanup logic in `batch_upload_skills` both deletes files inside the per-file loop and again in the `finally` block via `temp_paths`; you can simplify this by choosing a single cleanup mechanism to avoid redundant bookkeeping and potential inconsistencies.

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/skills.py" line_range="192" />
<code_context>
                 except Exception:
                     logger.warning(f"Failed to remove temp skill file: {temp_path}")

+    async def batch_upload_skills(self):
+        """批量上传多个 skill ZIP 文件"""
+        if DEMO_MODE:
</code_context>
<issue_to_address>
**issue (complexity):** 考虑通过将单个文件的处理逻辑和响应构建拆分到辅助函数中,并把临时文件清理交由这些辅助函数处理,从而重构 `batch_upload_skills`。

你可以通过抽取每个文件的处理逻辑,并集中构建响应,同时简化临时文件清理逻辑,从而降低 `batch_upload_skills` 中新增的复杂度。

### 1. 抽取单文件处理辅助函数(在两种上传路径中复用)

将单个文件的逻辑(校验、临时文件创建、安装和清理)移动到一个辅助函数中。这可以消除与 `upload_skill` 的重复逻辑,并简化 `batch_upload_skills` 的控制流:

```python
async def _install_skill_zip(
    self, upload_file, skill_mgr: SkillManager, temp_dir: str
) -> dict:
    filename = os.path.basename(upload_file.filename or "unknown.zip")

    if not filename.lower().endswith(".zip"):
        return {"filename": filename, "error": "Only .zip files are supported"}

    temp_path = os.path.join(temp_dir, f"batch_{filename}")
    try:
        await upload_file.save(temp_path)
        skill_name = skill_mgr.install_skill_from_zip(temp_path, overwrite=True)
        return {"filename": filename, "name": skill_name}
    except Exception as e:
        return {"filename": filename, "error": str(e)}
    finally:
        if os.path.exists(temp_path):
            try:
                os.remove(temp_path)
            except Exception:
                logger.warning(f"Failed to remove temp skill file: {temp_path}")
```

然后 `batch_upload_skills` 就会变成一个更清晰的循环:

```python
async def batch_upload_skills(self):
    if DEMO_MODE:
        return Response().error("You are not permitted to do this operation in demo mode").__dict__

    try:
        files = await request.files
        file_list = files.getlist("files")

        if not file_list:
            return Response().error("No files provided").__dict__

        succeeded, failed = [], []
        skill_mgr = SkillManager()
        temp_dir = get_astrbot_temp_path()
        os.makedirs(temp_dir, exist_ok=True)

        for file in file_list:
            result = await self._install_skill_zip(file, skill_mgr, temp_dir)
            if "error" in result:
                failed.append(result)
            else:
                succeeded.append(result)

        try:
            await sync_skills_to_active_sandboxes()
        except Exception:
            logger.warning("Failed to sync uploaded skills to active sandboxes.")

        return self._build_batch_upload_response(file_list, succeeded, failed)
    except Exception as e:
        logger.error(traceback.format_exc())
        return Response().error(str(e)).__dict__
```

你也可以在 `upload_skill` 中调用 `_install_skill_zip`,以避免在那里的临时文件逻辑重复。

### 2. 集中构建响应

与其在三个分支中重复构建相同结构的 `data`,不如把这部分逻辑提取到一个小的辅助函数中:

```python
def _build_batch_upload_response(self, files, succeeded, failed):
    total = len(files)
    success_count = len(succeeded)

    data = {
        "total": total,
        "succeeded": succeeded,
        "failed": failed,
    }

    if success_count == 0:
        msg = f"Upload failed for all {total} file(s)."
        resp = Response().error(msg)
        resp.data = data
        return resp.__dict__

    if success_count == total:
        msg = f"All {total} skill(s) uploaded successfully."
        return Response().ok(data, msg).__dict__

    msg = f"Partial success: {success_count}/{total} skill(s) uploaded."
    return Response().ok(data, msg).__dict__
```

这样可以去除重复的响应结构构建逻辑,让处理函数更专注于控制流。

### 3. 简化临时文件追踪`_install_skill_zip` 完全负责清理时,你可以移除 `batch_upload_skills` 中的 `temp_paths` 和最外层的 `finally`。全局兜底清理就不再需要了,因为每个文件对应的临时路径都在同一个地方进行管理。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你喜欢我们的代码审查,请考虑分享给更多人 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈不断改进代码审查质量。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In batch_upload_skills, consider generating unique temp filenames (e.g., with uuid4 or tempfile) instead of os.path.join(temp_dir, f"batch_{filename}") to avoid collisions when multiple uploads share the same filename and to reduce reliance on user-provided names in paths.
  • The temp file cleanup logic in batch_upload_skills both deletes files inside the per-file loop and again in the finally block via temp_paths; you can simplify this by choosing a single cleanup mechanism to avoid redundant bookkeeping and potential inconsistencies.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `batch_upload_skills`, consider generating unique temp filenames (e.g., with `uuid4` or `tempfile`) instead of `os.path.join(temp_dir, f"batch_{filename}")` to avoid collisions when multiple uploads share the same filename and to reduce reliance on user-provided names in paths.
- The temp file cleanup logic in `batch_upload_skills` both deletes files inside the per-file loop and again in the `finally` block via `temp_paths`; you can simplify this by choosing a single cleanup mechanism to avoid redundant bookkeeping and potential inconsistencies.

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/skills.py" line_range="192" />
<code_context>
                 except Exception:
                     logger.warning(f"Failed to remove temp skill file: {temp_path}")

+    async def batch_upload_skills(self):
+        """批量上传多个 skill ZIP 文件"""
+        if DEMO_MODE:
</code_context>
<issue_to_address>
**issue (complexity):** Consider refactoring `batch_upload_skills` by extracting per-file processing and response building into helpers while delegating temp file cleanup to them.

You can reduce the new complexity in `batch_upload_skills` by extracting the per‑file logic and centralizing response construction, while also simplifying temp file cleanup.

### 1. Extract single‑file processing helper (reuse in both upload paths)

Move the per‑file logic (validation, temp file creation, install, cleanup) into a helper. This removes duplication with `upload_skill` and flattens the control flow in `batch_upload_skills`:

```python
async def _install_skill_zip(
    self, upload_file, skill_mgr: SkillManager, temp_dir: str
) -> dict:
    filename = os.path.basename(upload_file.filename or "unknown.zip")

    if not filename.lower().endswith(".zip"):
        return {"filename": filename, "error": "Only .zip files are supported"}

    temp_path = os.path.join(temp_dir, f"batch_{filename}")
    try:
        await upload_file.save(temp_path)
        skill_name = skill_mgr.install_skill_from_zip(temp_path, overwrite=True)
        return {"filename": filename, "name": skill_name}
    except Exception as e:
        return {"filename": filename, "error": str(e)}
    finally:
        if os.path.exists(temp_path):
            try:
                os.remove(temp_path)
            except Exception:
                logger.warning(f"Failed to remove temp skill file: {temp_path}")
```

Then `batch_upload_skills` becomes a clean loop:

```python
async def batch_upload_skills(self):
    if DEMO_MODE:
        return Response().error("You are not permitted to do this operation in demo mode").__dict__

    try:
        files = await request.files
        file_list = files.getlist("files")

        if not file_list:
            return Response().error("No files provided").__dict__

        succeeded, failed = [], []
        skill_mgr = SkillManager()
        temp_dir = get_astrbot_temp_path()
        os.makedirs(temp_dir, exist_ok=True)

        for file in file_list:
            result = await self._install_skill_zip(file, skill_mgr, temp_dir)
            if "error" in result:
                failed.append(result)
            else:
                succeeded.append(result)

        try:
            await sync_skills_to_active_sandboxes()
        except Exception:
            logger.warning("Failed to sync uploaded skills to active sandboxes.")

        return self._build_batch_upload_response(file_list, succeeded, failed)
    except Exception as e:
        logger.error(traceback.format_exc())
        return Response().error(str(e)).__dict__
```

You can also call `_install_skill_zip` from `upload_skill` to avoid duplicating the temp file logic there.

### 2. Centralize response construction

Instead of three branches that all build the same `data` shape, move that logic to a small helper:

```python
def _build_batch_upload_response(self, files, succeeded, failed):
    total = len(files)
    success_count = len(succeeded)

    data = {
        "total": total,
        "succeeded": succeeded,
        "failed": failed,
    }

    if success_count == 0:
        msg = f"Upload failed for all {total} file(s)."
        resp = Response().error(msg)
        resp.data = data
        return resp.__dict__

    if success_count == total:
        msg = f"All {total} skill(s) uploaded successfully."
        return Response().ok(data, msg).__dict__

    msg = f"Partial success: {success_count}/{total} skill(s) uploaded."
    return Response().ok(data, msg).__dict__
```

This removes the repeated response shape and keeps the handler focused on control flow.

### 3. Simplify temp file tracking

With `_install_skill_zip` owning cleanup, you can drop `temp_paths` and the outer `finally` in `batch_upload_skills`. Global fallback cleanup is no longer needed because each file’s temp path is managed in one place.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds batch upload support for Skill ZIP archives in the AstrBot Dashboard, enabling users to upload multiple skill packages in one action and receive per-file results.

Changes:

  • Backend: adds POST /api/skills/batch-upload to install multiple ZIPs and return a per-file success/failure summary.
  • Frontend: enhances the Skills upload dialog to support multi-select, drag-and-drop, queued file list with statuses, and batch upload submission.
  • i18n/tests: adds new localized strings and introduces dashboard tests for the new batch upload endpoint.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
astrbot/dashboard/routes/skills.py Adds the batch upload API endpoint that saves, installs, and aggregates results for multiple ZIP files.
dashboard/src/components/extension/SkillsSection.vue Updates the upload UI to handle multi-file selection/drag-drop, show queue/status, and call the batch upload API.
dashboard/src/i18n/locales/zh-CN/features/extension.json Adds Chinese strings for the new batch upload UI.
dashboard/src/i18n/locales/en-US/features/extension.json Adds English strings for the new batch upload UI.
tests/test_dashboard.py Adds tests covering error and success flows for the batch upload endpoint.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

这个拉取请求为技能(skills)管理页面引入了批量上传功能,允许用户一次性上传多个 ZIP 文件。后端通过一个新的 API 端点来处理批量上传,前端则更新了 UI,支持多文件选择、拖放以及详细的上传状态反馈。

我的审查主要集中在后端实现上。我发现了一个潜在的错误:如果上传的多个文件同名,临时文件会发生冲突。我已经提出了修复建议以确保文件名的唯一性。此外,我还指出了部分冗余的清理逻辑,简化后可以提高代码的可维护性。前端的改动和新增的测试看起来很不错,不过测试用例可以进一步覆盖文件名冲突的场景。

@whatevertogo
Copy link
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link

To use Codex here, create a Codex account and connect to github.

@whatevertogo
Copy link
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link

To use Codex here, create a Codex account and connect to github.

@whatevertogo
Copy link
Contributor Author

@codex review it

@chatgpt-codex-connector
Copy link

To use Codex here, create a Codex account and connect to github.

@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Mar 7, 2026
@Soulter Soulter merged commit 812834b into AstrBotDevs:master Mar 7, 2026
6 checks passed
@whatevertogo whatevertogo deleted the feat/batch-upload-skills branch March 7, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature:plugin The bug / feature is about AstrBot plugin system. lgtm This PR has been approved by a maintainer size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants