@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.
- package/dist/{chunk-GMAX7TES.js → chunk-JXPW4JKV.js} +401 -290
- package/dist/chunk-JXPW4JKV.js.map +1 -0
- package/dist/index.cjs +596 -382
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -8
- package/dist/index.d.ts +31 -8
- package/dist/index.js +111 -8
- package/dist/index.js.map +1 -1
- package/dist/model-registry.cjs +45 -24
- package/dist/model-registry.cjs.map +1 -1
- package/dist/model-registry.d.cts +14 -1
- package/dist/model-registry.d.ts +14 -1
- package/dist/model-registry.js +3 -1
- package/package.json +2 -2
- package/dist/chunk-GMAX7TES.js.map +0 -1
|
@@ -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
|
|
250
|
+
import path3 from "path";
|
|
33
251
|
|
|
34
252
|
// src/logger.ts
|
|
35
253
|
import fs from "fs";
|
|
36
|
-
import
|
|
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(
|
|
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
|
-
|
|
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
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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",
|
|
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:
|
|
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:
|
|
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
|
|
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.
|
|
1245
|
-
|
|
1246
|
-
|
|
1329
|
+
await this.mutateLatest((latest) => {
|
|
1330
|
+
latest[provider] = creds;
|
|
1331
|
+
});
|
|
1247
1332
|
}
|
|
1248
1333
|
async clearCredentials(provider) {
|
|
1249
|
-
await this.
|
|
1250
|
-
|
|
1251
|
-
|
|
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
|
-
|
|
1270
|
-
await this.
|
|
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.
|
|
1279
|
-
await this.
|
|
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
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
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(
|
|
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
|
|
1354
|
-
|
|
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 &&
|
|
1360
|
-
refreshed.accountId =
|
|
1456
|
+
if (!refreshed.accountId && latestCreds.accountId) {
|
|
1457
|
+
refreshed.accountId = latestCreds.accountId;
|
|
1361
1458
|
}
|
|
1362
|
-
if (!refreshed.projectId &&
|
|
1363
|
-
refreshed.projectId =
|
|
1459
|
+
if (!refreshed.projectId && latestCreds.projectId) {
|
|
1460
|
+
refreshed.projectId = latestCreds.projectId;
|
|
1364
1461
|
}
|
|
1365
|
-
if (!refreshed.baseUrl &&
|
|
1366
|
-
refreshed.baseUrl =
|
|
1462
|
+
if (!refreshed.baseUrl && latestCreds.baseUrl) {
|
|
1463
|
+
refreshed.baseUrl = latestCreds.baseUrl;
|
|
1367
1464
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
1647
|
-
//
|
|
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-
|
|
2048
|
+
//# sourceMappingURL=chunk-JXPW4JKV.js.map
|