From 76f58b429e8bb24ef4a7ed50edaf3f08a00776c5 Mon Sep 17 00:00:00 2001 From: lemon07r Date: Sat, 9 May 2026 06:31:58 -0400 Subject: [PATCH] Sync kimi-cli 1.41.0 parity: bump version, drop OAuth scope --- AGENTS.md | 8 ++++++-- package.json | 2 +- src/constants.ts | 7 +++---- src/oauth.ts | 5 +++-- test/constants.test.ts | 5 ++--- test/oauth.test.ts | 4 +--- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 95a2829..f0c7ff5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,14 +26,18 @@ Every design decision here follows from that: we do device-flow OAuth to mirror ### Architecture -Three files, 1 job each. Do not add a fourth unless the existing three genuinely can't hold a new concern. +Each source file has one job. Do not add new files unless the existing ones genuinely can't hold a new concern. | File | Responsibility | |--------------------|--------------------------------------------------------------------------------| -| `src/constants.ts` | Pinned strings that must mirror upstream kimi-cli (version, endpoints, client id, scope). | +| `src/constants.ts` | Pinned strings that must mirror upstream kimi-cli (version, endpoints, client id). | | `src/headers.ts` | The seven `X-Msh-*` / UA headers + the persistent `~/.kimi/device_id` file. | | `src/oauth.ts` | Device-code start, device-code poll, refresh-token exchange, and `GET /coding/v1/models` discovery. | +| `src/auth-store.ts`| Read/write opencode's `auth.json` entries for this provider. | +| `src/auth-refresh.ts`| Lock-based token refresh with cross-instance coordination, `ensureFreshStoredAuth` for standalone callers. | | `src/index.ts` | Plugin entry (v1 `PluginModule` format). Wires `auth` (login + loader) plus the Kimi chat hooks/body rewrite. | +| `src/usage.ts` | Fetch and parse Kimi subscription usage (`/coding/v1/usages`). | +| `src/tui.tsx` | TUI slash command `/kimi:usage` — renders usage in an opencode dialog. | Data flow on a chat request: diff --git a/package.json b/package.json index 6b3f37e..9a48708 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opencode-kimi-full", - "version": "1.2.9", + "version": "1.3.0", "description": "OpenCode plugin that brings the official Kimi OAuth device flow and Kimi-specific coding request fields to opencode, matching upstream kimi-cli.", "license": "MIT", "repository": { diff --git a/src/constants.ts b/src/constants.ts index ab1203a..842e322 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,13 +1,13 @@ -// Values mirror kimi-cli v1.37.0 1:1. When upstream bumps, update here and +// Values mirror kimi-cli v1.41.0 1:1. When upstream bumps, update here and // nothing else in the codebase should hard-code these strings. // // Source of truth: research/kimi-cli/src/kimi_cli/constant.py, // research/kimi-cli/src/kimi_cli/auth/oauth.py // // NOTE: client_id is a public constant shipped inside the official CLI, not a -// secret. scope `kimi-code` is the upstream coding-agent OAuth scope. +// secret. -export const KIMI_CLI_VERSION = "1.37.0" +export const KIMI_CLI_VERSION = "1.41.0" // Upstream: research/kimi-cli/src/kimi_cli/constant.py get_user_agent() → // f"KimiCLI/{get_version()}". This must match verbatim — Moonshot's // `kimi-for-coding` backend 403s on any other UA prefix @@ -18,7 +18,6 @@ export const OAUTH_HOST = "https://auth.kimi.com" export const OAUTH_DEVICE_AUTH_URL = `${OAUTH_HOST}/api/oauth/device_authorization` export const OAUTH_TOKEN_URL = `${OAUTH_HOST}/api/oauth/token` export const OAUTH_CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098" -export const OAUTH_SCOPE = "kimi-code" export const OAUTH_DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code" export const OAUTH_REFRESH_GRANT = "refresh_token" diff --git a/src/oauth.ts b/src/oauth.ts index 11c8ce2..02797ff 100644 --- a/src/oauth.ts +++ b/src/oauth.ts @@ -4,7 +4,6 @@ import { OAUTH_DEVICE_AUTH_URL, OAUTH_DEVICE_GRANT, OAUTH_REFRESH_GRANT, - OAUTH_SCOPE, OAUTH_TOKEN_URL, } from "./constants.ts" import { kimiHeaders } from "./headers.ts" @@ -65,9 +64,11 @@ async function postForm(url: string, params: Record): Promise } export async function startDeviceAuth(): Promise { + // kimi-cli v1.41.0 dropped the `scope` parameter from the device + // authorization request (research/kimi-cli/src/kimi_cli/auth/oauth.py, + // request_device_authorization). Only `client_id` is sent now. return postForm(OAUTH_DEVICE_AUTH_URL, { client_id: OAUTH_CLIENT_ID, - scope: OAUTH_SCOPE, }) } diff --git a/test/constants.test.ts b/test/constants.test.ts index 7b4d6a9..4b19195 100644 --- a/test/constants.test.ts +++ b/test/constants.test.ts @@ -18,13 +18,12 @@ test("USER_AGENT embeds KIMI_CLI_VERSION", () => { test("OAuth constants match upstream kimi-cli exactly", () => { // Pinned values from research/kimi-cli/src/kimi_cli/auth/oauth.py. If these - // drift from upstream, tokens are issued against the wrong scope/client and - // the plugin no longer mirrors official kimi-cli auth. + // drift from upstream, tokens are issued against the wrong client and the + // plugin no longer mirrors official kimi-cli auth. expect(C.OAUTH_HOST).toBe("https://auth.kimi.com") expect(C.OAUTH_DEVICE_AUTH_URL).toBe("https://auth.kimi.com/api/oauth/device_authorization") expect(C.OAUTH_TOKEN_URL).toBe("https://auth.kimi.com/api/oauth/token") expect(C.OAUTH_CLIENT_ID).toBe("17e5f671-d194-4dfb-9706-5516cb48c098") - expect(C.OAUTH_SCOPE).toBe("kimi-code") expect(C.OAUTH_DEVICE_GRANT).toBe("urn:ietf:params:oauth:grant-type:device_code") expect(C.OAUTH_REFRESH_GRANT).toBe("refresh_token") }) diff --git a/test/oauth.test.ts b/test/oauth.test.ts index e3ac643..58c0fb3 100644 --- a/test/oauth.test.ts +++ b/test/oauth.test.ts @@ -4,7 +4,6 @@ import { OAUTH_DEVICE_AUTH_URL, OAUTH_DEVICE_GRANT, OAUTH_REFRESH_GRANT, - OAUTH_SCOPE, OAUTH_TOKEN_URL, } from "../src/constants.ts" import { pollDeviceToken, refreshToken, startDeviceAuth } from "../src/oauth.ts" @@ -20,7 +19,7 @@ afterEach(() => { mock = undefined }) -test("startDeviceAuth posts client_id+scope as form-encoded to the device endpoint", async () => { +test("startDeviceAuth posts client_id as form-encoded to the device endpoint", async () => { mock = installFetchMock(() => ({ body: { device_code: "dc", @@ -44,7 +43,6 @@ test("startDeviceAuth posts client_id+scope as form-encoded to the device endpoi expect(call.headers["x-msh-device-id"]).toMatch(/^[0-9a-f]{32}$/) expect(parseForm(call.body)).toEqual({ client_id: OAUTH_CLIENT_ID, - scope: OAUTH_SCOPE, }) })