pi-provider-kimi-code 0.6.9 → 0.6.10
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 +12 -13
- package/index.ts +24 -5
- package/package.json +9 -5
- package/src/constants.ts +2 -2
- package/src/tools/datasource.ts +22 -1
- package/src/usage.ts +98 -8
package/README.md
CHANGED
|
@@ -3,16 +3,10 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/pi-provider-kimi-code)
|
|
4
4
|
[](./LICENSE)
|
|
5
5
|
|
|
6
|
-
> **Kimi Code
|
|
7
|
-
|
|
8
|
-
Pi's built-in `kimi-coding` provider handles the LLM. This extension covers the rest: shared login state with the official CLI, file uploads through Kimi's Files API, and Kimi-native tools. It also keeps a legacy provider registration so existing model sessions still load. New users should use Pi's built-in model provider.
|
|
9
|
-
|
|
10
|
-
> **Live model catalog (legacy).** For backward compatibility, the extension still reads Kimi's live `/v1/models` catalog when it has credentials. New Pi users should rely on the built-in provider's generated catalog instead.
|
|
6
|
+
> **Kimi Code extension for Pi — use K3, K2.7 Code, and HighSpeed models with your Kimi Code Plan.** This extension brings the full Kimi Code surface into Pi: shared OAuth login state with the official `kimi-code` CLI, Kimi Files API uploads, and Kimi-native tools such as `moonshot_search`, `moonshot_fetch`, and `kimi_datasource`. (Pi v0.82.0+ ships a built-in `kimi-coding` provider for the LLM layer; this extension covers everything around it.)
|
|
11
7
|
|
|
12
8
|
## Why this exists
|
|
13
9
|
|
|
14
|
-
Pi is a small harness you adapt to your own workflow. Kimi Code is Moonshot's official coding agent. Pi now ships a built-in `kimi-coding` provider that covers the LLM, including OAuth login. This extension is for the parts of Kimi Code that Pi's built-in provider does not cover: the account session, file uploads, and Kimi's own tools/datasource APIs.
|
|
15
|
-
|
|
16
10
|
Use the built-in provider if you only want to chat with Kimi models. Install this extension when you also want the full Kimi Code surface inside Pi:
|
|
17
11
|
|
|
18
12
|
- **Share a session with the official `kimi-code` CLI.** Reuse an existing `kimi-code` login, or sync Pi's OAuth login back to `kimi-code` so both CLIs stay authenticated.
|
|
@@ -90,11 +84,11 @@ KIMI_API_KEY=sk-... pi
|
|
|
90
84
|
|
|
91
85
|
When this extension does register models, the official catalog determines what it publishes. The following IDs are the fallback set when catalog discovery is unavailable:
|
|
92
86
|
|
|
93
|
-
| Pi model ID | Default name
|
|
94
|
-
| --------------------------------------- |
|
|
95
|
-
| `kimi-coding/kimi-for-coding` | Kimi
|
|
96
|
-
| `kimi-coding/kimi-for-coding-highspeed` | Kimi
|
|
97
|
-
| `kimi-coding/k3` | Kimi K3
|
|
87
|
+
| Pi model ID | Default name | Source |
|
|
88
|
+
| --------------------------------------- | -------------------------- | ---------------- |
|
|
89
|
+
| `kimi-coding/kimi-for-coding` | Kimi for Coding | Official catalog |
|
|
90
|
+
| `kimi-coding/kimi-for-coding-highspeed` | Kimi for Coding High Speed | Official catalog |
|
|
91
|
+
| `kimi-coding/k3` | Kimi K3 | Official catalog |
|
|
98
92
|
|
|
99
93
|
Kimi's official catalog at `https://api.kimi.com/coding/v1/models` is authoritative for model availability and context windows. When discovery is unavailable, the extension uses the fallback models and defaults below.
|
|
100
94
|
|
|
@@ -119,7 +113,7 @@ Inside Pi, run:
|
|
|
119
113
|
/kimi-settings
|
|
120
114
|
```
|
|
121
115
|
|
|
122
|
-
That command shows the current server-side model name (e.g. "K2.7 Code High Speed"), your Kimi quota and Extra Usage balance, and lets you edit the home or project config. Changes apply to the active session tool set.
|
|
116
|
+
That command shows your signed-in account (via Kimi's `/me` endpoint), the current server-side model name (e.g. "K2.7 Code High Speed"), your Kimi quota and Extra Usage balance, and lets you edit the home or project config. Changes apply to the active session tool set.
|
|
123
117
|
|
|
124
118
|
Configurable settings include protocol mode, upload threshold, and per-tool enable/collapse.
|
|
125
119
|
|
|
@@ -170,6 +164,11 @@ Supported datasource names:
|
|
|
170
164
|
| `arxiv` | Academic paper search and metadata |
|
|
171
165
|
| `scholar` | Academic literature search |
|
|
172
166
|
| `yuandian_law` | Chinese legal database and case law |
|
|
167
|
+
| `wind` | A-share intraday series, funds, bonds |
|
|
168
|
+
| `imf` | IMF FX, CPI, and GDP forecasts |
|
|
169
|
+
| `gildata` | Natural-language stock/fund screening |
|
|
170
|
+
| `sec_edgar` | US SEC filings (10-K/10-Q, 13F, 8-K) |
|
|
171
|
+
| `sp_data` | S&P fundamentals and transcripts |
|
|
173
172
|
|
|
174
173
|
### Common notes
|
|
175
174
|
|
package/index.ts
CHANGED
|
@@ -77,7 +77,11 @@ import {
|
|
|
77
77
|
parseByteSizeInput,
|
|
78
78
|
} from "./src/settings-ui.ts";
|
|
79
79
|
import { setStoreResolvedKimiConfig, streamSimpleKimi } from "./src/stream.ts";
|
|
80
|
-
import {
|
|
80
|
+
import {
|
|
81
|
+
fetchKimiUsageSnapshot,
|
|
82
|
+
fetchKimiUserInfoSnapshot,
|
|
83
|
+
getKimiUsageToken,
|
|
84
|
+
} from "./src/usage.ts";
|
|
81
85
|
import { buildMoonshotFetchTool, buildMoonshotSearchTool } from "./src/tools/moonshot.ts";
|
|
82
86
|
import { buildKimiDatasourceTool } from "./src/tools/datasource.ts";
|
|
83
87
|
interface KimiRuntimeState {
|
|
@@ -186,8 +190,9 @@ async function openSettingsMenu(
|
|
|
186
190
|
state: KimiRuntimeState,
|
|
187
191
|
): Promise<void> {
|
|
188
192
|
const modelDiscoveryToken = getKimiUsageToken();
|
|
189
|
-
const [usageSnapshot, initialModelsRefreshed] = await Promise.all([
|
|
193
|
+
const [usageSnapshot, userInfoSnapshot, initialModelsRefreshed] = await Promise.all([
|
|
190
194
|
fetchKimiUsageSnapshot(),
|
|
195
|
+
fetchKimiUserInfoSnapshot(),
|
|
191
196
|
refreshModelExtras(state),
|
|
192
197
|
]);
|
|
193
198
|
let modelsRefreshed = initialModelsRefreshed;
|
|
@@ -197,6 +202,7 @@ async function openSettingsMenu(
|
|
|
197
202
|
}
|
|
198
203
|
if (modelsRefreshed) await registerKimiProvider(pi, state);
|
|
199
204
|
const usage = usageSnapshot.summary;
|
|
205
|
+
const account = userInfoSnapshot.summary;
|
|
200
206
|
|
|
201
207
|
const projectTrusted = await isKimiProjectConfigApproved(ctx, ctx.cwd);
|
|
202
208
|
const homeDraft = loadHomeKimiCodeConfig(os.homedir());
|
|
@@ -373,6 +379,9 @@ async function openSettingsMenu(
|
|
|
373
379
|
width,
|
|
374
380
|
),
|
|
375
381
|
"",
|
|
382
|
+
truncateToWidth(theme.fg("accent", theme.bold("Kimi account")), width),
|
|
383
|
+
truncateToWidth(` ${account}`, width),
|
|
384
|
+
"",
|
|
376
385
|
truncateToWidth(theme.fg("accent", theme.bold("Kimi usage")), width),
|
|
377
386
|
...usageLines,
|
|
378
387
|
"",
|
|
@@ -433,10 +442,20 @@ function buildKimiCatalogModels(state: KimiRuntimeState) {
|
|
|
433
442
|
}
|
|
434
443
|
|
|
435
444
|
// Node reports a missing subpath export (pi-ai <=0.79) and a missing package
|
|
436
|
-
// with these codes.
|
|
445
|
+
// with these codes. All mean "this pi-ai has no compat layer", which is an
|
|
437
446
|
// expected configuration rather than a failure worth reporting: the fallback
|
|
438
447
|
// simply does not exist there.
|
|
439
|
-
|
|
448
|
+
//
|
|
449
|
+
// MODULE_NOT_FOUND (the CJS code, without the ERR_ prefix) covers pi's jiti
|
|
450
|
+
// extension loader on old pi versions: it aliases the bare "@earendil-works/pi-ai"
|
|
451
|
+
// specifier straight to dist/index.js, so the "/compat" subpath degrades into
|
|
452
|
+
// the bogus file path "dist/index.js/compat" and fails legacy CJS resolution
|
|
453
|
+
// instead of exports-map resolution.
|
|
454
|
+
const MODULE_UNAVAILABLE_CODES = new Set([
|
|
455
|
+
"ERR_MODULE_NOT_FOUND",
|
|
456
|
+
"ERR_PACKAGE_PATH_NOT_EXPORTED",
|
|
457
|
+
"MODULE_NOT_FOUND",
|
|
458
|
+
]);
|
|
440
459
|
|
|
441
460
|
// Every api id this extension can hand out, not only the one for the currently
|
|
442
461
|
// configured protocol: sessions persist the api id that was current when the
|
|
@@ -590,7 +609,7 @@ async function registerKimiProvider(pi: ExtensionAPI, state: KimiRuntimeState):
|
|
|
590
609
|
|
|
591
610
|
// What pi throws from any `pi` method once the extension runtime behind it has
|
|
592
611
|
// been retired, which is what a /reload does. Same wording on every version
|
|
593
|
-
// this extension supports (0.78.1 through 0.
|
|
612
|
+
// this extension supports (0.78.1 through 0.83.0); if it ever changes, the
|
|
594
613
|
// worst case is that a reload race gets logged instead of ignored.
|
|
595
614
|
const STALE_EXTENSION_RUNTIME_PREFIX = "This extension ctx is stale";
|
|
596
615
|
|
package/package.json
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-provider-kimi-code",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "Kimi Code
|
|
3
|
+
"version": "0.6.10",
|
|
4
|
+
"description": "Kimi Code models (K3, K2.7, HighSpeed) in Pi: OAuth shared login with kimi-code CLI, file uploads, and Kimi-native tools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-provider",
|
|
7
7
|
"coding-agent",
|
|
8
|
+
"highspeed",
|
|
9
|
+
"k3",
|
|
8
10
|
"kimi",
|
|
9
11
|
"kimi-code",
|
|
12
|
+
"kimi-code-plan",
|
|
10
13
|
"kimi-for-coding",
|
|
11
14
|
"kimi-k2",
|
|
12
15
|
"kimi-k2.5",
|
|
13
16
|
"kimi-k2.6",
|
|
14
17
|
"kimi-k2.7",
|
|
18
|
+
"kimi-k3",
|
|
15
19
|
"llm",
|
|
16
20
|
"llm-provider",
|
|
17
21
|
"moonshot",
|
|
@@ -53,9 +57,9 @@
|
|
|
53
57
|
"proper-lockfile": "^4.1.2"
|
|
54
58
|
},
|
|
55
59
|
"devDependencies": {
|
|
56
|
-
"@earendil-works/pi-ai": "^0.
|
|
57
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
58
|
-
"@earendil-works/pi-tui": "^0.
|
|
60
|
+
"@earendil-works/pi-ai": "^0.83.0",
|
|
61
|
+
"@earendil-works/pi-coding-agent": "^0.83.0",
|
|
62
|
+
"@earendil-works/pi-tui": "^0.83.0",
|
|
59
63
|
"@j178/prek": "^0.4.4",
|
|
60
64
|
"@types/node": "^24.0.0",
|
|
61
65
|
"@types/proper-lockfile": "^4.1.4",
|
package/src/constants.ts
CHANGED
|
@@ -41,12 +41,12 @@ export function getDefaultBaseUrl(protocol: KimiWireProtocol): string {
|
|
|
41
41
|
|
|
42
42
|
export const DEFAULT_BASE_URL = getDefaultBaseUrl(ENV_KIMI_CODE_PROTOCOL);
|
|
43
43
|
|
|
44
|
-
export const PROVIDER_VERSION = "0.6.
|
|
44
|
+
export const PROVIDER_VERSION = "0.6.10";
|
|
45
45
|
|
|
46
46
|
// Upstream kimi-code CLI version — used in User-Agent and X-Msh-Version
|
|
47
47
|
// headers to match the official client's identity. Update this when
|
|
48
48
|
// syncing with https://github.com/MoonshotAI/kimi-code.
|
|
49
|
-
export const KIMI_UPSTREAM_VERSION = "0.
|
|
49
|
+
export const KIMI_UPSTREAM_VERSION = "0.34.0";
|
|
50
50
|
export const KIMI_CODE_USER_AGENT = `kimi-code-cli/${KIMI_UPSTREAM_VERSION}`;
|
|
51
51
|
export const KIMI_PLATFORM = "kimi_code_cli";
|
|
52
52
|
|
package/src/tools/datasource.ts
CHANGED
|
@@ -41,6 +41,27 @@ const DATASOURCE_DEFINITIONS = [
|
|
|
41
41
|
name: "yuandian_law",
|
|
42
42
|
description: "Chinese laws, regulations, judicial cases; labor contracts and legal clauses.",
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
name: "wind",
|
|
46
|
+
description:
|
|
47
|
+
"A-share intraday minute series, funds, bonds; map PE/PB/ROE-style field names via wind_search_fields first.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "imf",
|
|
51
|
+
description: "IMF macro data: FX rates, CPI, GDP forecasts, balance of payments.",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "gildata",
|
|
55
|
+
description: "Natural-language stock/fund screening.",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "sec_edgar",
|
|
59
|
+
description: "US SEC filings: 10-K/10-Q, S-1, Form 4, 13F, 8-K.",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "sp_data",
|
|
63
|
+
description: "S&P fundamentals: consensus estimates, valuation ratios, transcripts.",
|
|
64
|
+
},
|
|
44
65
|
] as const;
|
|
45
66
|
|
|
46
67
|
const DATASOURCE_NAMES = DATASOURCE_DEFINITIONS.map((source) => source.name);
|
|
@@ -50,7 +71,7 @@ export const kimiDatasourceSchema = Type.Object({
|
|
|
50
71
|
DATASOURCE_NAMES.map((name) => Type.Literal(name)),
|
|
51
72
|
{
|
|
52
73
|
description:
|
|
53
|
-
"Source: stock_finance_data stocks/reports; yahoo_finance quotes/options; world_bank_open_data GDP/population; tianyancha Chinese company registry/legal rep; arxiv preprints; scholar citations; yuandian_law Chinese laws/cases/contracts.",
|
|
74
|
+
"Source: stock_finance_data stocks/reports; yahoo_finance quotes/options; world_bank_open_data GDP/population; imf FX/CPI/GDP forecasts; tianyancha Chinese company registry/legal rep; arxiv preprints; scholar citations; yuandian_law Chinese laws/cases/contracts; wind A-share intraday minute series/funds/bonds (map field names via wind_search_fields first); gildata natural-language stock/fund screening; sec_edgar US filings (10-K/10-Q, S-1, Form 4, 13F, 8-K); sp_data S&P fundamentals (estimates, valuation, transcripts).",
|
|
54
75
|
},
|
|
55
76
|
),
|
|
56
77
|
api_name: Type.Optional(
|
package/src/usage.ts
CHANGED
|
@@ -217,6 +217,8 @@ function formatWindowLabel(value: unknown, fallbackLabel: string): string {
|
|
|
217
217
|
: unit.includes("MINUTE")
|
|
218
218
|
? duration
|
|
219
219
|
: undefined;
|
|
220
|
+
if (unit.includes("WEEK")) return `Current ${duration}w window`;
|
|
221
|
+
if (unit.includes("DAY")) return `Current ${duration}d window`;
|
|
220
222
|
if (!minutes) return fallbackLabel;
|
|
221
223
|
if (minutes % 60 === 0) return `Current ${minutes / 60}h window`;
|
|
222
224
|
return `Current ${minutes}m window`;
|
|
@@ -320,35 +322,123 @@ function fetchKimiUsage(token: string, signal: AbortSignal): Promise<Response> {
|
|
|
320
322
|
});
|
|
321
323
|
}
|
|
322
324
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
+
interface KimiSnapshotOptions {
|
|
326
|
+
timeoutMs?: number;
|
|
327
|
+
token?: string;
|
|
328
|
+
refreshOnUnauthorized?: boolean;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
interface KimiSnapshotEndpoint {
|
|
332
|
+
prefix: string;
|
|
333
|
+
request: (token: string, signal: AbortSignal) => Promise<Response>;
|
|
334
|
+
summarize: (payload: unknown) => string;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function fetchKimiSnapshot(
|
|
338
|
+
endpoint: KimiSnapshotEndpoint,
|
|
339
|
+
options: KimiSnapshotOptions = {},
|
|
325
340
|
): Promise<KimiUsageSnapshot> {
|
|
326
341
|
const token = options.token ?? getKimiUsageToken();
|
|
327
342
|
if (!token) {
|
|
328
|
-
return { summary:
|
|
343
|
+
return { summary: `${endpoint.prefix}: missing credentials. Run /login kimi-coding.` };
|
|
329
344
|
}
|
|
330
345
|
|
|
331
346
|
const controller = new AbortController();
|
|
332
347
|
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 15_000);
|
|
333
348
|
try {
|
|
334
|
-
let response = await
|
|
349
|
+
let response = await endpoint.request(token, controller.signal);
|
|
335
350
|
if (response.status === 401 && options.refreshOnUnauthorized !== false) {
|
|
336
351
|
const refreshed = await refreshKimiAuthToken(token, { signal: controller.signal });
|
|
337
|
-
if (refreshed) response = await
|
|
352
|
+
if (refreshed) response = await endpoint.request(refreshed, controller.signal);
|
|
338
353
|
}
|
|
339
354
|
if (!response.ok) {
|
|
340
|
-
return { summary:
|
|
355
|
+
return { summary: `${endpoint.prefix}: fetch failed (${response.status})` };
|
|
341
356
|
}
|
|
342
357
|
const payload = (await response.json()) as unknown;
|
|
343
|
-
return { summary:
|
|
358
|
+
return { summary: endpoint.summarize(payload) };
|
|
344
359
|
} catch (error) {
|
|
345
360
|
const message = error instanceof Error ? error.message : String(error);
|
|
346
|
-
return { summary:
|
|
361
|
+
return { summary: `${endpoint.prefix}: fetch failed (${message})` };
|
|
347
362
|
} finally {
|
|
348
363
|
clearTimeout(timeout);
|
|
349
364
|
}
|
|
350
365
|
}
|
|
351
366
|
|
|
367
|
+
export async function fetchKimiUsageSnapshot(
|
|
368
|
+
options: KimiSnapshotOptions = {},
|
|
369
|
+
): Promise<KimiUsageSnapshot> {
|
|
370
|
+
return fetchKimiSnapshot(
|
|
371
|
+
{ prefix: "Usage", request: fetchKimiUsage, summarize: parseUsageSummary },
|
|
372
|
+
options,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
352
376
|
export async function fetchKimiUsageSummary(): Promise<string> {
|
|
353
377
|
return (await fetchKimiUsageSnapshot()).summary;
|
|
354
378
|
}
|
|
379
|
+
|
|
380
|
+
export interface KimiUserInfo {
|
|
381
|
+
userId: string;
|
|
382
|
+
nickname: string;
|
|
383
|
+
userLevel: number;
|
|
384
|
+
userLevelName: string;
|
|
385
|
+
email?: string;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function parseKimiUserInfo(payload: unknown): KimiUserInfo | null {
|
|
389
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) return null;
|
|
390
|
+
const record = payload as Record<string, unknown>;
|
|
391
|
+
const userId = toStringValue(record.user_id);
|
|
392
|
+
if (!userId) return null;
|
|
393
|
+
const email = toStringValue(record.email);
|
|
394
|
+
return {
|
|
395
|
+
userId,
|
|
396
|
+
nickname: toStringValue(record.nickname) ?? "",
|
|
397
|
+
userLevel: toNumber(record.user_level) ?? 0,
|
|
398
|
+
userLevelName: toStringValue(record.user_level_name) ?? "",
|
|
399
|
+
...(email ? { email } : {}),
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export function formatKimiUserInfo(info: KimiUserInfo): string {
|
|
404
|
+
const parts = [info.nickname || info.userId];
|
|
405
|
+
if (info.userLevelName) {
|
|
406
|
+
parts.push(
|
|
407
|
+
info.userLevel > 0 ? `${info.userLevelName} (Lv ${info.userLevel})` : info.userLevelName,
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
if (info.email) parts.push(info.email);
|
|
411
|
+
return parts.join(" · ");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function buildKimiUserInfoUrl(baseUrl = getBaseUrl("openai")): string {
|
|
415
|
+
const normalized = baseUrl.replace(/\/+$/, "");
|
|
416
|
+
return normalized.endsWith("/v1") ? `${normalized}/me` : `${normalized}/v1/me`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function fetchKimiUserInfo(token: string, signal: AbortSignal): Promise<Response> {
|
|
420
|
+
return fetch(buildKimiUserInfoUrl(), {
|
|
421
|
+
method: "GET",
|
|
422
|
+
headers: {
|
|
423
|
+
...getKimiProviderHeaders(),
|
|
424
|
+
Authorization: `Bearer ${token}`,
|
|
425
|
+
},
|
|
426
|
+
signal,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export async function fetchKimiUserInfoSnapshot(
|
|
431
|
+
options: KimiSnapshotOptions = {},
|
|
432
|
+
): Promise<KimiUsageSnapshot> {
|
|
433
|
+
return fetchKimiSnapshot(
|
|
434
|
+
{
|
|
435
|
+
prefix: "Account",
|
|
436
|
+
request: fetchKimiUserInfo,
|
|
437
|
+
summarize: (payload) => {
|
|
438
|
+
const info = parseKimiUserInfo(payload);
|
|
439
|
+
return info ? formatKimiUserInfo(info) : "Account: malformed response";
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
options,
|
|
443
|
+
);
|
|
444
|
+
}
|