@prestyj/core 5.8.0 → 5.9.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,226 @@ 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
+ baseUrl: kimiCodeBaseUrl()
130
+ };
131
+ }
132
+ async function requestDeviceAuthorization() {
133
+ const { status, data } = await postForm("/api/oauth/device_authorization", {
134
+ client_id: CLIENT_ID
135
+ });
136
+ if (status !== 200) {
137
+ throw new Error(`Kimi device authorization failed (${status}): ${errorDetail(data)}`);
138
+ }
139
+ const userCode = data.user_code;
140
+ const deviceCode = data.device_code;
141
+ const verificationUriComplete = data.verification_uri_complete;
142
+ if (typeof userCode !== "string" || typeof deviceCode !== "string") {
143
+ throw new Error("Kimi device authorization response missing user_code/device_code.");
144
+ }
145
+ return {
146
+ userCode,
147
+ deviceCode,
148
+ verificationUri: typeof data.verification_uri === "string" ? data.verification_uri : "",
149
+ verificationUriComplete: typeof verificationUriComplete === "string" ? verificationUriComplete : "",
150
+ interval: Number(data.interval ?? 5) || 5
151
+ };
152
+ }
153
+ async function pollDeviceToken(deviceCode) {
154
+ const { status, data } = await postForm("/api/oauth/token", {
155
+ client_id: CLIENT_ID,
156
+ device_code: deviceCode,
157
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
158
+ });
159
+ if (status === 200 && typeof data.access_token === "string") {
160
+ return { kind: "success", creds: credsFromTokenResponse(data) };
161
+ }
162
+ if (status >= 500) {
163
+ throw new Error(`Kimi token polling server error (${status}): ${errorDetail(data)}`);
164
+ }
165
+ const errorCode = typeof data.error === "string" ? data.error : "unknown_error";
166
+ switch (errorCode) {
167
+ case "authorization_pending":
168
+ return { kind: "pending" };
169
+ case "slow_down":
170
+ return { kind: "slow_down" };
171
+ case "expired_token":
172
+ return { kind: "expired" };
173
+ case "access_denied":
174
+ return { kind: "denied" };
175
+ default:
176
+ throw new Error(`Kimi token polling failed (${status}): ${errorDetail(data)}`);
177
+ }
178
+ }
179
+ function sleep(ms) {
180
+ return new Promise((resolve) => {
181
+ setTimeout(resolve, ms);
182
+ });
183
+ }
184
+ async function loginKimi(callbacks) {
185
+ const auth = await requestDeviceAuthorization();
186
+ callbacks.onStatus(
187
+ `Visit ${auth.verificationUri || auth.verificationUriComplete} and enter code: ${auth.userCode}`
188
+ );
189
+ callbacks.onOpenUrl(auth.verificationUriComplete || auth.verificationUri);
190
+ callbacks.onStatus("Waiting for you to authorize in the browser...");
191
+ const deadline = Date.now() + DEVICE_TIMEOUT_MS;
192
+ let interval = Math.max(auth.interval, 1);
193
+ while (Date.now() < deadline) {
194
+ await sleep(interval * 1e3);
195
+ const result = await pollDeviceToken(auth.deviceCode);
196
+ if (result.kind === "success") return result.creds;
197
+ if (result.kind === "denied") {
198
+ throw new Error("Kimi authorization was denied.");
199
+ }
200
+ if (result.kind === "expired") {
201
+ throw new Error("Kimi device code expired. Please run login again.");
202
+ }
203
+ if (result.kind === "slow_down") {
204
+ interval += 5;
205
+ }
206
+ }
207
+ throw new Error("Kimi login timed out. Please run login again.");
208
+ }
209
+ async function refreshKimiToken(refreshToken) {
210
+ const { status, data } = await postForm("/api/oauth/token", {
211
+ client_id: CLIENT_ID,
212
+ grant_type: "refresh_token",
213
+ refresh_token: refreshToken
214
+ });
215
+ if (status === 200 && typeof data.access_token === "string") {
216
+ return credsFromTokenResponse(data, { fallbackRefreshToken: refreshToken });
217
+ }
218
+ const errorCode = typeof data.error === "string" ? data.error : "";
219
+ throw new Error(`Kimi token refresh failed (${status}): ${errorCode || errorDetail(data)}`);
220
+ }
221
+
5
222
  // src/auth-storage.ts
6
223
  import fs4 from "fs/promises";
224
+ import { readFileSync as readFileSync2 } from "fs";
7
225
  import crypto5 from "crypto";
8
226
 
9
227
  // src/oauth/anthropic.ts
@@ -29,15 +247,17 @@ async function generatePKCE() {
29
247
 
30
248
  // src/claude-code-version.ts
31
249
  import fs2 from "fs/promises";
32
- import path2 from "path";
250
+ import path3 from "path";
33
251
 
34
252
  // src/logger.ts
35
253
  import fs from "fs";
36
- import path from "path";
254
+ import path2 from "path";
37
255
  import { randomBytes } from "crypto";
38
256
  import { environmentSecrets, redactText, redactValue } from "@prestyj/ai";
39
257
  var MAX_BYTES = 10 * 1024 * 1024;
40
258
  var fd = null;
259
+ var bytesWritten = 0;
260
+ var capped = false;
41
261
  var sessionId = "";
42
262
  var appName = "app";
43
263
  var cleanups = [];
@@ -56,22 +276,37 @@ function rotateIfNeeded(filePath) {
56
276
  }
57
277
  }
58
278
  function openLog(filePath, name) {
59
- if (fd !== null) return false;
279
+ if (fd !== null || capped) return false;
60
280
  appName = name;
61
281
  exactSecrets = environmentSecrets(process.env);
62
282
  try {
63
- fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 448 });
283
+ fs.mkdirSync(path2.dirname(filePath), { recursive: true, mode: 448 });
64
284
  } catch {
65
285
  }
66
286
  rotateIfNeeded(filePath);
67
287
  try {
68
288
  fd = fs.openSync(filePath, "a");
69
289
  } catch {
290
+ fd = null;
291
+ bytesWritten = 0;
292
+ return false;
293
+ }
294
+ try {
295
+ bytesWritten = fs.fstatSync(fd).size;
296
+ } catch {
297
+ try {
298
+ fs.closeSync(fd);
299
+ } catch {
300
+ }
301
+ fd = null;
302
+ bytesWritten = 0;
70
303
  return false;
71
304
  }
72
305
  sessionId = randomBytes(4).toString("hex");
73
306
  try {
74
- fs.writeSync(fd, "\n");
307
+ if (bytesWritten < MAX_BYTES) {
308
+ bytesWritten += fs.writeSync(fd, "\n");
309
+ }
75
310
  } catch {
76
311
  }
77
312
  return true;
@@ -97,8 +332,23 @@ function log(level, category, message, data) {
97
332
  if (pairs) line += ` ${pairs}`;
98
333
  }
99
334
  line += "\n";
335
+ const lineBytes = Buffer.byteLength(line);
336
+ if (bytesWritten + lineBytes > MAX_BYTES) {
337
+ const capLine = `[${ts}] [sid=${sessionId}] [WARN] [logger] Log cap reached; file logging disabled until restart
338
+ `;
339
+ try {
340
+ if (bytesWritten + Buffer.byteLength(capLine) <= MAX_BYTES) {
341
+ bytesWritten += fs.writeSync(fd, capLine);
342
+ }
343
+ fs.closeSync(fd);
344
+ } catch {
345
+ }
346
+ fd = null;
347
+ capped = true;
348
+ return;
349
+ }
100
350
  try {
101
- fs.writeSync(fd, line);
351
+ bytesWritten += fs.writeSync(fd, line);
102
352
  } catch {
103
353
  }
104
354
  }
@@ -106,13 +356,16 @@ function registerLogCleanup(fn) {
106
356
  cleanups.push(fn);
107
357
  }
108
358
  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 {
359
+ if (fd !== null) {
360
+ if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
361
+ try {
362
+ if (fd !== null) fs.closeSync(fd);
363
+ } catch {
364
+ }
114
365
  }
115
366
  fd = null;
367
+ bytesWritten = 0;
368
+ capped = false;
116
369
  exactSecrets = [];
117
370
  for (const unsub of cleanups) unsub();
118
371
  cleanups = [];
@@ -126,7 +379,7 @@ var FALLBACK_VERSION = "2.1.88";
126
379
  var memoryCache = null;
127
380
  var inflight = null;
128
381
  function cachePath() {
129
- return path2.join(getAppPaths().agentDir, "claude-code-version.json");
382
+ return path3.join(getAppPaths().agentDir, "claude-code-version.json");
130
383
  }
131
384
  async function readDiskCache() {
132
385
  try {
@@ -208,7 +461,7 @@ async function getClaudeCliUserAgent() {
208
461
  }
209
462
 
210
463
  // src/oauth/anthropic.ts
211
- var CLIENT_ID = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
464
+ var CLIENT_ID2 = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
212
465
  var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
213
466
  var TOKEN_URLS = [
214
467
  "https://platform.claude.com/v1/oauth/token",
@@ -255,7 +508,7 @@ async function loginAnthropic(callbacks) {
255
508
  const state = crypto2.randomBytes(16).toString("hex");
256
509
  const params = new URLSearchParams({
257
510
  code: "true",
258
- client_id: CLIENT_ID,
511
+ client_id: CLIENT_ID2,
259
512
  response_type: "code",
260
513
  redirect_uri: REDIRECT_URI,
261
514
  scope: SCOPES,
@@ -276,7 +529,7 @@ async function exchangeAnthropicCode(code, state, verifier) {
276
529
  const data = await postTokenRequest(
277
530
  {
278
531
  grant_type: "authorization_code",
279
- client_id: CLIENT_ID,
532
+ client_id: CLIENT_ID2,
280
533
  code,
281
534
  state,
282
535
  redirect_uri: REDIRECT_URI,
@@ -290,7 +543,7 @@ async function refreshAnthropicToken(refreshToken) {
290
543
  const data = await postTokenRequest(
291
544
  {
292
545
  grant_type: "refresh_token",
293
- client_id: CLIENT_ID,
546
+ client_id: CLIENT_ID2,
294
547
  refresh_token: refreshToken
295
548
  },
296
549
  "token refresh"
@@ -301,7 +554,7 @@ async function refreshAnthropicToken(refreshToken) {
301
554
  // src/oauth/openai.ts
302
555
  import http from "http";
303
556
  import crypto3 from "crypto";
304
- var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
557
+ var CLIENT_ID3 = "app_EMoamEEZ73f0CkXaXp7hrann";
305
558
  var AUTHORIZE_URL2 = "https://auth.openai.com/oauth/authorize";
306
559
  var TOKEN_URL = "https://auth.openai.com/oauth/token";
307
560
  var REDIRECT_URI2 = "http://localhost:1455/auth/callback";
@@ -312,7 +565,7 @@ async function loginOpenAI(callbacks) {
312
565
  const state = crypto3.randomBytes(16).toString("hex");
313
566
  const url = new URL(AUTHORIZE_URL2);
314
567
  url.searchParams.set("response_type", "code");
315
- url.searchParams.set("client_id", CLIENT_ID2);
568
+ url.searchParams.set("client_id", CLIENT_ID3);
316
569
  url.searchParams.set("redirect_uri", REDIRECT_URI2);
317
570
  url.searchParams.set("scope", SCOPE);
318
571
  url.searchParams.set("code_challenge", challenge);
@@ -433,7 +686,7 @@ async function exchangeOpenAICode(code, verifier) {
433
686
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
434
687
  body: new URLSearchParams({
435
688
  grant_type: "authorization_code",
436
- client_id: CLIENT_ID2,
689
+ client_id: CLIENT_ID3,
437
690
  code,
438
691
  redirect_uri: REDIRECT_URI2,
439
692
  code_verifier: verifier
@@ -457,7 +710,7 @@ async function refreshOpenAIToken(refreshToken) {
457
710
  body: new URLSearchParams({
458
711
  grant_type: "refresh_token",
459
712
  refresh_token: refreshToken,
460
- client_id: CLIENT_ID2
713
+ client_id: CLIENT_ID3
461
714
  })
462
715
  });
463
716
  if (!response.ok) {
@@ -840,223 +1093,6 @@ function codeAssistHeaders(accessToken) {
840
1093
  };
841
1094
  }
842
1095
 
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
1096
  // src/file-lock.ts
1061
1097
  import fs3 from "fs/promises";
1062
1098
  import { setTimeout as setTimeout2 } from "timers/promises";
@@ -1122,6 +1158,26 @@ function isAlive(pid) {
1122
1158
  // src/auth-storage.ts
1123
1159
  var MOONSHOT_OAUTH_KEY = "moonshot-oauth";
1124
1160
  var XIAOMI_CREDITS_KEY = "xiaomi-credits";
1161
+ function activeBaseUrlEntry(data, provider) {
1162
+ if (provider === "moonshot") {
1163
+ const oauth = data[MOONSHOT_OAUTH_KEY];
1164
+ if (oauth) {
1165
+ const exhaustedUntil = oauth.usageExhaustedUntil ?? 0;
1166
+ if (Date.now() < exhaustedUntil && data["moonshot"]) return data["moonshot"];
1167
+ return oauth;
1168
+ }
1169
+ return data["moonshot"];
1170
+ }
1171
+ return data[provider];
1172
+ }
1173
+ function readStoredBaseUrlSync(authFile, provider) {
1174
+ try {
1175
+ const data = JSON.parse(readFileSync2(authFile, "utf-8"));
1176
+ return activeBaseUrlEntry(data, provider)?.baseUrl;
1177
+ } catch {
1178
+ return void 0;
1179
+ }
1180
+ }
1125
1181
  var REFRESH_SKEW_MS = 6e4;
1126
1182
  var USAGE_EXHAUSTED_DEFAULT_MS = 15 * 60 * 1e3;
1127
1183
  var STATIC_API_KEY_PROVIDERS = /* @__PURE__ */ new Set([
@@ -1197,6 +1253,16 @@ var AuthStorage = class {
1197
1253
  }
1198
1254
  return STATIC_API_KEY_PROVIDERS.has(provider);
1199
1255
  }
1256
+ /**
1257
+ * The base URL on the credential that is active right now, if any.
1258
+ * Synchronous — call only after load()/resolveCredentials() populated the
1259
+ * snapshot. For `moonshot` this is the Kimi For Coding URL whenever the
1260
+ * OAuth entry is the one resolveCredentials would serve (i.e. not currently
1261
+ * usage-exhausted with an API key configured).
1262
+ */
1263
+ getStoredBaseUrl(provider) {
1264
+ return activeBaseUrlEntry(this.data, provider)?.baseUrl;
1265
+ }
1200
1266
  async load() {
1201
1267
  await withFileLock(this.filePath, async () => {
1202
1268
  try {
@@ -1228,27 +1294,46 @@ var AuthStorage = class {
1228
1294
  /**
1229
1295
  * Force a re-read from disk, discarding the in-memory cache. Needed when
1230
1296
  * 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.
1297
+ * writes API keys natively without going through this instance.
1234
1298
  */
1235
1299
  async reload() {
1236
1300
  this.loaded = false;
1237
1301
  await this.load();
1238
1302
  }
1303
+ /**
1304
+ * Apply one provider-scoped mutation to the latest on-disk snapshot.
1305
+ * AuthStorage instances live in every app session/process, so writing this
1306
+ * instance's cached snapshot can erase credentials another instance just
1307
+ * added. The file lock only serializes writers; the re-read prevents stale
1308
+ * full-file overwrites.
1309
+ */
1310
+ async mutateLatest(mutator) {
1311
+ await this.ensureLoaded();
1312
+ await withFileLock(this.filePath, async () => {
1313
+ const latest = await readAuthData(this.filePath);
1314
+ mutator(latest);
1315
+ await atomicWriteFile(this.filePath, JSON.stringify(latest, null, 2));
1316
+ this.data = latest;
1317
+ });
1318
+ }
1319
+ async reloadLatest() {
1320
+ await withFileLock(this.filePath, async () => {
1321
+ this.data = await readAuthData(this.filePath);
1322
+ });
1323
+ }
1239
1324
  async getCredentials(provider) {
1240
1325
  await this.ensureLoaded();
1241
1326
  return this.data[provider];
1242
1327
  }
1243
1328
  async setCredentials(provider, creds) {
1244
- await this.ensureLoaded();
1245
- this.data[provider] = creds;
1246
- await this.save();
1329
+ await this.mutateLatest((latest) => {
1330
+ latest[provider] = creds;
1331
+ });
1247
1332
  }
1248
1333
  async clearCredentials(provider) {
1249
- await this.ensureLoaded();
1250
- delete this.data[provider];
1251
- await this.save();
1334
+ await this.mutateLatest((latest) => {
1335
+ delete latest[provider];
1336
+ });
1252
1337
  }
1253
1338
  /**
1254
1339
  * Mark the credential stored under `storageKey` as usage-exhausted until
@@ -1262,12 +1347,15 @@ var AuthStorage = class {
1262
1347
  * nothing is stored under `storageKey`.
1263
1348
  */
1264
1349
  async markUsageExhausted(storageKey, resetsAt) {
1265
- await this.ensureLoaded();
1266
- const creds = this.data[storageKey];
1267
- if (!creds) return;
1268
1350
  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();
1351
+ let marked = false;
1352
+ await this.mutateLatest((latest) => {
1353
+ const creds = latest[storageKey];
1354
+ if (!creds) return;
1355
+ creds.usageExhaustedUntil = until;
1356
+ marked = true;
1357
+ });
1358
+ if (!marked) return;
1271
1359
  log(
1272
1360
  "WARN",
1273
1361
  "auth",
@@ -1275,8 +1363,11 @@ var AuthStorage = class {
1275
1363
  );
1276
1364
  }
1277
1365
  async clearAll() {
1278
- this.data = {};
1279
- await this.save();
1366
+ await this.ensureLoaded();
1367
+ await withFileLock(this.filePath, async () => {
1368
+ this.data = {};
1369
+ await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1370
+ });
1280
1371
  }
1281
1372
  /**
1282
1373
  * Returns valid credentials, auto-refreshing if expired.
@@ -1286,6 +1377,10 @@ var AuthStorage = class {
1286
1377
  */
1287
1378
  async resolveCredentials(provider, opts) {
1288
1379
  await this.ensureLoaded();
1380
+ const directStorageKeys = opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider) ? opts.storageKeys : provider === "moonshot" ? [MOONSHOT_OAUTH_KEY, "moonshot"] : [provider];
1381
+ if (!directStorageKeys.some((key) => Boolean(this.data[key]))) {
1382
+ await this.reloadLatest();
1383
+ }
1289
1384
  if (opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider)) {
1290
1385
  for (const key of opts.storageKeys) {
1291
1386
  const creds2 = this.data[key];
@@ -1332,41 +1427,44 @@ var AuthStorage = class {
1332
1427
  const existing = this.refreshLocks.get(provider);
1333
1428
  if (existing) return existing;
1334
1429
  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 {
1430
+ const latest = await readAuthData(this.filePath);
1431
+ const latestCreds = latest[provider];
1432
+ if (!latestCreds) {
1433
+ this.data = latest;
1434
+ throw new NotLoggedInError(provider);
1435
+ }
1436
+ const credentialWasReplaced = latestCreds.accessToken !== creds.accessToken || latestCreds.refreshToken !== creds.refreshToken || latestCreds.expiresAt !== creds.expiresAt;
1437
+ if (credentialWasReplaced || !opts?.forceRefresh && Date.now() < latestCreds.expiresAt - REFRESH_SKEW_MS) {
1438
+ this.data = latest;
1439
+ return latestCreds;
1344
1440
  }
1345
1441
  const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : refreshOpenAIToken;
1346
1442
  let refreshed;
1347
1443
  try {
1348
- refreshed = await refreshFn(creds.refreshToken);
1444
+ refreshed = await refreshFn(latestCreds.refreshToken);
1349
1445
  } catch (err) {
1350
1446
  const msg = err instanceof Error ? err.message : String(err);
1351
1447
  const isAuthFailure = /\((401|400)\)/.test(msg) || /invalid_grant|invalid_token|invalid.*refresh/i.test(msg) || /unauthorized/i.test(msg);
1352
1448
  if (isAuthFailure) {
1353
- delete this.data[provider];
1354
- await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1449
+ delete latest[provider];
1450
+ this.data = latest;
1451
+ await atomicWriteFile(this.filePath, JSON.stringify(latest, null, 2));
1355
1452
  throw new NotLoggedInError(provider);
1356
1453
  }
1357
1454
  throw err;
1358
1455
  }
1359
- if (!refreshed.accountId && creds.accountId) {
1360
- refreshed.accountId = creds.accountId;
1456
+ if (!refreshed.accountId && latestCreds.accountId) {
1457
+ refreshed.accountId = latestCreds.accountId;
1361
1458
  }
1362
- if (!refreshed.projectId && creds.projectId) {
1363
- refreshed.projectId = creds.projectId;
1459
+ if (!refreshed.projectId && latestCreds.projectId) {
1460
+ refreshed.projectId = latestCreds.projectId;
1364
1461
  }
1365
- if (!refreshed.baseUrl && creds.baseUrl) {
1366
- refreshed.baseUrl = creds.baseUrl;
1462
+ if (!refreshed.baseUrl && latestCreds.baseUrl) {
1463
+ refreshed.baseUrl = latestCreds.baseUrl;
1367
1464
  }
1368
- this.data[provider] = refreshed;
1369
- await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1465
+ latest[provider] = refreshed;
1466
+ this.data = latest;
1467
+ await atomicWriteFile(this.filePath, JSON.stringify(latest, null, 2));
1370
1468
  return refreshed;
1371
1469
  });
1372
1470
  this.refreshLocks.set(provider, refreshPromise);
@@ -1384,12 +1482,16 @@ var AuthStorage = class {
1384
1482
  const creds = await this.resolveCredentials(provider);
1385
1483
  return creds.accessToken;
1386
1484
  }
1387
- async save() {
1388
- await withFileLock(this.filePath, async () => {
1389
- await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1390
- });
1391
- }
1392
1485
  };
1486
+ async function readAuthData(filePath) {
1487
+ try {
1488
+ const content = await fs4.readFile(filePath, "utf-8");
1489
+ return JSON.parse(content);
1490
+ } catch (error) {
1491
+ if (error.code === "ENOENT") return {};
1492
+ throw error;
1493
+ }
1494
+ }
1393
1495
  async function atomicWriteFile(filePath, content) {
1394
1496
  const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${crypto5.randomUUID().slice(0, 8)}.tmp`;
1395
1497
  try {
@@ -1482,7 +1584,8 @@ var MODELS = [
1482
1584
  // ── OpenAI (Codex) ─────────────────────────────────────
1483
1585
  // GPT-5.6 family — three agentic coding tiers launched July 2026. The public
1484
1586
  // Responses API advertises a 1.05M context window; OpenAI's Codex product
1485
- // catalog advertises 372K on the ChatGPT OAuth route. All three take
1587
+ // catalog advertises 272K on the ChatGPT OAuth route (corrected from the
1588
+ // initially advertised 372K — openai/codex PR #33972, Jul 18 2026 hotfix). All three take
1486
1589
  // text+image input, freeform apply_patch, text+image web search, and parallel
1487
1590
  // tool calls.
1488
1591
  {
@@ -1494,7 +1597,7 @@ var MODELS = [
1494
1597
  name: "GPT-5.6 Sol",
1495
1598
  provider: "openai",
1496
1599
  contextWindow: 105e4,
1497
- codexContextWindow: 372e3,
1600
+ codexContextWindow: 272e3,
1498
1601
  maxOutputTokens: 128e3,
1499
1602
  supportsThinking: true,
1500
1603
  supportsImages: true,
@@ -1509,7 +1612,7 @@ var MODELS = [
1509
1612
  name: "GPT-5.6 Terra",
1510
1613
  provider: "openai",
1511
1614
  contextWindow: 105e4,
1512
- codexContextWindow: 372e3,
1615
+ codexContextWindow: 272e3,
1513
1616
  maxOutputTokens: 128e3,
1514
1617
  supportsThinking: true,
1515
1618
  supportsImages: true,
@@ -1524,7 +1627,7 @@ var MODELS = [
1524
1627
  name: "GPT-5.6 Luna",
1525
1628
  provider: "openai",
1526
1629
  contextWindow: 105e4,
1527
- codexContextWindow: 372e3,
1630
+ codexContextWindow: 272e3,
1528
1631
  maxOutputTokens: 128e3,
1529
1632
  supportsThinking: true,
1530
1633
  supportsImages: true,
@@ -1643,8 +1746,9 @@ var MODELS = [
1643
1746
  },
1644
1747
  // ── Moonshot (Kimi) ────────────────────────────────────
1645
1748
  // 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.
1749
+ // work, and deep reasoning. Its effort ladder is server-declared as
1750
+ // low/high/max on both the public API (default max) and the Kimi For Coding
1751
+ // OAuth endpoint (default high); thinking can also be fully disabled.
1648
1752
  {
1649
1753
  id: "kimi-k3",
1650
1754
  name: "Kimi K3",
@@ -1878,6 +1982,11 @@ function getContextWindow(modelId, options) {
1878
1982
  function getMaxThinkingLevel(modelId) {
1879
1983
  return getModel(modelId)?.maxThinkingLevel ?? "high";
1880
1984
  }
1985
+ function getDefaultThinkingLevel(modelId, options) {
1986
+ const model = getModel(modelId);
1987
+ if (model?.id === "kimi-k3" && isKimiCodingEndpoint(options?.baseUrl)) return "high";
1988
+ return model?.maxThinkingLevel ?? "high";
1989
+ }
1881
1990
  function getSummaryModel(provider, currentModelId) {
1882
1991
  if (provider === "anthropic") {
1883
1992
  return MODELS.find((m) => m.id === "claude-sonnet-5");
@@ -1894,6 +2003,11 @@ function getFastModel(provider, currentModelId) {
1894
2003
  }
1895
2004
 
1896
2005
  export {
2006
+ kimiCodeBaseUrl,
2007
+ kimiCodingHeaders,
2008
+ isKimiCodingEndpoint,
2009
+ loginKimi,
2010
+ refreshKimiToken,
1897
2011
  generatePKCE,
1898
2012
  openLog,
1899
2013
  getSessionId,
@@ -1909,14 +2023,10 @@ export {
1909
2023
  refreshOpenAIToken,
1910
2024
  loginGemini,
1911
2025
  refreshGeminiToken,
1912
- kimiCodeBaseUrl,
1913
- kimiCodingHeaders,
1914
- isKimiCodingEndpoint,
1915
- loginKimi,
1916
- refreshKimiToken,
1917
2026
  withFileLock,
1918
2027
  MOONSHOT_OAUTH_KEY,
1919
2028
  XIAOMI_CREDITS_KEY,
2029
+ readStoredBaseUrlSync,
1920
2030
  AuthStorage,
1921
2031
  NotLoggedInError,
1922
2032
  MODELS,
@@ -1931,7 +2041,8 @@ export {
1931
2041
  getToolResultCharLimit,
1932
2042
  getContextWindow,
1933
2043
  getMaxThinkingLevel,
2044
+ getDefaultThinkingLevel,
1934
2045
  getSummaryModel,
1935
2046
  getFastModel
1936
2047
  };
1937
- //# sourceMappingURL=chunk-GMAX7TES.js.map
2048
+ //# sourceMappingURL=chunk-JXPW4JKV.js.map