Skip to content
This repository was archived by the owner on Sep 23, 2026. It is now read-only.

feat(cli): short-circuit entry points to a Kimi Code installer - #2666

Merged
sailist merged 1 commit into
mainfrom
bump-1.51
Sep 22, 2026
Merged

sailist merged 1 commit into
mainfrom
bump-1.51

Conversation

@sailist

@sailist sailist commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

kimi-cli (Python) is archived. This release (1.52.0) turns the package entry point into a migration path to the new Kimi Code CLI (TypeScript), so uv tool install kimi-cli lands users on the successor — complementary to #2659, which archived the repo and tombstoned the kimi-code PyPI package but left the kimi command itself untouched:

  • Bare kimi: fetches the Kimi Code install script from the CDN migration tips (migration.json) and runs it directly, no confirmation. CDN-side script updates take effect without a new package release (bash/powershell by platform; falls back to cached tips, then built-in defaults).
  • Everything else (-p, --help, subcommands, ...): prints a one-line deprecation notice only. --version additionally prints the version number.
  • Displayed install command follows the cached CDN tips, so a CDN-side script URL change is reflected in the notice instead of going stale.
  • Piped install scripts run with bash -o pipefail so a failure anywhere in the pipe (e.g. missing curl) is not masked.
  • The original Typer CLI is kept in the tree (run_original_cli) but is no longer reachable from the package entry point.

Test updates

  • New tests/cli/test_deprecation_gate.py: gate behavior (no-args installer, notice-only args, --version, CDN/default/failure paths, cache-driven display).
  • Test subprocess spawns now go through python -m kimi_cli.cli (the ungated internal entry) instead of the gated console script (tests/acp, tests/e2e, tests_e2e).
  • Fixed a latent bug in test_basic_e2e.py / test_media_e2e.py: _repo_root() returned tests/ (parents[1]), so the wire subprocess ran with tests/ as cwd and import acp resolved to tests/acp — exposed once the spawn switched to python -m.

Verification

  • Unit: 2909 passed; tests/acp + tests/e2e: 82 passed; tests_e2e: 52 passed; pyright 0 errors; ruff clean.
  • Docker (linux/arm64): end-to-end bare kimi run fetched the CDN script and installed Kimi Code 2.0.2 successfully.
  • Windows Server 2022: --version/--help/-p print the ps1 install command correctly.

Release note

When publishing 1.52.0, do not bump cdn.kimi.com/binaries/kimi-cli/latest past 1.50.0 — the client prioritizes the upgrade prompt over the migration prompt, so existing users would otherwise only see "upgrade to 1.52.0" and never the migration notice.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 3 potential issues.

2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment on lines +287 to +291
if tips is None:
tips = load_cached_tips()
if tips is None:
return None
return tips.install_sh, tips.install_ps1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Disabled migration still installs

When the CDN returns enabled: false, fetch_install_scripts still returns default scripts. Bare kimi executes one, defeating the migration kill switch.

Learn more

A tips payload with enabled: false is explicitly valid and can omit its migration block in parse_tips. Parsing that payload creates a KimiCodeTips object whose install commands use built-in defaults. fetch_install_scripts treats that object exactly like an enabled payload. The caller then executes the returned command, so disabling migration at the CDN does not disable the new automatic installer.

Example: The CDN publishes {"enabled": false, "message": {}} to halt migration. parse_tips supplies the default shell commands, bare kimi runs one, and installation proceeds instead of stopping.

Recommended fix: Preserve three states across fetch_install_scripts and _fetch_install_scripts: enabled scripts, unavailable/invalid tips, and explicitly disabled tips. Make run_kimi_code_installer exit without executing a command for the disabled state, while retaining built-in fallback only for unavailable or invalid tips.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +287 to +291
if tips is None:
tips = load_cached_tips()
if tips is None:
return None
return tips.install_sh, tips.install_ps1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Empty installer reports success

When the CDN provides an empty platform script, fetch_install_scripts returns it. The shell exits zero, so run_kimi_code_installer reports success without installing anything.

Learn more

The tips parser verifies that install_script.sh and install_script.ps1 are strings, but does not require non-empty values in parse_tips. The new helper forwards those values to run_kimi_code_installer. Both bash -c "" and an empty PowerShell command can exit successfully, so the final success message does not prove installation occurred.

Example: A payload contains "install_script": {"sh": "", "ps1": "valid-command"}. On Linux, bare kimi runs bash -o pipefail -c "", receives exit code 0, and prints “Done!” although no binary was installed.

Recommended fix: Reject empty install commands during parse_tips, matching _cached_install_scripts, so the installer uses the built-in fallback for malformed payloads. Validate each platform command before execution as a final safeguard.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

tips = load_cached_tips()
if tips is None:
return None
return tips.install_sh, tips.install_ps1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟥 Unsigned CDN payload executes arbitrary commands

A modified migration payload can supply arbitrary installer commands. Bare kimi executes them without confirmation, signature verification, or a pinned digest.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

kimi-cli (Python) is archived; this final release turns every entry
point into a migration path to the new Kimi Code CLI (TypeScript):

- bare `kimi` fetches the install script from the CDN migration tips
  and runs it directly (no confirmation); CDN-side script updates take
  effect without a new package release
- all other args (subcommands, -p, --help, ...) print a one-line
  deprecation notice; --version additionally prints the version
- the displayed install command follows the cached CDN tips so a
  CDN-side script URL change stays accurate
- run piped install scripts with `bash -o pipefail` so failures are
  not masked by the pipe
- original Typer CLI kept in the tree but unreachable from the
  package entry point
- tests: spawn the CLI via `python -m kimi_cli.cli` (ungated internal
  entry); fix latent _repo_root() bug in two e2e files that shadowed
  the `acp` package with tests/acp
- bump version to 1.51.0
@RealKai42

Copy link
Copy Markdown
Collaborator

@codex

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-22T09:36:13.382097Z f546871 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@sailist
sailist added this pull request to the merge queue Sep 22, 2026
Merged via the queue into main with commit 9ab1286 Sep 22, 2026
34 checks passed
@sailist
sailist deleted the bump-1.51 branch September 22, 2026 09:29

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5468718a6

ℹ️ 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".

Comment thread src/kimi_cli/__main__.py
Comment on lines +44 to +45
print(deprecation_message())
return 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Return a failure status for skipped operational commands

For non-informational invocations such as kimi --print -p ..., this path performs none of the requested work but returns success and writes the notice to stdout. Existing CI/evaluation scripts can therefore accept the notice as valid model output and continue with corrupted results; operational arguments should exit nonzero (and preferably send the notice to stderr), while informational flags such as --help and --version may still succeed.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli/__main__.py
Comment on lines +37 to +38
if not args:
return run_kimi_code_installer()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor the update opt-out before running the installer

When KIMI_CLI_NO_AUTO_UPDATE=1, a bare kimi still downloads and executes the successor's installer because the new gate never checks the opt-out. This contradicts the documented promise that the variable disables all update-related features and is especially problematic for Nix or other package-managed environments, where the variable may be set automatically specifically to prevent the CLI from installing software outside the package manager.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli/__main__.py
Comment on lines +22 to +23
try:
return _deprecation_gate(args)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Gate the standalone binary entry point too

This gate only affects kimi_cli.__main__, but the standalone artifacts published by the release workflow are built from src/kimi_cli/cli/__main__.py (kimi.spec line 14), which still dispatches directly to the original Typer CLI. Consequently, the 1.52 standalone kimi binaries will continue running the archived implementation instead of showing the notice or installing the successor; the PyInstaller entry script must route through this gate as well.

Useful? React with 👍 / 👎.

Comment on lines +288 to +290
tips = load_cached_tips()
if tips is None:
return None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve the migration payload's remote kill switch

When the CDN serves a valid payload with enabled: false, parse_tips deliberately accepts it as a remote kill switch and fills in default install commands when the migration block is absent, but this new function returns those commands without checking tips.enabled. A bare kimi therefore executes an installer even after the CDN has disabled migration, preventing operators from stopping a broken or compromised rollout; the disabled state needs to reach the caller without being converted into the built-in fallback.

Useful? React with 👍 / 👎.

Comment thread tests_e2e/wire_helpers.py
Comment on lines +512 to +515
parts = (
shlex.split(override, posix=os.name != "nt")
if override
else ["uv", "run", "python", "-m", "kimi_cli.cli"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep wire E2E tests on the documented public command

Changing the default to the internal module means this suite no longer exercises kimi --wire; it can pass while the installed/public command returns only the deprecation notice, so packaging and entry-point regressions are hidden. The scoped test guide explicitly requires uv run kimi as the default and reserves KIMI_E2E_WIRE_CMD for overrides, so retain the public command or explicitly override it only in jobs intended to test the legacy internal implementation.

AGENTS.md reference: tests_e2e/AGENTS.md:L8-L10

Useful? React with 👍 / 👎.

Comment thread tests_e2e/wire_helpers.py
Comment on lines +512 to +515
parts = (
shlex.split(override, posix=os.name != "nt")
if override
else ["uv", "run", "python", "-m", "kimi_cli.cli"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep wire E2E tests on the documented public command

Changing the default to the internal module means this suite no longer exercises kimi --wire; it can pass while the installed/public command returns only the deprecation notice, so packaging and entry-point regressions are hidden. The scoped test guide explicitly requires uv run kimi as the default and reserves KIMI_E2E_WIRE_CMD for overrides, so retain the public command or explicitly override it only in jobs intended to test the legacy internal implementation. 【F:tests_e2e/AGENTS.md��L8-L10】

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants