@mrciphersmith/keryx 0.2.81 → 0.2.83
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 +2 -1
- package/dist/cli.js +449 -92
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -233,7 +233,8 @@ What is in it today:
|
|
|
233
233
|
`/resume`, `/sessions`, `/status`, `/flows`, `/compact`, `/new`, and
|
|
234
234
|
`keryx sessions list|fork|export` — `fork` branches a conversation into a new
|
|
235
235
|
session that keeps its ancestry, without editing a transcript by hand.
|
|
236
|
-
`/status` is the session inspector
|
|
236
|
+
`/status` is the session inspector (identity, context window and limits when
|
|
237
|
+
the provider reported them); `/session-info` and `/info` are not aliases.
|
|
237
238
|
- **Responsive busy-turn UX.** A running main turn can be interrupted with
|
|
238
239
|
`/interrupt`; additional prompts are queued and answered as read-only side workers
|
|
239
240
|
(`side-1`) in the TUI so the shell stays usable under long-running turns.
|
package/dist/cli.js
CHANGED
|
@@ -36259,7 +36259,7 @@ async function loginDeviceCode(input) {
|
|
|
36259
36259
|
saveOAuthGrant(input.provider, grantFromTokens("device-code", tokens, input.now), input.dir);
|
|
36260
36260
|
return { ok: true, provider: input.provider };
|
|
36261
36261
|
} catch (err) {
|
|
36262
|
-
const message = err instanceof DeviceCodeError ? err.message : "device authorization failed";
|
|
36262
|
+
const message = err instanceof DeviceCodeError ? err.message : err instanceof Error ? err.message : "device authorization failed";
|
|
36263
36263
|
return { ok: false, error: message };
|
|
36264
36264
|
}
|
|
36265
36265
|
}
|
|
@@ -70772,11 +70772,11 @@ async function runGoalCommand(params) {
|
|
|
70772
70772
|
|
|
70773
70773
|
// src/tui/tui-shell.ts
|
|
70774
70774
|
init_metaproject_adapter();
|
|
70775
|
-
import {
|
|
70775
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
70776
70776
|
// package.json
|
|
70777
70777
|
var package_default = {
|
|
70778
70778
|
name: "@mrciphersmith/keryx",
|
|
70779
|
-
version: "0.2.
|
|
70779
|
+
version: "0.2.83",
|
|
70780
70780
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
70781
70781
|
private: false,
|
|
70782
70782
|
publishConfig: {
|
|
@@ -74085,6 +74085,345 @@ function openMcpTools(otui, chrome, options) {
|
|
|
74085
74085
|
// src/tui/tui-shell.ts
|
|
74086
74086
|
init_slate();
|
|
74087
74087
|
|
|
74088
|
+
// src/commands/model-limits.ts
|
|
74089
|
+
init_guard2();
|
|
74090
|
+
init_shell_config();
|
|
74091
|
+
init_providers();
|
|
74092
|
+
var MODEL_LIMITS_TIMEOUT_MS = 8000;
|
|
74093
|
+
var DEFAULT_OLLAMA_BASE_URL2 = "http://localhost:11434";
|
|
74094
|
+
var CONTEXT_WINDOW_KEYS = [
|
|
74095
|
+
"context_length",
|
|
74096
|
+
"context_window",
|
|
74097
|
+
"contextWindow",
|
|
74098
|
+
"max_model_len",
|
|
74099
|
+
"max_input_tokens",
|
|
74100
|
+
"maxInputTokens"
|
|
74101
|
+
];
|
|
74102
|
+
var NESTED_LIMIT_KEYS = ["architecture", "top_provider", "topProvider", "meta", "limits", "capabilities"];
|
|
74103
|
+
function asPositiveInt(value) {
|
|
74104
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
74105
|
+
return Math.floor(value);
|
|
74106
|
+
}
|
|
74107
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
74108
|
+
const parsed = Number(value);
|
|
74109
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
74110
|
+
return Math.floor(parsed);
|
|
74111
|
+
}
|
|
74112
|
+
}
|
|
74113
|
+
return;
|
|
74114
|
+
}
|
|
74115
|
+
function extractContextWindow(entry) {
|
|
74116
|
+
if (typeof entry !== "object" || entry === null) {
|
|
74117
|
+
return;
|
|
74118
|
+
}
|
|
74119
|
+
const rec = entry;
|
|
74120
|
+
for (const key of CONTEXT_WINDOW_KEYS) {
|
|
74121
|
+
const n = asPositiveInt(rec[key]);
|
|
74122
|
+
if (n !== undefined) {
|
|
74123
|
+
return n;
|
|
74124
|
+
}
|
|
74125
|
+
}
|
|
74126
|
+
for (const nested of NESTED_LIMIT_KEYS) {
|
|
74127
|
+
const inner = extractContextWindow(rec[nested]);
|
|
74128
|
+
if (inner !== undefined) {
|
|
74129
|
+
return inner;
|
|
74130
|
+
}
|
|
74131
|
+
}
|
|
74132
|
+
return;
|
|
74133
|
+
}
|
|
74134
|
+
function entryId(entry) {
|
|
74135
|
+
if (typeof entry !== "object" || entry === null) {
|
|
74136
|
+
return "";
|
|
74137
|
+
}
|
|
74138
|
+
const rec = entry;
|
|
74139
|
+
if (typeof rec.id === "string" && rec.id.length > 0) {
|
|
74140
|
+
return rec.id;
|
|
74141
|
+
}
|
|
74142
|
+
if (typeof rec.name === "string" && rec.name.length > 0) {
|
|
74143
|
+
return rec.name;
|
|
74144
|
+
}
|
|
74145
|
+
return "";
|
|
74146
|
+
}
|
|
74147
|
+
function findModelEntry(data, modelId) {
|
|
74148
|
+
const wanted = modelId.trim();
|
|
74149
|
+
if (wanted.length === 0) {
|
|
74150
|
+
return;
|
|
74151
|
+
}
|
|
74152
|
+
const lower = wanted.toLowerCase();
|
|
74153
|
+
const exact = data.find((entry) => entryId(entry).toLowerCase() === lower);
|
|
74154
|
+
if (exact !== undefined) {
|
|
74155
|
+
return exact;
|
|
74156
|
+
}
|
|
74157
|
+
const short = lower.includes("/") ? lower.split("/").pop() ?? lower : lower;
|
|
74158
|
+
return data.find((entry) => {
|
|
74159
|
+
const id = entryId(entry).toLowerCase();
|
|
74160
|
+
if (id.length === 0) {
|
|
74161
|
+
return false;
|
|
74162
|
+
}
|
|
74163
|
+
return id.endsWith(`/${short}`) || id.split("/").pop() === short;
|
|
74164
|
+
});
|
|
74165
|
+
}
|
|
74166
|
+
var REQUEST_LIMIT_HEADERS = [
|
|
74167
|
+
"x-ratelimit-limit-requests",
|
|
74168
|
+
"x-ratelimit-limit",
|
|
74169
|
+
"anthropic-ratelimit-requests-limit"
|
|
74170
|
+
];
|
|
74171
|
+
var REQUEST_REMAINING_HEADERS = [
|
|
74172
|
+
"x-ratelimit-remaining-requests",
|
|
74173
|
+
"x-ratelimit-remaining",
|
|
74174
|
+
"anthropic-ratelimit-requests-remaining"
|
|
74175
|
+
];
|
|
74176
|
+
var TOKEN_LIMIT_HEADERS = ["x-ratelimit-limit-tokens", "anthropic-ratelimit-tokens-limit"];
|
|
74177
|
+
var TOKEN_REMAINING_HEADERS = [
|
|
74178
|
+
"x-ratelimit-remaining-tokens",
|
|
74179
|
+
"anthropic-ratelimit-tokens-remaining"
|
|
74180
|
+
];
|
|
74181
|
+
var RESET_HEADERS = [
|
|
74182
|
+
"x-ratelimit-reset-requests",
|
|
74183
|
+
"x-ratelimit-reset",
|
|
74184
|
+
"anthropic-ratelimit-requests-reset",
|
|
74185
|
+
"retry-after"
|
|
74186
|
+
];
|
|
74187
|
+
function firstHeaderInt(headers, names) {
|
|
74188
|
+
for (const name of names) {
|
|
74189
|
+
const n = asPositiveInt(headers.get(name));
|
|
74190
|
+
if (n !== undefined) {
|
|
74191
|
+
return n;
|
|
74192
|
+
}
|
|
74193
|
+
}
|
|
74194
|
+
return;
|
|
74195
|
+
}
|
|
74196
|
+
function firstHeaderText(headers, names) {
|
|
74197
|
+
for (const name of names) {
|
|
74198
|
+
const raw = headers.get(name)?.trim();
|
|
74199
|
+
if (raw !== undefined && raw.length > 0) {
|
|
74200
|
+
return raw;
|
|
74201
|
+
}
|
|
74202
|
+
}
|
|
74203
|
+
return;
|
|
74204
|
+
}
|
|
74205
|
+
function parseRateLimitHeaders(headers) {
|
|
74206
|
+
const snapshot = {};
|
|
74207
|
+
const requestsLimit = firstHeaderInt(headers, REQUEST_LIMIT_HEADERS);
|
|
74208
|
+
const requestsRemaining = firstHeaderInt(headers, REQUEST_REMAINING_HEADERS);
|
|
74209
|
+
const tokensLimit = firstHeaderInt(headers, TOKEN_LIMIT_HEADERS);
|
|
74210
|
+
const tokensRemaining = firstHeaderInt(headers, TOKEN_REMAINING_HEADERS);
|
|
74211
|
+
const reset = firstHeaderText(headers, RESET_HEADERS);
|
|
74212
|
+
if (requestsLimit !== undefined)
|
|
74213
|
+
snapshot.requestsLimit = requestsLimit;
|
|
74214
|
+
if (requestsRemaining !== undefined)
|
|
74215
|
+
snapshot.requestsRemaining = requestsRemaining;
|
|
74216
|
+
if (tokensLimit !== undefined)
|
|
74217
|
+
snapshot.tokensLimit = tokensLimit;
|
|
74218
|
+
if (tokensRemaining !== undefined)
|
|
74219
|
+
snapshot.tokensRemaining = tokensRemaining;
|
|
74220
|
+
if (reset !== undefined)
|
|
74221
|
+
snapshot.reset = reset;
|
|
74222
|
+
return Object.keys(snapshot).length === 0 ? undefined : snapshot;
|
|
74223
|
+
}
|
|
74224
|
+
function formatRateLimit(limit) {
|
|
74225
|
+
if (limit === undefined) {
|
|
74226
|
+
return;
|
|
74227
|
+
}
|
|
74228
|
+
const parts = [];
|
|
74229
|
+
if (limit.requestsRemaining !== undefined && limit.requestsLimit !== undefined) {
|
|
74230
|
+
parts.push(`${limit.requestsRemaining}/${limit.requestsLimit} requests`);
|
|
74231
|
+
} else if (limit.requestsRemaining !== undefined) {
|
|
74232
|
+
parts.push(`${limit.requestsRemaining} requests left`);
|
|
74233
|
+
} else if (limit.requestsLimit !== undefined) {
|
|
74234
|
+
parts.push(`${limit.requestsLimit} request cap`);
|
|
74235
|
+
}
|
|
74236
|
+
if (limit.tokensRemaining !== undefined && limit.tokensLimit !== undefined) {
|
|
74237
|
+
parts.push(`${limit.tokensRemaining}/${limit.tokensLimit} tokens`);
|
|
74238
|
+
} else if (limit.tokensRemaining !== undefined) {
|
|
74239
|
+
parts.push(`${limit.tokensRemaining} tokens left`);
|
|
74240
|
+
}
|
|
74241
|
+
if (limit.reset !== undefined) {
|
|
74242
|
+
parts.push(`reset ${limit.reset}`);
|
|
74243
|
+
}
|
|
74244
|
+
return parts.length === 0 ? undefined : parts.join(" \xB7 ");
|
|
74245
|
+
}
|
|
74246
|
+
function extractOllamaContextWindow(body) {
|
|
74247
|
+
if (typeof body !== "object" || body === null) {
|
|
74248
|
+
return;
|
|
74249
|
+
}
|
|
74250
|
+
const rec = body;
|
|
74251
|
+
if (typeof rec.parameters === "string") {
|
|
74252
|
+
const match = /\bnum_ctx\s+(\d+)/.exec(rec.parameters);
|
|
74253
|
+
if (match?.[1] !== undefined) {
|
|
74254
|
+
const n = asPositiveInt(match[1]);
|
|
74255
|
+
if (n !== undefined) {
|
|
74256
|
+
return n;
|
|
74257
|
+
}
|
|
74258
|
+
}
|
|
74259
|
+
}
|
|
74260
|
+
const info = rec.model_info;
|
|
74261
|
+
if (typeof info === "object" && info !== null) {
|
|
74262
|
+
for (const [key, value] of Object.entries(info)) {
|
|
74263
|
+
if (key === "context_length" || key.endsWith(".context_length")) {
|
|
74264
|
+
const n = asPositiveInt(value);
|
|
74265
|
+
if (n !== undefined) {
|
|
74266
|
+
return n;
|
|
74267
|
+
}
|
|
74268
|
+
}
|
|
74269
|
+
}
|
|
74270
|
+
}
|
|
74271
|
+
return extractContextWindow(body);
|
|
74272
|
+
}
|
|
74273
|
+
async function timedFetch(fetchFn, url, init, timeoutMs) {
|
|
74274
|
+
const controller = new AbortController;
|
|
74275
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
74276
|
+
try {
|
|
74277
|
+
return await fetchFn(url, { ...init, signal: controller.signal });
|
|
74278
|
+
} catch {
|
|
74279
|
+
return;
|
|
74280
|
+
} finally {
|
|
74281
|
+
clearTimeout(timer);
|
|
74282
|
+
}
|
|
74283
|
+
}
|
|
74284
|
+
function ollamaShowAllowed(baseUrl) {
|
|
74285
|
+
let host;
|
|
74286
|
+
try {
|
|
74287
|
+
host = new URL(baseUrl).hostname;
|
|
74288
|
+
} catch {
|
|
74289
|
+
return false;
|
|
74290
|
+
}
|
|
74291
|
+
if (isPrivateEgressHost(host) && !isLoopbackHost(host)) {
|
|
74292
|
+
return false;
|
|
74293
|
+
}
|
|
74294
|
+
return true;
|
|
74295
|
+
}
|
|
74296
|
+
async function fetchCompatLimits(fetchFn, providerName, model, baseUrl, env, timeoutMs) {
|
|
74297
|
+
const compat = providerByName(providerName);
|
|
74298
|
+
if (compat === undefined) {
|
|
74299
|
+
return {};
|
|
74300
|
+
}
|
|
74301
|
+
const resolved = resolveProviderBaseUrl({ ...compat, ...baseUrl !== undefined ? { baseUrl } : {} }, env);
|
|
74302
|
+
const url = `${resolved.replace(/\/+$/, "")}${compat.modelsPath ?? DEFAULT_MODELS_PATH}`;
|
|
74303
|
+
const apiKey = providerApiKey(compat, env) ?? compat.apiKey;
|
|
74304
|
+
const init = {};
|
|
74305
|
+
if (apiKey !== undefined && apiKey.length > 0) {
|
|
74306
|
+
init.headers = { authorization: `Bearer ${apiKey}` };
|
|
74307
|
+
}
|
|
74308
|
+
const res = await timedFetch(fetchFn, url, init, timeoutMs);
|
|
74309
|
+
if (res === undefined || !res.ok) {
|
|
74310
|
+
return {};
|
|
74311
|
+
}
|
|
74312
|
+
const rateLimit = parseRateLimitHeaders(res.headers);
|
|
74313
|
+
let body;
|
|
74314
|
+
try {
|
|
74315
|
+
body = await res.json();
|
|
74316
|
+
} catch {
|
|
74317
|
+
return rateLimit === undefined ? {} : { rateLimit };
|
|
74318
|
+
}
|
|
74319
|
+
const data = body?.data;
|
|
74320
|
+
const entries = Array.isArray(data) ? data : Array.isArray(body) ? body : [];
|
|
74321
|
+
const entry = findModelEntry(entries, model);
|
|
74322
|
+
const contextWindow = extractContextWindow(entry);
|
|
74323
|
+
return {
|
|
74324
|
+
...contextWindow !== undefined ? { contextWindow, contextSource: "live-models" } : {},
|
|
74325
|
+
...rateLimit !== undefined ? { rateLimit } : {}
|
|
74326
|
+
};
|
|
74327
|
+
}
|
|
74328
|
+
async function fetchOllamaLimits(fetchFn, model, baseUrl, timeoutMs) {
|
|
74329
|
+
const resolved = (baseUrl ?? DEFAULT_OLLAMA_BASE_URL2).replace(/\/+$/, "");
|
|
74330
|
+
if (!ollamaShowAllowed(resolved)) {
|
|
74331
|
+
return {};
|
|
74332
|
+
}
|
|
74333
|
+
const res = await timedFetch(fetchFn, `${resolved}/api/show`, {
|
|
74334
|
+
method: "POST",
|
|
74335
|
+
headers: { "content-type": "application/json" },
|
|
74336
|
+
body: JSON.stringify({ name: model })
|
|
74337
|
+
}, timeoutMs);
|
|
74338
|
+
if (res === undefined || !res.ok) {
|
|
74339
|
+
return {};
|
|
74340
|
+
}
|
|
74341
|
+
let body;
|
|
74342
|
+
try {
|
|
74343
|
+
body = await res.json();
|
|
74344
|
+
} catch {
|
|
74345
|
+
return {};
|
|
74346
|
+
}
|
|
74347
|
+
const contextWindow = extractOllamaContextWindow(body);
|
|
74348
|
+
return contextWindow === undefined ? {} : { contextWindow, contextSource: "ollama-show" };
|
|
74349
|
+
}
|
|
74350
|
+
async function loadSessionLimits(input) {
|
|
74351
|
+
const fetchFn = input.fetch ?? globalThis.fetch;
|
|
74352
|
+
const env = envWithSavedApiKeys(input.env ?? process.env);
|
|
74353
|
+
const timeoutMs = input.timeoutMs ?? MODEL_LIMITS_TIMEOUT_MS;
|
|
74354
|
+
const provider = input.provider.trim();
|
|
74355
|
+
const model = input.model.trim();
|
|
74356
|
+
const limitsPromise = provider === "ollama" ? fetchOllamaLimits(fetchFn, model, input.baseUrl, timeoutMs) : providerByName(provider) !== undefined ? fetchCompatLimits(fetchFn, provider, model, input.baseUrl, env, timeoutMs) : Promise.resolve({});
|
|
74357
|
+
const capable = balanceCapableProvider(provider);
|
|
74358
|
+
const balancePromise = capable === undefined ? Promise.resolve(undefined) : fetchProviderBalance(fetchFn, { ...capable, ...input.baseUrl !== undefined ? { baseUrl: input.baseUrl } : {} }, providerApiKey(capable, env) ?? capable.apiKey, { timeoutMs });
|
|
74359
|
+
const [limits, balance] = await Promise.all([limitsPromise, balancePromise]);
|
|
74360
|
+
return {
|
|
74361
|
+
...limits,
|
|
74362
|
+
...balance !== undefined ? { balance } : {}
|
|
74363
|
+
};
|
|
74364
|
+
}
|
|
74365
|
+
|
|
74366
|
+
// src/tui/balance-panel.ts
|
|
74367
|
+
init_providers();
|
|
74368
|
+
init_shell_config();
|
|
74369
|
+
function formatBalance(balance) {
|
|
74370
|
+
if (balance === undefined) {
|
|
74371
|
+
return "\u2014";
|
|
74372
|
+
}
|
|
74373
|
+
const amount = balance.remaining ?? balance.total;
|
|
74374
|
+
const symbol = balance.currency === "USD" ? "$" : balance.currency === "EUR" ? "\u20AC" : balance.currency === "GBP" ? "\xA3" : `${balance.currency} `;
|
|
74375
|
+
return `${symbol}${amount.toFixed(2)}`;
|
|
74376
|
+
}
|
|
74377
|
+
function mountBalancePanel(sidebarTop, otui, renderer, options) {
|
|
74378
|
+
const core = otui;
|
|
74379
|
+
const r = renderer;
|
|
74380
|
+
const env = envWithSavedApiKeys(options.env ?? process.env);
|
|
74381
|
+
let current;
|
|
74382
|
+
let textNode;
|
|
74383
|
+
const capable = balanceCapableProvider(options.provider) !== undefined;
|
|
74384
|
+
if (capable) {
|
|
74385
|
+
const label = new core.TextRenderable(r, {
|
|
74386
|
+
id: "sb-balance-k",
|
|
74387
|
+
content: core.t`${core.dim("Balance")}`,
|
|
74388
|
+
marginTop: 1
|
|
74389
|
+
});
|
|
74390
|
+
sidebarTop.add(label);
|
|
74391
|
+
const value = new core.TextRenderable(r, {
|
|
74392
|
+
id: "sb-balance-v",
|
|
74393
|
+
content: core.t`${core.dim("\u2026")}`,
|
|
74394
|
+
onMouseDown: () => {
|
|
74395
|
+
refresh();
|
|
74396
|
+
}
|
|
74397
|
+
});
|
|
74398
|
+
textNode = value;
|
|
74399
|
+
sidebarTop.add(value);
|
|
74400
|
+
}
|
|
74401
|
+
const paint = (balance) => {
|
|
74402
|
+
current = balance;
|
|
74403
|
+
if (textNode !== undefined) {
|
|
74404
|
+
textNode.content = core.t`${core.dim(formatBalance(balance))}`;
|
|
74405
|
+
}
|
|
74406
|
+
};
|
|
74407
|
+
const refresh = async () => {
|
|
74408
|
+
const provider = balanceCapableProvider(options.provider);
|
|
74409
|
+
if (provider === undefined) {
|
|
74410
|
+
paint(undefined);
|
|
74411
|
+
return;
|
|
74412
|
+
}
|
|
74413
|
+
const apiKey = providerApiKey(provider, env);
|
|
74414
|
+
if (apiKey === undefined) {
|
|
74415
|
+
paint(undefined);
|
|
74416
|
+
return;
|
|
74417
|
+
}
|
|
74418
|
+
const base = resolveProviderBaseUrl(provider, env);
|
|
74419
|
+
const withBase = { ...provider, baseUrl: base };
|
|
74420
|
+
const balance = await fetchProviderBalance(options.fetch ?? globalThis.fetch, withBase, apiKey, { ...options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {} });
|
|
74421
|
+
paint(balance);
|
|
74422
|
+
};
|
|
74423
|
+
refresh();
|
|
74424
|
+
return { refresh, current: () => current };
|
|
74425
|
+
}
|
|
74426
|
+
|
|
74088
74427
|
// src/tui/context-usage.ts
|
|
74089
74428
|
var CONTEXT_BAR_WIDTH = 28;
|
|
74090
74429
|
function lastTurnUsed(usage) {
|
|
@@ -74103,6 +74442,14 @@ function renderUsageBar(filled, width = CONTEXT_BAR_WIDTH) {
|
|
|
74103
74442
|
const n = Math.max(0, Math.min(width, filled > 0 ? width : 0));
|
|
74104
74443
|
return `[${"\u2588".repeat(n)}${"\u2591".repeat(width - n)}]`;
|
|
74105
74444
|
}
|
|
74445
|
+
function renderWindowBar(used, window, width = CONTEXT_BAR_WIDTH) {
|
|
74446
|
+
if (window <= 0) {
|
|
74447
|
+
return renderUsageBar(used, width);
|
|
74448
|
+
}
|
|
74449
|
+
const ratio = Math.max(0, Math.min(1, used / window));
|
|
74450
|
+
const filled = Math.round(ratio * width);
|
|
74451
|
+
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}]`;
|
|
74452
|
+
}
|
|
74106
74453
|
function buildContextUsage(source) {
|
|
74107
74454
|
const lastIn = source.usage?.inputTokens;
|
|
74108
74455
|
const lastOut = source.usage?.outputTokens;
|
|
@@ -74110,6 +74457,7 @@ function buildContextUsage(source) {
|
|
|
74110
74457
|
const hasEstimate = source.estimateTokens !== undefined;
|
|
74111
74458
|
const total = hasEstimate ? source.estimateTokens ?? 0 : last ?? 0;
|
|
74112
74459
|
const estimated = hasEstimate || last === undefined;
|
|
74460
|
+
const window = source.contextWindow !== undefined && source.contextWindow > 0 ? source.contextWindow : undefined;
|
|
74113
74461
|
const segments = [];
|
|
74114
74462
|
if (hasEstimate) {
|
|
74115
74463
|
segments.push({ id: "history", label: "history (est.)", tokens: source.estimateTokens ?? 0 });
|
|
@@ -74120,12 +74468,16 @@ function buildContextUsage(source) {
|
|
|
74120
74468
|
if (lastOut !== undefined) {
|
|
74121
74469
|
segments.push({ id: "last-out", label: "last out", tokens: lastOut });
|
|
74122
74470
|
}
|
|
74471
|
+
if (window !== undefined) {
|
|
74472
|
+
segments.push({ id: "free", label: "free", tokens: Math.max(0, window - total) });
|
|
74473
|
+
}
|
|
74123
74474
|
return {
|
|
74124
74475
|
total,
|
|
74125
74476
|
estimated,
|
|
74477
|
+
...window !== undefined ? { window, usedPct: Math.min(100, total / window * 100), free: Math.max(0, window - total) } : {},
|
|
74126
74478
|
segments,
|
|
74127
|
-
bar: renderUsageBar(total),
|
|
74128
|
-
note: total === 0 ? "No context usage yet." : "No model context window is known. The bar is relative to used tokens, not a billed limit."
|
|
74479
|
+
bar: window === undefined ? renderUsageBar(total) : renderWindowBar(total, window),
|
|
74480
|
+
note: total === 0 ? "No context usage yet." : window === undefined ? "No model context window is known. The bar is relative to used tokens, not a billed limit." : `Context window ${window.toLocaleString()} tokens (provider-reported).`
|
|
74129
74481
|
};
|
|
74130
74482
|
}
|
|
74131
74483
|
function formatContextUsageText(view) {
|
|
@@ -74137,7 +74489,8 @@ function formatContextUsageText(view) {
|
|
|
74137
74489
|
const kind = view.estimated ? "tokens (estimate)" : "tokens";
|
|
74138
74490
|
const width = view.segments.reduce((max, segment) => Math.max(max, segment.label.length), 0);
|
|
74139
74491
|
const rows = view.segments.map((segment) => ` ${segment.label.padEnd(width)} ${segment.tokens}`);
|
|
74140
|
-
|
|
74492
|
+
const usedLine = view.window !== undefined ? ` Used ${view.total.toLocaleString()} / ${view.window.toLocaleString()} ${kind} (${(view.usedPct ?? 0).toFixed(1)}%)` : ` Used ${view.total} ${kind}`;
|
|
74493
|
+
return [`Context`, usedLine, ` ${view.bar}`, ...rows, "", ` ${view.note}`, ""].join(`
|
|
74141
74494
|
`);
|
|
74142
74495
|
}
|
|
74143
74496
|
|
|
@@ -74182,12 +74535,17 @@ function usageUsed(usage) {
|
|
|
74182
74535
|
return (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
|
|
74183
74536
|
}
|
|
74184
74537
|
function contextRow(source) {
|
|
74538
|
+
const window = source.limits?.contextWindow;
|
|
74185
74539
|
const used = usageUsed(source.usage);
|
|
74186
|
-
|
|
74187
|
-
|
|
74540
|
+
const usedLabel = used !== undefined ? `${used} tokens` : source.estimateTokens !== undefined ? `${source.estimateTokens} tokens (estimate)` : undefined;
|
|
74541
|
+
if (usedLabel !== undefined && window !== undefined) {
|
|
74542
|
+
return `${usedLabel} / ${window.toLocaleString()}`;
|
|
74543
|
+
}
|
|
74544
|
+
if (usedLabel !== undefined) {
|
|
74545
|
+
return usedLabel;
|
|
74188
74546
|
}
|
|
74189
|
-
if (
|
|
74190
|
-
return
|
|
74547
|
+
if (window !== undefined) {
|
|
74548
|
+
return `window ${window.toLocaleString()}`;
|
|
74191
74549
|
}
|
|
74192
74550
|
return MISSING;
|
|
74193
74551
|
}
|
|
@@ -74218,11 +74576,23 @@ function buildSessionInfoSnapshot(source) {
|
|
|
74218
74576
|
label: "Last turn output",
|
|
74219
74577
|
value: source.usage?.outputTokens === undefined ? MISSING : String(source.usage.outputTokens)
|
|
74220
74578
|
},
|
|
74221
|
-
{ label: "Context estimate", value: estimate }
|
|
74579
|
+
{ label: "Context estimate", value: estimate },
|
|
74580
|
+
{
|
|
74581
|
+
label: "Context window",
|
|
74582
|
+
value: source.limits?.contextWindow === undefined ? MISSING : source.limits.contextWindow.toLocaleString()
|
|
74583
|
+
}
|
|
74222
74584
|
];
|
|
74585
|
+
const rate = formatRateLimit(source.limits?.rateLimit);
|
|
74586
|
+
if (rate !== undefined) {
|
|
74587
|
+
usageRows.push({ label: "Rate limit", value: rate });
|
|
74588
|
+
}
|
|
74589
|
+
if (source.limits?.balance !== undefined) {
|
|
74590
|
+
usageRows.push({ label: "Balance", value: formatBalance(source.limits.balance) });
|
|
74591
|
+
}
|
|
74223
74592
|
const context = buildContextUsage({
|
|
74224
74593
|
estimateTokens: source.estimateTokens,
|
|
74225
|
-
usage: source.usage
|
|
74594
|
+
usage: source.usage,
|
|
74595
|
+
...source.limits?.contextWindow !== undefined ? { contextWindow: source.limits.contextWindow } : {}
|
|
74226
74596
|
});
|
|
74227
74597
|
const sessionWorkspaces = workspacesInSession(source.workspaces ?? [], {
|
|
74228
74598
|
sessionId: id,
|
|
@@ -74350,7 +74720,7 @@ function presentSessionInfo(openModal, otui, chrome, options) {
|
|
|
74350
74720
|
`), width);
|
|
74351
74721
|
return;
|
|
74352
74722
|
}
|
|
74353
|
-
paintRows(otui, renderer, body, snapshot.sessionRows, width);
|
|
74723
|
+
paintRows(otui, renderer, body, [...snapshot.sessionRows, ...snapshot.usageRows], width);
|
|
74354
74724
|
},
|
|
74355
74725
|
onClose: () => {
|
|
74356
74726
|
unsubscribeKey?.();
|
|
@@ -74437,7 +74807,7 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
74437
74807
|
{ name: "/sessions", description: "Open the session list and switch to one", modes: AGENT_ONLY },
|
|
74438
74808
|
{
|
|
74439
74809
|
name: "/status",
|
|
74440
|
-
description: "Show session identity, context, workspaces, and flows",
|
|
74810
|
+
description: "Show session identity, context window, limits, workspaces, and flows",
|
|
74441
74811
|
modes: BOTH
|
|
74442
74812
|
},
|
|
74443
74813
|
{ name: "/flows", description: "Browse project flows and inspect one", modes: BOTH },
|
|
@@ -75568,73 +75938,47 @@ function presentGamesModal(openModalFn, otui, chrome, options = {}, registry = c
|
|
|
75568
75938
|
function openGamesModal(otui, chrome, options = {}) {
|
|
75569
75939
|
return presentGamesModal((hostOtui, hostChrome, input) => openModal(hostOtui, hostChrome, input), otui, chrome, options);
|
|
75570
75940
|
}
|
|
75571
|
-
// src/tui/
|
|
75941
|
+
// src/tui/tui-shell.ts
|
|
75572
75942
|
init_providers();
|
|
75573
|
-
|
|
75574
|
-
|
|
75575
|
-
|
|
75576
|
-
|
|
75943
|
+
init_patch_risk();
|
|
75944
|
+
init_catalog();
|
|
75945
|
+
init_grants();
|
|
75946
|
+
init_login();
|
|
75947
|
+
|
|
75948
|
+
// src/lib/oauth/open-url.ts
|
|
75949
|
+
import { spawn as spawn5 } from "child_process";
|
|
75950
|
+
function browserOpenPlan(url, platform = process.platform, env = process.env) {
|
|
75951
|
+
if (url.length === 0) {
|
|
75952
|
+
return;
|
|
75577
75953
|
}
|
|
75578
|
-
|
|
75579
|
-
|
|
75580
|
-
|
|
75954
|
+
if (platform === "darwin") {
|
|
75955
|
+
return { cmd: "open", args: [url] };
|
|
75956
|
+
}
|
|
75957
|
+
if (platform === "win32") {
|
|
75958
|
+
return { cmd: "cmd", args: ["/c", "start", "", url] };
|
|
75959
|
+
}
|
|
75960
|
+
if (typeof env.DISPLAY === "string" && env.DISPLAY.length > 0) {
|
|
75961
|
+
return { cmd: "xdg-open", args: [url] };
|
|
75962
|
+
}
|
|
75963
|
+
if (typeof env.WAYLAND_DISPLAY === "string" && env.WAYLAND_DISPLAY.length > 0) {
|
|
75964
|
+
return { cmd: "xdg-open", args: [url] };
|
|
75965
|
+
}
|
|
75966
|
+
return;
|
|
75581
75967
|
}
|
|
75582
|
-
function
|
|
75583
|
-
const
|
|
75584
|
-
|
|
75585
|
-
|
|
75586
|
-
let current;
|
|
75587
|
-
let textNode;
|
|
75588
|
-
const capable = balanceCapableProvider(options.provider) !== undefined;
|
|
75589
|
-
if (capable) {
|
|
75590
|
-
const label = new core.TextRenderable(r, {
|
|
75591
|
-
id: "sb-balance-k",
|
|
75592
|
-
content: core.t`${core.dim("Balance")}`,
|
|
75593
|
-
marginTop: 1
|
|
75594
|
-
});
|
|
75595
|
-
sidebarTop.add(label);
|
|
75596
|
-
const value = new core.TextRenderable(r, {
|
|
75597
|
-
id: "sb-balance-v",
|
|
75598
|
-
content: core.t`${core.dim("\u2026")}`,
|
|
75599
|
-
onMouseDown: () => {
|
|
75600
|
-
refresh();
|
|
75601
|
-
}
|
|
75602
|
-
});
|
|
75603
|
-
textNode = value;
|
|
75604
|
-
sidebarTop.add(value);
|
|
75968
|
+
function openVerificationUrl(url, deps = {}) {
|
|
75969
|
+
const plan = browserOpenPlan(url, deps.platform ?? process.platform, deps.env ?? process.env);
|
|
75970
|
+
if (plan === undefined) {
|
|
75971
|
+
return;
|
|
75605
75972
|
}
|
|
75606
|
-
const
|
|
75607
|
-
|
|
75608
|
-
|
|
75609
|
-
|
|
75610
|
-
|
|
75611
|
-
}
|
|
75612
|
-
const refresh = async () => {
|
|
75613
|
-
const provider = balanceCapableProvider(options.provider);
|
|
75614
|
-
if (provider === undefined) {
|
|
75615
|
-
paint(undefined);
|
|
75616
|
-
return;
|
|
75617
|
-
}
|
|
75618
|
-
const apiKey = providerApiKey(provider, env);
|
|
75619
|
-
if (apiKey === undefined) {
|
|
75620
|
-
paint(undefined);
|
|
75621
|
-
return;
|
|
75622
|
-
}
|
|
75623
|
-
const base = resolveProviderBaseUrl(provider, env);
|
|
75624
|
-
const withBase = { ...provider, baseUrl: base };
|
|
75625
|
-
const balance = await fetchProviderBalance(options.fetch ?? globalThis.fetch, withBase, apiKey, { ...options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {} });
|
|
75626
|
-
paint(balance);
|
|
75627
|
-
};
|
|
75628
|
-
refresh();
|
|
75629
|
-
return { refresh, current: () => current };
|
|
75973
|
+
const spawnFn = deps.spawn ?? spawn5;
|
|
75974
|
+
try {
|
|
75975
|
+
const child = spawnFn(plan.cmd, plan.args, { stdio: "ignore", detached: true });
|
|
75976
|
+
child.on("error", () => {});
|
|
75977
|
+
child.unref();
|
|
75978
|
+
} catch {}
|
|
75630
75979
|
}
|
|
75631
75980
|
|
|
75632
75981
|
// src/tui/tui-shell.ts
|
|
75633
|
-
init_providers();
|
|
75634
|
-
init_patch_risk();
|
|
75635
|
-
init_catalog();
|
|
75636
|
-
init_grants();
|
|
75637
|
-
init_login();
|
|
75638
75982
|
init_shell_config();
|
|
75639
75983
|
init_provider_config();
|
|
75640
75984
|
|
|
@@ -80094,26 +80438,26 @@ function runDeviceLoginInTui(otui, r, provider) {
|
|
|
80094
80438
|
fetch: (input, init) => globalThis.fetch(input, init),
|
|
80095
80439
|
signal: controller.signal,
|
|
80096
80440
|
onChallenge: (challenge) => {
|
|
80097
|
-
status.content = otui.t`${otui.bold(challenge.userCode)}\n${otui.dim(challenge.verificationUri)}\n${otui.dim("Waiting for authorization\u2026")}`;
|
|
80098
|
-
|
|
80099
|
-
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? undefined : "xdg-open";
|
|
80100
|
-
if (cmd !== undefined) {
|
|
80101
|
-
try {
|
|
80102
|
-
spawn5(cmd, [url], { stdio: "ignore", detached: true }).unref();
|
|
80103
|
-
} catch {}
|
|
80104
|
-
}
|
|
80441
|
+
status.content = otui.t`${otui.bold(challenge.userCode)}\n${otui.dim(challenge.verificationUri)}\n${otui.dim("Open that URL on any device and enter the code. Waiting for authorization\u2026")}`;
|
|
80442
|
+
openVerificationUrl(challenge.verificationUriComplete ?? challenge.verificationUri);
|
|
80105
80443
|
}
|
|
80106
80444
|
}).then((result) => {
|
|
80107
80445
|
if (controller.signal.aborted) {
|
|
80108
80446
|
return;
|
|
80109
80447
|
}
|
|
80110
|
-
cleanup();
|
|
80111
80448
|
if (result.ok) {
|
|
80449
|
+
cleanup();
|
|
80112
80450
|
applyOAuthAccessToEnv();
|
|
80113
80451
|
resolve(true);
|
|
80114
80452
|
return;
|
|
80115
80453
|
}
|
|
80116
|
-
|
|
80454
|
+
status.content = otui.t`${otui.red("\u2717")} ${otui.bold(result.error)} ${otui.dim("(Esc to go back)")}`;
|
|
80455
|
+
}).catch((err) => {
|
|
80456
|
+
if (controller.signal.aborted) {
|
|
80457
|
+
return;
|
|
80458
|
+
}
|
|
80459
|
+
const message = err instanceof Error ? err.message : "device authorization failed";
|
|
80460
|
+
status.content = otui.t`${otui.red("\u2717")} ${otui.bold(message)} ${otui.dim("(Esc to go back)")}`;
|
|
80117
80461
|
});
|
|
80118
80462
|
});
|
|
80119
80463
|
}
|
|
@@ -81334,12 +81678,18 @@ Staying in the current session.
|
|
|
81334
81678
|
(async () => {
|
|
81335
81679
|
const cwd = inspectorCwd();
|
|
81336
81680
|
const [workspaces, flows] = await Promise.all([loadInspectorWorkspaces(cwd), loadInspectorFlows(cwd)]);
|
|
81681
|
+
const limits = await loadSessionLimits({
|
|
81682
|
+
provider: currentSel.provider,
|
|
81683
|
+
model: currentSel.model,
|
|
81684
|
+
...currentSel.baseUrl !== undefined ? { baseUrl: currentSel.baseUrl } : {}
|
|
81685
|
+
});
|
|
81337
81686
|
const snapshot = buildSessionInfoSnapshot({
|
|
81338
81687
|
summary: liveSession.summary,
|
|
81339
81688
|
selection: currentSel,
|
|
81340
81689
|
version: package_default.version,
|
|
81341
81690
|
usage: lastUsage,
|
|
81342
81691
|
estimateTokens: estimateContextTokens(history),
|
|
81692
|
+
limits,
|
|
81343
81693
|
sessionText: history.map((message) => message.content).join(`
|
|
81344
81694
|
`),
|
|
81345
81695
|
workspaces,
|
|
@@ -82887,11 +83237,17 @@ ${formatThemeList(getThemeId())}`)}`);
|
|
|
82887
83237
|
return;
|
|
82888
83238
|
}
|
|
82889
83239
|
const summary = cwd !== undefined ? latestSession(cwd) : undefined;
|
|
83240
|
+
const limits = await loadSessionLimits({
|
|
83241
|
+
provider: selection.provider,
|
|
83242
|
+
model: selection.model,
|
|
83243
|
+
...selection.baseUrl !== undefined ? { baseUrl: selection.baseUrl } : {}
|
|
83244
|
+
});
|
|
82890
83245
|
const snapshot = buildSessionInfoSnapshot({
|
|
82891
83246
|
summary,
|
|
82892
83247
|
selection,
|
|
82893
83248
|
version: package_default.version,
|
|
82894
83249
|
estimateTokens: estimateContextTokens(seen),
|
|
83250
|
+
limits,
|
|
82895
83251
|
sessionText: seen.map((message) => message.content).join(`
|
|
82896
83252
|
`),
|
|
82897
83253
|
workspaces,
|
|
@@ -83126,6 +83482,11 @@ Starting a new session.
|
|
|
83126
83482
|
selection: { provider: providerName, model: modelName },
|
|
83127
83483
|
version: package_default.version,
|
|
83128
83484
|
estimateTokens: estimateContextTokens(history),
|
|
83485
|
+
limits: await loadSessionLimits({
|
|
83486
|
+
provider: providerName,
|
|
83487
|
+
model: modelName,
|
|
83488
|
+
...baseUrl !== undefined ? { baseUrl } : {}
|
|
83489
|
+
}),
|
|
83129
83490
|
sessionText: history.map((message) => message.content).join(`
|
|
83130
83491
|
`),
|
|
83131
83492
|
workspaces,
|
|
@@ -83955,6 +84316,10 @@ New session ${shortSessionId(live.summary.id)}.
|
|
|
83955
84316
|
version: package_default.version,
|
|
83956
84317
|
usage: lastUsage,
|
|
83957
84318
|
estimateTokens: estimateContextTokens(history),
|
|
84319
|
+
limits: await loadSessionLimits({
|
|
84320
|
+
provider: deps.providerId,
|
|
84321
|
+
model: deps.modelId
|
|
84322
|
+
}),
|
|
83958
84323
|
sessionText: history.map((message) => message.content).join(`
|
|
83959
84324
|
`),
|
|
83960
84325
|
workspaces,
|
|
@@ -90567,7 +90932,6 @@ init_providers();
|
|
|
90567
90932
|
init_catalog();
|
|
90568
90933
|
init_grants();
|
|
90569
90934
|
init_login();
|
|
90570
|
-
import { spawn as spawn8 } from "child_process";
|
|
90571
90935
|
function printAuthHelp() {
|
|
90572
90936
|
console.log(`Usage:
|
|
90573
90937
|
keryx auth list [--json]
|
|
@@ -90579,13 +90943,6 @@ Providers with a sanctioned subscription login: grok (SuperGrok), openai (ChatGP
|
|
|
90579
90943
|
Claude Pro/Max, Gemini Google-account login, and DeepSeek OAuth are not offered.
|
|
90580
90944
|
`);
|
|
90581
90945
|
}
|
|
90582
|
-
function openVerificationUrl(url) {
|
|
90583
|
-
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
90584
|
-
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
90585
|
-
try {
|
|
90586
|
-
spawn8(cmd, args, { stdio: "ignore", detached: true }).unref();
|
|
90587
|
-
} catch {}
|
|
90588
|
-
}
|
|
90589
90946
|
async function authCommand(args) {
|
|
90590
90947
|
const command = args[0];
|
|
90591
90948
|
if (!command || command === "--help" || command === "-h") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.83",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|