@exreve/exk 1.0.64 → 1.0.66

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.
@@ -81,6 +81,11 @@ const PROVIDERS = {
81
81
  baseUrl: '', // Set dynamically when proxy starts
82
82
  models: ['zai-glm-4.7'],
83
83
  },
84
+ kimi: {
85
+ apiKey: '', // Populated from ai-config.json kimiApiKey (served by backend)
86
+ baseUrl: 'https://api.kimi.com/coding',
87
+ models: ['kimi-k2.6'],
88
+ },
84
89
  };
85
90
  /** Resolve which provider to use based on model name or explicit provider ID.
86
91
  * 1. Populate provider API keys from ai-config.json (served by backend).
@@ -93,6 +98,7 @@ function resolveProvider(model, providerId) {
93
98
  PROVIDERS.minimax.apiKey = aiConfig.minimaxApiKey || process.env.MINIMAX_API_KEY || '';
94
99
  PROVIDERS.openrouter.apiKey = aiConfig.openrouterApiKey || process.env.OPENROUTER_API_KEY || '';
95
100
  PROVIDERS.cerebras.apiKey = aiConfig.cerebrasApiKey || process.env.CEREBRAS_API_KEY || '';
101
+ PROVIDERS.kimi.apiKey = aiConfig.kimiApiKey || process.env.KIMI_API_KEY || '';
96
102
  if (!PROVIDERS.zai.apiKey)
97
103
  PROVIDERS.zai.apiKey = aiConfig.apiKey || '';
98
104
  // 1. Explicit provider selection
@@ -129,12 +135,13 @@ function loadAiConfig() {
129
135
  const minimaxApiKey = typeof config.minimaxApiKey === 'string' ? config.minimaxApiKey.trim() : '';
130
136
  const openrouterApiKey = typeof config.openrouterApiKey === 'string' ? config.openrouterApiKey.trim() : '';
131
137
  const cerebrasApiKey = typeof config.cerebrasApiKey === 'string' ? config.cerebrasApiKey.trim() : '';
132
- const result = { apiKey, baseUrl, model, proxy, minimaxApiKey, openrouterApiKey, cerebrasApiKey };
138
+ const kimiApiKey = typeof config.kimiApiKey === 'string' ? config.kimiApiKey.trim() : '';
139
+ const result = { apiKey, baseUrl, model, proxy, minimaxApiKey, openrouterApiKey, cerebrasApiKey, kimiApiKey };
133
140
  _aiConfigCache = { data: result, ts: now };
134
141
  return result;
135
142
  }
136
143
  catch {
137
- const fallback = { apiKey: '', baseUrl: '', model: DEFAULT_AI_MODEL, proxy: '', minimaxApiKey: '', openrouterApiKey: '', cerebrasApiKey: '' };
144
+ const fallback = { apiKey: '', baseUrl: '', model: DEFAULT_AI_MODEL, proxy: '', minimaxApiKey: '', openrouterApiKey: '', cerebrasApiKey: '', kimiApiKey: '' };
138
145
  _aiConfigCache = { data: fallback, ts: now };
139
146
  return fallback;
140
147
  }
@@ -217,6 +224,14 @@ function envForClaudeCodeChild(_localModel, resolvedProvider) {
217
224
  env.ANTHROPIC_DEFAULT_HAIKU_MODEL = resolvedProvider.model;
218
225
  env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
219
226
  }
227
+ // For Kimi specifically: override ALL model aliases
228
+ if (resolvedProvider?.provider === 'kimi') {
229
+ env.ANTHROPIC_MODEL = resolvedProvider.model;
230
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL = resolvedProvider.model;
231
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL = resolvedProvider.model;
232
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL = resolvedProvider.model;
233
+ env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
234
+ }
220
235
  // Apply proxy if enabled
221
236
  const proxyToggle = readProxyToggle();
222
237
  if (proxyToggle.enabled && proxy) {
@@ -634,6 +649,14 @@ export class AgentSessionManager {
634
649
  settingsEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL = resolved.model;
635
650
  settingsEnv.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
636
651
  }
652
+ // For Kimi: also override all model aliases in settings
653
+ if (resolved.provider === 'kimi') {
654
+ settingsEnv.ANTHROPIC_MODEL = resolved.model;
655
+ settingsEnv.ANTHROPIC_DEFAULT_SONNET_MODEL = resolved.model;
656
+ settingsEnv.ANTHROPIC_DEFAULT_OPUS_MODEL = resolved.model;
657
+ settingsEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL = resolved.model;
658
+ settingsEnv.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
659
+ }
637
660
  // For Cerebras: start anthropic-proxy-rs and route through it
638
661
  if (resolved.provider === 'cerebras') {
639
662
  const proxyUrl = await ensureProxy({
@@ -298,23 +298,45 @@ export async function executeGenImage(args, _config) {
298
298
  if (!dataUrl || !dataUrl.startsWith('data:')) {
299
299
  return { content: [{ type: 'text', text: `Error: Unexpected image format from Flux API.` }], isError: true };
300
300
  }
301
- // Decode base64 and save to temp dir
301
+ // Decode base64
302
302
  const base64 = dataUrl.split(',')[1];
303
303
  if (!base64) {
304
304
  return { content: [{ type: 'text', text: 'Error: Empty image data from Flux API.' }], isError: true };
305
305
  }
306
- const imgBuf = Buffer.from(base64, 'base64');
306
+ const rawBuf = Buffer.from(base64, 'base64');
307
+ const rawKb = (rawBuf.length / 1024).toFixed(0);
308
+ // Compress to WebP with sharp (high quality, same dimensions)
309
+ let finalBuf;
310
+ let ext = 'webp';
311
+ try {
312
+ const sharp = (await import('sharp')).default;
313
+ finalBuf = await sharp(rawBuf)
314
+ .webp({ quality: 90, effort: 4 })
315
+ .toBuffer();
316
+ // If compression didn't help (rare), keep original PNG
317
+ if (finalBuf.length >= rawBuf.length) {
318
+ finalBuf = rawBuf;
319
+ ext = 'png';
320
+ }
321
+ }
322
+ catch {
323
+ // sharp not available — save raw PNG
324
+ finalBuf = rawBuf;
325
+ ext = 'png';
326
+ }
307
327
  const tmpDir = path.join(os.tmpdir(), 'talk-to-code', 'gen-images');
308
328
  fs.mkdirSync(tmpDir, { recursive: true });
309
329
  const safeName = (args.filename || `gen_${Date.now()}`).replace(/[^a-zA-Z0-9._-]/g, '_');
310
- const fileName = safeName.endsWith('.png') ? safeName : `${safeName}.png`;
330
+ const baseName = safeName.replace(/\.(png|webp|jpg|jpeg)$/i, '');
331
+ const fileName = `${baseName}.${ext}`;
311
332
  const filePath = path.join(tmpDir, fileName);
312
- fs.writeFileSync(filePath, imgBuf);
313
- const sizeKb = (imgBuf.length / 1024).toFixed(0);
333
+ fs.writeFileSync(filePath, finalBuf);
334
+ const sizeKb = (finalBuf.length / 1024).toFixed(0);
335
+ const saved = ext === 'webp' ? ` (${rawKb} KB PNG → ${sizeKb} KB WebP)` : '';
314
336
  return {
315
337
  content: [{
316
338
  type: 'text',
317
- text: `Image generated successfully.\n\nFile: ${filePath}\nSize: ${sizeKb} KB\nPrompt: ${args.prompt}`,
339
+ text: `Image generated successfully.\n\nFile: ${filePath}\nSize: ${sizeKb} KB${saved}\nPrompt: ${args.prompt}`,
318
340
  }],
319
341
  details: { filePath, sizeKb, prompt: args.prompt },
320
342
  };
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exreve/exk",
3
- "version": "1.0.64",
3
+ "version": "1.0.66",
4
4
  "description": "exk - Control Claude CLI with voice and programmable interfaces",
5
5
  "type": "module",
6
6
  "bin": {