@ycodium-ai/agent-kimi 0.2.2690 → 0.2.2696
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 -0
- package/dist/index.js +487 -71
- package/locales/en.json +6 -0
- package/locales/zh-CN.json +6 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -12,6 +12,18 @@ customModels } }`; launch arguments (`acp`) and the account-directory variable (
|
|
|
12
12
|
- Models come from the read-only `kimi provider list --json` (`src/kimiModels.ts`); the version
|
|
13
13
|
from `kimi --version`. No probe session is created.
|
|
14
14
|
- Sign-in is `kimi login` (device-code flow), upgrade is `kimi upgrade`.
|
|
15
|
+
- Usage (ADR-0034), per instance, read only when the user asks. The account directory is the
|
|
16
|
+
`homePath` setting, an explicit `KIMI_CODE_HOME` in the instance environment, then the process
|
|
17
|
+
environment, then `~/.kimi-code`:
|
|
18
|
+
- Quota (`src/kimiQuota.ts`): `GET <base_url>/usages` with the access token from
|
|
19
|
+
`credentials/kimi-code.json`, through `host.fetch`. `base_url` is `KIMI_CODE_BASE_URL`, else the
|
|
20
|
+
one in `config.toml`, else the CLI default `https://api.kimi.com/coding/v1`; a non-official host
|
|
21
|
+
reports `customEndpoint`. The token lives ~15 minutes and only the CLI renews it: an expired token
|
|
22
|
+
reports `missingCredentials`, the refresh token is never used. Windows are labelled from
|
|
23
|
+
`{duration, timeUnit}` (`5h`, `7d`).
|
|
24
|
+
- Tokens (`src/kimiTokens.ts`): `readTokenUsage` reads the `usage.record` lines of every
|
|
25
|
+
`sessions/*/*/agents/*/wire.jsonl` (the ACP reply carries no usage), deduplicating fork copies, with an
|
|
26
|
+
in-memory byte-offset resume.
|
|
15
27
|
- Manifest permission is `{ kind: "executor" }`.
|
|
16
28
|
|
|
17
29
|
Shipped pre-installed as `core.agent-kimi`.
|
package/dist/index.js
CHANGED
|
@@ -89,8 +89,8 @@ var KIMI_CARD = {
|
|
|
89
89
|
};
|
|
90
90
|
|
|
91
91
|
// src/kimiInstance.ts
|
|
92
|
-
import * as
|
|
93
|
-
import * as
|
|
92
|
+
import * as NodeOS4 from "node:os";
|
|
93
|
+
import * as NodePath5 from "node:path";
|
|
94
94
|
import { mergeLaunchEnv, modelSelectionStringOption } from "@ycodium-ai/plugin-api/executor";
|
|
95
95
|
import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
|
|
96
96
|
|
|
@@ -1363,14 +1363,14 @@ function codePointLength(str) {
|
|
|
1363
1363
|
const units = str.length;
|
|
1364
1364
|
if (!highSurrogate.test(str))
|
|
1365
1365
|
return units;
|
|
1366
|
-
let
|
|
1366
|
+
let count2 = units;
|
|
1367
1367
|
for (let i = 0; i < units - 1; i++) {
|
|
1368
1368
|
if ((str.charCodeAt(i) & 64512) === 55296 && (str.charCodeAt(i + 1) & 64512) === 56320) {
|
|
1369
|
-
|
|
1369
|
+
count2--;
|
|
1370
1370
|
i++;
|
|
1371
1371
|
}
|
|
1372
1372
|
}
|
|
1373
|
-
return
|
|
1373
|
+
return count2;
|
|
1374
1374
|
}
|
|
1375
1375
|
function getLengthableOrigin(input2) {
|
|
1376
1376
|
if (Array.isArray(input2))
|
|
@@ -5175,7 +5175,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
5175
5175
|
var NONE = 0;
|
|
5176
5176
|
var ASSUMED = 1;
|
|
5177
5177
|
var PROVEN = 2;
|
|
5178
|
-
function isRecursive(inst, stack,
|
|
5178
|
+
function isRecursive(inst, stack, resolve3) {
|
|
5179
5179
|
const cached2 = recursive.get(inst);
|
|
5180
5180
|
if (cached2 !== void 0)
|
|
5181
5181
|
return cached2 ? PROVEN : NONE;
|
|
@@ -5185,7 +5185,7 @@ function isRecursive(inst, stack, resolve2) {
|
|
|
5185
5185
|
let result = NONE;
|
|
5186
5186
|
const check2 = (child) => {
|
|
5187
5187
|
if (result !== PROVEN && child?._zod) {
|
|
5188
|
-
const answer = isRecursive(child, stack,
|
|
5188
|
+
const answer = isRecursive(child, stack, resolve3);
|
|
5189
5189
|
if (answer > result)
|
|
5190
5190
|
result = answer;
|
|
5191
5191
|
}
|
|
@@ -5196,7 +5196,7 @@ function isRecursive(inst, stack, resolve2) {
|
|
|
5196
5196
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
5197
5197
|
if (spread && !desc.enumerable)
|
|
5198
5198
|
continue;
|
|
5199
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
5199
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve3) : NONE;
|
|
5200
5200
|
if (child > answer)
|
|
5201
5201
|
answer = child;
|
|
5202
5202
|
}
|
|
@@ -5260,7 +5260,7 @@ function isRecursive(inst, stack, resolve2) {
|
|
|
5260
5260
|
break;
|
|
5261
5261
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
5262
5262
|
case "lazy": {
|
|
5263
|
-
const inner = def._cachedInner ?? (
|
|
5263
|
+
const inner = def._cachedInner ?? (resolve3 ? inst._zod.innerType : void 0);
|
|
5264
5264
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
5265
5265
|
break;
|
|
5266
5266
|
}
|
|
@@ -5725,8 +5725,8 @@ function az_default() {
|
|
|
5725
5725
|
}
|
|
5726
5726
|
|
|
5727
5727
|
// ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/be.js
|
|
5728
|
-
function getBelarusianPlural(
|
|
5729
|
-
const absCount = Math.abs(
|
|
5728
|
+
function getBelarusianPlural(count2, one, few, many) {
|
|
5729
|
+
const absCount = Math.abs(count2);
|
|
5730
5730
|
const lastDigit = absCount % 10;
|
|
5731
5731
|
const lastTwoDigits = absCount % 100;
|
|
5732
5732
|
if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
|
|
@@ -8373,8 +8373,8 @@ function hu_default() {
|
|
|
8373
8373
|
}
|
|
8374
8374
|
|
|
8375
8375
|
// ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/hy.js
|
|
8376
|
-
function getArmenianPlural(
|
|
8377
|
-
return Math.abs(
|
|
8376
|
+
function getArmenianPlural(count2, one, many) {
|
|
8377
|
+
return Math.abs(count2) === 1 ? one : many;
|
|
8378
8378
|
}
|
|
8379
8379
|
function withDefiniteArticle(word) {
|
|
8380
8380
|
if (!word)
|
|
@@ -11112,8 +11112,8 @@ function ro_default() {
|
|
|
11112
11112
|
}
|
|
11113
11113
|
|
|
11114
11114
|
// ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/locales/ru.js
|
|
11115
|
-
function getRussianPlural(
|
|
11116
|
-
const absCount = Math.abs(
|
|
11115
|
+
function getRussianPlural(count2, one, few, many) {
|
|
11116
|
+
const absCount = Math.abs(count2);
|
|
11117
11117
|
const lastDigit = absCount % 10;
|
|
11118
11118
|
const lastTwoDigits = absCount % 100;
|
|
11119
11119
|
if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
|
|
@@ -21924,11 +21924,11 @@ var Connection = class {
|
|
|
21924
21924
|
const id = this.nextRequestId++;
|
|
21925
21925
|
let cancel = () => {
|
|
21926
21926
|
};
|
|
21927
|
-
const response = new Promise((
|
|
21927
|
+
const response = new Promise((resolve3, reject) => {
|
|
21928
21928
|
const pendingResponse = {
|
|
21929
21929
|
resolve: (value) => {
|
|
21930
21930
|
try {
|
|
21931
|
-
|
|
21931
|
+
resolve3(mapResponse ? mapResponse(value) : value);
|
|
21932
21932
|
} catch (error62) {
|
|
21933
21933
|
reject(error62);
|
|
21934
21934
|
}
|
|
@@ -21985,8 +21985,8 @@ var Connection = class {
|
|
|
21985
21985
|
this.stream = stream;
|
|
21986
21986
|
this.staticHandlers = handlers;
|
|
21987
21987
|
this.allowBatches = options?.allowBatches ?? true;
|
|
21988
|
-
this.closedPromise = new Promise((
|
|
21989
|
-
this.abortController.signal.addEventListener("abort", () =>
|
|
21988
|
+
this.closedPromise = new Promise((resolve3) => {
|
|
21989
|
+
this.abortController.signal.addEventListener("abort", () => resolve3());
|
|
21990
21990
|
});
|
|
21991
21991
|
void this.receive();
|
|
21992
21992
|
}
|
|
@@ -22054,9 +22054,9 @@ var Connection = class {
|
|
|
22054
22054
|
return;
|
|
22055
22055
|
}
|
|
22056
22056
|
const responseBatch = isResponseBatch(batch);
|
|
22057
|
-
const responseCount = responseBatch ? 0 : batch.reduce((
|
|
22057
|
+
const responseCount = responseBatch ? 0 : batch.reduce((count2, message) => count2 + (isNotificationMessage(message) ? 0 : 1), 0);
|
|
22058
22058
|
let remaining = responseCount;
|
|
22059
|
-
let remainingNotifications = batch.reduce((
|
|
22059
|
+
let remainingNotifications = batch.reduce((count2, message) => count2 + (isNotificationMessage(message) ? 1 : 0), 0);
|
|
22060
22060
|
let responseSent = false;
|
|
22061
22061
|
const responses = [];
|
|
22062
22062
|
const sendResponsesIfReady = async () => {
|
|
@@ -22880,8 +22880,8 @@ var AsyncQueue = class {
|
|
|
22880
22880
|
if (this.failed) {
|
|
22881
22881
|
return Promise.reject(this.failure);
|
|
22882
22882
|
}
|
|
22883
|
-
return new Promise((
|
|
22884
|
-
this.waiters.push({ resolve:
|
|
22883
|
+
return new Promise((resolve3, reject) => {
|
|
22884
|
+
this.waiters.push({ resolve: resolve3, reject });
|
|
22885
22885
|
});
|
|
22886
22886
|
}
|
|
22887
22887
|
};
|
|
@@ -23868,8 +23868,8 @@ function createAcpConnection(options) {
|
|
|
23868
23868
|
}
|
|
23869
23869
|
await dispatchMessage(value);
|
|
23870
23870
|
};
|
|
23871
|
-
const pullEnqueued = async (
|
|
23872
|
-
for (let i = 0; i <
|
|
23871
|
+
const pullEnqueued = async (count2) => {
|
|
23872
|
+
for (let i = 0; i < count2; i += 1) {
|
|
23873
23873
|
const { value, done } = await incoming.read();
|
|
23874
23874
|
if (done || value === void 0) {
|
|
23875
23875
|
return;
|
|
@@ -23970,8 +23970,8 @@ function createAcpConnection(options) {
|
|
|
23970
23970
|
failIfClosed();
|
|
23971
23971
|
const id = nextRequestId;
|
|
23972
23972
|
nextRequestId += 1;
|
|
23973
|
-
const { promise: promise2, resolve:
|
|
23974
|
-
pending.set(String(id), { method, resolve:
|
|
23973
|
+
const { promise: promise2, resolve: resolve3, reject } = Promise.withResolvers();
|
|
23974
|
+
pending.set(String(id), { method, resolve: resolve3, reject });
|
|
23975
23975
|
const message = params === void 0 ? { jsonrpc: "2.0", id, method } : { jsonrpc: "2.0", id, method, params };
|
|
23976
23976
|
try {
|
|
23977
23977
|
await Promise.race([writeMessage(message), promise2]);
|
|
@@ -23992,8 +23992,8 @@ function createAcpConnection(options) {
|
|
|
23992
23992
|
if (inboundBatches === 0) {
|
|
23993
23993
|
return;
|
|
23994
23994
|
}
|
|
23995
|
-
await new Promise((
|
|
23996
|
-
idleWaiters.push(
|
|
23995
|
+
await new Promise((resolve3) => {
|
|
23996
|
+
idleWaiters.push(resolve3);
|
|
23997
23997
|
if (inboundBatches === 0) {
|
|
23998
23998
|
releaseIdleWaiters();
|
|
23999
23999
|
}
|
|
@@ -24172,8 +24172,8 @@ var UnboundedEventStream = class {
|
|
|
24172
24172
|
iterators.subscribers.delete(subscriber);
|
|
24173
24173
|
return { value: void 0, done: true };
|
|
24174
24174
|
}
|
|
24175
|
-
await new Promise((
|
|
24176
|
-
subscriber.wake =
|
|
24175
|
+
await new Promise((resolve3) => {
|
|
24176
|
+
subscriber.wake = resolve3;
|
|
24177
24177
|
});
|
|
24178
24178
|
}
|
|
24179
24179
|
},
|
|
@@ -24563,8 +24563,8 @@ var Mutex = class {
|
|
|
24563
24563
|
this.locked = true;
|
|
24564
24564
|
return Promise.resolve();
|
|
24565
24565
|
}
|
|
24566
|
-
return new Promise((
|
|
24567
|
-
this.waiters.push(
|
|
24566
|
+
return new Promise((resolve3) => {
|
|
24567
|
+
this.waiters.push(resolve3);
|
|
24568
24568
|
});
|
|
24569
24569
|
}
|
|
24570
24570
|
tryAcquire() {
|
|
@@ -24889,6 +24889,13 @@ function parseSessionModeState(sessionResponse) {
|
|
|
24889
24889
|
availableModes
|
|
24890
24890
|
};
|
|
24891
24891
|
}
|
|
24892
|
+
function acpCommandsToSlashCommands(commands) {
|
|
24893
|
+
return commands.map((command) => ({
|
|
24894
|
+
name: command.name,
|
|
24895
|
+
...command.description === void 0 ? {} : { description: command.description },
|
|
24896
|
+
...command.inputHint === void 0 ? {} : { input: { hint: command.inputHint } }
|
|
24897
|
+
}));
|
|
24898
|
+
}
|
|
24892
24899
|
function parseAvailableCommandsUpdate(params) {
|
|
24893
24900
|
const update = params.update;
|
|
24894
24901
|
if (update["sessionUpdate"] !== "available_commands_update") {
|
|
@@ -25340,24 +25347,7 @@ function filterMcpServersByAgentCapabilities(mcpServers, agentCapabilities) {
|
|
|
25340
25347
|
});
|
|
25341
25348
|
}
|
|
25342
25349
|
|
|
25343
|
-
// ../../packages/ycodium-acp-executor/src/
|
|
25344
|
-
var defaultVendorHooks = {
|
|
25345
|
-
completedStopReason: (response) => response.stopReason
|
|
25346
|
-
};
|
|
25347
|
-
|
|
25348
|
-
// ../../packages/ycodium-acp-executor/src/executor.ts
|
|
25349
|
-
var DEFAULT_CANCEL_GRACE_MS = 5e3;
|
|
25350
|
-
var userInputAnswerOptionId = (question, answers) => {
|
|
25351
|
-
const answer = answers[question.id];
|
|
25352
|
-
const selected = typeof answer === "string" ? answer : Array.isArray(answer) && answer.length === 1 && typeof answer[0] === "string" ? answer[0] : void 0;
|
|
25353
|
-
return selected !== void 0 && question.options.some((option) => option.id === selected) ? selected : void 0;
|
|
25354
|
-
};
|
|
25355
|
-
function defaultNowIso() {
|
|
25356
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
25357
|
-
}
|
|
25358
|
-
function defaultRandomId() {
|
|
25359
|
-
return NodeCrypto.randomUUID();
|
|
25360
|
-
}
|
|
25350
|
+
// ../../packages/ycodium-acp-executor/src/model-selection.ts
|
|
25361
25351
|
function normalizeModelId(value) {
|
|
25362
25352
|
return value?.trim() || void 0;
|
|
25363
25353
|
}
|
|
@@ -25376,6 +25366,25 @@ function modelSelectionStateFromSetup(setup) {
|
|
|
25376
25366
|
advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels)
|
|
25377
25367
|
};
|
|
25378
25368
|
}
|
|
25369
|
+
|
|
25370
|
+
// ../../packages/ycodium-acp-executor/src/vendor-hooks.ts
|
|
25371
|
+
var defaultVendorHooks = {
|
|
25372
|
+
completedStopReason: (response) => response.stopReason
|
|
25373
|
+
};
|
|
25374
|
+
|
|
25375
|
+
// ../../packages/ycodium-acp-executor/src/executor.ts
|
|
25376
|
+
var DEFAULT_CANCEL_GRACE_MS = 5e3;
|
|
25377
|
+
var userInputAnswerOptionId = (question, answers) => {
|
|
25378
|
+
const answer = answers[question.id];
|
|
25379
|
+
const selected = typeof answer === "string" ? answer : Array.isArray(answer) && answer.length === 1 && typeof answer[0] === "string" ? answer[0] : void 0;
|
|
25380
|
+
return selected !== void 0 && question.options.some((option) => option.id === selected) ? selected : void 0;
|
|
25381
|
+
};
|
|
25382
|
+
function defaultNowIso() {
|
|
25383
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
25384
|
+
}
|
|
25385
|
+
function defaultRandomId() {
|
|
25386
|
+
return NodeCrypto.randomUUID();
|
|
25387
|
+
}
|
|
25379
25388
|
function createAcpExecutor(options) {
|
|
25380
25389
|
const hooks = { ...defaultVendorHooks, ...options.hooks };
|
|
25381
25390
|
const cancelGraceMs = options.cancelGraceMs ?? DEFAULT_CANCEL_GRACE_MS;
|
|
@@ -25585,6 +25594,7 @@ function createAcpExecutor(options) {
|
|
|
25585
25594
|
const commands = parseAvailableCommandsUpdate(params);
|
|
25586
25595
|
if (commands !== void 0) {
|
|
25587
25596
|
availableCommands = commands;
|
|
25597
|
+
options.onAvailableCommands?.(commands);
|
|
25588
25598
|
}
|
|
25589
25599
|
if (sessionLoadInFlight) {
|
|
25590
25600
|
droppedReplayUpdates += parsed.events.filter((event) => event._tag !== "ModeChanged").length;
|
|
@@ -25863,14 +25873,14 @@ function createAcpExecutor(options) {
|
|
|
25863
25873
|
}
|
|
25864
25874
|
}
|
|
25865
25875
|
const requestId = randomId();
|
|
25866
|
-
const outcome = await new Promise((
|
|
25876
|
+
const outcome = await new Promise((resolve3) => {
|
|
25867
25877
|
const pending = {
|
|
25868
25878
|
requestId,
|
|
25869
25879
|
request: params,
|
|
25870
25880
|
permissionRequest,
|
|
25871
25881
|
turnId: activeTurnId,
|
|
25872
25882
|
settled: false,
|
|
25873
|
-
resolve:
|
|
25883
|
+
resolve: resolve3
|
|
25874
25884
|
};
|
|
25875
25885
|
approvals.add(pending);
|
|
25876
25886
|
emit(
|
|
@@ -26178,14 +26188,14 @@ function createAcpExecutor(options) {
|
|
|
26178
26188
|
if (!promptMutex.isLocked) {
|
|
26179
26189
|
return true;
|
|
26180
26190
|
}
|
|
26181
|
-
return await new Promise((
|
|
26191
|
+
return await new Promise((resolve3) => {
|
|
26182
26192
|
const timer = setTimeout(() => {
|
|
26183
26193
|
promptWaiters = promptWaiters.filter((waiter) => waiter !== onSettle);
|
|
26184
|
-
|
|
26194
|
+
resolve3(false);
|
|
26185
26195
|
}, timeoutMs);
|
|
26186
26196
|
const onSettle = () => {
|
|
26187
26197
|
clearTimeout(timer);
|
|
26188
|
-
|
|
26198
|
+
resolve3(true);
|
|
26189
26199
|
};
|
|
26190
26200
|
promptWaiters.push(onSettle);
|
|
26191
26201
|
});
|
|
@@ -26651,7 +26661,7 @@ function toAcpMcpServers(entries) {
|
|
|
26651
26661
|
|
|
26652
26662
|
// ../../packages/ycodium-acp-executor/src/probe.ts
|
|
26653
26663
|
function withProbeTimeout(promise2, timeoutMs, what) {
|
|
26654
|
-
return new Promise((
|
|
26664
|
+
return new Promise((resolve3, reject) => {
|
|
26655
26665
|
const timer = setTimeout(() => {
|
|
26656
26666
|
reject(new Error(`${what} timed out after ${timeoutMs}ms`));
|
|
26657
26667
|
}, timeoutMs);
|
|
@@ -26659,7 +26669,7 @@ function withProbeTimeout(promise2, timeoutMs, what) {
|
|
|
26659
26669
|
promise2.then(
|
|
26660
26670
|
(value) => {
|
|
26661
26671
|
clearTimeout(timer);
|
|
26662
|
-
|
|
26672
|
+
resolve3(value);
|
|
26663
26673
|
},
|
|
26664
26674
|
(error62) => {
|
|
26665
26675
|
clearTimeout(timer);
|
|
@@ -26944,6 +26954,14 @@ function readKimiContextWindow(input2) {
|
|
|
26944
26954
|
return contents === void 0 ? void 0 : parseKimiContextWindow(contents);
|
|
26945
26955
|
}
|
|
26946
26956
|
|
|
26957
|
+
// src/kimiQuota.ts
|
|
26958
|
+
import * as NodeFS3 from "node:fs";
|
|
26959
|
+
import * as NodeOS2 from "node:os";
|
|
26960
|
+
import * as NodePath3 from "node:path";
|
|
26961
|
+
import {
|
|
26962
|
+
parseRetryAfterMs
|
|
26963
|
+
} from "@ycodium-ai/plugin-api/usage";
|
|
26964
|
+
|
|
26947
26965
|
// src/kimiAccount.ts
|
|
26948
26966
|
import * as NodeFS2 from "node:fs";
|
|
26949
26967
|
import * as NodeOS from "node:os";
|
|
@@ -26971,6 +26989,389 @@ function readKimiAuthState(resolvedHomePath) {
|
|
|
26971
26989
|
return nonEmptyString(record2["refresh_token"]) || nonEmptyString(record2["access_token"]) ? "signed-in" : "signed-out";
|
|
26972
26990
|
}
|
|
26973
26991
|
|
|
26992
|
+
// src/kimiQuota.ts
|
|
26993
|
+
var KIMI_OFFICIAL_HOSTS = /* @__PURE__ */ new Set(["api.kimi.com", "api.kimi.ai"]);
|
|
26994
|
+
var KIMI_DEFAULT_BASE_URL = "https://api.kimi.com/coding/v1";
|
|
26995
|
+
var KIMI_USAGES_PATH = "/usages";
|
|
26996
|
+
var QUOTA_TIMEOUT_MS = 1e4;
|
|
26997
|
+
var TOKEN_EXPIRY_SKEW_MS = 3e4;
|
|
26998
|
+
var asObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
26999
|
+
var asString3 = (value) => typeof value === "string" && value.length > 0 ? value : null;
|
|
27000
|
+
var asNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
27001
|
+
var asCount = (value) => {
|
|
27002
|
+
const direct = asNumber(value);
|
|
27003
|
+
if (direct !== null) return direct;
|
|
27004
|
+
const text = asString3(value);
|
|
27005
|
+
if (text === null) return null;
|
|
27006
|
+
const parsed = Number(text);
|
|
27007
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
27008
|
+
};
|
|
27009
|
+
var parseJson = (raw) => {
|
|
27010
|
+
try {
|
|
27011
|
+
return JSON.parse(raw);
|
|
27012
|
+
} catch {
|
|
27013
|
+
return null;
|
|
27014
|
+
}
|
|
27015
|
+
};
|
|
27016
|
+
var clampPercent = (value) => Math.min(100, Math.max(0, value));
|
|
27017
|
+
var expiryMs = (value) => {
|
|
27018
|
+
const raw = asNumber(value);
|
|
27019
|
+
if (raw === null || raw <= 0) return null;
|
|
27020
|
+
return raw > 1e12 ? raw : raw * 1e3;
|
|
27021
|
+
};
|
|
27022
|
+
var extractKimiToken = (auth, nowMs) => {
|
|
27023
|
+
const record2 = asObject(auth);
|
|
27024
|
+
if (record2 === null) return null;
|
|
27025
|
+
const token = asString3(record2["access_token"]);
|
|
27026
|
+
if (token === null) return null;
|
|
27027
|
+
const expiresAtMs = expiryMs(record2["expires_at"]);
|
|
27028
|
+
if (expiresAtMs === null) return token;
|
|
27029
|
+
return expiresAtMs - nowMs < TOKEN_EXPIRY_SKEW_MS ? null : token;
|
|
27030
|
+
};
|
|
27031
|
+
var isOfficialUrl = (value) => {
|
|
27032
|
+
const parsed = URL.parse(value.trim());
|
|
27033
|
+
return parsed === null ? false : KIMI_OFFICIAL_HOSTS.has(parsed.hostname.toLowerCase());
|
|
27034
|
+
};
|
|
27035
|
+
var readTomlScalar = (raw) => {
|
|
27036
|
+
const quoted = /^(["'])(.*?)\1/.exec(raw.trim());
|
|
27037
|
+
if (quoted?.[2] !== void 0) return quoted[2];
|
|
27038
|
+
const [beforeComment = ""] = raw.split("#");
|
|
27039
|
+
return beforeComment.trim();
|
|
27040
|
+
};
|
|
27041
|
+
var TABLE_HEADER = /^\s*\[\s*([^\]]+?)\s*\]\s*(?:#.*)?$/;
|
|
27042
|
+
var BASE_URL_LINE = /^\s*base_url\s*=\s*(\S.*)$/;
|
|
27043
|
+
var kimiConfigBaseUrls = (contents) => {
|
|
27044
|
+
const found = [];
|
|
27045
|
+
let table = "";
|
|
27046
|
+
for (const line of contents.split("\n")) {
|
|
27047
|
+
const header = TABLE_HEADER.exec(line);
|
|
27048
|
+
if (header !== null) {
|
|
27049
|
+
table = header[1] ?? "";
|
|
27050
|
+
continue;
|
|
27051
|
+
}
|
|
27052
|
+
const match = BASE_URL_LINE.exec(line);
|
|
27053
|
+
if (match === null) continue;
|
|
27054
|
+
const url2 = readTomlScalar(match[1] ?? "");
|
|
27055
|
+
if (url2.length > 0) found.push({ url: url2, isTool: table.startsWith("tools.") });
|
|
27056
|
+
}
|
|
27057
|
+
return found;
|
|
27058
|
+
};
|
|
27059
|
+
var usagesUrlFrom = (baseUrl) => baseUrl.trim().replace(/\/+$/, "") + KIMI_USAGES_PATH;
|
|
27060
|
+
var kimiUsagesEndpoint = (input2) => {
|
|
27061
|
+
const fromEnv = input2.envBaseUrl?.trim() ?? "";
|
|
27062
|
+
if (fromEnv.length > 0) {
|
|
27063
|
+
return isOfficialUrl(fromEnv) ? { usagesUrl: usagesUrlFrom(fromEnv) } : { custom: true };
|
|
27064
|
+
}
|
|
27065
|
+
const configured = input2.configToml === null ? [] : kimiConfigBaseUrls(input2.configToml);
|
|
27066
|
+
if (configured.some((entry) => !isOfficialUrl(entry.url))) return { custom: true };
|
|
27067
|
+
const root = configured.find((entry) => !entry.isTool)?.url ?? KIMI_DEFAULT_BASE_URL;
|
|
27068
|
+
return { usagesUrl: usagesUrlFrom(root) };
|
|
27069
|
+
};
|
|
27070
|
+
var TIME_UNIT_MINUTES = {
|
|
27071
|
+
TIME_UNIT_MINUTE: 1,
|
|
27072
|
+
TIME_UNIT_HOUR: 60,
|
|
27073
|
+
TIME_UNIT_DAY: 1440,
|
|
27074
|
+
TIME_UNIT_WEEK: 10080
|
|
27075
|
+
};
|
|
27076
|
+
var kimiWindowSpan = (window) => {
|
|
27077
|
+
const record2 = asObject(window);
|
|
27078
|
+
if (record2 === null) return null;
|
|
27079
|
+
const duration3 = asCount(record2["duration"]);
|
|
27080
|
+
const unit = asString3(record2["timeUnit"]);
|
|
27081
|
+
const minutesPerUnit = unit === null ? void 0 : TIME_UNIT_MINUTES[unit];
|
|
27082
|
+
if (duration3 === null || duration3 <= 0 || minutesPerUnit === void 0) return null;
|
|
27083
|
+
const minutes = duration3 * minutesPerUnit;
|
|
27084
|
+
const label = minutes % 1440 === 0 ? `${minutes / 1440}d` : minutes % 60 === 0 ? `${minutes / 60}h` : `${minutes}m`;
|
|
27085
|
+
return { label, windowMs: minutes * 6e4 };
|
|
27086
|
+
};
|
|
27087
|
+
var windowFrom = (id, label, detail, windowMs) => {
|
|
27088
|
+
const record2 = asObject(detail);
|
|
27089
|
+
if (record2 === null) return null;
|
|
27090
|
+
const limit = asCount(record2["limit"]);
|
|
27091
|
+
if (limit === null || limit <= 0) return null;
|
|
27092
|
+
const remaining = asCount(record2["remaining"]);
|
|
27093
|
+
const used = asCount(record2["used"]) ?? (remaining === null ? null : limit - remaining);
|
|
27094
|
+
if (used === null) return null;
|
|
27095
|
+
return {
|
|
27096
|
+
id,
|
|
27097
|
+
label,
|
|
27098
|
+
usedPercent: clampPercent(used / limit * 100),
|
|
27099
|
+
resetsAt: asString3(record2["resetTime"]),
|
|
27100
|
+
...windowMs === void 0 ? {} : { windowMs }
|
|
27101
|
+
};
|
|
27102
|
+
};
|
|
27103
|
+
var kimiPlanName = (body) => {
|
|
27104
|
+
const level = asString3(asObject(asObject(asObject(body)?.["user"])?.["membership"])?.["level"]);
|
|
27105
|
+
if (level === null) return void 0;
|
|
27106
|
+
const bare = level.replace(/^LEVEL_/, "").toLowerCase();
|
|
27107
|
+
return bare.length === 0 ? void 0 : bare.charAt(0).toUpperCase() + bare.slice(1);
|
|
27108
|
+
};
|
|
27109
|
+
var kimiQuotaWindows = (body) => {
|
|
27110
|
+
const root = asObject(body);
|
|
27111
|
+
const windows = [];
|
|
27112
|
+
const subscription = windowFrom(
|
|
27113
|
+
"subscription",
|
|
27114
|
+
{ key: "usage.window.subscription" },
|
|
27115
|
+
root?.["usage"],
|
|
27116
|
+
void 0
|
|
27117
|
+
);
|
|
27118
|
+
if (subscription !== null) windows.push(subscription);
|
|
27119
|
+
const limits = root?.["limits"];
|
|
27120
|
+
if (Array.isArray(limits)) {
|
|
27121
|
+
for (const [index, entry] of limits.entries()) {
|
|
27122
|
+
const row = asObject(entry);
|
|
27123
|
+
if (row === null) continue;
|
|
27124
|
+
const span = kimiWindowSpan(row["window"]);
|
|
27125
|
+
const name = asString3(row["name"]);
|
|
27126
|
+
const label = span !== null ? { key: "usage.window.duration", values: { duration: span.label } } : name !== null ? { text: name } : null;
|
|
27127
|
+
if (label === null) continue;
|
|
27128
|
+
const window = windowFrom(`limit-${index}`, label, row["detail"], span?.windowMs);
|
|
27129
|
+
if (window !== null) windows.push(window);
|
|
27130
|
+
}
|
|
27131
|
+
}
|
|
27132
|
+
return windows;
|
|
27133
|
+
};
|
|
27134
|
+
var kimiEffectiveHome = (resolvedHomePath, processEnvHome) => {
|
|
27135
|
+
if (resolvedHomePath.trim().length > 0) return resolvedHomePath;
|
|
27136
|
+
const fromEnv = processEnvHome?.trim() ?? "";
|
|
27137
|
+
return fromEnv.length > 0 ? fromEnv : "";
|
|
27138
|
+
};
|
|
27139
|
+
var createKimiUsage = (input2) => {
|
|
27140
|
+
const now = input2.now ?? Date.now;
|
|
27141
|
+
const processEnv = input2.processEnv ?? process.env;
|
|
27142
|
+
const home = input2.homePath.trim().length > 0 ? input2.homePath : NodePath3.join(NodeOS2.homedir(), ".kimi-code");
|
|
27143
|
+
const credentialsPath = kimiCredentialsPath(input2.homePath);
|
|
27144
|
+
const readQuota = async (request) => {
|
|
27145
|
+
let configToml = null;
|
|
27146
|
+
try {
|
|
27147
|
+
configToml = NodeFS3.readFileSync(NodePath3.join(home, "config.toml"), "utf8");
|
|
27148
|
+
} catch (error62) {
|
|
27149
|
+
if (error62.code !== "ENOENT") {
|
|
27150
|
+
return { status: "unavailable", reason: "unreachable" };
|
|
27151
|
+
}
|
|
27152
|
+
}
|
|
27153
|
+
const endpoint = kimiUsagesEndpoint({
|
|
27154
|
+
envBaseUrl: input2.environment["KIMI_CODE_BASE_URL"] ?? processEnv["KIMI_CODE_BASE_URL"],
|
|
27155
|
+
configToml
|
|
27156
|
+
});
|
|
27157
|
+
if ("custom" in endpoint) return { status: "unavailable", reason: "customEndpoint" };
|
|
27158
|
+
let token = null;
|
|
27159
|
+
try {
|
|
27160
|
+
token = extractKimiToken(parseJson(NodeFS3.readFileSync(credentialsPath, "utf8")), now());
|
|
27161
|
+
} catch {
|
|
27162
|
+
token = null;
|
|
27163
|
+
}
|
|
27164
|
+
if (token === null) return { status: "unavailable", reason: "missingCredentials" };
|
|
27165
|
+
let response;
|
|
27166
|
+
try {
|
|
27167
|
+
response = await input2.fetch(endpoint.usagesUrl, {
|
|
27168
|
+
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
|
|
27169
|
+
signal: AbortSignal.any([
|
|
27170
|
+
...request.signal === void 0 ? [] : [request.signal],
|
|
27171
|
+
AbortSignal.timeout(QUOTA_TIMEOUT_MS)
|
|
27172
|
+
])
|
|
27173
|
+
});
|
|
27174
|
+
} catch {
|
|
27175
|
+
return { status: "unavailable", reason: "unreachable" };
|
|
27176
|
+
}
|
|
27177
|
+
if (response.status === 401 || response.status === 403) {
|
|
27178
|
+
return { status: "unavailable", reason: "authRejected" };
|
|
27179
|
+
}
|
|
27180
|
+
if (response.status === 429) {
|
|
27181
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"), now());
|
|
27182
|
+
return {
|
|
27183
|
+
status: "unavailable",
|
|
27184
|
+
reason: "rateLimited",
|
|
27185
|
+
...retryAfterMs === null ? {} : { retryAfterMs }
|
|
27186
|
+
};
|
|
27187
|
+
}
|
|
27188
|
+
if (!response.ok) return { status: "unavailable", reason: "unreachable" };
|
|
27189
|
+
let body;
|
|
27190
|
+
try {
|
|
27191
|
+
body = parseJson(await response.text());
|
|
27192
|
+
} catch {
|
|
27193
|
+
return { status: "unavailable", reason: "unreachable" };
|
|
27194
|
+
}
|
|
27195
|
+
if (asObject(body) === null) return { status: "unavailable", reason: "unreachable" };
|
|
27196
|
+
const plan = kimiPlanName(body);
|
|
27197
|
+
return {
|
|
27198
|
+
status: "available",
|
|
27199
|
+
...plan === void 0 ? {} : { plan },
|
|
27200
|
+
windows: kimiQuotaWindows(body),
|
|
27201
|
+
balances: [],
|
|
27202
|
+
accountKey: credentialsPath
|
|
27203
|
+
};
|
|
27204
|
+
};
|
|
27205
|
+
return { readQuota };
|
|
27206
|
+
};
|
|
27207
|
+
|
|
27208
|
+
// src/kimiTokens.ts
|
|
27209
|
+
import * as NodeFS4 from "node:fs";
|
|
27210
|
+
import * as NodeOS3 from "node:os";
|
|
27211
|
+
import * as NodePath4 from "node:path";
|
|
27212
|
+
var MTIME_SLACK_MS = 36 * 60 * 60 * 1e3;
|
|
27213
|
+
var GUARD_LENGTH = 64;
|
|
27214
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27215
|
+
var count = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
|
|
27216
|
+
var parseKimiUsageLine = (line) => {
|
|
27217
|
+
if (!line.includes('"usage.record"')) return void 0;
|
|
27218
|
+
let parsed;
|
|
27219
|
+
try {
|
|
27220
|
+
parsed = JSON.parse(line);
|
|
27221
|
+
} catch {
|
|
27222
|
+
return "malformed";
|
|
27223
|
+
}
|
|
27224
|
+
if (!isRecord8(parsed) || parsed["type"] !== "usage.record") return void 0;
|
|
27225
|
+
const usage = parsed["usage"];
|
|
27226
|
+
const model = typeof parsed["model"] === "string" ? parsed["model"].trim() : "";
|
|
27227
|
+
const timeMs = parsed["time"];
|
|
27228
|
+
if (!isRecord8(usage) || model.length === 0 || typeof timeMs !== "number" || !Number.isFinite(timeMs)) {
|
|
27229
|
+
return "malformed";
|
|
27230
|
+
}
|
|
27231
|
+
const totals = {
|
|
27232
|
+
uncachedInputTokens: count(usage["inputOther"]),
|
|
27233
|
+
cachedInputTokens: count(usage["inputCacheRead"]),
|
|
27234
|
+
cacheCreationTokens: count(usage["inputCacheCreation"]),
|
|
27235
|
+
outputTokens: count(usage["output"]),
|
|
27236
|
+
reasoningTokens: 0
|
|
27237
|
+
};
|
|
27238
|
+
return {
|
|
27239
|
+
timeMs,
|
|
27240
|
+
model,
|
|
27241
|
+
totals,
|
|
27242
|
+
key: `${timeMs}\0${model}\0${totals.uncachedInputTokens}\0${totals.cachedInputTokens}\0${totals.cacheCreationTokens}\0${totals.outputTokens}`
|
|
27243
|
+
};
|
|
27244
|
+
};
|
|
27245
|
+
var hasTokens = (totals) => totals.uncachedInputTokens > 0 || totals.cachedInputTokens > 0 || totals.cacheCreationTokens > 0 || totals.outputTokens > 0;
|
|
27246
|
+
var readDirs = (dir) => {
|
|
27247
|
+
try {
|
|
27248
|
+
return NodeFS4.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
27249
|
+
} catch {
|
|
27250
|
+
return [];
|
|
27251
|
+
}
|
|
27252
|
+
};
|
|
27253
|
+
var listWireLogs = (sessionsRoot) => {
|
|
27254
|
+
const found = [];
|
|
27255
|
+
for (const workspace of readDirs(sessionsRoot)) {
|
|
27256
|
+
for (const session of readDirs(NodePath4.join(sessionsRoot, workspace))) {
|
|
27257
|
+
const agentsDir = NodePath4.join(sessionsRoot, workspace, session, "agents");
|
|
27258
|
+
for (const agent of readDirs(agentsDir)) {
|
|
27259
|
+
found.push({ file: NodePath4.join(agentsDir, agent, "wire.jsonl"), sessionId: session });
|
|
27260
|
+
}
|
|
27261
|
+
}
|
|
27262
|
+
}
|
|
27263
|
+
return found;
|
|
27264
|
+
};
|
|
27265
|
+
var readRange = (file2, from, to) => {
|
|
27266
|
+
const length = Math.max(0, to - from);
|
|
27267
|
+
const buffer = Buffer.alloc(length);
|
|
27268
|
+
const fd = NodeFS4.openSync(file2, "r");
|
|
27269
|
+
try {
|
|
27270
|
+
let read = 0;
|
|
27271
|
+
while (read < length) {
|
|
27272
|
+
const n = NodeFS4.readSync(fd, buffer, read, length - read, from + read);
|
|
27273
|
+
if (n === 0) break;
|
|
27274
|
+
read += n;
|
|
27275
|
+
}
|
|
27276
|
+
return read === length ? buffer : buffer.subarray(0, read);
|
|
27277
|
+
} finally {
|
|
27278
|
+
NodeFS4.closeSync(fd);
|
|
27279
|
+
}
|
|
27280
|
+
};
|
|
27281
|
+
var guardOf = (file2, size) => readRange(file2, 0, Math.min(size, GUARD_LENGTH)).toString("base64");
|
|
27282
|
+
var createKimiTokenReader = (input2) => {
|
|
27283
|
+
const home = input2.homePath.trim().length > 0 ? NodePath4.resolve(input2.homePath) : NodePath4.join(NodeOS3.homedir(), ".kimi-code");
|
|
27284
|
+
const sessionsRoot = NodePath4.join(home, "sessions");
|
|
27285
|
+
const memos = /* @__PURE__ */ new Map();
|
|
27286
|
+
const refresh = (file2, size) => {
|
|
27287
|
+
const guard = guardOf(file2, size);
|
|
27288
|
+
const memo2 = memos.get(file2);
|
|
27289
|
+
const resumable = memo2 !== void 0 && memo2.size <= size && memo2.guard === guard;
|
|
27290
|
+
if (resumable && memo2.size === size) return memo2;
|
|
27291
|
+
const from = resumable ? memo2.offset : 0;
|
|
27292
|
+
const chunk = readRange(file2, from, size);
|
|
27293
|
+
const lastNewline = chunk.lastIndexOf(10);
|
|
27294
|
+
const complete = lastNewline < 0 ? Buffer.alloc(0) : chunk.subarray(0, lastNewline + 1);
|
|
27295
|
+
const next = resumable ? { ...memo2, size, offset: from + complete.length } : { size, guard, offset: complete.length, malformed: 0, records: [] };
|
|
27296
|
+
for (const line of complete.toString("utf8").split("\n")) {
|
|
27297
|
+
if (line.length === 0) continue;
|
|
27298
|
+
const parsed = parseKimiUsageLine(line);
|
|
27299
|
+
if (parsed === void 0) continue;
|
|
27300
|
+
if (parsed === "malformed") next.malformed += 1;
|
|
27301
|
+
else next.records.push(parsed);
|
|
27302
|
+
}
|
|
27303
|
+
memos.set(file2, next);
|
|
27304
|
+
return next;
|
|
27305
|
+
};
|
|
27306
|
+
const readTokenUsage = async (request) => {
|
|
27307
|
+
const sinceMs = Date.parse(request.since);
|
|
27308
|
+
const untilMs = Date.parse(request.until);
|
|
27309
|
+
if (!NodeFS4.existsSync(sessionsRoot)) {
|
|
27310
|
+
return {
|
|
27311
|
+
records: [],
|
|
27312
|
+
source: {
|
|
27313
|
+
homePath: home,
|
|
27314
|
+
status: "missing",
|
|
27315
|
+
scannedFiles: 0,
|
|
27316
|
+
skippedFiles: 0,
|
|
27317
|
+
message: `No Kimi sessions directory at ${sessionsRoot}`
|
|
27318
|
+
}
|
|
27319
|
+
};
|
|
27320
|
+
}
|
|
27321
|
+
const records = [];
|
|
27322
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27323
|
+
let scannedFiles = 0;
|
|
27324
|
+
let skippedFiles = 0;
|
|
27325
|
+
let failedFiles = 0;
|
|
27326
|
+
let malformedRecords = 0;
|
|
27327
|
+
for (const { file: file2, sessionId } of listWireLogs(sessionsRoot)) {
|
|
27328
|
+
let size;
|
|
27329
|
+
try {
|
|
27330
|
+
const stat = NodeFS4.statSync(file2);
|
|
27331
|
+
if (stat.mtimeMs < sinceMs - MTIME_SLACK_MS) {
|
|
27332
|
+
skippedFiles += 1;
|
|
27333
|
+
continue;
|
|
27334
|
+
}
|
|
27335
|
+
size = stat.size;
|
|
27336
|
+
} catch {
|
|
27337
|
+
continue;
|
|
27338
|
+
}
|
|
27339
|
+
let memo2;
|
|
27340
|
+
try {
|
|
27341
|
+
memo2 = refresh(file2, size);
|
|
27342
|
+
} catch {
|
|
27343
|
+
failedFiles += 1;
|
|
27344
|
+
continue;
|
|
27345
|
+
}
|
|
27346
|
+
scannedFiles += 1;
|
|
27347
|
+
malformedRecords += memo2.malformed;
|
|
27348
|
+
for (const record2 of memo2.records) {
|
|
27349
|
+
if (record2.timeMs < sinceMs || record2.timeMs >= untilMs) continue;
|
|
27350
|
+
if (!hasTokens(record2.totals) || seen.has(record2.key)) continue;
|
|
27351
|
+
seen.add(record2.key);
|
|
27352
|
+
records.push({
|
|
27353
|
+
at: new Date(record2.timeMs).toISOString(),
|
|
27354
|
+
model: record2.model,
|
|
27355
|
+
sessionId,
|
|
27356
|
+
totals: record2.totals
|
|
27357
|
+
});
|
|
27358
|
+
}
|
|
27359
|
+
}
|
|
27360
|
+
return {
|
|
27361
|
+
records,
|
|
27362
|
+
source: {
|
|
27363
|
+
homePath: home,
|
|
27364
|
+
status: failedFiles > 0 ? "partial" : "ok",
|
|
27365
|
+
scannedFiles,
|
|
27366
|
+
skippedFiles: skippedFiles + failedFiles,
|
|
27367
|
+
malformedRecords,
|
|
27368
|
+
...failedFiles > 0 ? { message: `${failedFiles} session log(s) could not be read` } : {}
|
|
27369
|
+
}
|
|
27370
|
+
};
|
|
27371
|
+
};
|
|
27372
|
+
return { readTokenUsage };
|
|
27373
|
+
};
|
|
27374
|
+
|
|
26974
27375
|
// src/kimiModels.ts
|
|
26975
27376
|
import {
|
|
26976
27377
|
describeExecutorSpawnFailure,
|
|
@@ -26985,10 +27386,10 @@ var probeFailureDetail = (error62) => (error62 instanceof Error ? error62.messag
|
|
|
26985
27386
|
var PROBE_TIMEOUT_MS = 15e3;
|
|
26986
27387
|
var MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
26987
27388
|
var KIMI_MODEL_LIST_ARGS = ["provider", "list", "--json"];
|
|
26988
|
-
function
|
|
27389
|
+
function isRecord9(value) {
|
|
26989
27390
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26990
27391
|
}
|
|
26991
|
-
function
|
|
27392
|
+
function asString4(value) {
|
|
26992
27393
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
26993
27394
|
}
|
|
26994
27395
|
function positiveInt2(value) {
|
|
@@ -27001,14 +27402,14 @@ function kimiProbeModels(stdout) {
|
|
|
27001
27402
|
} catch {
|
|
27002
27403
|
return void 0;
|
|
27003
27404
|
}
|
|
27004
|
-
if (!
|
|
27405
|
+
if (!isRecord9(parsed)) return void 0;
|
|
27005
27406
|
const models = parsed["models"];
|
|
27006
|
-
if (!
|
|
27407
|
+
if (!isRecord9(models)) return void 0;
|
|
27007
27408
|
const out = [];
|
|
27008
27409
|
for (const [slug, entry] of Object.entries(models)) {
|
|
27009
27410
|
const trimmed = slug.trim();
|
|
27010
|
-
if (trimmed.length === 0 || !
|
|
27011
|
-
const name =
|
|
27411
|
+
if (trimmed.length === 0 || !isRecord9(entry)) continue;
|
|
27412
|
+
const name = asString4(entry["displayName"]);
|
|
27012
27413
|
const contextWindow = positiveInt2(entry["maxContextSize"]);
|
|
27013
27414
|
out.push({
|
|
27014
27415
|
slug: trimmed,
|
|
@@ -27107,8 +27508,8 @@ function acpSessionModeToApply(requestedNativeModeId, startedModeId) {
|
|
|
27107
27508
|
// src/kimiInstance.ts
|
|
27108
27509
|
var RESUME_SCHEMA_VERSION = 1;
|
|
27109
27510
|
var CLIENT_INFO = { name: "ycodium-agent-kimi", version: "0.1.0" };
|
|
27110
|
-
var yieldTick = () => new Promise((
|
|
27111
|
-
setImmediate(
|
|
27511
|
+
var yieldTick = () => new Promise((resolve3) => {
|
|
27512
|
+
setImmediate(resolve3);
|
|
27112
27513
|
});
|
|
27113
27514
|
var EventBus = class {
|
|
27114
27515
|
#queued = [];
|
|
@@ -27135,17 +27536,17 @@ var EventBus = class {
|
|
|
27135
27536
|
const queued = this.#queued.shift();
|
|
27136
27537
|
if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
|
|
27137
27538
|
if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
|
|
27138
|
-
return new Promise((
|
|
27139
|
-
this.#waiters.push(
|
|
27539
|
+
return new Promise((resolve3) => {
|
|
27540
|
+
this.#waiters.push(resolve3);
|
|
27140
27541
|
});
|
|
27141
27542
|
}
|
|
27142
27543
|
};
|
|
27143
27544
|
}
|
|
27144
27545
|
};
|
|
27145
27546
|
function expandHomePath(value) {
|
|
27146
|
-
if (value === "~") return
|
|
27547
|
+
if (value === "~") return NodeOS4.homedir();
|
|
27147
27548
|
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
27148
|
-
return
|
|
27549
|
+
return NodePath5.join(NodeOS4.homedir(), value.slice(2));
|
|
27149
27550
|
}
|
|
27150
27551
|
return value;
|
|
27151
27552
|
}
|
|
@@ -27170,7 +27571,7 @@ function nowIso() {
|
|
|
27170
27571
|
}
|
|
27171
27572
|
function kimiContinuationKey(recipe) {
|
|
27172
27573
|
const homePath = recipe.homePath.trim();
|
|
27173
|
-
const resolvedHomePath = homePath.length > 0 ?
|
|
27574
|
+
const resolvedHomePath = homePath.length > 0 ? NodePath5.resolve(expandHomePath(homePath)) : "";
|
|
27174
27575
|
return `kimiCode:home:${recipe.binaryPath.trim()}\0${resolvedHomePath}`;
|
|
27175
27576
|
}
|
|
27176
27577
|
function resolveHomePath(recipe) {
|
|
@@ -27199,6 +27600,7 @@ var createKimiInstance = (config2, host) => {
|
|
|
27199
27600
|
const events = new EventBus();
|
|
27200
27601
|
const threads = /* @__PURE__ */ new Map();
|
|
27201
27602
|
let disposed = false;
|
|
27603
|
+
const usageHome = kimiEffectiveHome(resolveHomePath(recipe), process.env[recipe.homeEnvVar]);
|
|
27202
27604
|
const requireThread = (threadId) => {
|
|
27203
27605
|
const booked = threads.get(threadId);
|
|
27204
27606
|
if (!booked) throw new Error(`unknown thread ${threadId}`);
|
|
@@ -27312,6 +27714,11 @@ var createKimiInstance = (config2, host) => {
|
|
|
27312
27714
|
providerInstanceId: host.instanceId,
|
|
27313
27715
|
clientInfo: CLIENT_INFO,
|
|
27314
27716
|
log: host.log,
|
|
27717
|
+
// agent 自己宣告的斜杠命令按会话报给宿主,输入区的 / 菜单才有它们。
|
|
27718
|
+
onAvailableCommands: (commands) => host.reportSessionInventory?.({
|
|
27719
|
+
threadId: input2.threadId,
|
|
27720
|
+
slashCommands: acpCommandsToSlashCommands(commands)
|
|
27721
|
+
}),
|
|
27315
27722
|
...requestedModelId === void 0 ? {} : { requestedModelId },
|
|
27316
27723
|
...input2.bindingGeneration === void 0 ? {} : { originBindingGeneration: input2.bindingGeneration },
|
|
27317
27724
|
hooks,
|
|
@@ -27469,6 +27876,15 @@ var createKimiInstance = (config2, host) => {
|
|
|
27469
27876
|
return { threadId, turns: booked.turns };
|
|
27470
27877
|
},
|
|
27471
27878
|
events,
|
|
27879
|
+
// 额度与 token 都读这个实例自己的账号目录(与它的 CLI 同一个)。
|
|
27880
|
+
usage: {
|
|
27881
|
+
...createKimiUsage({
|
|
27882
|
+
homePath: usageHome,
|
|
27883
|
+
environment: recipe.environment,
|
|
27884
|
+
fetch: host.fetch
|
|
27885
|
+
}),
|
|
27886
|
+
...createKimiTokenReader({ homePath: usageHome })
|
|
27887
|
+
},
|
|
27472
27888
|
continuationKey: kimiContinuationKey(recipe),
|
|
27473
27889
|
dispose: async () => {
|
|
27474
27890
|
if (disposed) return;
|
package/locales/en.json
CHANGED
|
@@ -10,5 +10,11 @@
|
|
|
10
10
|
"description": "Sessions and the `kimi login` credentials live here, so providers sharing this directory can pick up each other's threads and providers with different directories cannot. Leave empty to use the CLI's default.",
|
|
11
11
|
"placeholder": "e.g. ~/.kimi-code-work"
|
|
12
12
|
}
|
|
13
|
+
},
|
|
14
|
+
"usage": {
|
|
15
|
+
"window": {
|
|
16
|
+
"subscription": "Subscription",
|
|
17
|
+
"duration": "{{duration}} window"
|
|
18
|
+
}
|
|
13
19
|
}
|
|
14
20
|
}
|
package/locales/zh-CN.json
CHANGED
|
@@ -10,5 +10,11 @@
|
|
|
10
10
|
"description": "会话和 `kimi login` 的凭据都保存在这里,因此共用同一目录的 provider 可以接手彼此的会话,目录不同的则不能。留空则使用该命令行工具的默认目录。",
|
|
11
11
|
"placeholder": "例如 ~/.kimi-code-work"
|
|
12
12
|
}
|
|
13
|
+
},
|
|
14
|
+
"usage": {
|
|
15
|
+
"window": {
|
|
16
|
+
"subscription": "订阅",
|
|
17
|
+
"duration": "{{duration}} 窗口"
|
|
18
|
+
}
|
|
13
19
|
}
|
|
14
20
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycodium-ai/agent-kimi",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2696",
|
|
4
4
|
"description": "Kimi Code executor: runs `kimi acp` over Agent Client Protocol stdio.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ycodium",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"@types/node": "24.12.4",
|
|
22
22
|
"esbuild": "0.28.1",
|
|
23
23
|
"vite-plus": "0.2.2",
|
|
24
|
-
"ycodium-
|
|
25
|
-
"
|
|
26
|
-
"ycodium-executor
|
|
24
|
+
"@ycodium-ai/plugin-api": "0.2.2696",
|
|
25
|
+
"ycodium-executor-testing": "0.0.0",
|
|
26
|
+
"ycodium-acp-executor": "0.2.2696"
|
|
27
27
|
},
|
|
28
28
|
"ycodium": {
|
|
29
29
|
"plugin": "./dist/index.js",
|