Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
59d9eb1
feat(kimi-code): add China/International region selection for OAuth l…
liruifengv Aug 12, 2026
ddfd3c8
fix(oauth): keep an explicit default-slot login ahead of the install …
liruifengv Aug 13, 2026
b0b7116
fix(agent-core-v2): thread the default-slot key through capability re…
liruifengv Aug 13, 2026
605ada1
fix(agent-core-v2): honor the region-marker opt-out for the telemetry…
liruifengv Aug 13, 2026
0eba850
Merge remote-tracking branch 'origin/main' into feat/oauth-region-split
liruifengv Aug 18, 2026
f9349df
feat(cli): show region site domains in login platform selector
liruifengv Aug 18, 2026
12b3c4f
Merge remote-tracking branch 'origin/main' into feat/oauth-region-split
liruifengv Aug 18, 2026
56b287f
chore: reword oauth login changesets
liruifengv Aug 18, 2026
4bd7e82
fix: honor the region marker opt-out in the CLI and capability resolvers
liruifengv Aug 18, 2026
ba46a0a
refactor: rename login region values to mainland-cn and global
liruifengv Aug 19, 2026
adc68d8
fix: keep the --region help text in English
liruifengv Aug 19, 2026
2c0774f
fix: simplify the --region help text to site domains
liruifengv Aug 19, 2026
1dc232a
feat: drop the suggested login platform order
liruifengv Aug 19, 2026
a037b11
feat: split a browser-safe region profile table out of the region res…
liruifengv Aug 19, 2026
e48d6b6
Revert "feat: split a browser-safe region profile table out of the re…
liruifengv Aug 19, 2026
9cdaab4
fix: read the install marker from the bootstrapped home directory
liruifengv Aug 19, 2026
715baf6
fix: resolve the server plugin marketplace from the active login region
liruifengv Aug 19, 2026
4c69299
feat: expose the login region option through the klient auth facade
liruifengv Aug 19, 2026
6f607b8
fix: drop a comment from the v2 auth region test
liruifengv Aug 19, 2026
d5f3b03
fix: keep scoped base-only logins on their environment for a bare login
liruifengv Aug 19, 2026
12597b2
fix: invalidate the region cache on the provider-manager logout path
liruifengv Aug 19, 2026
7c0ed41
fix: route client-config fetches through the active region profile
liruifengv Aug 19, 2026
2a49766
fix: resolve the telemetry endpoint per flush so a login region switc…
liruifengv Aug 19, 2026
401cb00
test: expect the telemetry endpoint resolver in the CLI init assertions
liruifengv Aug 19, 2026
ddd06a7
fix: resolve the default telemetry endpoint from the bootstrapped home
liruifengv Aug 19, 2026
43f1616
chore: reword the oauth login changeset around the two login methods
liruifengv Aug 19, 2026
cc857f7
chore: trim the oauth login changeset to the headline
liruifengv Aug 19, 2026
bee4988
feat: let hosts override the region marker env through the server boo…
liruifengv Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/oauth-region-split.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": minor
---

Support two OAuth login methods — kimi.ai and kimi.com.
5 changes: 5 additions & 0 deletions .changeset/sdk-auth-login-region.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code-sdk": patch
---

Add an optional region parameter to the auth login API for selecting the OAuth login endpoint (.com or .ai deployment).
9 changes: 6 additions & 3 deletions apps/kimi-code/src/cli/sub/acp-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { getVersion } from '#/cli/version';
import { KIMI_CODE_HOME_ENV } from '#/constant/app';
import { getDataDir } from '#/utils/paths';

import { runLoginFlow } from './login-flow';
import { parseRegionFlag, runLoginFlow } from './login-flow';

export function registerNativeAcpCommand(parent: Command): void {
parent
Expand All @@ -36,9 +36,12 @@ export function registerNativeAcpCommand(parent: Command): void {
'Run the device-code login flow then exit (entry point for ACP terminal-auth).',
false,
)
.action(async (opts: { login?: boolean }) => {
.option('--region <region>', 'Login region used together with --login: "mainland-cn" (kimi.com) or "global" (kimi.ai).')
.action(async (opts: { login?: boolean; region?: string }) => {
if (opts.login === true) {
await runLoginFlow();
await runLoginFlow({
region: opts.region === undefined ? undefined : parseRegionFlag(opts.region),
});
return;
}
// Forward `KIMI_CODE_HOME` (if set) into `authMethods[0].env` so the
Expand Down
9 changes: 6 additions & 3 deletions apps/kimi-code/src/cli/sub/acp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { buildSkillSlashCommands } from '#/tui/commands/skills';

import { isLegacyEnabled } from '../experimental-v2';
import { registerNativeAcpCommand } from './acp-native';
import { runLoginFlow } from './login-flow';
import { parseRegionFlag, runLoginFlow } from './login-flow';

export function registerAcpCommand(parent: Command): void {
if (!isLegacyEnabled()) {
Expand All @@ -51,9 +51,12 @@ export function registerAcpCommand(parent: Command): void {
'Run the device-code login flow then exit (entry point for ACP terminal-auth).',
false,
)
.action(async (opts: { login?: boolean }) => {
.option('--region <region>', 'Login region used together with --login: "mainland-cn" (kimi.com) or "global" (kimi.ai).')
.action(async (opts: { login?: boolean; region?: string }) => {
if (opts.login === true) {
await runLoginFlow();
await runLoginFlow({
region: opts.region === undefined ? undefined : parseRegionFlag(opts.region),
});
return;
}
const identity = createKimiCodeHostIdentity();
Expand Down
19 changes: 18 additions & 1 deletion apps/kimi-code/src/cli/sub/login-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,27 @@
*/

import { createKimiHarness } from '@moonshot-ai/kimi-code-sdk';
import type { KimiRegion } from '@moonshot-ai/kimi-code-oauth';

import { createKimiCodeHostIdentity } from '#/cli/version';
import { openUrl } from '#/utils/open-url';
import { persistedKimiOAuthRef, regionForBareLogin } from '#/utils/region';

export async function runLoginFlow(): Promise<never> {
/** Parse a `--region` CLI flag; exits with an actionable message on bad input. */
export function parseRegionFlag(value: string): KimiRegion {
if (value !== 'mainland-cn' && value !== 'global') {
process.stderr.write(`Invalid --region "${value}" (expected "mainland-cn" or "global").\n`);
process.exit(1);
}
return value;
}

export async function runLoginFlow(options: { region?: KimiRegion } = {}): Promise<never> {
// No flag: a fresh install follows the resolved region (env/marker/
// default); an existing login keeps its own environment (see
// regionForBareLogin — the default slot re-pins mainland-cn, a scoped slot
// keeps its configured hosts).
const region = options.region ?? regionForBareLogin(persistedKimiOAuthRef());
const identity = createKimiCodeHostIdentity();
const harness = createKimiHarness({
identity,
Expand All @@ -23,6 +39,7 @@ export async function runLoginFlow(): Promise<never> {
try {
const result = await harness.auth.login(undefined, {
signal: controller.signal,
region,
onDeviceCode: (data) => {
const url = data.verificationUriComplete || data.verificationUri;
// Print the manual fallback before attempting to open the user's
Expand Down
12 changes: 9 additions & 3 deletions apps/kimi-code/src/cli/sub/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@

import type { Command } from 'commander';

import { runLoginFlow } from './login-flow';
import { parseRegionFlag, runLoginFlow } from './login-flow';

export function registerLoginCommand(parent: Command): void {
parent
.command('login')
.description('Authenticate with Kimi Code CLI via the device-code flow.')
.action(async () => {
await runLoginFlow();
.option(
'--region <region>',
'Login region: "mainland-cn" (kimi.com) or "global" (kimi.ai).',
)
.action(async (opts: { region?: string }) => {
await runLoginFlow({
region: opts.region === undefined ? undefined : parseRegionFlag(opts.region),
});
});
}
3 changes: 3 additions & 0 deletions apps/kimi-code/src/cli/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@moonshot-ai/kimi-telemetry';

import { CLI_USER_AGENT_PRODUCT, WEB_UI_MODE } from '#/constant/app';
import { currentKimiProfile } from '#/utils/region';

import { createKimiCodeHostIdentity } from './version';

Expand Down Expand Up @@ -57,6 +58,7 @@ export function initializeCliTelemetry(options: InitializeCliTelemetryOptions):
uiMode: options.uiMode,
model: options.model ?? options.config.defaultModel,
sessionId: options.sessionId,
endpoint: () => currentKimiProfile().telemetryEndpoint,
getAccessToken: async () =>
(await options.harness.auth.getCachedAccessToken(KIMI_CODE_PROVIDER_NAME)) ?? null,
});
Expand Down Expand Up @@ -105,6 +107,7 @@ export function initializeServerTelemetry(
version: options.version,
uiMode: WEB_UI_MODE,
model: config.defaultModel,
endpoint: () => currentKimiProfile().telemetryEndpoint,
getAccessToken: async () => (await auth.getCachedAccessToken(KIMI_CODE_PROVIDER_NAME)) ?? null,
});

Expand Down
6 changes: 3 additions & 3 deletions apps/kimi-code/src/cli/update/cdn.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { valid } from 'semver';
import { z } from 'zod';

import { KIMI_CODE_CDN_LATEST_JSON_URL, KIMI_CODE_CDN_LATEST_URL } from '#/constant/app';
import { kimiCodeCdnLatestJsonUrl, kimiCodeCdnLatestUrl } from '#/constant/app';

import type { UpdateManifest } from './types';

Expand Down Expand Up @@ -58,7 +58,7 @@ async function fetchWithTimeout(fetchImpl: typeof fetch, input: string): Promise
export async function fetchLatestVersionFromCdn(
fetchImpl: typeof fetch = fetch,
): Promise<string> {
const response = await fetchWithTimeout(fetchImpl, KIMI_CODE_CDN_LATEST_URL);
const response = await fetchWithTimeout(fetchImpl, kimiCodeCdnLatestUrl());
if (!response.ok) {
throw new Error(`CDN /latest returned HTTP ${response.status}`);
}
Expand All @@ -70,7 +70,7 @@ export async function fetchLatestVersionFromCdn(
}

async function fetchUpdateManifestFromCdn(fetchImpl: typeof fetch): Promise<UpdateManifest> {
const response = await fetchWithTimeout(fetchImpl, KIMI_CODE_CDN_LATEST_JSON_URL);
const response = await fetchWithTimeout(fetchImpl, kimiCodeCdnLatestJsonUrl());
if (!response.ok) {
throw new Error(`CDN /latest.json returned HTTP ${response.status}`);
}
Expand Down
6 changes: 3 additions & 3 deletions apps/kimi-code/src/cli/update/native-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { valid } from 'semver';
import { z } from 'zod';

import { KIMI_CODE_CDN_BINARIES_BASE } from '#/constant/app';
import { kimiCodeCdnBinariesBase } from '#/constant/app';

const MANIFEST_FETCH_TIMEOUT_MS = 10_000;

Expand All @@ -33,11 +33,11 @@ export type NativeReleaseManifest = z.infer<typeof NativeReleaseManifestSchema>;
export type NativePlatformEntry = z.infer<typeof PlatformEntrySchema>;

export function nativeManifestUrl(version: string): string {
return `${KIMI_CODE_CDN_BINARIES_BASE}/${version}/manifest.json`;
return `${kimiCodeCdnBinariesBase()}/${version}/manifest.json`;
}

export function nativeBinaryUrl(version: string, filename: string): string {
return `${KIMI_CODE_CDN_BINARIES_BASE}/${version}/${filename}`;
return `${kimiCodeCdnBinariesBase()}/${version}/${filename}`;
}

/**
Expand Down
20 changes: 12 additions & 8 deletions apps/kimi-code/src/cli/update/preflight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { log, type Logger } from '@moonshot-ai/kimi-code-sdk';
import type { TelemetryProperties } from '@moonshot-ai/kimi-telemetry';

import {
KIMI_CODE_OFFICIAL_INSTALL_URL,
NATIVE_INSTALL_COMMAND_UNIX,
NATIVE_INSTALL_COMMAND_WIN,
kimiCodeOfficialInstallUrl,
nativeInstallCommandUnix,
nativeInstallCommandWin,
} from '#/constant/app';
import { loadTuiConfig } from '#/tui/config';
import { resolveCommandPath } from '#/utils/process/resolve-command';
Expand Down Expand Up @@ -82,7 +82,7 @@ export function installCommandFor(
case 'homebrew':
return 'brew upgrade kimi-code';
case 'native':
return platform === 'win32' ? NATIVE_INSTALL_COMMAND_WIN : NATIVE_INSTALL_COMMAND_UNIX;
return platform === 'win32' ? nativeInstallCommandWin() : nativeInstallCommandUnix();
case 'unsupported':
return `npm install -g ${NPM_PACKAGE_NAME}@${version}`;
}
Expand Down Expand Up @@ -184,9 +184,13 @@ function resolveInstallSpawn(
return { resolvedCmd, args, shell: platform === 'win32' };
}

const THIRD_PARTY_SOURCE_NOTE =
'\nNote: Third-party sources may lag behind the official release.\n' +
`For the latest updates, use the official installer: ${KIMI_CODE_OFFICIAL_INSTALL_URL}\n`;
// Built per call: the official-installer URL follows the current region.
function thirdPartySourceNote(): string {
return (
'\nNote: Third-party sources may lag behind the official release.\n' +
`For the latest updates, use the official installer: ${kimiCodeOfficialInstallUrl()}\n`
);
}

export function renderManualUpdateMessage(
currentVersion: string,
Expand Down Expand Up @@ -217,7 +221,7 @@ export function renderManualUpdateMessage(
`(${currentVersion} -> ${target.version}).\n` +
`Detected install source: ${sourceDesc}\n` +
`To update manually, run: ${installCommand}\n` +
(source === 'homebrew' ? THIRD_PARTY_SOURCE_NOTE : '')
(source === 'homebrew' ? thirdPartySourceNote() : '')
);
}

Expand Down
68 changes: 49 additions & 19 deletions apps/kimi-code/src/constant/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { ErrorCodes } from '@moonshot-ai/kimi-code-sdk';
import { kimiCdnContentUrl } from '@moonshot-ai/kimi-code-oauth';

import { currentKimiProfile } from '#/utils/region';

export const PRODUCT_NAME = 'Kimi Code';
export const CLI_COMMAND_NAME = 'kimi';
Expand Down Expand Up @@ -74,7 +77,9 @@ export const OAUTH_LOGIN_REQUIRED_CODE = ErrorCodes.AUTH_LOGIN_REQUIRED;
export const FEEDBACK_ISSUE_URL = 'https://github.com/MoonshotAI/kimi-code/issues';
// Sign-up / sign-in page offered to signed-out users so they can create an
// account and submit feedback through the authenticated channel next time.
export const KIMI_CODE_SIGNUP_URL = 'https://www.kimi.com/code';
export function kimiCodeSignupUrl(): string {
return `${currentKimiProfile().siteBase}/code`;
}

// Sent in the feedback `version` field so the backend can distinguish this
// TypeScript client from clients that send a bare version.
Expand All @@ -84,34 +89,59 @@ export const FEEDBACK_VERSION_PREFIX = 'kimi-code-';
export const FEEDBACK_TELEMETRY_EVENT = 'feedback_submitted';

// CDN source of truth: all version checks and native install scripts pull from here.
export const KIMI_CODE_CDN_BASE = 'https://code.kimi.com/kimi-code';
export const KIMI_CODE_CDN_LATEST_URL = `${KIMI_CODE_CDN_BASE}/latest`;
// The off-session endpoints derive from the current region profile so a
// global login points at the .ai deployment; they are resolved per call so
// a region switch (login/logout + refreshKimiRegion) takes effect immediately.
export function kimiCodeCdnBase(): string {
return currentKimiProfile().cdnBase;
}
export function kimiCodeCdnLatestUrl(): string {
return `${kimiCodeCdnBase()}/latest`;
}
// Rollout manifest consumed by update checks; the plain-text `/latest` above
// stays unchanged forever — already-shipped clients hard-fail on non-semver
// bodies, and the CDN install scripts read it for fresh installs.
export const KIMI_CODE_CDN_LATEST_JSON_URL = `${KIMI_CODE_CDN_BASE}/latest.json`;
export function kimiCodeCdnLatestJsonUrl(): string {
return `${kimiCodeCdnBase()}/latest.json`;
}
// Per-release native artifacts: `/binaries/<version>/manifest.json` +
// `/binaries/<version>/kimi-code-<target>[.exe]` — the bare platform binary
// (same layout install.ps1 consumes).
export const KIMI_CODE_CDN_BINARIES_BASE = `${KIMI_CODE_CDN_BASE}/binaries`;
export const KIMI_CODE_TIPS_BANNER_URL = 'https://cdn.kimi.com/kimi-code-tips/tips.json';
// The marketplace catalog location constants live in the shared
// agent-core-v2 plugin domain (kap-server consumes them from there).
// Deep-path import: this module is evaluated on every CLI invocation, so it
// must not pull in the engine root.
export {
KIMI_CODE_PLUGIN_MARKETPLACE_URL,
KIMI_CODE_PLUGIN_MARKETPLACE_URL_ENV,
} from '@moonshot-ai/agent-core-v2/app/plugin/marketplace';
export function kimiCodeCdnBinariesBase(): string {
return `${kimiCodeCdnBase()}/binaries`;
}
// The tips banner rides the content CDN, which both regions currently share.
export function kimiCodeTipsBannerUrl(): string {
return kimiCdnContentUrl('kimi-code-tips/tips.json');
}
// The marketplace env override name lives in the shared agent-core-v2 plugin
// domain (kap-server consumes it from there). Deep-path import: this module is
// evaluated on every CLI invocation, so it must not pull in the engine root.
export { KIMI_CODE_PLUGIN_MARKETPLACE_URL_ENV } from '@moonshot-ai/agent-core-v2/app/plugin/marketplace';
// The CLI-side default catalog derives from the current region profile; the
// env override above takes priority at the call site.
export function kimiCodePluginMarketplaceUrl(): string {
return `${kimiCodeCdnBase()}/plugins/marketplace.json`;
Comment thread
liruifengv marked this conversation as resolved.
}
// Official plugins whose usage bills against the user's plan quota. Installing
// one of these shows a quota note after the install result.
export const QUOTA_CONSUMING_PLUGIN_IDS: readonly string[] = ['kimi-datasource'];
export const KIMI_CODE_INSTALL_SH_URL = `${KIMI_CODE_CDN_BASE}/install.sh`;
export const KIMI_CODE_INSTALL_PS1_URL = `${KIMI_CODE_CDN_BASE}/install.ps1`;
export function kimiCodeInstallShUrl(): string {
return `${kimiCodeCdnBase()}/install.sh`;
}
export function kimiCodeInstallPs1Url(): string {
return `${kimiCodeCdnBase()}/install.ps1`;
}
// Official download page, referenced by prompt copy that steers users away
// from third-party install sources.
export const KIMI_CODE_OFFICIAL_INSTALL_URL = 'https://www.kimi.com/code';
export function kimiCodeOfficialInstallUrl(): string {
return `${currentKimiProfile().siteBase}/code`;
}

// Native install commands, split by platform. Use these for prompt copy and spawn calls only; do not assemble the strings elsewhere.
export const NATIVE_INSTALL_COMMAND_UNIX = `curl -fsSL ${KIMI_CODE_INSTALL_SH_URL} | bash`;
export const NATIVE_INSTALL_COMMAND_WIN = `irm ${KIMI_CODE_INSTALL_PS1_URL} | iex`;
export function nativeInstallCommandUnix(): string {
return `curl -fsSL ${kimiCodeInstallShUrl()} | bash`;
}
export function nativeInstallCommandWin(): string {
return `irm ${kimiCodeInstallPs1Url()} | iex`;
}
4 changes: 2 additions & 2 deletions apps/kimi-code/src/tui/banner/banner-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';

import { eq, gte, lt, valid } from 'semver';

import { KIMI_CODE_TIPS_BANNER_URL } from '#/constant/app';
import { kimiCodeTipsBannerUrl } from '#/constant/app';
import type { BannerDisplay, BannerState } from '#/tui/types';

import type { BannerDisplayState } from './state';
Expand Down Expand Up @@ -315,7 +315,7 @@ export function selectDisplayableBanner({
export class BannerProvider {
constructor(
private readonly clientVersion: string,
private readonly url: string = KIMI_CODE_TIPS_BANNER_URL,
private readonly url: string = kimiCodeTipsBannerUrl(),
) {}

async load(
Expand Down
Loading
Loading