pi-provider-kimi-code 0.5.1 → 0.6.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.
- package/README.md +94 -46
- package/index.ts +310 -110
- package/package.json +8 -8
- package/src/config.ts +475 -72
- package/src/constants.ts +29 -11
- package/src/device.ts +14 -0
- package/src/models.ts +115 -51
- package/src/payload.ts +90 -47
- package/src/schema-dedup.ts +256 -0
- package/src/stream.ts +70 -17
- package/src/tools/common.ts +115 -0
- package/src/tools/datasource.ts +239 -0
- package/src/tools/moonshot.ts +22 -111
package/index.ts
CHANGED
|
@@ -18,43 +18,43 @@
|
|
|
18
18
|
* src/stream.ts — empty-response filter + streamSimpleKimi orchestrator
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
22
21
|
import {
|
|
23
22
|
AuthStorage,
|
|
24
23
|
type ExtensionAPI,
|
|
25
24
|
type ExtensionCommandContext,
|
|
25
|
+
type ExtensionFactory,
|
|
26
26
|
} from "@earendil-works/pi-coding-agent";
|
|
27
27
|
import os from "node:os";
|
|
28
28
|
import { relative } from "node:path";
|
|
29
29
|
|
|
30
30
|
import {
|
|
31
31
|
type KimiCodeConfig,
|
|
32
|
+
type KimiCodeConfigPatch,
|
|
33
|
+
KIMI_TOOL_NAMES,
|
|
32
34
|
getProjectKimiCodeConfigPath,
|
|
33
35
|
getGlobalKimiCodeConfigPath,
|
|
34
36
|
loadHomeKimiCodeConfig,
|
|
35
37
|
loadKimiCodeConfig,
|
|
38
|
+
loadKimiCodeConfigSources,
|
|
36
39
|
loadProjectKimiCodeConfig,
|
|
37
40
|
saveHomeKimiCodeConfig,
|
|
38
41
|
saveProjectKimiCodeConfig,
|
|
42
|
+
type KimiToolName,
|
|
39
43
|
} from "./src/config.ts";
|
|
40
|
-
import {
|
|
41
|
-
DEFAULT_KIMI_MODEL_INPUT,
|
|
42
|
-
KIMI_API_TYPE,
|
|
43
|
-
PROVIDER_ID,
|
|
44
|
-
getBaseUrl,
|
|
45
|
-
} from "./src/constants.ts";
|
|
44
|
+
import { KIMI_CODE_VERSION, PROVIDER_ID, getBaseUrl, getKimiApiType } from "./src/constants.ts";
|
|
46
45
|
import { getCommonHeaders } from "./src/device.ts";
|
|
47
46
|
import {
|
|
48
47
|
type KimiOAuthCredentials,
|
|
49
|
-
|
|
48
|
+
type KimiOAuthExtras,
|
|
49
|
+
buildKimiModelFromConfig,
|
|
50
50
|
applyKimiOAuthExtrasToModel,
|
|
51
|
+
discoverKimiModelMetadata,
|
|
52
|
+
resolveKimiModelConfig,
|
|
51
53
|
} from "./src/models.ts";
|
|
52
|
-
import { loginKimiCode, refreshKimiCodeToken } from "./src/oauth.ts";
|
|
53
|
-
import { streamSimpleKimi } from "./src/stream.ts";
|
|
54
|
+
import { loginKimiCode, refreshKimiAuthToken, refreshKimiCodeToken } from "./src/oauth.ts";
|
|
55
|
+
import { setStoreResolvedKimiConfig, streamSimpleKimi } from "./src/stream.ts";
|
|
54
56
|
import { buildMoonshotFetchTool, buildMoonshotSearchTool } from "./src/tools/moonshot.ts";
|
|
55
|
-
|
|
56
|
-
const MOONSHOT_TOOL_NAMES = ["moonshot_search", "moonshot_fetch"] as const;
|
|
57
|
-
type MoonshotToolName = (typeof MOONSHOT_TOOL_NAMES)[number];
|
|
57
|
+
import { buildKimiDatasourceTool } from "./src/tools/datasource.ts";
|
|
58
58
|
type KimiConfigScope = "project" | "home";
|
|
59
59
|
const MEMBERSHIP_LEVEL_NAMES: Record<string, string> = {
|
|
60
60
|
LEVEL_FREE: "Free",
|
|
@@ -71,30 +71,37 @@ interface UsageRow {
|
|
|
71
71
|
limit: number;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
interface KimiRuntimeState {
|
|
75
|
+
cwd: string;
|
|
76
|
+
config: KimiCodeConfig;
|
|
77
|
+
modelExtras: KimiOAuthExtras;
|
|
78
|
+
overrides?: KimiCodeConfigPatch;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function buildKimiTool(toolName: KimiToolName, config: KimiCodeConfig) {
|
|
82
|
+
const opts = { defaultCollapsed: config.tools[toolName].default_collapsed };
|
|
83
|
+
if (toolName === "moonshot_search") return buildMoonshotSearchTool(opts);
|
|
84
|
+
if (toolName === "moonshot_fetch") return buildMoonshotFetchTool(opts);
|
|
85
|
+
if (toolName === "kimi_datasource") return buildKimiDatasourceTool(opts);
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
74
89
|
function registerConfiguredMoonshotTools(
|
|
75
90
|
pi: ExtensionAPI,
|
|
76
91
|
config: KimiCodeConfig,
|
|
77
92
|
options: { updateActiveTools: boolean },
|
|
78
93
|
): void {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
if (config.tools.moonshot_fetch.enabled) {
|
|
87
|
-
pi.registerTool(
|
|
88
|
-
buildMoonshotFetchTool({
|
|
89
|
-
defaultCollapsed: config.tools.moonshot_fetch.default_collapsed,
|
|
90
|
-
}),
|
|
91
|
-
);
|
|
94
|
+
for (const toolName of KIMI_TOOL_NAMES) {
|
|
95
|
+
if (config.tools[toolName].enabled) {
|
|
96
|
+
const tool = buildKimiTool(toolName, config);
|
|
97
|
+
if (tool) pi.registerTool(tool);
|
|
98
|
+
}
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
if (!options.updateActiveTools) return;
|
|
95
102
|
|
|
96
103
|
const activeTools = new Set(pi.getActiveTools());
|
|
97
|
-
for (const toolName of
|
|
104
|
+
for (const toolName of KIMI_TOOL_NAMES) {
|
|
98
105
|
if (config.tools[toolName].enabled) {
|
|
99
106
|
activeTools.add(toolName);
|
|
100
107
|
} else {
|
|
@@ -104,6 +111,29 @@ function registerConfiguredMoonshotTools(
|
|
|
104
111
|
pi.setActiveTools([...activeTools]);
|
|
105
112
|
}
|
|
106
113
|
|
|
114
|
+
function reloadEffectiveKimiRuntimeConfig(state: KimiRuntimeState, cwd: string): KimiCodeConfig {
|
|
115
|
+
const config = loadKimiCodeConfig({ cwd, home: os.homedir() }, state.overrides);
|
|
116
|
+
state.cwd = cwd;
|
|
117
|
+
state.config = config;
|
|
118
|
+
setStoreResolvedKimiConfig({
|
|
119
|
+
model: resolveKimiModelConfig(config.model, state.modelExtras),
|
|
120
|
+
protocol: config.protocol,
|
|
121
|
+
uploads: config.uploads,
|
|
122
|
+
});
|
|
123
|
+
return config;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function applyEffectiveKimiRuntimeConfig(
|
|
127
|
+
pi: ExtensionAPI,
|
|
128
|
+
state: KimiRuntimeState,
|
|
129
|
+
cwd: string,
|
|
130
|
+
options: { updateActiveTools: boolean },
|
|
131
|
+
): KimiCodeConfig {
|
|
132
|
+
const config = reloadEffectiveKimiRuntimeConfig(state, cwd);
|
|
133
|
+
registerConfiguredMoonshotTools(pi, config, options);
|
|
134
|
+
return config;
|
|
135
|
+
}
|
|
136
|
+
|
|
107
137
|
function getKimiUsageToken(): string | null {
|
|
108
138
|
const credential = AuthStorage.create().get(PROVIDER_ID);
|
|
109
139
|
if (credential?.type === "oauth" && credential.access) return credential.access;
|
|
@@ -186,6 +216,17 @@ function quotaBar(remaining: number, limit: number): string {
|
|
|
186
216
|
return `[${"#".repeat(filled)}${"-".repeat(width - filled)}]`;
|
|
187
217
|
}
|
|
188
218
|
|
|
219
|
+
function fetchKimiUsage(token: string, signal: AbortSignal): Promise<Response> {
|
|
220
|
+
return fetch("https://api.kimi.com/coding/v1/usages", {
|
|
221
|
+
method: "GET",
|
|
222
|
+
headers: {
|
|
223
|
+
...getCommonHeaders(),
|
|
224
|
+
Authorization: `Bearer ${token}`,
|
|
225
|
+
},
|
|
226
|
+
signal,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
189
230
|
async function fetchKimiUsageSummary(): Promise<string> {
|
|
190
231
|
const token = getKimiUsageToken();
|
|
191
232
|
if (!token) return "Usage: missing credentials. Run /login kimi-coding.";
|
|
@@ -193,14 +234,11 @@ async function fetchKimiUsageSummary(): Promise<string> {
|
|
|
193
234
|
const controller = new AbortController();
|
|
194
235
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
195
236
|
try {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
},
|
|
202
|
-
signal: controller.signal,
|
|
203
|
-
});
|
|
237
|
+
let response = await fetchKimiUsage(token, controller.signal);
|
|
238
|
+
if (response.status === 401) {
|
|
239
|
+
const refreshed = await refreshKimiAuthToken(token);
|
|
240
|
+
if (refreshed) response = await fetchKimiUsage(refreshed, controller.signal);
|
|
241
|
+
}
|
|
204
242
|
if (!response.ok) return `Usage: fetch failed (${response.status})`;
|
|
205
243
|
return parseUsageSummary(await response.json());
|
|
206
244
|
} catch (error) {
|
|
@@ -212,7 +250,7 @@ async function fetchKimiUsageSummary(): Promise<string> {
|
|
|
212
250
|
}
|
|
213
251
|
|
|
214
252
|
function moonshotStatus(config: KimiCodeConfig): string {
|
|
215
|
-
return
|
|
253
|
+
return KIMI_TOOL_NAMES.map((toolName) => {
|
|
216
254
|
const tool = config.tools[toolName];
|
|
217
255
|
const enabled = tool.enabled ? "enabled" : "disabled";
|
|
218
256
|
const collapsed = tool.default_collapsed ? "collapsed" : "expanded";
|
|
@@ -220,8 +258,9 @@ function moonshotStatus(config: KimiCodeConfig): string {
|
|
|
220
258
|
}).join("\n");
|
|
221
259
|
}
|
|
222
260
|
|
|
223
|
-
function toggleEnabled(config: KimiCodeConfig, toolName:
|
|
261
|
+
function toggleEnabled(config: KimiCodeConfig, toolName: KimiToolName): KimiCodeConfig {
|
|
224
262
|
return {
|
|
263
|
+
...config,
|
|
225
264
|
tools: {
|
|
226
265
|
...config.tools,
|
|
227
266
|
[toolName]: {
|
|
@@ -232,8 +271,9 @@ function toggleEnabled(config: KimiCodeConfig, toolName: MoonshotToolName): Kimi
|
|
|
232
271
|
};
|
|
233
272
|
}
|
|
234
273
|
|
|
235
|
-
function toggleCollapsed(config: KimiCodeConfig, toolName:
|
|
274
|
+
function toggleCollapsed(config: KimiCodeConfig, toolName: KimiToolName): KimiCodeConfig {
|
|
236
275
|
return {
|
|
276
|
+
...config,
|
|
237
277
|
tools: {
|
|
238
278
|
...config.tools,
|
|
239
279
|
[toolName]: {
|
|
@@ -244,13 +284,34 @@ function toggleCollapsed(config: KimiCodeConfig, toolName: MoonshotToolName): Ki
|
|
|
244
284
|
};
|
|
245
285
|
}
|
|
246
286
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
287
|
+
function setProtocol(config: KimiCodeConfig, protocol: KimiCodeConfig["protocol"]): KimiCodeConfig {
|
|
288
|
+
return { ...config, protocol };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function setUploadThreshold(config: KimiCodeConfig, thresholdBytes: number): KimiCodeConfig {
|
|
292
|
+
return { ...config, uploads: { ...config.uploads, thresholdBytes } };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function refreshModelExtras(state: KimiRuntimeState): Promise<void> {
|
|
296
|
+
const token = getKimiUsageToken();
|
|
297
|
+
if (!token) return;
|
|
298
|
+
const extras = await discoverKimiModelMetadata(token, state.config.protocol);
|
|
299
|
+
if (Object.keys(extras).length > 0) {
|
|
300
|
+
Object.assign(state.modelExtras, extras);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function runKimiCommand(
|
|
305
|
+
pi: ExtensionAPI,
|
|
306
|
+
ctx: ExtensionCommandContext,
|
|
307
|
+
state: KimiRuntimeState,
|
|
308
|
+
): Promise<void> {
|
|
309
|
+
let config = applyEffectiveKimiRuntimeConfig(pi, state, ctx.cwd, { updateActiveTools: true });
|
|
310
|
+
let [usage] = await Promise.all([fetchKimiUsageSummary(), refreshModelExtras(state)]);
|
|
250
311
|
ctx.ui.notify(usage);
|
|
251
312
|
|
|
252
313
|
while (true) {
|
|
253
|
-
const choice = await ctx.ui.select(buildKimiMainTitle(config, ctx.cwd), [
|
|
314
|
+
const choice = await ctx.ui.select(buildKimiMainTitle(config, ctx.cwd, state.modelExtras), [
|
|
254
315
|
`Edit project config (${relative(ctx.cwd, getProjectKimiCodeConfigPath(ctx.cwd))})`,
|
|
255
316
|
`Edit home config (${homeRelative(getGlobalKimiCodeConfigPath(os.homedir()))})`,
|
|
256
317
|
"Refresh usage",
|
|
@@ -264,9 +325,9 @@ async function runKimiCommand(pi: ExtensionAPI, ctx: ExtensionCommandContext): P
|
|
|
264
325
|
continue;
|
|
265
326
|
}
|
|
266
327
|
if (choice.startsWith("Edit project config")) {
|
|
267
|
-
config = await editConfigScope(pi, ctx, "project");
|
|
328
|
+
config = await editConfigScope(pi, ctx, state, "project");
|
|
268
329
|
} else if (choice.startsWith("Edit home config")) {
|
|
269
|
-
config = await editConfigScope(pi, ctx, "home");
|
|
330
|
+
config = await editConfigScope(pi, ctx, state, "home");
|
|
270
331
|
}
|
|
271
332
|
}
|
|
272
333
|
}
|
|
@@ -274,32 +335,78 @@ async function runKimiCommand(pi: ExtensionAPI, ctx: ExtensionCommandContext): P
|
|
|
274
335
|
async function editConfigScope(
|
|
275
336
|
pi: ExtensionAPI,
|
|
276
337
|
ctx: ExtensionCommandContext,
|
|
338
|
+
state: KimiRuntimeState,
|
|
277
339
|
scope: KimiConfigScope,
|
|
278
340
|
): Promise<KimiCodeConfig> {
|
|
279
341
|
let current = loadScopeKimiCodeConfig(scope, ctx.cwd);
|
|
280
342
|
while (true) {
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
"Back",
|
|
285
|
-
]);
|
|
343
|
+
const items = KIMI_TOOL_NAMES.map((name) => toolMenuItem(current, name));
|
|
344
|
+
items.push(protocolMenuItem(current), uploadThresholdMenuItem(current), "Back");
|
|
345
|
+
const choice = await ctx.ui.select(buildConfigScopeTitle(scope, current, ctx.cwd), items);
|
|
286
346
|
if (!choice || choice === "Back") {
|
|
287
347
|
return loadKimiCodeConfig({ cwd: ctx.cwd, home: os.homedir() });
|
|
288
348
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
349
|
+
const toolName = KIMI_TOOL_NAMES.find((name) => choice.startsWith(name));
|
|
350
|
+
if (toolName) {
|
|
351
|
+
current = await editMoonshotTool(pi, ctx, state, scope, current, toolName);
|
|
352
|
+
} else if (choice.startsWith("Protocol")) {
|
|
353
|
+
current = await editProtocol(pi, ctx, state, scope, current);
|
|
354
|
+
} else if (choice.startsWith("Upload threshold")) {
|
|
355
|
+
current = await editUploadThreshold(pi, ctx, state, scope, current);
|
|
293
356
|
}
|
|
294
357
|
}
|
|
295
358
|
}
|
|
296
359
|
|
|
360
|
+
async function editProtocol(
|
|
361
|
+
pi: ExtensionAPI,
|
|
362
|
+
ctx: ExtensionCommandContext,
|
|
363
|
+
state: KimiRuntimeState,
|
|
364
|
+
scope: KimiConfigScope,
|
|
365
|
+
config: KimiCodeConfig,
|
|
366
|
+
): Promise<KimiCodeConfig> {
|
|
367
|
+
const choice = await ctx.ui.select(`Edit protocol\n\n${formatProtocolStatus(config)}`, [
|
|
368
|
+
"Use openai protocol",
|
|
369
|
+
"Use anthropic protocol",
|
|
370
|
+
"Back",
|
|
371
|
+
]);
|
|
372
|
+
if (!choice || choice === "Back") return config;
|
|
373
|
+
const protocol = choice.includes("anthropic") ? "anthropic" : "openai";
|
|
374
|
+
const current = setProtocol(config, protocol);
|
|
375
|
+
saveAndApplyKimiCodeConfig(pi, ctx, state, scope, current);
|
|
376
|
+
ctx.ui.notify("Saved protocol config", "info");
|
|
377
|
+
return current;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function editUploadThreshold(
|
|
381
|
+
pi: ExtensionAPI,
|
|
382
|
+
ctx: ExtensionCommandContext,
|
|
383
|
+
state: KimiRuntimeState,
|
|
384
|
+
scope: KimiConfigScope,
|
|
385
|
+
config: KimiCodeConfig,
|
|
386
|
+
): Promise<KimiCodeConfig> {
|
|
387
|
+
const input = await ctx.ui.input(
|
|
388
|
+
"Edit upload threshold\n\nExamples: 512 KiB, 2 MiB, 1.5 MB. Plain numbers are MiB.",
|
|
389
|
+
formatByteSize(config.uploads.thresholdBytes),
|
|
390
|
+
);
|
|
391
|
+
if (input === undefined) return config;
|
|
392
|
+
const thresholdBytes = parseByteSizeInput(input);
|
|
393
|
+
if (thresholdBytes === undefined || thresholdBytes <= 0) {
|
|
394
|
+
ctx.ui.notify("Upload threshold must be a positive size, for example 2 MiB", "error");
|
|
395
|
+
return config;
|
|
396
|
+
}
|
|
397
|
+
const current = setUploadThreshold(config, thresholdBytes);
|
|
398
|
+
saveAndApplyKimiCodeConfig(pi, ctx, state, scope, current);
|
|
399
|
+
ctx.ui.notify("Saved upload threshold config", "info");
|
|
400
|
+
return current;
|
|
401
|
+
}
|
|
402
|
+
|
|
297
403
|
async function editMoonshotTool(
|
|
298
404
|
pi: ExtensionAPI,
|
|
299
405
|
ctx: ExtensionCommandContext,
|
|
406
|
+
state: KimiRuntimeState,
|
|
300
407
|
scope: KimiConfigScope,
|
|
301
408
|
config: KimiCodeConfig,
|
|
302
|
-
toolName:
|
|
409
|
+
toolName: KimiToolName,
|
|
303
410
|
): Promise<KimiCodeConfig> {
|
|
304
411
|
let current = config;
|
|
305
412
|
while (true) {
|
|
@@ -318,13 +425,23 @@ async function editMoonshotTool(
|
|
|
318
425
|
} else if (choice.endsWith("previews by default")) {
|
|
319
426
|
current = toggleCollapsed(current, toolName);
|
|
320
427
|
}
|
|
321
|
-
|
|
322
|
-
const effective = loadKimiCodeConfig({ cwd: ctx.cwd, home: os.homedir() });
|
|
323
|
-
registerConfiguredMoonshotTools(pi, effective, { updateActiveTools: true });
|
|
428
|
+
saveAndApplyKimiCodeConfig(pi, ctx, state, scope, current);
|
|
324
429
|
ctx.ui.notify(`Saved ${toolName} config`, "info");
|
|
430
|
+
return current;
|
|
325
431
|
}
|
|
326
432
|
}
|
|
327
433
|
|
|
434
|
+
function saveAndApplyKimiCodeConfig(
|
|
435
|
+
pi: ExtensionAPI,
|
|
436
|
+
ctx: ExtensionCommandContext,
|
|
437
|
+
state: KimiRuntimeState,
|
|
438
|
+
scope: KimiConfigScope,
|
|
439
|
+
config: KimiCodeConfig,
|
|
440
|
+
): void {
|
|
441
|
+
saveScopeKimiCodeConfig(scope, ctx.cwd, config);
|
|
442
|
+
applyEffectiveKimiRuntimeConfig(pi, state, ctx.cwd, { updateActiveTools: true });
|
|
443
|
+
}
|
|
444
|
+
|
|
328
445
|
function loadScopeKimiCodeConfig(scope: KimiConfigScope, cwd: string): KimiCodeConfig {
|
|
329
446
|
if (scope === "project") return loadProjectKimiCodeConfig(cwd);
|
|
330
447
|
return loadHomeKimiCodeConfig(os.homedir());
|
|
@@ -342,8 +459,22 @@ function saveScopeKimiCodeConfig(
|
|
|
342
459
|
}
|
|
343
460
|
}
|
|
344
461
|
|
|
345
|
-
function buildKimiMainTitle(
|
|
346
|
-
|
|
462
|
+
function buildKimiMainTitle(config: KimiCodeConfig, cwd: string, extras: KimiOAuthExtras): string {
|
|
463
|
+
const sources = loadKimiCodeConfigSources({ cwd, home: os.homedir() });
|
|
464
|
+
const modelName = extras.modelDisplay || "kimi-for-coding";
|
|
465
|
+
return [
|
|
466
|
+
`Kimi settings (provider v${KIMI_CODE_VERSION})`,
|
|
467
|
+
"",
|
|
468
|
+
`Model: ${modelName}`,
|
|
469
|
+
`Protocol: ${config.protocol} (${sources.protocol})`,
|
|
470
|
+
`Upload threshold: ${formatByteSize(config.uploads.thresholdBytes)} (${sources.uploads.thresholdBytes})`,
|
|
471
|
+
"",
|
|
472
|
+
"Effective tools:",
|
|
473
|
+
...KIMI_TOOL_NAMES.map((toolName) => {
|
|
474
|
+
const enabled = config.tools[toolName].enabled ? "enabled" : "disabled";
|
|
475
|
+
return `- ${toolName}: ${enabled} (${sources.tools[toolName].enabled})`;
|
|
476
|
+
}),
|
|
477
|
+
].join("\n");
|
|
347
478
|
}
|
|
348
479
|
|
|
349
480
|
function buildConfigScopeTitle(
|
|
@@ -355,7 +486,15 @@ function buildConfigScopeTitle(
|
|
|
355
486
|
scope === "project"
|
|
356
487
|
? relative(cwd, getProjectKimiCodeConfigPath(cwd))
|
|
357
488
|
: homeRelative(getGlobalKimiCodeConfigPath(os.homedir()));
|
|
358
|
-
return [
|
|
489
|
+
return [
|
|
490
|
+
`Edit ${scope} config`,
|
|
491
|
+
`File: ${path}`,
|
|
492
|
+
"",
|
|
493
|
+
formatProtocolStatus(config),
|
|
494
|
+
formatUploadThresholdStatus(config),
|
|
495
|
+
"",
|
|
496
|
+
moonshotStatus(config),
|
|
497
|
+
].join("\n");
|
|
359
498
|
}
|
|
360
499
|
|
|
361
500
|
function homeRelative(filePath: string): string {
|
|
@@ -363,66 +502,127 @@ function homeRelative(filePath: string): string {
|
|
|
363
502
|
return filePath.startsWith(`${home}/`) ? `~/${filePath.slice(home.length + 1)}` : filePath;
|
|
364
503
|
}
|
|
365
504
|
|
|
366
|
-
function toolMenuItem(config: KimiCodeConfig, toolName:
|
|
505
|
+
function toolMenuItem(config: KimiCodeConfig, toolName: KimiToolName): string {
|
|
367
506
|
return `${toolName} -> ${formatToolStatus(config, toolName)}`;
|
|
368
507
|
}
|
|
369
508
|
|
|
370
|
-
function
|
|
509
|
+
function protocolMenuItem(config: KimiCodeConfig): string {
|
|
510
|
+
return `Protocol -> ${config.protocol}`;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function uploadThresholdMenuItem(config: KimiCodeConfig): string {
|
|
514
|
+
return `Upload threshold -> ${formatByteSize(config.uploads.thresholdBytes)}`;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function formatByteSize(bytes: number): string {
|
|
518
|
+
const units = [
|
|
519
|
+
{ suffix: "GiB", size: 1024 ** 3 },
|
|
520
|
+
{ suffix: "MiB", size: 1024 ** 2 },
|
|
521
|
+
{ suffix: "KiB", size: 1024 },
|
|
522
|
+
];
|
|
523
|
+
for (const unit of units) {
|
|
524
|
+
if (bytes >= unit.size && bytes % unit.size === 0) {
|
|
525
|
+
return `${bytes / unit.size} ${unit.suffix}`;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
for (const unit of units) {
|
|
529
|
+
if (bytes >= unit.size) {
|
|
530
|
+
return `${(bytes / unit.size).toFixed(2).replace(/\.00$/, "")} ${unit.suffix}`;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return `${bytes} B`;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function parseByteSizeInput(input: string): number | undefined {
|
|
537
|
+
const match = input.trim().match(/^(\d+(?:\.\d+)?)\s*(b|bytes?|kib|kb|mib|mb|gib|gb)?$/i);
|
|
538
|
+
if (!match) return undefined;
|
|
539
|
+
const value = Number(match[1]);
|
|
540
|
+
if (!Number.isFinite(value) || value <= 0) return undefined;
|
|
541
|
+
const unit = (match[2] ?? "mib").toLowerCase();
|
|
542
|
+
const multipliers: Record<string, number> = {
|
|
543
|
+
b: 1,
|
|
544
|
+
byte: 1,
|
|
545
|
+
bytes: 1,
|
|
546
|
+
kib: 1024,
|
|
547
|
+
kb: 1000,
|
|
548
|
+
mib: 1024 ** 2,
|
|
549
|
+
mb: 1000 ** 2,
|
|
550
|
+
gib: 1024 ** 3,
|
|
551
|
+
gb: 1000 ** 3,
|
|
552
|
+
};
|
|
553
|
+
const multiplier = multipliers[unit];
|
|
554
|
+
if (!multiplier) return undefined;
|
|
555
|
+
const bytes = Math.round(value * multiplier);
|
|
556
|
+
return Number.isSafeInteger(bytes) && bytes > 0 ? bytes : undefined;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function formatProtocolStatus(config: KimiCodeConfig): string {
|
|
560
|
+
return `protocol: ${config.protocol}`;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function formatUploadThresholdStatus(config: KimiCodeConfig): string {
|
|
564
|
+
return `upload threshold: ${formatByteSize(config.uploads.thresholdBytes)}`;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function formatToolStatus(config: KimiCodeConfig, toolName: KimiToolName): string {
|
|
371
568
|
const tool = config.tools[toolName];
|
|
372
569
|
const enabled = tool.enabled ? "enabled" : "disabled";
|
|
373
570
|
const collapsed = tool.default_collapsed ? "default collapsed" : "default expanded";
|
|
374
571
|
return `${enabled}, ${collapsed}`;
|
|
375
572
|
}
|
|
376
573
|
|
|
377
|
-
export
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
574
|
+
export function KimiCode(overrides?: KimiCodeConfigPatch): ExtensionFactory {
|
|
575
|
+
return async (pi: ExtensionAPI) => {
|
|
576
|
+
const cwd = process.cwd();
|
|
577
|
+
const config = loadKimiCodeConfig({ cwd, home: os.homedir() }, overrides);
|
|
578
|
+
const baseModel = buildKimiModelFromConfig(config.model);
|
|
579
|
+
const discoveryToken = getKimiUsageToken();
|
|
580
|
+
const discovered = discoveryToken
|
|
581
|
+
? await discoverKimiModelMetadata(discoveryToken, config.protocol)
|
|
582
|
+
: {};
|
|
583
|
+
const state: KimiRuntimeState = { cwd, config, modelExtras: discovered, overrides };
|
|
584
|
+
reloadEffectiveKimiRuntimeConfig(state, cwd);
|
|
585
|
+
const model = applyKimiOAuthExtrasToModel(baseModel, discovered);
|
|
586
|
+
|
|
587
|
+
pi.registerProvider(PROVIDER_ID, {
|
|
588
|
+
baseUrl: getBaseUrl(config.protocol),
|
|
589
|
+
apiKey: "$KIMI_API_KEY",
|
|
590
|
+
api: getKimiApiType(config.protocol),
|
|
591
|
+
streamSimple: streamSimpleKimi,
|
|
592
|
+
|
|
593
|
+
models: [model],
|
|
594
|
+
|
|
595
|
+
oauth: {
|
|
596
|
+
name: "Kimi Code (OAuth)",
|
|
597
|
+
login: loginKimiCode,
|
|
598
|
+
refreshToken: refreshKimiCodeToken,
|
|
599
|
+
getApiKey: (cred) => cred.access,
|
|
600
|
+
// Reflect server-side model identity on the registered model after login
|
|
601
|
+
// / refresh. We never rewrite the model id (pi-side `/model` selections
|
|
602
|
+
// and persisted sessions reference it); only the human-facing name, the
|
|
603
|
+
// context window, and an out-of-band `wireModelId` carried into the
|
|
604
|
+
// request payload by streamSimpleKimi.
|
|
605
|
+
modifyModels: (models, cred) => {
|
|
606
|
+
const extras = cred as KimiOAuthCredentials;
|
|
607
|
+
state.modelExtras = extras;
|
|
608
|
+
reloadEffectiveKimiRuntimeConfig(state, state.cwd);
|
|
609
|
+
return models.map((model) => {
|
|
610
|
+
if (model.id !== "kimi-for-coding") return model;
|
|
611
|
+
return applyKimiOAuthExtrasToModel(model, extras);
|
|
612
|
+
});
|
|
613
|
+
},
|
|
416
614
|
},
|
|
417
|
-
}
|
|
418
|
-
});
|
|
615
|
+
});
|
|
419
616
|
|
|
420
|
-
|
|
617
|
+
registerConfiguredMoonshotTools(pi, state.config, { updateActiveTools: false });
|
|
421
618
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
619
|
+
pi.registerCommand("kimi-settings", {
|
|
620
|
+
description: "Show Kimi usage and configure optional Kimi tools",
|
|
621
|
+
handler: async (_args, ctx) => {
|
|
622
|
+
await runKimiCommand(pi, ctx, state);
|
|
623
|
+
},
|
|
624
|
+
});
|
|
625
|
+
};
|
|
428
626
|
}
|
|
627
|
+
|
|
628
|
+
export default KimiCode();
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-provider-kimi-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Use Kimi Code in Pi without losing the Kimi-specific bits",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-provider",
|
|
7
7
|
"coding-agent",
|
|
8
|
-
"device-code",
|
|
9
8
|
"kimi",
|
|
10
|
-
"kimi-
|
|
9
|
+
"kimi-code",
|
|
11
10
|
"kimi-for-coding",
|
|
12
11
|
"kimi-k2",
|
|
13
12
|
"kimi-k2.5",
|
|
14
13
|
"kimi-k2.6",
|
|
14
|
+
"kimi-k2.7",
|
|
15
15
|
"llm",
|
|
16
16
|
"llm-provider",
|
|
17
17
|
"moonshot",
|
|
@@ -20,10 +20,8 @@
|
|
|
20
20
|
"pi",
|
|
21
21
|
"pi-coding-agent",
|
|
22
22
|
"pi-extension",
|
|
23
|
-
"pi-mono",
|
|
24
23
|
"pi-package",
|
|
25
|
-
"pi-provider"
|
|
26
|
-
"subscription"
|
|
24
|
+
"pi-provider"
|
|
27
25
|
],
|
|
28
26
|
"bugs": {
|
|
29
27
|
"url": "https://github.com/Leechael/pi-provider-kimi-code/issues"
|
|
@@ -54,7 +52,8 @@
|
|
|
54
52
|
"devDependencies": {
|
|
55
53
|
"@earendil-works/pi-ai": "^0.78.0",
|
|
56
54
|
"@earendil-works/pi-coding-agent": "^0.78.0",
|
|
57
|
-
"@
|
|
55
|
+
"@earendil-works/pi-tui": "^0.78.1",
|
|
56
|
+
"@j178/prek": "^0.4.4",
|
|
58
57
|
"@types/node": "^24.0.0",
|
|
59
58
|
"oxfmt": "^0.44.0",
|
|
60
59
|
"oxlint": "^1.59.0",
|
|
@@ -62,7 +61,8 @@
|
|
|
62
61
|
},
|
|
63
62
|
"peerDependencies": {
|
|
64
63
|
"@earendil-works/pi-ai": "*",
|
|
65
|
-
"@earendil-works/pi-coding-agent": "*"
|
|
64
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
65
|
+
"@earendil-works/pi-tui": "*"
|
|
66
66
|
},
|
|
67
67
|
"pi": {
|
|
68
68
|
"extensions": [
|