fix(plugin): support installing from .zip URL#2126
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for installing Kimi CLI plugins directly from remote .zip URLs, routing those URLs through the existing zip-extraction/install flow (instead of the git-clone heuristic), and updates docs/tests accordingly.
Changes:
- Extend
kimi plugin installto accepthttp(s).zipURLs (download viahttpx, extract into temp dir, then install). - Factor zip extraction +
plugin.jsondiscovery into a shared helper used by both local zip files and remote zip URLs. - Add pytest coverage for zip URL cases and update en/zh plugin documentation.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/kimi_cli/cli/plugin.py |
Adds zip URL download path, shared zip extraction helper, and updates CLI help/error text. |
tests/core/test_plugin.py |
Adds new tests covering zip URL success/failure and GitHub archive .zip routing. |
docs/zh/customization/plugins.md |
Documents remote zip URL installation examples. |
docs/en/customization/plugins.md |
Documents remote zip URL installation examples. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| with zipfile.ZipFile(zip_path, "r") as zf: | ||
| for member in zf.namelist(): | ||
| member_path = (tmp / member).resolve() | ||
| if not member_path.is_relative_to(tmp.resolve()): | ||
| shutil.rmtree(tmp, ignore_errors=True) | ||
| typer.echo(f"Error: zip contains unsafe path: {member}", err=True) | ||
| raise typer.Exit(1) | ||
| zf.extractall(tmp) |
| for candidate in [tmp] + sorted(tmp.iterdir()): | ||
| if candidate.is_dir() and (candidate / "plugin.json").exists(): | ||
| return candidate, tmp | ||
| dirs = [d for d in tmp.iterdir() if d.is_dir() and not d.name.startswith("_")] | ||
| if len(dirs) == 1 and (dirs[0] / "plugin.json").exists(): | ||
| return dirs[0], tmp |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 11bf9e2845
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| typer.echo(f"Error: download failed: {exc}", err=True) | ||
| raise typer.Exit(1) from exc | ||
|
|
||
| return _extract_zip_to_plugin(zip_path, tmp) |
There was a problem hiding this comment.
Handle invalid downloaded ZIP archives gracefully
When a .zip URL returns non-ZIP content (for example, an HTML error page with status 200), _extract_zip_to_plugin raises zipfile.BadZipFile and this propagates out of _resolve_source unhandled. In this path, users get a Python traceback instead of a CLI error, and the temp directory created for the download is not cleaned up because install_cmd never reaches its cleanup finally. Please catch ZIP parsing/extraction errors around the remote ZIP path and convert them to typer.Exit after removing the temp dir.
Useful? React with 👍 / 👎.
Summary
kimi plugin installnow accepts http(s) URLs that point to a.ziparchive — the file is streamed to a temp dir viahttpxand then runs through the existing extraction path.https://github.com/owner/repo/archive/refs/heads/main.zipno longer get misrouted intogit clone.plugin.jsondiscovery) is factored into a shared helper so local.zipfiles and remote.zipURLs go through the same code.Test plan
uv run pytest tests/core/test_plugin.py— 47 passed (4 new cases: happy path, query string, GitHub archive URL skips git clone, download failure cleans up tmp).kimi plugin install https://cdn.kimi.com/kimi-code-plugins/kimi-datasource.zipon a clean~/.kimi/plugins/installskimi-datasource v2.0.0.