@prestyj/core 4.10.1

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.
@@ -0,0 +1,359 @@
1
+ export { ContextWindowOptions, DEFAULT_MAX_VIDEO_BYTES, MODELS, ModelInfo, getContextWindow, getDefaultModel, getMaxThinkingLevel, getModel, getModelsForProvider, getSummaryModel, getVideoByteLimit, usesOpenAICodexTransport } from './model-registry.cjs';
2
+ import { Provider, ThinkingLevel } from '@prestyj/ai';
3
+ export { AppPaths, getAppPaths } from './paths.cjs';
4
+
5
+ declare function getSupportedThinkingLevels(provider: Provider, model: string): readonly ThinkingLevel[];
6
+ declare function isThinkingLevelSupported(provider: Provider, model: string, level: ThinkingLevel): boolean;
7
+ declare function getNextThinkingLevel(provider: Provider, model: string, current: ThinkingLevel | undefined): ThinkingLevel | undefined;
8
+
9
+ type LogLevel = "INFO" | "ERROR" | "WARN" | "DEBUG";
10
+ /**
11
+ * Open the debug log in append mode, tagging this process with a session id and
12
+ * remembering `name` for the shutdown line. Idempotent — re-calling while open
13
+ * is a no-op. Returns true only when it *newly* opened the file (so callers can
14
+ * write a one-time startup line); returns false if already open or if the file
15
+ * could not be opened.
16
+ */
17
+ declare function openLog(filePath: string, name: string): boolean;
18
+ /** Session identifier included on every log line as `sid=<id>`. */
19
+ declare function getSessionId(): string;
20
+ /** True if the logger has an open file descriptor. */
21
+ declare function isLoggerOpen(): boolean;
22
+ /** Write a timestamped log line. No-op if the logger is not open. */
23
+ declare function log(level: LogLevel, category: string, message: string, data?: Record<string, unknown>): void;
24
+ /**
25
+ * Register a cleanup callback (e.g. an EventBus unsubscriber) to run when the
26
+ * logger closes. Lets app-side bridges hook into the shared lifecycle without
27
+ * the core needing to know about app types.
28
+ */
29
+ declare function registerLogCleanup(fn: () => void): void;
30
+ /**
31
+ * Write a shutdown line (unless suppressed), close the file descriptor, and run
32
+ * any registered cleanups.
33
+ */
34
+ declare function closeLogger(opts?: {
35
+ shutdownLine?: boolean;
36
+ }): void;
37
+
38
+ /**
39
+ * Simple file-based lock with PID tracking and stale detection.
40
+ * Uses atomic file creation (wx flag) to prevent races.
41
+ */
42
+ declare function withFileLock<T>(filePath: string, fn: () => Promise<T>): Promise<T>;
43
+
44
+ /**
45
+ * Resolve the current Claude Code release version for spoofing the claude-cli
46
+ * User-Agent on OAuth and inference requests. Cached in-memory for the process
47
+ * lifetime and on disk for 24h. Falls back to a hardcoded constant if the npm
48
+ * registry is unreachable and no cache exists.
49
+ */
50
+ declare function getClaudeCodeVersion(): Promise<string>;
51
+ /** Build the User-Agent string Anthropic's OAuth + inference edges expect. */
52
+ declare function getClaudeCliUserAgent(): Promise<string>;
53
+
54
+ interface OAuthCredentials {
55
+ accessToken: string;
56
+ refreshToken: string;
57
+ expiresAt: number;
58
+ accountId?: string;
59
+ projectId?: string;
60
+ baseUrl?: string;
61
+ }
62
+ interface OAuthLoginCallbacks {
63
+ onOpenUrl: (url: string) => void;
64
+ onPromptCode: (message: string) => Promise<string>;
65
+ onStatus: (message: string) => void;
66
+ }
67
+
68
+ /**
69
+ * Storage key for Kimi Code OAuth credentials. Kept distinct from the
70
+ * `moonshot` API-key entry so a user can configure BOTH and we always
71
+ * prefer OAuth for the logical `moonshot` provider.
72
+ */
73
+ declare const MOONSHOT_OAUTH_KEY = "moonshot-oauth";
74
+ declare class AuthStorage {
75
+ private data;
76
+ private filePath;
77
+ private loaded;
78
+ /** Per-provider lock to serialize concurrent refresh calls. */
79
+ private refreshLocks;
80
+ constructor(filePath?: string);
81
+ /** Path to the on-disk auth file. Useful for status output. */
82
+ get path(): string;
83
+ /** List provider keys with stored credentials. */
84
+ listProviders(): Promise<string[]>;
85
+ /** True if credentials exist for `provider`. */
86
+ hasCredentials(provider: string): Promise<boolean>;
87
+ /**
88
+ * True if the user has any usable auth for the logical provider. For
89
+ * `moonshot` this is satisfied by either the Kimi OAuth credential or the
90
+ * Moonshot API key.
91
+ */
92
+ hasProviderAuth(provider: string): Promise<boolean>;
93
+ /**
94
+ * True if the active credential for `provider` is a static API key with no
95
+ * refresh mechanism. For `moonshot` this is only true when the Kimi OAuth
96
+ * credential is absent (a present OAuth credential is refreshable).
97
+ */
98
+ isStaticApiKey(provider: string): Promise<boolean>;
99
+ load(): Promise<void>;
100
+ private ensureLoaded;
101
+ getCredentials(provider: string): Promise<OAuthCredentials | undefined>;
102
+ setCredentials(provider: string, creds: OAuthCredentials): Promise<void>;
103
+ clearCredentials(provider: string): Promise<void>;
104
+ clearAll(): Promise<void>;
105
+ /**
106
+ * Returns valid credentials, auto-refreshing if expired.
107
+ * If `forceRefresh` is true, refreshes even if the token hasn't expired
108
+ * (useful when the provider rejects a token with 401 before its stored expiry).
109
+ * Throws if not logged in.
110
+ */
111
+ resolveCredentials(provider: string, opts?: {
112
+ forceRefresh?: boolean;
113
+ }): Promise<OAuthCredentials>;
114
+ /**
115
+ * Returns a valid access token, auto-refreshing if expired.
116
+ * Throws if not logged in.
117
+ */
118
+ resolveToken(provider: string): Promise<string>;
119
+ private save;
120
+ }
121
+ declare class NotLoggedInError extends Error {
122
+ provider: string;
123
+ constructor(provider: string);
124
+ }
125
+
126
+ declare function generatePKCE(): Promise<{
127
+ verifier: string;
128
+ challenge: string;
129
+ }>;
130
+
131
+ declare function loginAnthropic(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
132
+ declare function refreshAnthropicToken(refreshToken: string): Promise<OAuthCredentials>;
133
+
134
+ declare function loginOpenAI(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
135
+ declare function refreshOpenAIToken(refreshToken: string): Promise<OAuthCredentials>;
136
+
137
+ declare function loginGemini(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
138
+ declare function refreshGeminiToken(refreshToken: string): Promise<OAuthCredentials>;
139
+
140
+ /**
141
+ * Kimi Code OAuth — Device Authorization Grant (RFC 8628).
142
+ *
143
+ * Mirrors MoonshotAI/kimi-code's managed-auth flow. Three form-encoded
144
+ * POST endpoints against the OAuth host (default `https://auth.kimi.com`):
145
+ *
146
+ * - `/api/oauth/device_authorization` (client_id) → device + user code
147
+ * - `/api/oauth/token` (grant_type=device_code) → poll until authorized
148
+ * - `/api/oauth/token` (grant_type=refresh_token) → refresh access token
149
+ *
150
+ * Unlike Anthropic/OpenAI/Gemini (browser-redirect PKCE), this is a
151
+ * device-code/poll flow: we show the user a URL + code, they authorize in a
152
+ * browser on any device, and we poll for the token.
153
+ *
154
+ * After login the issued token is used against the managed coding API
155
+ * (`https://api.kimi.com/coding/v1`, distinct from the `api.moonshot.ai`
156
+ * API-key endpoint) via `Authorization: Bearer <access_token>`. We persist
157
+ * that base URL on the credential so the runtime routes there automatically.
158
+ */
159
+
160
+ /** Managed coding API base URL the issued OAuth token is used against. */
161
+ declare function kimiCodeBaseUrl(): string;
162
+ /**
163
+ * Headers the Kimi For Coding API requires on every model request. The
164
+ * managed endpoint gates access to recognized coding agents: requests must
165
+ * carry a `kimi_code_cli` platform identity and matching `User-Agent`, or the
166
+ * server rejects with "only available for Coding Agents". Attach these to the
167
+ * inference client's default headers whenever the Kimi OAuth token is used.
168
+ */
169
+ declare function kimiCodingHeaders(): Record<string, string>;
170
+ /**
171
+ * True if `baseUrl` targets the Kimi For Coding managed endpoint (the URL
172
+ * persisted on Kimi OAuth credentials). Callers use this to decide whether to
173
+ * attach `kimiCodingHeaders()` — the Moonshot API-key path uses a different
174
+ * host and must NOT receive the coding-agent identity headers.
175
+ */
176
+ declare function isKimiCodingEndpoint(baseUrl: string | undefined): boolean;
177
+ /**
178
+ * Drive the Kimi device-code flow end-to-end. Shows the verification URL +
179
+ * user code via callbacks, opens the browser, and polls until the user
180
+ * authorizes (or a 15-minute local timeout elapses).
181
+ */
182
+ declare function loginKimi(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
183
+ /** Exchange a refresh token for a fresh Kimi access token. */
184
+ declare function refreshKimiToken(refreshToken: string): Promise<OAuthCredentials>;
185
+
186
+ /**
187
+ * Minimal Telegram Bot API client using raw fetch().
188
+ * Supports long polling, markdown messages, inline keyboards, and message splitting.
189
+ */
190
+ interface TelegramConfig {
191
+ botToken: string;
192
+ /** Only accept messages from this Telegram user ID. */
193
+ allowedUserId: number;
194
+ }
195
+ interface TelegramUpdate {
196
+ update_id: number;
197
+ message?: {
198
+ message_id: number;
199
+ from: {
200
+ id: number;
201
+ first_name: string;
202
+ };
203
+ chat: {
204
+ id: number;
205
+ type: string;
206
+ title?: string;
207
+ };
208
+ text?: string;
209
+ voice?: {
210
+ file_id: string;
211
+ duration: number;
212
+ mime_type?: string;
213
+ file_size?: number;
214
+ };
215
+ };
216
+ callback_query?: {
217
+ id: string;
218
+ from: {
219
+ id: number;
220
+ };
221
+ message: {
222
+ chat: {
223
+ id: number;
224
+ };
225
+ };
226
+ data: string;
227
+ };
228
+ my_chat_member?: {
229
+ chat: {
230
+ id: number;
231
+ type: string;
232
+ title?: string;
233
+ };
234
+ from: {
235
+ id: number;
236
+ };
237
+ new_chat_member: {
238
+ status: string;
239
+ };
240
+ };
241
+ }
242
+ interface InlineButton {
243
+ text: string;
244
+ callback_data: string;
245
+ }
246
+ /** Incoming message with chat context. */
247
+ interface TelegramMessage {
248
+ text: string;
249
+ chatId: number;
250
+ chatType: "private" | "group" | "supergroup" | "channel";
251
+ chatTitle?: string;
252
+ }
253
+ /** Incoming voice note with chat context. */
254
+ interface TelegramVoiceMessage {
255
+ fileId: string;
256
+ duration: number;
257
+ chatId: number;
258
+ chatType: "private" | "group" | "supergroup" | "channel";
259
+ chatTitle?: string;
260
+ }
261
+ declare class TelegramBot {
262
+ private token;
263
+ private allowedUserId;
264
+ private offset;
265
+ private running;
266
+ private onMessage;
267
+ private onVoiceMessage;
268
+ private onCallback;
269
+ private onBotAdded;
270
+ private onBotRemoved;
271
+ constructor(config: TelegramConfig);
272
+ /** Register handler for incoming text messages. */
273
+ onText(handler: (msg: TelegramMessage) => void): void;
274
+ /** Register handler for incoming voice notes. */
275
+ onVoice(handler: (msg: TelegramVoiceMessage) => void): void;
276
+ /** Register handler for inline keyboard button presses. */
277
+ onCallbackQuery(handler: (data: string, chatId: number) => void): void;
278
+ /** Register handler for when the bot is added to a group. */
279
+ onAddedToGroup(handler: (chatId: number, chatTitle?: string) => void): void;
280
+ /** Register handler for when the bot is removed from a group. */
281
+ onRemovedFromGroup(handler: (chatId: number) => void): void;
282
+ /** Start long polling. Blocks until stop() is called. */
283
+ start(): Promise<void>;
284
+ /** Stop long polling. */
285
+ stop(): void;
286
+ /** Send a text message to a specific chat. Converts markdown and splits long messages. */
287
+ send(chatId: number, text: string, buttons?: InlineButton[][]): Promise<void>;
288
+ /** Send a plain text message (no markdown parsing) to a specific chat. */
289
+ sendPlain(chatId: number, text: string): Promise<void>;
290
+ /** Send a typing indicator to a specific chat. */
291
+ sendTyping(chatId: number): Promise<void>;
292
+ /** Get a direct download URL for a Telegram file. */
293
+ getFileUrl(fileId: string): Promise<string>;
294
+ private getUpdates;
295
+ private handleUpdate;
296
+ private apiCall;
297
+ }
298
+
299
+ /**
300
+ * Voice note transcription using local Whisper model.
301
+ * Uses @huggingface/transformers (pure JS/WASM) — no native deps, no API keys.
302
+ * Model (~75MB) is downloaded on first use and cached locally.
303
+ */
304
+ /** Optional callback for model download progress. */
305
+ type ProgressCallback = (info: {
306
+ status: string;
307
+ progress?: number;
308
+ file?: string;
309
+ }) => void;
310
+ /** Set a callback to receive model download progress updates. */
311
+ declare function setProgressCallback(cb: ProgressCallback | null): void;
312
+ /**
313
+ * Resample audio from one sample rate to another using linear interpolation.
314
+ */
315
+ declare function resample(audio: Float32Array, fromRate: number, toRate: number): Float32Array;
316
+ /**
317
+ * Downmix multi-channel audio to mono by averaging all channels.
318
+ */
319
+ declare function downmixToMono(channelData: Float32Array[]): Float32Array;
320
+ /**
321
+ * Decode OGG Opus audio buffer to 16kHz mono PCM Float32Array.
322
+ */
323
+ declare function decodeOggOpus(buffer: Uint8Array): Promise<Float32Array>;
324
+ /** Whether the model has been loaded already. */
325
+ declare function isModelLoaded(): boolean;
326
+ /**
327
+ * Transcribe a voice message from its Telegram file URL.
328
+ * Downloads the OGG Opus file, decodes to PCM, and runs Whisper locally.
329
+ */
330
+ declare function transcribeVoice(fileUrl: string): Promise<string>;
331
+
332
+ interface AutoUpdateConfig {
333
+ /** npm package to self-update, e.g. "@prestyj/cli". */
334
+ packageName: string;
335
+ /**
336
+ * Absolute path to this app's update-state.json, or a thunk resolving it.
337
+ * A thunk keeps path resolution lazy so callers can derive it from
338
+ * `os.homedir()` without freezing the value at module-load time (which
339
+ * breaks tests that mock the home directory after import).
340
+ */
341
+ stateFilePath: string | (() => string);
342
+ /** Builds the in-session "update available" notification. */
343
+ periodicMessage?: (args: {
344
+ currentVersion: string;
345
+ latestVersion: string;
346
+ updateCommand: string;
347
+ }) => string;
348
+ }
349
+ interface AutoUpdater {
350
+ checkAndAutoUpdate(currentVersion: string): string | null;
351
+ getPendingUpdate(currentVersion: string): {
352
+ latestVersion: string;
353
+ } | null;
354
+ startPeriodicUpdateCheck(currentVersion: string, onUpdate: (message: string) => void): void;
355
+ stopPeriodicUpdateCheck(): void;
356
+ }
357
+ declare function createAutoUpdater(config: AutoUpdateConfig): AutoUpdater;
358
+
359
+ export { AuthStorage, type AutoUpdateConfig, type AutoUpdater, type InlineButton, type LogLevel, MOONSHOT_OAUTH_KEY, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, type ProgressCallback, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, closeLogger, createAutoUpdater, decodeOggOpus, downmixToMono, generatePKCE, getClaudeCliUserAgent, getClaudeCodeVersion, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isKimiCodingEndpoint, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, kimiCodeBaseUrl, kimiCodingHeaders, log, loginAnthropic, loginGemini, loginKimi, loginOpenAI, openLog, refreshAnthropicToken, refreshGeminiToken, refreshKimiToken, refreshOpenAIToken, registerLogCleanup, resample, setProgressCallback, transcribeVoice, withFileLock };