@prestyj/core 5.7.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/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { ContextWindowOptions, DEFAULT_MAX_VIDEO_BYTES, MODELS, ModelInfo, getAuthStorageKey, getAuthStorageKeys, getContextWindow, getDefaultModel, getFastModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, getVideoByteLimit, usesOpenAICodexTransport } from './model-registry.cjs';
1
+ export { ContextWindowOptions, DEFAULT_MAX_VIDEO_BYTES, MODELS, ModelInfo, getAuthStorageKey, getAuthStorageKeys, getContextWindow, getDefaultModel, getDefaultThinkingLevel, getFastModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, getToolResultCharLimit, getVideoByteLimit, usesOpenAICodexTransport } from './model-registry.cjs';
2
2
  import { Provider, ThinkingLevel } from '@prestyj/ai';
3
3
  export { AppPaths, getAppPaths } from './paths.cjs';
4
4
 
@@ -58,6 +58,11 @@ interface OAuthCredentials {
58
58
  accountId?: string;
59
59
  projectId?: string;
60
60
  baseUrl?: string;
61
+ /** ms timestamp until which this credential's usage window is exhausted.
62
+ * Set by AuthStorage.markUsageExhausted() when the provider rejects with a
63
+ * usage/quota stop; while in the future, resolution may skip this credential
64
+ * in favor of a configured fallback (e.g. Kimi OAuth → Moonshot API key). */
65
+ usageExhaustedUntil?: number;
61
66
  }
62
67
  interface OAuthLoginCallbacks {
63
68
  onOpenUrl: (url: string) => void;
@@ -80,6 +85,14 @@ declare const MOONSHOT_OAUTH_KEY = "moonshot-oauth";
80
85
  * order, is decided per-model via `getAuthStorageKeys()` in model-registry.ts.
81
86
  */
82
87
  declare const XIAOMI_CREDITS_KEY = "xiaomi-credits";
88
+ /**
89
+ * Synchronous baseUrl read straight from the auth file, for boot paths that
90
+ * need the active endpoint before an AuthStorage instance exists (e.g. the
91
+ * CLI's sync main()). Missing/corrupt files yield undefined — callers treat
92
+ * that as the provider's public endpoint. Read-only: safe without the file
93
+ * lock (a torn mid-write read just falls back to undefined).
94
+ */
95
+ declare function readStoredBaseUrlSync(authFile: string, provider: string): string | undefined;
83
96
  declare class AuthStorage {
84
97
  private data;
85
98
  private filePath;
@@ -113,19 +126,46 @@ declare class AuthStorage {
113
126
  * credential is absent (a present OAuth credential is refreshable).
114
127
  */
115
128
  isStaticApiKey(provider: string): Promise<boolean>;
129
+ /**
130
+ * The base URL on the credential that is active right now, if any.
131
+ * Synchronous — call only after load()/resolveCredentials() populated the
132
+ * snapshot. For `moonshot` this is the Kimi For Coding URL whenever the
133
+ * OAuth entry is the one resolveCredentials would serve (i.e. not currently
134
+ * usage-exhausted with an API key configured).
135
+ */
136
+ getStoredBaseUrl(provider: string): string | undefined;
116
137
  load(): Promise<void>;
117
138
  private ensureLoaded;
118
139
  /**
119
140
  * Force a re-read from disk, discarding the in-memory cache. Needed when
120
141
  * another process mutates the auth file out-of-band — e.g. the desktop app
121
- * writes API keys natively (Rust → ~/.ezcoder/auth.json) without going through
122
- * this instance, so a long-lived daemon's cache would otherwise stay stale and
123
- * never see a newly added provider key.
142
+ * writes API keys natively without going through this instance.
124
143
  */
125
144
  reload(): Promise<void>;
145
+ /**
146
+ * Apply one provider-scoped mutation to the latest on-disk snapshot.
147
+ * AuthStorage instances live in every app session/process, so writing this
148
+ * instance's cached snapshot can erase credentials another instance just
149
+ * added. The file lock only serializes writers; the re-read prevents stale
150
+ * full-file overwrites.
151
+ */
152
+ private mutateLatest;
153
+ private reloadLatest;
126
154
  getCredentials(provider: string): Promise<OAuthCredentials | undefined>;
127
155
  setCredentials(provider: string, creds: OAuthCredentials): Promise<void>;
128
156
  clearCredentials(provider: string): Promise<void>;
157
+ /**
158
+ * Mark the credential stored under `storageKey` as usage-exhausted until
159
+ * `resetsAt` (unix SECONDS, from the provider's rate-limit response) or a
160
+ * 15-minute default when no reset time is known. While the mark is in the
161
+ * future, `resolveCredentials("moonshot")` serves the Moonshot API key
162
+ * instead of the Kimi OAuth credential (when both are configured) — OAuth
163
+ * stays the preferred credential and is retried automatically once the mark
164
+ * lapses. Persisted to auth.json so a restart (or another ezcoder-app window)
165
+ * doesn't burn a request rediscovering the same exhausted window. No-op if
166
+ * nothing is stored under `storageKey`.
167
+ */
168
+ markUsageExhausted(storageKey: string, resetsAt?: number): Promise<void>;
129
169
  clearAll(): Promise<void>;
130
170
  /**
131
171
  * Returns valid credentials, auto-refreshing if expired.
@@ -142,14 +182,13 @@ declare class AuthStorage {
142
182
  * Throws if not logged in.
143
183
  */
144
184
  resolveToken(provider: string): Promise<string>;
145
- private save;
146
185
  }
147
186
  declare class NotLoggedInError extends Error {
148
187
  provider: string;
149
188
  constructor(provider: string);
150
189
  }
151
190
 
152
- type SubscriptionUsageProvider = "anthropic" | "openai";
191
+ type SubscriptionUsageProvider = "anthropic" | "openai" | "moonshot";
153
192
  interface SubscriptionUsageWindow {
154
193
  kind: "current" | "weekly";
155
194
  label: string;
@@ -165,7 +204,8 @@ interface SubscriptionUsageSnapshot {
165
204
  }
166
205
  declare class SubscriptionUsageError extends Error {
167
206
  readonly status?: number | undefined;
168
- constructor(message: string, status?: number | undefined);
207
+ readonly retryAfterMs?: number | undefined;
208
+ constructor(message: string, status?: number | undefined, retryAfterMs?: number | undefined);
169
209
  }
170
210
  type FetchFn = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
171
211
  interface FetchSubscriptionUsageOptions {
@@ -413,4 +453,4 @@ interface AutoUpdater {
413
453
  }
414
454
  declare function createAutoUpdater(config: AutoUpdateConfig): AutoUpdater;
415
455
 
416
- export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, MOONSHOT_OAUTH_KEY, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, SubscriptionUsageError, type SubscriptionUsageProvider, type SubscriptionUsageSnapshot, type SubscriptionUsageWindow, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, XIAOMI_CREDITS_KEY, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, fetchSubscriptionUsage, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isKimiCodingEndpoint, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, kimiCodeBaseUrl, kimiCodingHeaders, log, loginAnthropic, loginGemini, loginKimi, loginOpenAI, openLog, refreshAnthropicToken, refreshGeminiToken, refreshKimiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };
456
+ export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, MOONSHOT_OAUTH_KEY, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, SubscriptionUsageError, type SubscriptionUsageProvider, type SubscriptionUsageSnapshot, type SubscriptionUsageWindow, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, XIAOMI_CREDITS_KEY, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, fetchSubscriptionUsage, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isKimiCodingEndpoint, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, kimiCodeBaseUrl, kimiCodingHeaders, log, loginAnthropic, loginGemini, loginKimi, loginOpenAI, openLog, readStoredBaseUrlSync, refreshAnthropicToken, refreshGeminiToken, refreshKimiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { ContextWindowOptions, DEFAULT_MAX_VIDEO_BYTES, MODELS, ModelInfo, getAuthStorageKey, getAuthStorageKeys, getContextWindow, getDefaultModel, getFastModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, getVideoByteLimit, usesOpenAICodexTransport } from './model-registry.js';
1
+ export { ContextWindowOptions, DEFAULT_MAX_VIDEO_BYTES, MODELS, ModelInfo, getAuthStorageKey, getAuthStorageKeys, getContextWindow, getDefaultModel, getDefaultThinkingLevel, getFastModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, getToolResultCharLimit, getVideoByteLimit, usesOpenAICodexTransport } from './model-registry.js';
2
2
  import { Provider, ThinkingLevel } from '@prestyj/ai';
3
3
  export { AppPaths, getAppPaths } from './paths.js';
4
4
 
@@ -58,6 +58,11 @@ interface OAuthCredentials {
58
58
  accountId?: string;
59
59
  projectId?: string;
60
60
  baseUrl?: string;
61
+ /** ms timestamp until which this credential's usage window is exhausted.
62
+ * Set by AuthStorage.markUsageExhausted() when the provider rejects with a
63
+ * usage/quota stop; while in the future, resolution may skip this credential
64
+ * in favor of a configured fallback (e.g. Kimi OAuth → Moonshot API key). */
65
+ usageExhaustedUntil?: number;
61
66
  }
62
67
  interface OAuthLoginCallbacks {
63
68
  onOpenUrl: (url: string) => void;
@@ -80,6 +85,14 @@ declare const MOONSHOT_OAUTH_KEY = "moonshot-oauth";
80
85
  * order, is decided per-model via `getAuthStorageKeys()` in model-registry.ts.
81
86
  */
82
87
  declare const XIAOMI_CREDITS_KEY = "xiaomi-credits";
88
+ /**
89
+ * Synchronous baseUrl read straight from the auth file, for boot paths that
90
+ * need the active endpoint before an AuthStorage instance exists (e.g. the
91
+ * CLI's sync main()). Missing/corrupt files yield undefined — callers treat
92
+ * that as the provider's public endpoint. Read-only: safe without the file
93
+ * lock (a torn mid-write read just falls back to undefined).
94
+ */
95
+ declare function readStoredBaseUrlSync(authFile: string, provider: string): string | undefined;
83
96
  declare class AuthStorage {
84
97
  private data;
85
98
  private filePath;
@@ -113,19 +126,46 @@ declare class AuthStorage {
113
126
  * credential is absent (a present OAuth credential is refreshable).
114
127
  */
115
128
  isStaticApiKey(provider: string): Promise<boolean>;
129
+ /**
130
+ * The base URL on the credential that is active right now, if any.
131
+ * Synchronous — call only after load()/resolveCredentials() populated the
132
+ * snapshot. For `moonshot` this is the Kimi For Coding URL whenever the
133
+ * OAuth entry is the one resolveCredentials would serve (i.e. not currently
134
+ * usage-exhausted with an API key configured).
135
+ */
136
+ getStoredBaseUrl(provider: string): string | undefined;
116
137
  load(): Promise<void>;
117
138
  private ensureLoaded;
118
139
  /**
119
140
  * Force a re-read from disk, discarding the in-memory cache. Needed when
120
141
  * another process mutates the auth file out-of-band — e.g. the desktop app
121
- * writes API keys natively (Rust → ~/.ezcoder/auth.json) without going through
122
- * this instance, so a long-lived daemon's cache would otherwise stay stale and
123
- * never see a newly added provider key.
142
+ * writes API keys natively without going through this instance.
124
143
  */
125
144
  reload(): Promise<void>;
145
+ /**
146
+ * Apply one provider-scoped mutation to the latest on-disk snapshot.
147
+ * AuthStorage instances live in every app session/process, so writing this
148
+ * instance's cached snapshot can erase credentials another instance just
149
+ * added. The file lock only serializes writers; the re-read prevents stale
150
+ * full-file overwrites.
151
+ */
152
+ private mutateLatest;
153
+ private reloadLatest;
126
154
  getCredentials(provider: string): Promise<OAuthCredentials | undefined>;
127
155
  setCredentials(provider: string, creds: OAuthCredentials): Promise<void>;
128
156
  clearCredentials(provider: string): Promise<void>;
157
+ /**
158
+ * Mark the credential stored under `storageKey` as usage-exhausted until
159
+ * `resetsAt` (unix SECONDS, from the provider's rate-limit response) or a
160
+ * 15-minute default when no reset time is known. While the mark is in the
161
+ * future, `resolveCredentials("moonshot")` serves the Moonshot API key
162
+ * instead of the Kimi OAuth credential (when both are configured) — OAuth
163
+ * stays the preferred credential and is retried automatically once the mark
164
+ * lapses. Persisted to auth.json so a restart (or another ezcoder-app window)
165
+ * doesn't burn a request rediscovering the same exhausted window. No-op if
166
+ * nothing is stored under `storageKey`.
167
+ */
168
+ markUsageExhausted(storageKey: string, resetsAt?: number): Promise<void>;
129
169
  clearAll(): Promise<void>;
130
170
  /**
131
171
  * Returns valid credentials, auto-refreshing if expired.
@@ -142,14 +182,13 @@ declare class AuthStorage {
142
182
  * Throws if not logged in.
143
183
  */
144
184
  resolveToken(provider: string): Promise<string>;
145
- private save;
146
185
  }
147
186
  declare class NotLoggedInError extends Error {
148
187
  provider: string;
149
188
  constructor(provider: string);
150
189
  }
151
190
 
152
- type SubscriptionUsageProvider = "anthropic" | "openai";
191
+ type SubscriptionUsageProvider = "anthropic" | "openai" | "moonshot";
153
192
  interface SubscriptionUsageWindow {
154
193
  kind: "current" | "weekly";
155
194
  label: string;
@@ -165,7 +204,8 @@ interface SubscriptionUsageSnapshot {
165
204
  }
166
205
  declare class SubscriptionUsageError extends Error {
167
206
  readonly status?: number | undefined;
168
- constructor(message: string, status?: number | undefined);
207
+ readonly retryAfterMs?: number | undefined;
208
+ constructor(message: string, status?: number | undefined, retryAfterMs?: number | undefined);
169
209
  }
170
210
  type FetchFn = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
171
211
  interface FetchSubscriptionUsageOptions {
@@ -413,4 +453,4 @@ interface AutoUpdater {
413
453
  }
414
454
  declare function createAutoUpdater(config: AutoUpdateConfig): AutoUpdater;
415
455
 
416
- export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, MOONSHOT_OAUTH_KEY, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, SubscriptionUsageError, type SubscriptionUsageProvider, type SubscriptionUsageSnapshot, type SubscriptionUsageWindow, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, XIAOMI_CREDITS_KEY, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, fetchSubscriptionUsage, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isKimiCodingEndpoint, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, kimiCodeBaseUrl, kimiCodingHeaders, log, loginAnthropic, loginGemini, loginKimi, loginOpenAI, openLog, refreshAnthropicToken, refreshGeminiToken, refreshKimiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };
456
+ export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, MOONSHOT_OAUTH_KEY, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, SubscriptionUsageError, type SubscriptionUsageProvider, type SubscriptionUsageSnapshot, type SubscriptionUsageWindow, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, XIAOMI_CREDITS_KEY, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, fetchSubscriptionUsage, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isKimiCodingEndpoint, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, kimiCodeBaseUrl, kimiCodingHeaders, log, loginAnthropic, loginGemini, loginKimi, loginOpenAI, openLog, readStoredBaseUrlSync, refreshAnthropicToken, refreshGeminiToken, refreshKimiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };
package/dist/index.js CHANGED
@@ -13,12 +13,14 @@ import {
13
13
  getClaudeCodeVersion,
14
14
  getContextWindow,
15
15
  getDefaultModel,
16
+ getDefaultThinkingLevel,
16
17
  getFastModel,
17
18
  getMaxThinkingLevel,
18
19
  getModel,
19
20
  getModelsForProvider,
20
21
  getSessionId,
21
22
  getSummaryModel,
23
+ getToolResultCharLimit,
22
24
  getVideoByteLimit,
23
25
  isKimiCodingEndpoint,
24
26
  isLoggerOpen,
@@ -30,6 +32,7 @@ import {
30
32
  loginKimi,
31
33
  loginOpenAI,
32
34
  openLog,
35
+ readStoredBaseUrlSync,
33
36
  refreshAnthropicToken,
34
37
  refreshGeminiToken,
35
38
  refreshKimiToken,
@@ -37,7 +40,7 @@ import {
37
40
  registerLogCleanup,
38
41
  usesOpenAICodexTransport,
39
42
  withFileLock
40
- } from "./chunk-RQADK6HE.js";
43
+ } from "./chunk-JXPW4JKV.js";
41
44
  import {
42
45
  getAppPaths
43
46
  } from "./chunk-WCIV25KV.js";
@@ -53,6 +56,7 @@ var OPENAI_GPT_56_THINKING_LEVELS = [
53
56
  "ultra"
54
57
  ];
55
58
  var SAKANA_THINKING_LEVELS = ["high", "xhigh"];
59
+ var XAI_THINKING_LEVELS = ["low", "medium", "high"];
56
60
  var ANTHROPIC_OPUS_48_47_THINKING_LEVELS = [
57
61
  "low",
58
62
  "medium",
@@ -66,12 +70,19 @@ var ANTHROPIC_ADAPTIVE_THINKING_LEVELS = [
66
70
  "high",
67
71
  "max"
68
72
  ];
73
+ var MOONSHOT_K3_THINKING_LEVELS = ["low", "high", "max"];
69
74
  function isOpenAIGptModel(provider, model) {
70
75
  return provider === "openai" && model.startsWith("gpt-");
71
76
  }
72
77
  function isSakanaModel(provider) {
73
78
  return provider === "sakana";
74
79
  }
80
+ function isXaiModel(provider) {
81
+ return provider === "xai";
82
+ }
83
+ function isMoonshotK3Model(provider, model) {
84
+ return provider === "moonshot" && model === "kimi-k3";
85
+ }
75
86
  function isAnthropicOpus48Or47Model(provider, model) {
76
87
  return provider === "anthropic" && /opus-4-8|opus-4-7/.test(model);
77
88
  }
@@ -91,6 +102,12 @@ function getSupportedThinkingLevels(provider, model) {
91
102
  if (maxIndex2 === -1) return SAKANA_THINKING_LEVELS;
92
103
  return SAKANA_THINKING_LEVELS.slice(0, maxIndex2 + 1);
93
104
  }
105
+ if (isXaiModel(provider)) {
106
+ const maxIndex2 = XAI_THINKING_LEVELS.indexOf(maxLevel);
107
+ if (maxIndex2 === -1) return XAI_THINKING_LEVELS;
108
+ return XAI_THINKING_LEVELS.slice(0, maxIndex2 + 1);
109
+ }
110
+ if (isMoonshotK3Model(provider, model)) return MOONSHOT_K3_THINKING_LEVELS;
94
111
  if (!isOpenAIGptModel(provider, model)) return [maxLevel];
95
112
  const levels = model.startsWith("gpt-5.6-") ? OPENAI_GPT_56_THINKING_LEVELS : OPENAI_GPT_THINKING_LEVELS;
96
113
  const maxIndex = levels.indexOf(maxLevel);
@@ -102,7 +119,7 @@ function isThinkingLevelSupported(provider, model, level) {
102
119
  }
103
120
  function getNextThinkingLevel(provider, model, current) {
104
121
  const supportedLevels = getSupportedThinkingLevels(provider, model);
105
- const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model) || isSakanaModel(provider);
122
+ const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model) || isSakanaModel(provider) || isXaiModel(provider) || isMoonshotK3Model(provider, model);
106
123
  if (!shouldCycleLevels) {
107
124
  return current ? void 0 : supportedLevels[0];
108
125
  }
@@ -114,9 +131,10 @@ function getNextThinkingLevel(provider, model, current) {
114
131
 
115
132
  // src/provider-usage.ts
116
133
  var SubscriptionUsageError = class extends Error {
117
- constructor(message, status) {
134
+ constructor(message, status, retryAfterMs2) {
118
135
  super(message);
119
136
  this.status = status;
137
+ this.retryAfterMs = retryAfterMs2;
120
138
  this.name = "SubscriptionUsageError";
121
139
  }
122
140
  };
@@ -166,12 +184,20 @@ function normalizedCodexWindow(window, fallbackKind, now) {
166
184
  resetsAt: codexResetAt(window, now)
167
185
  };
168
186
  }
169
- async function readUsageResponse(response) {
187
+ function retryAfterMs(response, now) {
188
+ const value = response.headers.get("retry-after")?.trim();
189
+ if (!value) return void 0;
190
+ if (/^\d+(?:\.\d+)?$/.test(value)) return Math.ceil(Number(value) * 1e3);
191
+ const retryAt = Date.parse(value);
192
+ return Number.isFinite(retryAt) ? Math.max(0, retryAt - now) : void 0;
193
+ }
194
+ async function readUsageResponse(response, now) {
170
195
  const text = await response.text();
171
196
  if (!response.ok) {
172
197
  throw new SubscriptionUsageError(
173
198
  `Subscription usage request failed with HTTP ${response.status}`,
174
- response.status
199
+ response.status,
200
+ retryAfterMs(response, now)
175
201
  );
176
202
  }
177
203
  try {
@@ -192,7 +218,7 @@ async function fetchAnthropicUsage(credentials, fetchFn, signal, now) {
192
218
  "User-Agent": "ezcoder"
193
219
  }
194
220
  });
195
- const data = await readUsageResponse(response);
221
+ const data = await readUsageResponse(response, now());
196
222
  const windows = [];
197
223
  const currentPercent = clampPercent(data.five_hour?.utilization);
198
224
  if (currentPercent !== void 0) {
@@ -228,7 +254,7 @@ async function fetchCodexUsage(credentials, fetchFn, signal, now) {
228
254
  };
229
255
  if (credentials.accountId) headers["ChatGPT-Account-Id"] = credentials.accountId;
230
256
  const response = await fetchFn(CODEX_USAGE_URL, { method: "GET", signal, headers });
231
- const data = await readUsageResponse(response);
257
+ const data = await readUsageResponse(response, now());
232
258
  const windows = [
233
259
  normalizedCodexWindow(data.rate_limit?.primary_window, "current", now()),
234
260
  normalizedCodexWindow(data.rate_limit?.secondary_window, "weekly", now())
@@ -244,12 +270,97 @@ async function fetchCodexUsage(credentials, fetchFn, signal, now) {
244
270
  fetchedAt: now()
245
271
  };
246
272
  }
273
+ function kimiNumber(value) {
274
+ if (typeof value === "string" && value.trim()) {
275
+ const parsed = Number(value);
276
+ return Number.isFinite(parsed) ? parsed : void 0;
277
+ }
278
+ return finiteNumber(value);
279
+ }
280
+ function kimiUsagePercent(detail) {
281
+ const used = kimiNumber(detail?.used);
282
+ const limit = kimiNumber(detail?.limit);
283
+ if (used === void 0 || limit === void 0 || limit <= 0) return void 0;
284
+ return Math.min(100, Math.max(0, used / limit * 100));
285
+ }
286
+ function kimiWindowMinutes(window) {
287
+ const duration = kimiNumber(window?.duration);
288
+ if (duration === void 0 || duration <= 0) return void 0;
289
+ switch (window?.timeUnit) {
290
+ case "TIME_UNIT_SECOND":
291
+ return duration / 60;
292
+ case "TIME_UNIT_MINUTE":
293
+ return duration;
294
+ case "TIME_UNIT_HOUR":
295
+ return duration * 60;
296
+ case "TIME_UNIT_DAY":
297
+ return duration * 24 * 60;
298
+ default:
299
+ return void 0;
300
+ }
301
+ }
302
+ async function fetchKimiUsage(credentials, fetchFn, signal, now) {
303
+ const response = await fetchFn(`${kimiCodeBaseUrl()}/usages`, {
304
+ method: "GET",
305
+ signal,
306
+ headers: {
307
+ Authorization: "Bearer " + credentials.accessToken,
308
+ Accept: "application/json",
309
+ // The managed coding endpoint gates on the kimi-code-cli client identity,
310
+ // same as model requests.
311
+ ...kimiCodingHeaders()
312
+ }
313
+ });
314
+ const data = await readUsageResponse(response, now());
315
+ const windows = [];
316
+ const weeklyPercent = kimiUsagePercent(data.usage);
317
+ if (weeklyPercent !== void 0) {
318
+ windows.push({
319
+ kind: "weekly",
320
+ label: "Weekly",
321
+ usedPercent: weeklyPercent,
322
+ resetsAt: isoTimestamp(data.usage?.resetTime)
323
+ });
324
+ }
325
+ for (const limit of data.limits ?? []) {
326
+ const minutes = kimiWindowMinutes(limit?.window);
327
+ const percent = kimiUsagePercent(limit?.detail);
328
+ if (minutes === void 0 || percent === void 0) continue;
329
+ if (minutes >= 6 * 24 * 60) {
330
+ if (!windows.some((window) => window.kind === "weekly")) {
331
+ windows.push({
332
+ kind: "weekly",
333
+ label: "Weekly",
334
+ usedPercent: percent,
335
+ resetsAt: isoTimestamp(limit?.detail?.resetTime)
336
+ });
337
+ }
338
+ continue;
339
+ }
340
+ windows.push({
341
+ kind: "current",
342
+ label: `${Math.max(1, Math.round(minutes / 60))}-hour`,
343
+ usedPercent: percent,
344
+ resetsAt: isoTimestamp(limit?.detail?.resetTime)
345
+ });
346
+ }
347
+ windows.sort((left, right) => {
348
+ if (left.kind === right.kind) return 0;
349
+ return left.kind === "current" ? -1 : 1;
350
+ });
351
+ return {
352
+ provider: "moonshot",
353
+ displayName: "Kimi",
354
+ windows,
355
+ fetchedAt: now()
356
+ };
357
+ }
247
358
  async function fetchSubscriptionUsage(provider, credentials, options = {}) {
248
359
  const fetchFn = options.fetchFn ?? fetch;
249
360
  const now = options.now ?? Date.now;
250
361
  const signal = AbortSignal.timeout(options.timeoutMs ?? 8e3);
251
362
  try {
252
- return provider === "anthropic" ? await fetchAnthropicUsage(credentials, fetchFn, signal, now) : await fetchCodexUsage(credentials, fetchFn, signal, now);
363
+ return provider === "anthropic" ? await fetchAnthropicUsage(credentials, fetchFn, signal, now) : provider === "openai" ? await fetchCodexUsage(credentials, fetchFn, signal, now) : await fetchKimiUsage(credentials, fetchFn, signal, now);
253
364
  } catch (error) {
254
365
  if (error instanceof SubscriptionUsageError) throw error;
255
366
  const message = error instanceof Error ? error.message : String(error);
@@ -754,6 +865,7 @@ export {
754
865
  getClaudeCodeVersion,
755
866
  getContextWindow,
756
867
  getDefaultModel,
868
+ getDefaultThinkingLevel,
757
869
  getFastModel,
758
870
  getMaxThinkingLevel,
759
871
  getModel,
@@ -762,6 +874,7 @@ export {
762
874
  getSessionId,
763
875
  getSummaryModel,
764
876
  getSupportedThinkingLevels,
877
+ getToolResultCharLimit,
765
878
  getVideoByteLimit,
766
879
  isKimiCodingEndpoint,
767
880
  isLoggerOpen,
@@ -775,6 +888,7 @@ export {
775
888
  loginKimi,
776
889
  loginOpenAI,
777
890
  openLog,
891
+ readStoredBaseUrlSync,
778
892
  refreshAnthropicToken,
779
893
  refreshGeminiToken,
780
894
  refreshKimiToken,