@kenkaiiii/gg-core 4.4.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { ContextWindowOptions, MODELS, ModelInfo, getContextWindow, getDefaultModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, usesOpenAICodexTransport } from './model-registry.cjs';
1
+ export { ContextWindowOptions, DEFAULT_MAX_VIDEO_BYTES, MODELS, ModelInfo, getContextWindow, getDefaultModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, getVideoByteLimit, usesOpenAICodexTransport } from './model-registry.cjs';
2
2
  import { Provider, ThinkingLevel } from '@kenkaiiii/gg-ai';
3
3
  export { AppPaths, getAppPaths } from './paths.cjs';
4
4
 
@@ -65,6 +65,12 @@ interface OAuthLoginCallbacks {
65
65
  onStatus: (message: string) => void;
66
66
  }
67
67
 
68
+ /**
69
+ * Storage key for Kimi Code OAuth credentials. Kept distinct from the
70
+ * `moonshot` API-key entry so a user can configure BOTH and we always
71
+ * prefer OAuth for the logical `moonshot` provider.
72
+ */
73
+ declare const MOONSHOT_OAUTH_KEY = "moonshot-oauth";
68
74
  declare class AuthStorage {
69
75
  private data;
70
76
  private filePath;
@@ -78,6 +84,18 @@ declare class AuthStorage {
78
84
  listProviders(): Promise<string[]>;
79
85
  /** True if credentials exist for `provider`. */
80
86
  hasCredentials(provider: string): Promise<boolean>;
87
+ /**
88
+ * True if the user has any usable auth for the logical provider. For
89
+ * `moonshot` this is satisfied by either the Kimi OAuth credential or the
90
+ * Moonshot API key.
91
+ */
92
+ hasProviderAuth(provider: string): Promise<boolean>;
93
+ /**
94
+ * True if the active credential for `provider` is a static API key with no
95
+ * refresh mechanism. For `moonshot` this is only true when the Kimi OAuth
96
+ * credential is absent (a present OAuth credential is refreshable).
97
+ */
98
+ isStaticApiKey(provider: string): Promise<boolean>;
81
99
  load(): Promise<void>;
82
100
  private ensureLoaded;
83
101
  getCredentials(provider: string): Promise<OAuthCredentials | undefined>;
@@ -119,6 +137,52 @@ declare function refreshOpenAIToken(refreshToken: string): Promise<OAuthCredenti
119
137
  declare function loginGemini(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
120
138
  declare function refreshGeminiToken(refreshToken: string): Promise<OAuthCredentials>;
121
139
 
140
+ /**
141
+ * Kimi Code OAuth — Device Authorization Grant (RFC 8628).
142
+ *
143
+ * Mirrors MoonshotAI/kimi-code's managed-auth flow. Three form-encoded
144
+ * POST endpoints against the OAuth host (default `https://auth.kimi.com`):
145
+ *
146
+ * - `/api/oauth/device_authorization` (client_id) → device + user code
147
+ * - `/api/oauth/token` (grant_type=device_code) → poll until authorized
148
+ * - `/api/oauth/token` (grant_type=refresh_token) → refresh access token
149
+ *
150
+ * Unlike Anthropic/OpenAI/Gemini (browser-redirect PKCE), this is a
151
+ * device-code/poll flow: we show the user a URL + code, they authorize in a
152
+ * browser on any device, and we poll for the token.
153
+ *
154
+ * After login the issued token is used against the managed coding API
155
+ * (`https://api.kimi.com/coding/v1`, distinct from the `api.moonshot.ai`
156
+ * API-key endpoint) via `Authorization: Bearer <access_token>`. We persist
157
+ * that base URL on the credential so the runtime routes there automatically.
158
+ */
159
+
160
+ /** Managed coding API base URL the issued OAuth token is used against. */
161
+ declare function kimiCodeBaseUrl(): string;
162
+ /**
163
+ * Headers the Kimi For Coding API requires on every model request. The
164
+ * managed endpoint gates access to recognized coding agents: requests must
165
+ * carry a `kimi_code_cli` platform identity and matching `User-Agent`, or the
166
+ * server rejects with "only available for Coding Agents". Attach these to the
167
+ * inference client's default headers whenever the Kimi OAuth token is used.
168
+ */
169
+ declare function kimiCodingHeaders(): Record<string, string>;
170
+ /**
171
+ * True if `baseUrl` targets the Kimi For Coding managed endpoint (the URL
172
+ * persisted on Kimi OAuth credentials). Callers use this to decide whether to
173
+ * attach `kimiCodingHeaders()` — the Moonshot API-key path uses a different
174
+ * host and must NOT receive the coding-agent identity headers.
175
+ */
176
+ declare function isKimiCodingEndpoint(baseUrl: string | undefined): boolean;
177
+ /**
178
+ * Drive the Kimi device-code flow end-to-end. Shows the verification URL +
179
+ * user code via callbacks, opens the browser, and polls until the user
180
+ * authorizes (or a 15-minute local timeout elapses).
181
+ */
182
+ declare function loginKimi(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
183
+ /** Exchange a refresh token for a fresh Kimi access token. */
184
+ declare function refreshKimiToken(refreshToken: string): Promise<OAuthCredentials>;
185
+
122
186
  /**
123
187
  * Minimal Telegram Bot API client using raw fetch().
124
188
  * Supports long polling, markdown messages, inline keyboards, and message splitting.
@@ -292,4 +356,4 @@ interface AutoUpdater {
292
356
  }
293
357
  declare function createAutoUpdater(config: AutoUpdateConfig): AutoUpdater;
294
358
 
295
- export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, log, loginAnthropic, loginGemini, loginOpenAI, openLog, refreshAnthropicToken, refreshGeminiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };
359
+ export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, MOONSHOT_OAUTH_KEY, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isKimiCodingEndpoint, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, kimiCodeBaseUrl, kimiCodingHeaders, log, loginAnthropic, loginGemini, loginKimi, loginOpenAI, openLog, refreshAnthropicToken, refreshGeminiToken, refreshKimiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { ContextWindowOptions, MODELS, ModelInfo, getContextWindow, getDefaultModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, usesOpenAICodexTransport } from './model-registry.js';
1
+ export { ContextWindowOptions, DEFAULT_MAX_VIDEO_BYTES, MODELS, ModelInfo, getContextWindow, getDefaultModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, getVideoByteLimit, usesOpenAICodexTransport } from './model-registry.js';
2
2
  import { Provider, ThinkingLevel } from '@kenkaiiii/gg-ai';
3
3
  export { AppPaths, getAppPaths } from './paths.js';
4
4
 
@@ -65,6 +65,12 @@ interface OAuthLoginCallbacks {
65
65
  onStatus: (message: string) => void;
66
66
  }
67
67
 
68
+ /**
69
+ * Storage key for Kimi Code OAuth credentials. Kept distinct from the
70
+ * `moonshot` API-key entry so a user can configure BOTH and we always
71
+ * prefer OAuth for the logical `moonshot` provider.
72
+ */
73
+ declare const MOONSHOT_OAUTH_KEY = "moonshot-oauth";
68
74
  declare class AuthStorage {
69
75
  private data;
70
76
  private filePath;
@@ -78,6 +84,18 @@ declare class AuthStorage {
78
84
  listProviders(): Promise<string[]>;
79
85
  /** True if credentials exist for `provider`. */
80
86
  hasCredentials(provider: string): Promise<boolean>;
87
+ /**
88
+ * True if the user has any usable auth for the logical provider. For
89
+ * `moonshot` this is satisfied by either the Kimi OAuth credential or the
90
+ * Moonshot API key.
91
+ */
92
+ hasProviderAuth(provider: string): Promise<boolean>;
93
+ /**
94
+ * True if the active credential for `provider` is a static API key with no
95
+ * refresh mechanism. For `moonshot` this is only true when the Kimi OAuth
96
+ * credential is absent (a present OAuth credential is refreshable).
97
+ */
98
+ isStaticApiKey(provider: string): Promise<boolean>;
81
99
  load(): Promise<void>;
82
100
  private ensureLoaded;
83
101
  getCredentials(provider: string): Promise<OAuthCredentials | undefined>;
@@ -119,6 +137,52 @@ declare function refreshOpenAIToken(refreshToken: string): Promise<OAuthCredenti
119
137
  declare function loginGemini(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
120
138
  declare function refreshGeminiToken(refreshToken: string): Promise<OAuthCredentials>;
121
139
 
140
+ /**
141
+ * Kimi Code OAuth — Device Authorization Grant (RFC 8628).
142
+ *
143
+ * Mirrors MoonshotAI/kimi-code's managed-auth flow. Three form-encoded
144
+ * POST endpoints against the OAuth host (default `https://auth.kimi.com`):
145
+ *
146
+ * - `/api/oauth/device_authorization` (client_id) → device + user code
147
+ * - `/api/oauth/token` (grant_type=device_code) → poll until authorized
148
+ * - `/api/oauth/token` (grant_type=refresh_token) → refresh access token
149
+ *
150
+ * Unlike Anthropic/OpenAI/Gemini (browser-redirect PKCE), this is a
151
+ * device-code/poll flow: we show the user a URL + code, they authorize in a
152
+ * browser on any device, and we poll for the token.
153
+ *
154
+ * After login the issued token is used against the managed coding API
155
+ * (`https://api.kimi.com/coding/v1`, distinct from the `api.moonshot.ai`
156
+ * API-key endpoint) via `Authorization: Bearer <access_token>`. We persist
157
+ * that base URL on the credential so the runtime routes there automatically.
158
+ */
159
+
160
+ /** Managed coding API base URL the issued OAuth token is used against. */
161
+ declare function kimiCodeBaseUrl(): string;
162
+ /**
163
+ * Headers the Kimi For Coding API requires on every model request. The
164
+ * managed endpoint gates access to recognized coding agents: requests must
165
+ * carry a `kimi_code_cli` platform identity and matching `User-Agent`, or the
166
+ * server rejects with "only available for Coding Agents". Attach these to the
167
+ * inference client's default headers whenever the Kimi OAuth token is used.
168
+ */
169
+ declare function kimiCodingHeaders(): Record<string, string>;
170
+ /**
171
+ * True if `baseUrl` targets the Kimi For Coding managed endpoint (the URL
172
+ * persisted on Kimi OAuth credentials). Callers use this to decide whether to
173
+ * attach `kimiCodingHeaders()` — the Moonshot API-key path uses a different
174
+ * host and must NOT receive the coding-agent identity headers.
175
+ */
176
+ declare function isKimiCodingEndpoint(baseUrl: string | undefined): boolean;
177
+ /**
178
+ * Drive the Kimi device-code flow end-to-end. Shows the verification URL +
179
+ * user code via callbacks, opens the browser, and polls until the user
180
+ * authorizes (or a 15-minute local timeout elapses).
181
+ */
182
+ declare function loginKimi(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
183
+ /** Exchange a refresh token for a fresh Kimi access token. */
184
+ declare function refreshKimiToken(refreshToken: string): Promise<OAuthCredentials>;
185
+
122
186
  /**
123
187
  * Minimal Telegram Bot API client using raw fetch().
124
188
  * Supports long polling, markdown messages, inline keyboards, and message splitting.
@@ -292,4 +356,4 @@ interface AutoUpdater {
292
356
  }
293
357
  declare function createAutoUpdater(config: AutoUpdateConfig): AutoUpdater;
294
358
 
295
- export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, log, loginAnthropic, loginGemini, loginOpenAI, openLog, refreshAnthropicToken, refreshGeminiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };
359
+ export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, MOONSHOT_OAUTH_KEY, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isKimiCodingEndpoint, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, kimiCodeBaseUrl, kimiCodingHeaders, log, loginAnthropic, loginGemini, loginKimi, loginOpenAI, openLog, refreshAnthropicToken, refreshGeminiToken, refreshKimiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ DEFAULT_MAX_VIDEO_BYTES,
2
3
  MODELS,
3
4
  getContextWindow,
4
5
  getDefaultModel,
@@ -6,8 +7,9 @@ import {
6
7
  getModel,
7
8
  getModelsForProvider,
8
9
  getSummaryModel,
10
+ getVideoByteLimit,
9
11
  usesOpenAICodexTransport
10
- } from "./chunk-USAVZGPP.js";
12
+ } from "./chunk-LLWQ3Z76.js";
11
13
  import {
12
14
  getAppPaths
13
15
  } from "./chunk-TZNVRILI.js";
@@ -951,7 +953,232 @@ function codeAssistHeaders(accessToken) {
951
953
  };
952
954
  }
953
955
 
956
+ // src/oauth/kimi.ts
957
+ import { execFileSync } from "child_process";
958
+ import { randomUUID } from "crypto";
959
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
960
+ import { arch, hostname, release, type } from "os";
961
+ import path3 from "path";
962
+ var CLIENT_ID3 = "17e5f671-d194-4dfb-9706-5516cb48c098";
963
+ var DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
964
+ var DEFAULT_CODING_BASE_URL = "https://api.kimi.com/coding/v1";
965
+ var KIMI_PLATFORM = "kimi_code_cli";
966
+ var DEFAULT_KIMI_VERSION = "1.0.11";
967
+ var DEVICE_TIMEOUT_MS = 15 * 60 * 1e3;
968
+ function oauthHost() {
969
+ const host = process.env.KIMI_CODE_OAUTH_HOST ?? process.env.KIMI_OAUTH_HOST ?? DEFAULT_OAUTH_HOST;
970
+ return host.replace(/\/+$/, "");
971
+ }
972
+ function kimiCodeBaseUrl() {
973
+ return (process.env.KIMI_CODE_BASE_URL ?? DEFAULT_CODING_BASE_URL).replace(/\/+$/, "");
974
+ }
975
+ function kimiVersion() {
976
+ const v = process.env.KIMI_CODE_VERSION ?? DEFAULT_KIMI_VERSION;
977
+ return asciiHeader(v, DEFAULT_KIMI_VERSION);
978
+ }
979
+ function asciiHeader(value, fallback = "unknown") {
980
+ const cleaned = value.replace(/[^\u0020-\u007E]/g, "").trim();
981
+ return cleaned.length > 0 ? cleaned : fallback;
982
+ }
983
+ function macOsProductVersion() {
984
+ try {
985
+ const version = execFileSync("/usr/bin/sw_vers", ["-productVersion"], {
986
+ encoding: "utf-8",
987
+ timeout: 1e3
988
+ }).trim();
989
+ return version.length > 0 ? version : void 0;
990
+ } catch {
991
+ return void 0;
992
+ }
993
+ }
994
+ function deviceModel() {
995
+ const os = type();
996
+ const version = release();
997
+ const osArch = arch();
998
+ if (os === "Darwin") return `macOS ${macOsProductVersion() ?? version} ${osArch}`;
999
+ if (os === "Windows_NT") return `Windows ${version} ${osArch}`;
1000
+ return `${os} ${version} ${osArch}`.trim();
1001
+ }
1002
+ function deviceId() {
1003
+ const idPath = path3.join(getAppPaths().agentDir, "kimi_device_id");
1004
+ if (existsSync(idPath)) {
1005
+ try {
1006
+ const text = readFileSync(idPath, "utf-8").trim();
1007
+ if (text.length > 0) return text;
1008
+ } catch {
1009
+ }
1010
+ }
1011
+ const id = randomUUID();
1012
+ try {
1013
+ mkdirSync(getAppPaths().agentDir, { recursive: true, mode: 448 });
1014
+ writeFileSync(idPath, id, { encoding: "utf-8", mode: 384 });
1015
+ } catch {
1016
+ }
1017
+ return id;
1018
+ }
1019
+ function deviceHeaders() {
1020
+ return {
1021
+ "X-Msh-Platform": KIMI_PLATFORM,
1022
+ "X-Msh-Version": kimiVersion(),
1023
+ "X-Msh-Device-Name": asciiHeader(hostname()),
1024
+ "X-Msh-Device-Model": asciiHeader(deviceModel()),
1025
+ "X-Msh-Os-Version": asciiHeader(release()),
1026
+ "X-Msh-Device-Id": deviceId()
1027
+ };
1028
+ }
1029
+ function kimiCodingHeaders() {
1030
+ return {
1031
+ "User-Agent": `kimi-code-cli/${kimiVersion()}`,
1032
+ ...deviceHeaders()
1033
+ };
1034
+ }
1035
+ function isKimiCodingEndpoint(baseUrl) {
1036
+ if (typeof baseUrl !== "string" || baseUrl.length === 0) return false;
1037
+ const normalized = baseUrl.replace(/\/+$/, "");
1038
+ return normalized === kimiCodeBaseUrl() || /(^|\.)kimi\.com/i.test(normalized);
1039
+ }
1040
+ async function postForm(endpoint, params) {
1041
+ const response = await fetch(`${oauthHost()}${endpoint}`, {
1042
+ method: "POST",
1043
+ headers: {
1044
+ ...deviceHeaders(),
1045
+ "Content-Type": "application/x-www-form-urlencoded",
1046
+ Accept: "application/json"
1047
+ },
1048
+ body: new URLSearchParams(params).toString()
1049
+ });
1050
+ let data = {};
1051
+ try {
1052
+ const parsed = await response.json();
1053
+ if (parsed && typeof parsed === "object") data = parsed;
1054
+ } catch {
1055
+ }
1056
+ return { status: response.status, data };
1057
+ }
1058
+ function errorDetail(data) {
1059
+ const desc = data.error_description ?? data.message ?? data.error;
1060
+ return typeof desc === "string" && desc.length > 0 ? desc : "unknown error";
1061
+ }
1062
+ function credsFromTokenResponse(data) {
1063
+ const accessToken = data.access_token;
1064
+ const refreshToken = data.refresh_token;
1065
+ const expiresIn = Number(data.expires_in);
1066
+ if (typeof accessToken !== "string" || accessToken.length === 0) {
1067
+ throw new Error("Kimi OAuth response missing access_token.");
1068
+ }
1069
+ if (typeof refreshToken !== "string" || refreshToken.length === 0) {
1070
+ throw new Error("Kimi OAuth response missing refresh_token.");
1071
+ }
1072
+ if (!Number.isFinite(expiresIn) || expiresIn <= 0) {
1073
+ throw new Error("Kimi OAuth response missing or invalid expires_in.");
1074
+ }
1075
+ return {
1076
+ accessToken,
1077
+ refreshToken,
1078
+ expiresAt: Date.now() + expiresIn * 1e3,
1079
+ baseUrl: kimiCodeBaseUrl()
1080
+ };
1081
+ }
1082
+ async function requestDeviceAuthorization() {
1083
+ const { status, data } = await postForm("/api/oauth/device_authorization", {
1084
+ client_id: CLIENT_ID3
1085
+ });
1086
+ if (status !== 200) {
1087
+ throw new Error(`Kimi device authorization failed (${status}): ${errorDetail(data)}`);
1088
+ }
1089
+ const userCode = data.user_code;
1090
+ const deviceCode = data.device_code;
1091
+ const verificationUriComplete = data.verification_uri_complete;
1092
+ if (typeof userCode !== "string" || typeof deviceCode !== "string") {
1093
+ throw new Error("Kimi device authorization response missing user_code/device_code.");
1094
+ }
1095
+ return {
1096
+ userCode,
1097
+ deviceCode,
1098
+ verificationUri: typeof data.verification_uri === "string" ? data.verification_uri : "",
1099
+ verificationUriComplete: typeof verificationUriComplete === "string" ? verificationUriComplete : "",
1100
+ interval: Number(data.interval ?? 5) || 5
1101
+ };
1102
+ }
1103
+ async function pollDeviceToken(deviceCode) {
1104
+ const { status, data } = await postForm("/api/oauth/token", {
1105
+ client_id: CLIENT_ID3,
1106
+ device_code: deviceCode,
1107
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
1108
+ });
1109
+ if (status === 200 && typeof data.access_token === "string") {
1110
+ return { kind: "success", creds: credsFromTokenResponse(data) };
1111
+ }
1112
+ if (status >= 500) {
1113
+ throw new Error(`Kimi token polling server error (${status}): ${errorDetail(data)}`);
1114
+ }
1115
+ const errorCode = typeof data.error === "string" ? data.error : "unknown_error";
1116
+ switch (errorCode) {
1117
+ case "authorization_pending":
1118
+ return { kind: "pending" };
1119
+ case "slow_down":
1120
+ return { kind: "slow_down" };
1121
+ case "expired_token":
1122
+ return { kind: "expired" };
1123
+ case "access_denied":
1124
+ return { kind: "denied" };
1125
+ default:
1126
+ throw new Error(`Kimi token polling failed (${status}): ${errorDetail(data)}`);
1127
+ }
1128
+ }
1129
+ function sleep(ms) {
1130
+ return new Promise((resolve) => {
1131
+ setTimeout(resolve, ms);
1132
+ });
1133
+ }
1134
+ async function loginKimi(callbacks) {
1135
+ const auth = await requestDeviceAuthorization();
1136
+ callbacks.onStatus(
1137
+ `Visit ${auth.verificationUri || auth.verificationUriComplete} and enter code: ${auth.userCode}`
1138
+ );
1139
+ callbacks.onOpenUrl(auth.verificationUriComplete || auth.verificationUri);
1140
+ callbacks.onStatus("Waiting for you to authorize in the browser...");
1141
+ const deadline = Date.now() + DEVICE_TIMEOUT_MS;
1142
+ let interval = Math.max(auth.interval, 1);
1143
+ while (Date.now() < deadline) {
1144
+ await sleep(interval * 1e3);
1145
+ const result = await pollDeviceToken(auth.deviceCode);
1146
+ if (result.kind === "success") return result.creds;
1147
+ if (result.kind === "denied") {
1148
+ throw new Error("Kimi authorization was denied.");
1149
+ }
1150
+ if (result.kind === "expired") {
1151
+ throw new Error("Kimi device code expired. Please run login again.");
1152
+ }
1153
+ if (result.kind === "slow_down") {
1154
+ interval += 5;
1155
+ }
1156
+ }
1157
+ throw new Error("Kimi login timed out. Please run login again.");
1158
+ }
1159
+ async function refreshKimiToken(refreshToken) {
1160
+ const { status, data } = await postForm("/api/oauth/token", {
1161
+ client_id: CLIENT_ID3,
1162
+ grant_type: "refresh_token",
1163
+ refresh_token: refreshToken
1164
+ });
1165
+ if (status === 200 && typeof data.access_token === "string") {
1166
+ return credsFromTokenResponse(data);
1167
+ }
1168
+ const errorCode = typeof data.error === "string" ? data.error : "";
1169
+ throw new Error(`Kimi token refresh failed (${status}): ${errorCode || errorDetail(data)}`);
1170
+ }
1171
+
954
1172
  // src/auth-storage.ts
1173
+ var MOONSHOT_OAUTH_KEY = "moonshot-oauth";
1174
+ var STATIC_API_KEY_PROVIDERS = /* @__PURE__ */ new Set([
1175
+ "glm",
1176
+ "moonshot",
1177
+ "xiaomi",
1178
+ "minimax",
1179
+ "deepseek",
1180
+ "openrouter"
1181
+ ]);
955
1182
  var AuthStorage = class {
956
1183
  data = {};
957
1184
  filePath;
@@ -975,6 +1202,30 @@ var AuthStorage = class {
975
1202
  await this.ensureLoaded();
976
1203
  return Boolean(this.data[provider]);
977
1204
  }
1205
+ /**
1206
+ * True if the user has any usable auth for the logical provider. For
1207
+ * `moonshot` this is satisfied by either the Kimi OAuth credential or the
1208
+ * Moonshot API key.
1209
+ */
1210
+ async hasProviderAuth(provider) {
1211
+ await this.ensureLoaded();
1212
+ if (provider === "moonshot") {
1213
+ return Boolean(this.data[MOONSHOT_OAUTH_KEY] || this.data["moonshot"]);
1214
+ }
1215
+ return Boolean(this.data[provider]);
1216
+ }
1217
+ /**
1218
+ * True if the active credential for `provider` is a static API key with no
1219
+ * refresh mechanism. For `moonshot` this is only true when the Kimi OAuth
1220
+ * credential is absent (a present OAuth credential is refreshable).
1221
+ */
1222
+ async isStaticApiKey(provider) {
1223
+ await this.ensureLoaded();
1224
+ if (provider === "moonshot" && this.data[MOONSHOT_OAUTH_KEY]) {
1225
+ return false;
1226
+ }
1227
+ return STATIC_API_KEY_PROVIDERS.has(provider);
1228
+ }
978
1229
  async load() {
979
1230
  await withFileLock(this.filePath, async () => {
980
1231
  try {
@@ -1029,11 +1280,21 @@ var AuthStorage = class {
1029
1280
  */
1030
1281
  async resolveCredentials(provider, opts) {
1031
1282
  await this.ensureLoaded();
1283
+ if (provider === "moonshot" && this.data[MOONSHOT_OAUTH_KEY]) {
1284
+ try {
1285
+ return await this.resolveCredentials(MOONSHOT_OAUTH_KEY, opts);
1286
+ } catch (err) {
1287
+ if (err instanceof NotLoggedInError && this.data["moonshot"]) {
1288
+ return this.data["moonshot"];
1289
+ }
1290
+ throw err;
1291
+ }
1292
+ }
1032
1293
  const creds = this.data[provider];
1033
1294
  if (!creds) {
1034
1295
  throw new NotLoggedInError(provider);
1035
1296
  }
1036
- if (provider === "glm" || provider === "moonshot" || provider === "xiaomi" || provider === "minimax" || provider === "deepseek" || provider === "openrouter") {
1297
+ if (STATIC_API_KEY_PROVIDERS.has(provider)) {
1037
1298
  return creds;
1038
1299
  }
1039
1300
  if (!opts?.forceRefresh && Date.now() < creds.expiresAt) {
@@ -1052,7 +1313,7 @@ var AuthStorage = class {
1052
1313
  }
1053
1314
  } catch {
1054
1315
  }
1055
- const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : refreshOpenAIToken;
1316
+ const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : refreshOpenAIToken;
1056
1317
  let refreshed;
1057
1318
  try {
1058
1319
  refreshed = await refreshFn(creds.refreshToken);
@@ -1170,7 +1431,7 @@ var TelegramBot = class {
1170
1431
  } catch (err) {
1171
1432
  if (!this.running) break;
1172
1433
  console.error(`[telegram] Poll error: ${err instanceof Error ? err.message : err}`);
1173
- await sleep(3e3);
1434
+ await sleep2(3e3);
1174
1435
  }
1175
1436
  }
1176
1437
  }
@@ -1340,7 +1601,7 @@ function splitMessage(text) {
1340
1601
  }
1341
1602
  return chunks;
1342
1603
  }
1343
- function sleep(ms) {
1604
+ function sleep2(ms) {
1344
1605
  return new Promise((r) => setTimeout(r, ms));
1345
1606
  }
1346
1607
 
@@ -1427,7 +1688,7 @@ async function transcribeVoice(fileUrl) {
1427
1688
  // src/auto-update.ts
1428
1689
  import { spawn } from "child_process";
1429
1690
  import fs5 from "fs";
1430
- import path3 from "path";
1691
+ import path4 from "path";
1431
1692
  var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
1432
1693
  var FETCH_TIMEOUT_MS2 = 1e4;
1433
1694
  function compareVersions(a, b) {
@@ -1469,7 +1730,7 @@ function createAutoUpdater(config) {
1469
1730
  function writeState(state) {
1470
1731
  try {
1471
1732
  const filePath = stateFilePath();
1472
- fs5.mkdirSync(path3.dirname(filePath), { recursive: true, mode: 448 });
1733
+ fs5.mkdirSync(path4.dirname(filePath), { recursive: true, mode: 448 });
1473
1734
  fs5.writeFileSync(filePath, JSON.stringify(state));
1474
1735
  } catch {
1475
1736
  }
@@ -1594,7 +1855,9 @@ function createAutoUpdater(config) {
1594
1855
  }
1595
1856
  export {
1596
1857
  AuthStorage,
1858
+ DEFAULT_MAX_VIDEO_BYTES,
1597
1859
  MODELS,
1860
+ MOONSHOT_OAUTH_KEY,
1598
1861
  NotLoggedInError,
1599
1862
  TelegramBot,
1600
1863
  closeLogger,
@@ -1614,16 +1877,22 @@ export {
1614
1877
  getSessionId,
1615
1878
  getSummaryModel,
1616
1879
  getSupportedThinkingLevels,
1880
+ getVideoByteLimit,
1881
+ isKimiCodingEndpoint,
1617
1882
  isLoggerOpen,
1618
1883
  isModelLoaded,
1619
1884
  isThinkingLevelSupported,
1885
+ kimiCodeBaseUrl,
1886
+ kimiCodingHeaders,
1620
1887
  log,
1621
1888
  loginAnthropic,
1622
1889
  loginGemini,
1890
+ loginKimi,
1623
1891
  loginOpenAI,
1624
1892
  openLog,
1625
1893
  refreshAnthropicToken,
1626
1894
  refreshGeminiToken,
1895
+ refreshKimiToken,
1627
1896
  refreshOpenAIToken,
1628
1897
  registerLogCleanup,
1629
1898
  resample,