@prestyj/core 5.8.0 → 5.10.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.
@@ -2,8 +2,227 @@ import {
2
2
  getAppPaths
3
3
  } from "./chunk-WCIV25KV.js";
4
4
 
5
+ // src/oauth/kimi.ts
6
+ import { execFileSync } from "child_process";
7
+ import { randomUUID } from "crypto";
8
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
9
+ import { arch, hostname, release, type } from "os";
10
+ import path from "path";
11
+ var CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098";
12
+ var DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
13
+ var DEFAULT_CODING_BASE_URL = "https://api.kimi.com/coding/v1";
14
+ var KIMI_PLATFORM = "kimi_code_cli";
15
+ var DEFAULT_KIMI_VERSION = "1.0.11";
16
+ var DEVICE_TIMEOUT_MS = 15 * 60 * 1e3;
17
+ function oauthHost() {
18
+ const host = process.env.KIMI_CODE_OAUTH_HOST ?? process.env.KIMI_OAUTH_HOST ?? DEFAULT_OAUTH_HOST;
19
+ return host.replace(/\/+$/, "");
20
+ }
21
+ function kimiCodeBaseUrl() {
22
+ return (process.env.KIMI_CODE_BASE_URL ?? DEFAULT_CODING_BASE_URL).replace(/\/+$/, "");
23
+ }
24
+ function kimiVersion() {
25
+ const v = process.env.KIMI_CODE_VERSION ?? DEFAULT_KIMI_VERSION;
26
+ return asciiHeader(v, DEFAULT_KIMI_VERSION);
27
+ }
28
+ function asciiHeader(value, fallback = "unknown") {
29
+ const cleaned = value.replace(/[^\u0020-\u007E]/g, "").trim();
30
+ return cleaned.length > 0 ? cleaned : fallback;
31
+ }
32
+ function macOsProductVersion() {
33
+ try {
34
+ const version = execFileSync("/usr/bin/sw_vers", ["-productVersion"], {
35
+ encoding: "utf-8",
36
+ timeout: 1e3
37
+ }).trim();
38
+ return version.length > 0 ? version : void 0;
39
+ } catch {
40
+ return void 0;
41
+ }
42
+ }
43
+ function deviceModel() {
44
+ const os = type();
45
+ const version = release();
46
+ const osArch = arch();
47
+ if (os === "Darwin") return `macOS ${macOsProductVersion() ?? version} ${osArch}`;
48
+ if (os === "Windows_NT") return `Windows ${version} ${osArch}`;
49
+ return `${os} ${version} ${osArch}`.trim();
50
+ }
51
+ function deviceId() {
52
+ const idPath = path.join(getAppPaths().agentDir, "kimi_device_id");
53
+ if (existsSync(idPath)) {
54
+ try {
55
+ const text = readFileSync(idPath, "utf-8").trim();
56
+ if (text.length > 0) return text;
57
+ } catch {
58
+ }
59
+ }
60
+ const id = randomUUID();
61
+ try {
62
+ mkdirSync(getAppPaths().agentDir, { recursive: true, mode: 448 });
63
+ writeFileSync(idPath, id, { encoding: "utf-8", mode: 384 });
64
+ } catch {
65
+ }
66
+ return id;
67
+ }
68
+ function deviceHeaders() {
69
+ return {
70
+ "X-Msh-Platform": KIMI_PLATFORM,
71
+ "X-Msh-Version": kimiVersion(),
72
+ "X-Msh-Device-Name": asciiHeader(hostname()),
73
+ "X-Msh-Device-Model": asciiHeader(deviceModel()),
74
+ "X-Msh-Os-Version": asciiHeader(release()),
75
+ "X-Msh-Device-Id": deviceId()
76
+ };
77
+ }
78
+ function kimiCodingHeaders() {
79
+ return {
80
+ "User-Agent": `kimi-code-cli/${kimiVersion()}`,
81
+ ...deviceHeaders()
82
+ };
83
+ }
84
+ function isKimiCodingEndpoint(baseUrl) {
85
+ if (typeof baseUrl !== "string" || baseUrl.length === 0) return false;
86
+ const normalized = baseUrl.replace(/\/+$/, "");
87
+ return normalized === kimiCodeBaseUrl() || /(^|\.)kimi\.com/i.test(normalized);
88
+ }
89
+ async function postForm(endpoint, params) {
90
+ const response = await fetch(`${oauthHost()}${endpoint}`, {
91
+ method: "POST",
92
+ headers: {
93
+ ...deviceHeaders(),
94
+ "Content-Type": "application/x-www-form-urlencoded",
95
+ Accept: "application/json"
96
+ },
97
+ body: new URLSearchParams(params).toString()
98
+ });
99
+ let data = {};
100
+ try {
101
+ const parsed = await response.json();
102
+ if (parsed && typeof parsed === "object") data = parsed;
103
+ } catch {
104
+ }
105
+ return { status: response.status, data };
106
+ }
107
+ function errorDetail(data) {
108
+ const desc = data.error_description ?? data.message ?? data.error;
109
+ return typeof desc === "string" && desc.length > 0 ? desc : "unknown error";
110
+ }
111
+ function credsFromTokenResponse(data, opts) {
112
+ const accessToken = data.access_token;
113
+ const responseRefreshToken = data.refresh_token;
114
+ const expiresIn = Number(data.expires_in);
115
+ if (typeof accessToken !== "string" || accessToken.length === 0) {
116
+ throw new Error("Kimi OAuth response missing access_token.");
117
+ }
118
+ const refreshToken = typeof responseRefreshToken === "string" && responseRefreshToken.length > 0 ? responseRefreshToken : opts?.fallbackRefreshToken ?? "";
119
+ if (refreshToken.length === 0) {
120
+ throw new Error("Kimi OAuth response missing refresh_token.");
121
+ }
122
+ if (!Number.isFinite(expiresIn) || expiresIn <= 0) {
123
+ throw new Error("Kimi OAuth response missing or invalid expires_in.");
124
+ }
125
+ return {
126
+ accessToken,
127
+ refreshToken,
128
+ expiresAt: Date.now() + expiresIn * 1e3,
129
+ expiresIn,
130
+ baseUrl: kimiCodeBaseUrl()
131
+ };
132
+ }
133
+ async function requestDeviceAuthorization() {
134
+ const { status, data } = await postForm("/api/oauth/device_authorization", {
135
+ client_id: CLIENT_ID
136
+ });
137
+ if (status !== 200) {
138
+ throw new Error(`Kimi device authorization failed (${status}): ${errorDetail(data)}`);
139
+ }
140
+ const userCode = data.user_code;
141
+ const deviceCode = data.device_code;
142
+ const verificationUriComplete = data.verification_uri_complete;
143
+ if (typeof userCode !== "string" || typeof deviceCode !== "string") {
144
+ throw new Error("Kimi device authorization response missing user_code/device_code.");
145
+ }
146
+ return {
147
+ userCode,
148
+ deviceCode,
149
+ verificationUri: typeof data.verification_uri === "string" ? data.verification_uri : "",
150
+ verificationUriComplete: typeof verificationUriComplete === "string" ? verificationUriComplete : "",
151
+ interval: Number(data.interval ?? 5) || 5
152
+ };
153
+ }
154
+ async function pollDeviceToken(deviceCode) {
155
+ const { status, data } = await postForm("/api/oauth/token", {
156
+ client_id: CLIENT_ID,
157
+ device_code: deviceCode,
158
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
159
+ });
160
+ if (status === 200 && typeof data.access_token === "string") {
161
+ return { kind: "success", creds: credsFromTokenResponse(data) };
162
+ }
163
+ if (status >= 500) {
164
+ throw new Error(`Kimi token polling server error (${status}): ${errorDetail(data)}`);
165
+ }
166
+ const errorCode = typeof data.error === "string" ? data.error : "unknown_error";
167
+ switch (errorCode) {
168
+ case "authorization_pending":
169
+ return { kind: "pending" };
170
+ case "slow_down":
171
+ return { kind: "slow_down" };
172
+ case "expired_token":
173
+ return { kind: "expired" };
174
+ case "access_denied":
175
+ return { kind: "denied" };
176
+ default:
177
+ throw new Error(`Kimi token polling failed (${status}): ${errorDetail(data)}`);
178
+ }
179
+ }
180
+ function sleep(ms) {
181
+ return new Promise((resolve) => {
182
+ setTimeout(resolve, ms);
183
+ });
184
+ }
185
+ async function loginKimi(callbacks) {
186
+ const auth = await requestDeviceAuthorization();
187
+ callbacks.onStatus(
188
+ `Visit ${auth.verificationUri || auth.verificationUriComplete} and enter code: ${auth.userCode}`
189
+ );
190
+ callbacks.onOpenUrl(auth.verificationUriComplete || auth.verificationUri);
191
+ callbacks.onStatus("Waiting for you to authorize in the browser...");
192
+ const deadline = Date.now() + DEVICE_TIMEOUT_MS;
193
+ let interval = Math.max(auth.interval, 1);
194
+ while (Date.now() < deadline) {
195
+ await sleep(interval * 1e3);
196
+ const result = await pollDeviceToken(auth.deviceCode);
197
+ if (result.kind === "success") return result.creds;
198
+ if (result.kind === "denied") {
199
+ throw new Error("Kimi authorization was denied.");
200
+ }
201
+ if (result.kind === "expired") {
202
+ throw new Error("Kimi device code expired. Please run login again.");
203
+ }
204
+ if (result.kind === "slow_down") {
205
+ interval += 5;
206
+ }
207
+ }
208
+ throw new Error("Kimi login timed out. Please run login again.");
209
+ }
210
+ async function refreshKimiToken(refreshToken) {
211
+ const { status, data } = await postForm("/api/oauth/token", {
212
+ client_id: CLIENT_ID,
213
+ grant_type: "refresh_token",
214
+ refresh_token: refreshToken
215
+ });
216
+ if (status === 200 && typeof data.access_token === "string") {
217
+ return credsFromTokenResponse(data, { fallbackRefreshToken: refreshToken });
218
+ }
219
+ const errorCode = typeof data.error === "string" ? data.error : "";
220
+ throw new Error(`Kimi token refresh failed (${status}): ${errorCode || errorDetail(data)}`);
221
+ }
222
+
5
223
  // src/auth-storage.ts
6
224
  import fs4 from "fs/promises";
225
+ import { readFileSync as readFileSync2 } from "fs";
7
226
  import crypto5 from "crypto";
8
227
 
9
228
  // src/oauth/anthropic.ts
@@ -29,15 +248,17 @@ async function generatePKCE() {
29
248
 
30
249
  // src/claude-code-version.ts
31
250
  import fs2 from "fs/promises";
32
- import path2 from "path";
251
+ import path3 from "path";
33
252
 
34
253
  // src/logger.ts
35
254
  import fs from "fs";
36
- import path from "path";
255
+ import path2 from "path";
37
256
  import { randomBytes } from "crypto";
38
257
  import { environmentSecrets, redactText, redactValue } from "@prestyj/ai";
39
258
  var MAX_BYTES = 10 * 1024 * 1024;
40
259
  var fd = null;
260
+ var bytesWritten = 0;
261
+ var capped = false;
41
262
  var sessionId = "";
42
263
  var appName = "app";
43
264
  var cleanups = [];
@@ -56,22 +277,37 @@ function rotateIfNeeded(filePath) {
56
277
  }
57
278
  }
58
279
  function openLog(filePath, name) {
59
- if (fd !== null) return false;
280
+ if (fd !== null || capped) return false;
60
281
  appName = name;
61
282
  exactSecrets = environmentSecrets(process.env);
62
283
  try {
63
- fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 448 });
284
+ fs.mkdirSync(path2.dirname(filePath), { recursive: true, mode: 448 });
64
285
  } catch {
65
286
  }
66
287
  rotateIfNeeded(filePath);
67
288
  try {
68
289
  fd = fs.openSync(filePath, "a");
69
290
  } catch {
291
+ fd = null;
292
+ bytesWritten = 0;
293
+ return false;
294
+ }
295
+ try {
296
+ bytesWritten = fs.fstatSync(fd).size;
297
+ } catch {
298
+ try {
299
+ fs.closeSync(fd);
300
+ } catch {
301
+ }
302
+ fd = null;
303
+ bytesWritten = 0;
70
304
  return false;
71
305
  }
72
306
  sessionId = randomBytes(4).toString("hex");
73
307
  try {
74
- fs.writeSync(fd, "\n");
308
+ if (bytesWritten < MAX_BYTES) {
309
+ bytesWritten += fs.writeSync(fd, "\n");
310
+ }
75
311
  } catch {
76
312
  }
77
313
  return true;
@@ -97,8 +333,23 @@ function log(level, category, message, data) {
97
333
  if (pairs) line += ` ${pairs}`;
98
334
  }
99
335
  line += "\n";
336
+ const lineBytes = Buffer.byteLength(line);
337
+ if (bytesWritten + lineBytes > MAX_BYTES) {
338
+ const capLine = `[${ts}] [sid=${sessionId}] [WARN] [logger] Log cap reached; file logging disabled until restart
339
+ `;
340
+ try {
341
+ if (bytesWritten + Buffer.byteLength(capLine) <= MAX_BYTES) {
342
+ bytesWritten += fs.writeSync(fd, capLine);
343
+ }
344
+ fs.closeSync(fd);
345
+ } catch {
346
+ }
347
+ fd = null;
348
+ capped = true;
349
+ return;
350
+ }
100
351
  try {
101
- fs.writeSync(fd, line);
352
+ bytesWritten += fs.writeSync(fd, line);
102
353
  } catch {
103
354
  }
104
355
  }
@@ -106,13 +357,16 @@ function registerLogCleanup(fn) {
106
357
  cleanups.push(fn);
107
358
  }
108
359
  function closeLogger(opts) {
109
- if (fd === null) return;
110
- if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
111
- try {
112
- fs.closeSync(fd);
113
- } catch {
360
+ if (fd !== null) {
361
+ if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
362
+ try {
363
+ if (fd !== null) fs.closeSync(fd);
364
+ } catch {
365
+ }
114
366
  }
115
367
  fd = null;
368
+ bytesWritten = 0;
369
+ capped = false;
116
370
  exactSecrets = [];
117
371
  for (const unsub of cleanups) unsub();
118
372
  cleanups = [];
@@ -126,7 +380,7 @@ var FALLBACK_VERSION = "2.1.88";
126
380
  var memoryCache = null;
127
381
  var inflight = null;
128
382
  function cachePath() {
129
- return path2.join(getAppPaths().agentDir, "claude-code-version.json");
383
+ return path3.join(getAppPaths().agentDir, "claude-code-version.json");
130
384
  }
131
385
  async function readDiskCache() {
132
386
  try {
@@ -208,7 +462,7 @@ async function getClaudeCliUserAgent() {
208
462
  }
209
463
 
210
464
  // src/oauth/anthropic.ts
211
- var CLIENT_ID = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
465
+ var CLIENT_ID2 = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
212
466
  var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
213
467
  var TOKEN_URLS = [
214
468
  "https://platform.claude.com/v1/oauth/token",
@@ -255,7 +509,7 @@ async function loginAnthropic(callbacks) {
255
509
  const state = crypto2.randomBytes(16).toString("hex");
256
510
  const params = new URLSearchParams({
257
511
  code: "true",
258
- client_id: CLIENT_ID,
512
+ client_id: CLIENT_ID2,
259
513
  response_type: "code",
260
514
  redirect_uri: REDIRECT_URI,
261
515
  scope: SCOPES,
@@ -276,7 +530,7 @@ async function exchangeAnthropicCode(code, state, verifier) {
276
530
  const data = await postTokenRequest(
277
531
  {
278
532
  grant_type: "authorization_code",
279
- client_id: CLIENT_ID,
533
+ client_id: CLIENT_ID2,
280
534
  code,
281
535
  state,
282
536
  redirect_uri: REDIRECT_URI,
@@ -290,7 +544,7 @@ async function refreshAnthropicToken(refreshToken) {
290
544
  const data = await postTokenRequest(
291
545
  {
292
546
  grant_type: "refresh_token",
293
- client_id: CLIENT_ID,
547
+ client_id: CLIENT_ID2,
294
548
  refresh_token: refreshToken
295
549
  },
296
550
  "token refresh"
@@ -301,7 +555,7 @@ async function refreshAnthropicToken(refreshToken) {
301
555
  // src/oauth/openai.ts
302
556
  import http from "http";
303
557
  import crypto3 from "crypto";
304
- var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
558
+ var CLIENT_ID3 = "app_EMoamEEZ73f0CkXaXp7hrann";
305
559
  var AUTHORIZE_URL2 = "https://auth.openai.com/oauth/authorize";
306
560
  var TOKEN_URL = "https://auth.openai.com/oauth/token";
307
561
  var REDIRECT_URI2 = "http://localhost:1455/auth/callback";
@@ -312,7 +566,7 @@ async function loginOpenAI(callbacks) {
312
566
  const state = crypto3.randomBytes(16).toString("hex");
313
567
  const url = new URL(AUTHORIZE_URL2);
314
568
  url.searchParams.set("response_type", "code");
315
- url.searchParams.set("client_id", CLIENT_ID2);
569
+ url.searchParams.set("client_id", CLIENT_ID3);
316
570
  url.searchParams.set("redirect_uri", REDIRECT_URI2);
317
571
  url.searchParams.set("scope", SCOPE);
318
572
  url.searchParams.set("code_challenge", challenge);
@@ -433,7 +687,7 @@ async function exchangeOpenAICode(code, verifier) {
433
687
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
434
688
  body: new URLSearchParams({
435
689
  grant_type: "authorization_code",
436
- client_id: CLIENT_ID2,
690
+ client_id: CLIENT_ID3,
437
691
  code,
438
692
  redirect_uri: REDIRECT_URI2,
439
693
  code_verifier: verifier
@@ -457,7 +711,7 @@ async function refreshOpenAIToken(refreshToken) {
457
711
  body: new URLSearchParams({
458
712
  grant_type: "refresh_token",
459
713
  refresh_token: refreshToken,
460
- client_id: CLIENT_ID2
714
+ client_id: CLIENT_ID3
461
715
  })
462
716
  });
463
717
  if (!response.ok) {
@@ -840,223 +1094,6 @@ function codeAssistHeaders(accessToken) {
840
1094
  };
841
1095
  }
842
1096
 
843
- // src/oauth/kimi.ts
844
- import { execFileSync } from "child_process";
845
- import { randomUUID } from "crypto";
846
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
847
- import { arch, hostname, release, type } from "os";
848
- import path3 from "path";
849
- var CLIENT_ID3 = "17e5f671-d194-4dfb-9706-5516cb48c098";
850
- var DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
851
- var DEFAULT_CODING_BASE_URL = "https://api.kimi.com/coding/v1";
852
- var KIMI_PLATFORM = "kimi_code_cli";
853
- var DEFAULT_KIMI_VERSION = "1.0.11";
854
- var DEVICE_TIMEOUT_MS = 15 * 60 * 1e3;
855
- function oauthHost() {
856
- const host = process.env.KIMI_CODE_OAUTH_HOST ?? process.env.KIMI_OAUTH_HOST ?? DEFAULT_OAUTH_HOST;
857
- return host.replace(/\/+$/, "");
858
- }
859
- function kimiCodeBaseUrl() {
860
- return (process.env.KIMI_CODE_BASE_URL ?? DEFAULT_CODING_BASE_URL).replace(/\/+$/, "");
861
- }
862
- function kimiVersion() {
863
- const v = process.env.KIMI_CODE_VERSION ?? DEFAULT_KIMI_VERSION;
864
- return asciiHeader(v, DEFAULT_KIMI_VERSION);
865
- }
866
- function asciiHeader(value, fallback = "unknown") {
867
- const cleaned = value.replace(/[^\u0020-\u007E]/g, "").trim();
868
- return cleaned.length > 0 ? cleaned : fallback;
869
- }
870
- function macOsProductVersion() {
871
- try {
872
- const version = execFileSync("/usr/bin/sw_vers", ["-productVersion"], {
873
- encoding: "utf-8",
874
- timeout: 1e3
875
- }).trim();
876
- return version.length > 0 ? version : void 0;
877
- } catch {
878
- return void 0;
879
- }
880
- }
881
- function deviceModel() {
882
- const os = type();
883
- const version = release();
884
- const osArch = arch();
885
- if (os === "Darwin") return `macOS ${macOsProductVersion() ?? version} ${osArch}`;
886
- if (os === "Windows_NT") return `Windows ${version} ${osArch}`;
887
- return `${os} ${version} ${osArch}`.trim();
888
- }
889
- function deviceId() {
890
- const idPath = path3.join(getAppPaths().agentDir, "kimi_device_id");
891
- if (existsSync(idPath)) {
892
- try {
893
- const text = readFileSync(idPath, "utf-8").trim();
894
- if (text.length > 0) return text;
895
- } catch {
896
- }
897
- }
898
- const id = randomUUID();
899
- try {
900
- mkdirSync(getAppPaths().agentDir, { recursive: true, mode: 448 });
901
- writeFileSync(idPath, id, { encoding: "utf-8", mode: 384 });
902
- } catch {
903
- }
904
- return id;
905
- }
906
- function deviceHeaders() {
907
- return {
908
- "X-Msh-Platform": KIMI_PLATFORM,
909
- "X-Msh-Version": kimiVersion(),
910
- "X-Msh-Device-Name": asciiHeader(hostname()),
911
- "X-Msh-Device-Model": asciiHeader(deviceModel()),
912
- "X-Msh-Os-Version": asciiHeader(release()),
913
- "X-Msh-Device-Id": deviceId()
914
- };
915
- }
916
- function kimiCodingHeaders() {
917
- return {
918
- "User-Agent": `kimi-code-cli/${kimiVersion()}`,
919
- ...deviceHeaders()
920
- };
921
- }
922
- function isKimiCodingEndpoint(baseUrl) {
923
- if (typeof baseUrl !== "string" || baseUrl.length === 0) return false;
924
- const normalized = baseUrl.replace(/\/+$/, "");
925
- return normalized === kimiCodeBaseUrl() || /(^|\.)kimi\.com/i.test(normalized);
926
- }
927
- async function postForm(endpoint, params) {
928
- const response = await fetch(`${oauthHost()}${endpoint}`, {
929
- method: "POST",
930
- headers: {
931
- ...deviceHeaders(),
932
- "Content-Type": "application/x-www-form-urlencoded",
933
- Accept: "application/json"
934
- },
935
- body: new URLSearchParams(params).toString()
936
- });
937
- let data = {};
938
- try {
939
- const parsed = await response.json();
940
- if (parsed && typeof parsed === "object") data = parsed;
941
- } catch {
942
- }
943
- return { status: response.status, data };
944
- }
945
- function errorDetail(data) {
946
- const desc = data.error_description ?? data.message ?? data.error;
947
- return typeof desc === "string" && desc.length > 0 ? desc : "unknown error";
948
- }
949
- function credsFromTokenResponse(data, opts) {
950
- const accessToken = data.access_token;
951
- const responseRefreshToken = data.refresh_token;
952
- const expiresIn = Number(data.expires_in);
953
- if (typeof accessToken !== "string" || accessToken.length === 0) {
954
- throw new Error("Kimi OAuth response missing access_token.");
955
- }
956
- const refreshToken = typeof responseRefreshToken === "string" && responseRefreshToken.length > 0 ? responseRefreshToken : opts?.fallbackRefreshToken ?? "";
957
- if (refreshToken.length === 0) {
958
- throw new Error("Kimi OAuth response missing refresh_token.");
959
- }
960
- if (!Number.isFinite(expiresIn) || expiresIn <= 0) {
961
- throw new Error("Kimi OAuth response missing or invalid expires_in.");
962
- }
963
- return {
964
- accessToken,
965
- refreshToken,
966
- expiresAt: Date.now() + expiresIn * 1e3,
967
- baseUrl: kimiCodeBaseUrl()
968
- };
969
- }
970
- async function requestDeviceAuthorization() {
971
- const { status, data } = await postForm("/api/oauth/device_authorization", {
972
- client_id: CLIENT_ID3
973
- });
974
- if (status !== 200) {
975
- throw new Error(`Kimi device authorization failed (${status}): ${errorDetail(data)}`);
976
- }
977
- const userCode = data.user_code;
978
- const deviceCode = data.device_code;
979
- const verificationUriComplete = data.verification_uri_complete;
980
- if (typeof userCode !== "string" || typeof deviceCode !== "string") {
981
- throw new Error("Kimi device authorization response missing user_code/device_code.");
982
- }
983
- return {
984
- userCode,
985
- deviceCode,
986
- verificationUri: typeof data.verification_uri === "string" ? data.verification_uri : "",
987
- verificationUriComplete: typeof verificationUriComplete === "string" ? verificationUriComplete : "",
988
- interval: Number(data.interval ?? 5) || 5
989
- };
990
- }
991
- async function pollDeviceToken(deviceCode) {
992
- const { status, data } = await postForm("/api/oauth/token", {
993
- client_id: CLIENT_ID3,
994
- device_code: deviceCode,
995
- grant_type: "urn:ietf:params:oauth:grant-type:device_code"
996
- });
997
- if (status === 200 && typeof data.access_token === "string") {
998
- return { kind: "success", creds: credsFromTokenResponse(data) };
999
- }
1000
- if (status >= 500) {
1001
- throw new Error(`Kimi token polling server error (${status}): ${errorDetail(data)}`);
1002
- }
1003
- const errorCode = typeof data.error === "string" ? data.error : "unknown_error";
1004
- switch (errorCode) {
1005
- case "authorization_pending":
1006
- return { kind: "pending" };
1007
- case "slow_down":
1008
- return { kind: "slow_down" };
1009
- case "expired_token":
1010
- return { kind: "expired" };
1011
- case "access_denied":
1012
- return { kind: "denied" };
1013
- default:
1014
- throw new Error(`Kimi token polling failed (${status}): ${errorDetail(data)}`);
1015
- }
1016
- }
1017
- function sleep(ms) {
1018
- return new Promise((resolve) => {
1019
- setTimeout(resolve, ms);
1020
- });
1021
- }
1022
- async function loginKimi(callbacks) {
1023
- const auth = await requestDeviceAuthorization();
1024
- callbacks.onStatus(
1025
- `Visit ${auth.verificationUri || auth.verificationUriComplete} and enter code: ${auth.userCode}`
1026
- );
1027
- callbacks.onOpenUrl(auth.verificationUriComplete || auth.verificationUri);
1028
- callbacks.onStatus("Waiting for you to authorize in the browser...");
1029
- const deadline = Date.now() + DEVICE_TIMEOUT_MS;
1030
- let interval = Math.max(auth.interval, 1);
1031
- while (Date.now() < deadline) {
1032
- await sleep(interval * 1e3);
1033
- const result = await pollDeviceToken(auth.deviceCode);
1034
- if (result.kind === "success") return result.creds;
1035
- if (result.kind === "denied") {
1036
- throw new Error("Kimi authorization was denied.");
1037
- }
1038
- if (result.kind === "expired") {
1039
- throw new Error("Kimi device code expired. Please run login again.");
1040
- }
1041
- if (result.kind === "slow_down") {
1042
- interval += 5;
1043
- }
1044
- }
1045
- throw new Error("Kimi login timed out. Please run login again.");
1046
- }
1047
- async function refreshKimiToken(refreshToken) {
1048
- const { status, data } = await postForm("/api/oauth/token", {
1049
- client_id: CLIENT_ID3,
1050
- grant_type: "refresh_token",
1051
- refresh_token: refreshToken
1052
- });
1053
- if (status === 200 && typeof data.access_token === "string") {
1054
- return credsFromTokenResponse(data, { fallbackRefreshToken: refreshToken });
1055
- }
1056
- const errorCode = typeof data.error === "string" ? data.error : "";
1057
- throw new Error(`Kimi token refresh failed (${status}): ${errorCode || errorDetail(data)}`);
1058
- }
1059
-
1060
1097
  // src/file-lock.ts
1061
1098
  import fs3 from "fs/promises";
1062
1099
  import { setTimeout as setTimeout2 } from "timers/promises";
@@ -1122,7 +1159,34 @@ function isAlive(pid) {
1122
1159
  // src/auth-storage.ts
1123
1160
  var MOONSHOT_OAUTH_KEY = "moonshot-oauth";
1124
1161
  var XIAOMI_CREDITS_KEY = "xiaomi-credits";
1125
- var REFRESH_SKEW_MS = 6e4;
1162
+ var LOCAL_AUTH_KEY_PREFIX = "local:";
1163
+ var LOCAL_CREDENTIAL_LIFETIME_MS = 100 * 365 * 24 * 60 * 60 * 1e3;
1164
+ function activeBaseUrlEntry(data, provider) {
1165
+ if (provider === "moonshot") {
1166
+ const oauth = data[MOONSHOT_OAUTH_KEY];
1167
+ if (oauth) {
1168
+ const exhaustedUntil = oauth.usageExhaustedUntil ?? 0;
1169
+ if (Date.now() < exhaustedUntil && data["moonshot"]) return data["moonshot"];
1170
+ return oauth;
1171
+ }
1172
+ return data["moonshot"];
1173
+ }
1174
+ return data[provider];
1175
+ }
1176
+ function readStoredBaseUrlSync(authFile, provider) {
1177
+ try {
1178
+ const data = JSON.parse(readFileSync2(authFile, "utf-8"));
1179
+ return activeBaseUrlEntry(data, provider)?.baseUrl;
1180
+ } catch {
1181
+ return void 0;
1182
+ }
1183
+ }
1184
+ var MIN_REFRESH_THRESHOLD_MS = 3e5;
1185
+ var REFRESH_THRESHOLD_RATIO = 0.5;
1186
+ function refreshThresholdMs(creds) {
1187
+ const lifetimeMs = (creds.expiresIn ?? 0) * 1e3;
1188
+ return Math.max(MIN_REFRESH_THRESHOLD_MS, lifetimeMs * REFRESH_THRESHOLD_RATIO);
1189
+ }
1126
1190
  var USAGE_EXHAUSTED_DEFAULT_MS = 15 * 60 * 1e3;
1127
1191
  var STATIC_API_KEY_PROVIDERS = /* @__PURE__ */ new Set([
1128
1192
  "glm",
@@ -1132,7 +1196,9 @@ var STATIC_API_KEY_PROVIDERS = /* @__PURE__ */ new Set([
1132
1196
  "deepseek",
1133
1197
  "openrouter",
1134
1198
  "sakana",
1135
- "xai"
1199
+ "xai",
1200
+ // Local endpoints: a fixed (usually placeholder) key, never refreshable.
1201
+ "local"
1136
1202
  ]);
1137
1203
  var AuthStorage = class {
1138
1204
  data = {};
@@ -1181,8 +1247,33 @@ var AuthStorage = class {
1181
1247
  if (provider === "xiaomi") {
1182
1248
  return Boolean(this.data["xiaomi"] || this.data[XIAOMI_CREDITS_KEY]);
1183
1249
  }
1250
+ if (provider === "local") {
1251
+ return Object.keys(this.data).some((key) => key.startsWith(LOCAL_AUTH_KEY_PREFIX));
1252
+ }
1184
1253
  return Boolean(this.data[provider]);
1185
1254
  }
1255
+ /** Endpoint ids that currently have a `local:<id>` credential stored. */
1256
+ async listLocalEndpointIds() {
1257
+ await this.ensureLoaded();
1258
+ return Object.keys(this.data).filter((key) => key.startsWith(LOCAL_AUTH_KEY_PREFIX)).map((key) => key.slice(LOCAL_AUTH_KEY_PREFIX.length));
1259
+ }
1260
+ /**
1261
+ * Write (or refresh) the credential for one local endpoint. The `baseUrl` is
1262
+ * what `effectiveBaseUrl` later picks up, and `accessToken` is the endpoint's
1263
+ * key — a placeholder for the servers that ignore it.
1264
+ */
1265
+ async setLocalEndpoint(endpointId, baseUrl, apiKey) {
1266
+ await this.setCredentials(`${LOCAL_AUTH_KEY_PREFIX}${endpointId}`, {
1267
+ accessToken: apiKey && apiKey.length > 0 ? apiKey : "local",
1268
+ refreshToken: "",
1269
+ expiresAt: Date.now() + LOCAL_CREDENTIAL_LIFETIME_MS,
1270
+ baseUrl
1271
+ });
1272
+ }
1273
+ /** Remove one local endpoint's credential. No-op when it isn't stored. */
1274
+ async removeLocalEndpoint(endpointId) {
1275
+ await this.clearCredentials(`${LOCAL_AUTH_KEY_PREFIX}${endpointId}`);
1276
+ }
1186
1277
  /**
1187
1278
  * True if the active credential for `provider` is a static API key with no
1188
1279
  * refresh mechanism. For `moonshot` this is only true when the Kimi OAuth
@@ -1197,6 +1288,16 @@ var AuthStorage = class {
1197
1288
  }
1198
1289
  return STATIC_API_KEY_PROVIDERS.has(provider);
1199
1290
  }
1291
+ /**
1292
+ * The base URL on the credential that is active right now, if any.
1293
+ * Synchronous — call only after load()/resolveCredentials() populated the
1294
+ * snapshot. For `moonshot` this is the Kimi For Coding URL whenever the
1295
+ * OAuth entry is the one resolveCredentials would serve (i.e. not currently
1296
+ * usage-exhausted with an API key configured).
1297
+ */
1298
+ getStoredBaseUrl(provider) {
1299
+ return activeBaseUrlEntry(this.data, provider)?.baseUrl;
1300
+ }
1200
1301
  async load() {
1201
1302
  await withFileLock(this.filePath, async () => {
1202
1303
  try {
@@ -1228,27 +1329,46 @@ var AuthStorage = class {
1228
1329
  /**
1229
1330
  * Force a re-read from disk, discarding the in-memory cache. Needed when
1230
1331
  * another process mutates the auth file out-of-band — e.g. the desktop app
1231
- * writes API keys natively (Rust → ~/.ezcoder/auth.json) without going through
1232
- * this instance, so a long-lived daemon's cache would otherwise stay stale and
1233
- * never see a newly added provider key.
1332
+ * writes API keys natively without going through this instance.
1234
1333
  */
1235
1334
  async reload() {
1236
1335
  this.loaded = false;
1237
1336
  await this.load();
1238
1337
  }
1338
+ /**
1339
+ * Apply one provider-scoped mutation to the latest on-disk snapshot.
1340
+ * AuthStorage instances live in every app session/process, so writing this
1341
+ * instance's cached snapshot can erase credentials another instance just
1342
+ * added. The file lock only serializes writers; the re-read prevents stale
1343
+ * full-file overwrites.
1344
+ */
1345
+ async mutateLatest(mutator) {
1346
+ await this.ensureLoaded();
1347
+ await withFileLock(this.filePath, async () => {
1348
+ const latest = await readAuthData(this.filePath);
1349
+ mutator(latest);
1350
+ await atomicWriteFile(this.filePath, JSON.stringify(latest, null, 2));
1351
+ this.data = latest;
1352
+ });
1353
+ }
1354
+ async reloadLatest() {
1355
+ await withFileLock(this.filePath, async () => {
1356
+ this.data = await readAuthData(this.filePath);
1357
+ });
1358
+ }
1239
1359
  async getCredentials(provider) {
1240
1360
  await this.ensureLoaded();
1241
1361
  return this.data[provider];
1242
1362
  }
1243
1363
  async setCredentials(provider, creds) {
1244
- await this.ensureLoaded();
1245
- this.data[provider] = creds;
1246
- await this.save();
1364
+ await this.mutateLatest((latest) => {
1365
+ latest[provider] = creds;
1366
+ });
1247
1367
  }
1248
1368
  async clearCredentials(provider) {
1249
- await this.ensureLoaded();
1250
- delete this.data[provider];
1251
- await this.save();
1369
+ await this.mutateLatest((latest) => {
1370
+ delete latest[provider];
1371
+ });
1252
1372
  }
1253
1373
  /**
1254
1374
  * Mark the credential stored under `storageKey` as usage-exhausted until
@@ -1262,12 +1382,15 @@ var AuthStorage = class {
1262
1382
  * nothing is stored under `storageKey`.
1263
1383
  */
1264
1384
  async markUsageExhausted(storageKey, resetsAt) {
1265
- await this.ensureLoaded();
1266
- const creds = this.data[storageKey];
1267
- if (!creds) return;
1268
1385
  const until = resetsAt !== void 0 && resetsAt * 1e3 > Date.now() ? resetsAt * 1e3 : Date.now() + USAGE_EXHAUSTED_DEFAULT_MS;
1269
- creds.usageExhaustedUntil = until;
1270
- await this.save();
1386
+ let marked = false;
1387
+ await this.mutateLatest((latest) => {
1388
+ const creds = latest[storageKey];
1389
+ if (!creds) return;
1390
+ creds.usageExhaustedUntil = until;
1391
+ marked = true;
1392
+ });
1393
+ if (!marked) return;
1271
1394
  log(
1272
1395
  "WARN",
1273
1396
  "auth",
@@ -1275,8 +1398,11 @@ var AuthStorage = class {
1275
1398
  );
1276
1399
  }
1277
1400
  async clearAll() {
1278
- this.data = {};
1279
- await this.save();
1401
+ await this.ensureLoaded();
1402
+ await withFileLock(this.filePath, async () => {
1403
+ this.data = {};
1404
+ await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1405
+ });
1280
1406
  }
1281
1407
  /**
1282
1408
  * Returns valid credentials, auto-refreshing if expired.
@@ -1286,6 +1412,10 @@ var AuthStorage = class {
1286
1412
  */
1287
1413
  async resolveCredentials(provider, opts) {
1288
1414
  await this.ensureLoaded();
1415
+ const directStorageKeys = opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider) ? opts.storageKeys : provider === "moonshot" ? [MOONSHOT_OAUTH_KEY, "moonshot"] : [provider];
1416
+ if (!directStorageKeys.some((key) => Boolean(this.data[key]))) {
1417
+ await this.reloadLatest();
1418
+ }
1289
1419
  if (opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider)) {
1290
1420
  for (const key of opts.storageKeys) {
1291
1421
  const creds2 = this.data[key];
@@ -1326,47 +1456,50 @@ var AuthStorage = class {
1326
1456
  if (STATIC_API_KEY_PROVIDERS.has(provider)) {
1327
1457
  return creds;
1328
1458
  }
1329
- if (!opts?.forceRefresh && Date.now() < creds.expiresAt - REFRESH_SKEW_MS) {
1459
+ if (!opts?.forceRefresh && Date.now() < creds.expiresAt - refreshThresholdMs(creds)) {
1330
1460
  return creds;
1331
1461
  }
1332
1462
  const existing = this.refreshLocks.get(provider);
1333
1463
  if (existing) return existing;
1334
1464
  const refreshPromise = withFileLock(this.filePath, async () => {
1335
- try {
1336
- const content = await fs4.readFile(this.filePath, "utf-8");
1337
- const freshData = JSON.parse(content);
1338
- const freshCreds = freshData[provider];
1339
- if (freshCreds && !opts?.forceRefresh && Date.now() < freshCreds.expiresAt - REFRESH_SKEW_MS) {
1340
- this.data[provider] = freshCreds;
1341
- return freshCreds;
1342
- }
1343
- } catch {
1465
+ const latest = await readAuthData(this.filePath);
1466
+ const latestCreds = latest[provider];
1467
+ if (!latestCreds) {
1468
+ this.data = latest;
1469
+ throw new NotLoggedInError(provider);
1470
+ }
1471
+ const credentialWasReplaced = latestCreds.accessToken !== creds.accessToken || latestCreds.refreshToken !== creds.refreshToken || latestCreds.expiresAt !== creds.expiresAt;
1472
+ if (credentialWasReplaced || !opts?.forceRefresh && Date.now() < latestCreds.expiresAt - refreshThresholdMs(latestCreds)) {
1473
+ this.data = latest;
1474
+ return latestCreds;
1344
1475
  }
1345
1476
  const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : refreshOpenAIToken;
1346
1477
  let refreshed;
1347
1478
  try {
1348
- refreshed = await refreshFn(creds.refreshToken);
1479
+ refreshed = await refreshFn(latestCreds.refreshToken);
1349
1480
  } catch (err) {
1350
1481
  const msg = err instanceof Error ? err.message : String(err);
1351
1482
  const isAuthFailure = /\((401|400)\)/.test(msg) || /invalid_grant|invalid_token|invalid.*refresh/i.test(msg) || /unauthorized/i.test(msg);
1352
1483
  if (isAuthFailure) {
1353
- delete this.data[provider];
1354
- await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1484
+ delete latest[provider];
1485
+ this.data = latest;
1486
+ await atomicWriteFile(this.filePath, JSON.stringify(latest, null, 2));
1355
1487
  throw new NotLoggedInError(provider);
1356
1488
  }
1357
1489
  throw err;
1358
1490
  }
1359
- if (!refreshed.accountId && creds.accountId) {
1360
- refreshed.accountId = creds.accountId;
1491
+ if (!refreshed.accountId && latestCreds.accountId) {
1492
+ refreshed.accountId = latestCreds.accountId;
1361
1493
  }
1362
- if (!refreshed.projectId && creds.projectId) {
1363
- refreshed.projectId = creds.projectId;
1494
+ if (!refreshed.projectId && latestCreds.projectId) {
1495
+ refreshed.projectId = latestCreds.projectId;
1364
1496
  }
1365
- if (!refreshed.baseUrl && creds.baseUrl) {
1366
- refreshed.baseUrl = creds.baseUrl;
1497
+ if (!refreshed.baseUrl && latestCreds.baseUrl) {
1498
+ refreshed.baseUrl = latestCreds.baseUrl;
1367
1499
  }
1368
- this.data[provider] = refreshed;
1369
- await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1500
+ latest[provider] = refreshed;
1501
+ this.data = latest;
1502
+ await atomicWriteFile(this.filePath, JSON.stringify(latest, null, 2));
1370
1503
  return refreshed;
1371
1504
  });
1372
1505
  this.refreshLocks.set(provider, refreshPromise);
@@ -1384,12 +1517,16 @@ var AuthStorage = class {
1384
1517
  const creds = await this.resolveCredentials(provider);
1385
1518
  return creds.accessToken;
1386
1519
  }
1387
- async save() {
1388
- await withFileLock(this.filePath, async () => {
1389
- await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1390
- });
1391
- }
1392
1520
  };
1521
+ async function readAuthData(filePath) {
1522
+ try {
1523
+ const content = await fs4.readFile(filePath, "utf-8");
1524
+ return JSON.parse(content);
1525
+ } catch (error) {
1526
+ if (error.code === "ENOENT") return {};
1527
+ throw error;
1528
+ }
1529
+ }
1393
1530
  async function atomicWriteFile(filePath, content) {
1394
1531
  const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${crypto5.randomUUID().slice(0, 8)}.tmp`;
1395
1532
  try {
@@ -1444,8 +1581,12 @@ var MODELS = [
1444
1581
  // maxThinkingLevel: "max",
1445
1582
  // },
1446
1583
  {
1447
- id: "claude-opus-4-8",
1448
- name: "Claude Opus 4.8",
1584
+ // Released 2026-07-24 — "For complex agentic coding and enterprise work".
1585
+ // Near-Fable capability at half the price ($5/$25 vs $10/$50). Adaptive
1586
+ // thinking with the full effort ladder (low→max, xhigh included); dateless
1587
+ // ID is the canonical pinned snapshot (post-4.6 naming scheme).
1588
+ id: "claude-opus-5",
1589
+ name: "Claude Opus 5",
1449
1590
  provider: "anthropic",
1450
1591
  contextWindow: 1e6,
1451
1592
  maxOutputTokens: 128e3,
@@ -1482,7 +1623,8 @@ var MODELS = [
1482
1623
  // ── OpenAI (Codex) ─────────────────────────────────────
1483
1624
  // GPT-5.6 family — three agentic coding tiers launched July 2026. The public
1484
1625
  // Responses API advertises a 1.05M context window; OpenAI's Codex product
1485
- // catalog advertises 372K on the ChatGPT OAuth route. All three take
1626
+ // catalog advertises 272K on the ChatGPT OAuth route (corrected from the
1627
+ // initially advertised 372K — openai/codex PR #33972, Jul 18 2026 hotfix). All three take
1486
1628
  // text+image input, freeform apply_patch, text+image web search, and parallel
1487
1629
  // tool calls.
1488
1630
  {
@@ -1494,7 +1636,7 @@ var MODELS = [
1494
1636
  name: "GPT-5.6 Sol",
1495
1637
  provider: "openai",
1496
1638
  contextWindow: 105e4,
1497
- codexContextWindow: 372e3,
1639
+ codexContextWindow: 272e3,
1498
1640
  maxOutputTokens: 128e3,
1499
1641
  supportsThinking: true,
1500
1642
  supportsImages: true,
@@ -1509,7 +1651,7 @@ var MODELS = [
1509
1651
  name: "GPT-5.6 Terra",
1510
1652
  provider: "openai",
1511
1653
  contextWindow: 105e4,
1512
- codexContextWindow: 372e3,
1654
+ codexContextWindow: 272e3,
1513
1655
  maxOutputTokens: 128e3,
1514
1656
  supportsThinking: true,
1515
1657
  supportsImages: true,
@@ -1524,7 +1666,7 @@ var MODELS = [
1524
1666
  name: "GPT-5.6 Luna",
1525
1667
  provider: "openai",
1526
1668
  contextWindow: 105e4,
1527
- codexContextWindow: 372e3,
1669
+ codexContextWindow: 272e3,
1528
1670
  maxOutputTokens: 128e3,
1529
1671
  supportsThinking: true,
1530
1672
  supportsImages: true,
@@ -1643,8 +1785,9 @@ var MODELS = [
1643
1785
  },
1644
1786
  // ── Moonshot (Kimi) ────────────────────────────────────
1645
1787
  // K3 is Kimi's 2.8T-parameter flagship for long-horizon coding, knowledge
1646
- // work, and deep reasoning. It always reasons at the `max` effort; the public
1647
- // API uses `reasoning_effort`, while Kimi Code OAuth keeps its managed wire shape.
1788
+ // work, and deep reasoning. Its effort ladder is server-declared as
1789
+ // low/high/max on both the public API (default max) and the Kimi For Coding
1790
+ // OAuth endpoint (default high); thinking can also be fully disabled.
1648
1791
  {
1649
1792
  id: "kimi-k3",
1650
1793
  name: "Kimi K3",
@@ -1829,14 +1972,30 @@ var MODELS = [
1829
1972
  maxThinkingLevel: "high"
1830
1973
  }
1831
1974
  ];
1975
+ var runtimeModels = /* @__PURE__ */ new Map();
1976
+ function registerRuntimeModels(models) {
1977
+ for (const model of models) runtimeModels.set(model.id, model);
1978
+ }
1979
+ function clearRuntimeModels(predicate) {
1980
+ if (!predicate) {
1981
+ runtimeModels.clear();
1982
+ return;
1983
+ }
1984
+ for (const [id, model] of runtimeModels) {
1985
+ if (predicate(model)) runtimeModels.delete(id);
1986
+ }
1987
+ }
1988
+ function getAllModels() {
1989
+ return [...MODELS, ...runtimeModels.values()];
1990
+ }
1832
1991
  function getModel(id) {
1833
- return MODELS.find((m) => m.id === id);
1992
+ return MODELS.find((m) => m.id === id) ?? runtimeModels.get(id);
1834
1993
  }
1835
1994
  function getModelsForProvider(provider) {
1836
- return MODELS.filter((m) => m.provider === provider);
1995
+ return getAllModels().filter((m) => m.provider === provider);
1837
1996
  }
1838
1997
  function getAuthStorageKeys(provider, modelId) {
1839
- const model = MODELS.find((m) => m.id === modelId && m.provider === provider);
1998
+ const model = getAllModels().find((m) => m.id === modelId && m.provider === provider);
1840
1999
  return model?.authStorageKeys ?? [provider];
1841
2000
  }
1842
2001
  function getAuthStorageKey(provider, modelId) {
@@ -1859,8 +2018,23 @@ function getDefaultModel(provider) {
1859
2018
  if (provider === "openrouter") return MODELS.find((m) => m.id === "qwen/qwen3.6-plus");
1860
2019
  if (provider === "sakana") return MODELS.find((m) => m.id === "fugu");
1861
2020
  if (provider === "xai") return MODELS.find((m) => m.id === "grok-4.5");
2021
+ if (provider === "local") {
2022
+ return getModelsForProvider("local")[0] ?? PLACEHOLDER_LOCAL_MODEL;
2023
+ }
1862
2024
  return MODELS.find((m) => m.id === "claude-sonnet-5");
1863
2025
  }
2026
+ var PLACEHOLDER_LOCAL_MODEL = {
2027
+ id: "local/none/none",
2028
+ name: "No local model discovered",
2029
+ provider: "local",
2030
+ contextWindow: 8192,
2031
+ maxOutputTokens: 2048,
2032
+ supportsThinking: false,
2033
+ supportsImages: false,
2034
+ supportsVideo: false,
2035
+ costTier: "low",
2036
+ maxThinkingLevel: "high"
2037
+ };
1864
2038
  function usesOpenAICodexTransport(options) {
1865
2039
  return options?.provider === "openai" && Boolean(options.accountId);
1866
2040
  }
@@ -1878,6 +2052,11 @@ function getContextWindow(modelId, options) {
1878
2052
  function getMaxThinkingLevel(modelId) {
1879
2053
  return getModel(modelId)?.maxThinkingLevel ?? "high";
1880
2054
  }
2055
+ function getDefaultThinkingLevel(modelId, options) {
2056
+ const model = getModel(modelId);
2057
+ if (model?.id === "kimi-k3" && isKimiCodingEndpoint(options?.baseUrl)) return "high";
2058
+ return model?.maxThinkingLevel ?? "high";
2059
+ }
1881
2060
  function getSummaryModel(provider, currentModelId) {
1882
2061
  if (provider === "anthropic") {
1883
2062
  return MODELS.find((m) => m.id === "claude-sonnet-5");
@@ -1894,6 +2073,11 @@ function getFastModel(provider, currentModelId) {
1894
2073
  }
1895
2074
 
1896
2075
  export {
2076
+ kimiCodeBaseUrl,
2077
+ kimiCodingHeaders,
2078
+ isKimiCodingEndpoint,
2079
+ loginKimi,
2080
+ refreshKimiToken,
1897
2081
  generatePKCE,
1898
2082
  openLog,
1899
2083
  getSessionId,
@@ -1909,17 +2093,17 @@ export {
1909
2093
  refreshOpenAIToken,
1910
2094
  loginGemini,
1911
2095
  refreshGeminiToken,
1912
- kimiCodeBaseUrl,
1913
- kimiCodingHeaders,
1914
- isKimiCodingEndpoint,
1915
- loginKimi,
1916
- refreshKimiToken,
1917
2096
  withFileLock,
1918
2097
  MOONSHOT_OAUTH_KEY,
1919
2098
  XIAOMI_CREDITS_KEY,
2099
+ LOCAL_AUTH_KEY_PREFIX,
2100
+ readStoredBaseUrlSync,
1920
2101
  AuthStorage,
1921
2102
  NotLoggedInError,
1922
2103
  MODELS,
2104
+ registerRuntimeModels,
2105
+ clearRuntimeModels,
2106
+ getAllModels,
1923
2107
  getModel,
1924
2108
  getModelsForProvider,
1925
2109
  getAuthStorageKeys,
@@ -1931,7 +2115,8 @@ export {
1931
2115
  getToolResultCharLimit,
1932
2116
  getContextWindow,
1933
2117
  getMaxThinkingLevel,
2118
+ getDefaultThinkingLevel,
1934
2119
  getSummaryModel,
1935
2120
  getFastModel
1936
2121
  };
1937
- //# sourceMappingURL=chunk-GMAX7TES.js.map
2122
+ //# sourceMappingURL=chunk-TPGCBL7I.js.map