@sma1lboy/kobe 0.5.0 → 0.5.2
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/dist/bin/kobed.js +190 -5
- package/dist/cli/index.js +95 -9
- package/package.json +1 -1
package/dist/bin/kobed.js
CHANGED
|
@@ -353,7 +353,24 @@ function extractMessage(record, fallbackSessionId) {
|
|
|
353
353
|
const content = inner.content;
|
|
354
354
|
const ts = typeof record.timestamp === "string" ? record.timestamp : new Date().toISOString();
|
|
355
355
|
const sid = typeof record.sessionId === "string" ? record.sessionId : fallbackSessionId;
|
|
356
|
-
|
|
356
|
+
const usage = extractUsage(inner.usage);
|
|
357
|
+
return usage ? { role, content, timestamp: ts, sessionId: sid, usage } : { role, content, timestamp: ts, sessionId: sid };
|
|
358
|
+
}
|
|
359
|
+
function extractUsage(v) {
|
|
360
|
+
if (!isObject(v))
|
|
361
|
+
return;
|
|
362
|
+
const inTok = typeof v.input_tokens === "number" ? v.input_tokens : undefined;
|
|
363
|
+
const outTok = typeof v.output_tokens === "number" ? v.output_tokens : undefined;
|
|
364
|
+
if (inTok === undefined || outTok === undefined)
|
|
365
|
+
return;
|
|
366
|
+
const cacheRead = typeof v.cache_read_input_tokens === "number" ? v.cache_read_input_tokens : undefined;
|
|
367
|
+
const cacheCreate = typeof v.cache_creation_input_tokens === "number" ? v.cache_creation_input_tokens : undefined;
|
|
368
|
+
return {
|
|
369
|
+
input_tokens: inTok,
|
|
370
|
+
output_tokens: outTok,
|
|
371
|
+
...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {},
|
|
372
|
+
...cacheCreate !== undefined ? { cache_creation_input_tokens: cacheCreate } : {}
|
|
373
|
+
};
|
|
357
374
|
}
|
|
358
375
|
function isObject(v) {
|
|
359
376
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -1021,13 +1038,15 @@ __export(exports_bridge, {
|
|
|
1021
1038
|
import { writeFile } from "fs/promises";
|
|
1022
1039
|
import { homedir as homedir4 } from "os";
|
|
1023
1040
|
import { join } from "path";
|
|
1041
|
+
import { fileURLToPath } from "url";
|
|
1024
1042
|
async function startBridge(orch, opts = {}) {
|
|
1025
1043
|
const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir4();
|
|
1026
1044
|
const runDir = join(home, ".kobe", "run");
|
|
1027
1045
|
const socketPath = join(runDir, `bridge-${process.pid}.sock`);
|
|
1028
1046
|
const mcpConfigPath = join(runDir, `mcp-${process.pid}.json`);
|
|
1029
1047
|
const server = await startBridgeServer(orch, socketPath);
|
|
1030
|
-
const
|
|
1048
|
+
const moduleExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
|
|
1049
|
+
const entry = fileURLToPath(new URL(`../../cli/index${moduleExt}`, import.meta.url));
|
|
1031
1050
|
const mcpConfig = {
|
|
1032
1051
|
mcpServers: {
|
|
1033
1052
|
kobe: {
|
|
@@ -2961,6 +2980,9 @@ class Orchestrator {
|
|
|
2961
2980
|
tasksSignal() {
|
|
2962
2981
|
return this.tasksAcc;
|
|
2963
2982
|
}
|
|
2983
|
+
planUsageSignal() {
|
|
2984
|
+
return () => null;
|
|
2985
|
+
}
|
|
2964
2986
|
subscribeTasks(listener) {
|
|
2965
2987
|
return this.store.subscribe(listener);
|
|
2966
2988
|
}
|
|
@@ -4294,9 +4316,166 @@ init_paths2();
|
|
|
4294
4316
|
|
|
4295
4317
|
// src/daemon/server.ts
|
|
4296
4318
|
init_paths2();
|
|
4297
|
-
import { mkdir as mkdir3, readFile as
|
|
4319
|
+
import { mkdir as mkdir3, readFile as readFile5, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
|
|
4298
4320
|
import { createServer as createServer2 } from "net";
|
|
4299
4321
|
import { dirname as dirname4 } from "path";
|
|
4322
|
+
|
|
4323
|
+
// src/engine/claude-code-local/plan-usage.ts
|
|
4324
|
+
import { execFile } from "child_process";
|
|
4325
|
+
import { createHash } from "crypto";
|
|
4326
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
4327
|
+
import { homedir as homedir10, userInfo } from "os";
|
|
4328
|
+
import { join as join6 } from "path";
|
|
4329
|
+
import { promisify } from "util";
|
|
4330
|
+
var execFileAsync = promisify(execFile);
|
|
4331
|
+
var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
4332
|
+
var FETCH_TIMEOUT_MS = 5000;
|
|
4333
|
+
var KEYCHAIN_BASE = "Claude Code";
|
|
4334
|
+
var KEYCHAIN_SUFFIX = "-credentials";
|
|
4335
|
+
function keychainServiceName() {
|
|
4336
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
4337
|
+
if (!configDir)
|
|
4338
|
+
return `${KEYCHAIN_BASE}${KEYCHAIN_SUFFIX}`;
|
|
4339
|
+
const hash = createHash("sha256").update(configDir).digest("hex").slice(0, 8);
|
|
4340
|
+
return `${KEYCHAIN_BASE}${KEYCHAIN_SUFFIX}-${hash}`;
|
|
4341
|
+
}
|
|
4342
|
+
function keychainAccount() {
|
|
4343
|
+
return process.env.USER || userInfo().username || "claude-code-user";
|
|
4344
|
+
}
|
|
4345
|
+
async function readKeychainToken() {
|
|
4346
|
+
if (process.platform !== "darwin")
|
|
4347
|
+
return null;
|
|
4348
|
+
try {
|
|
4349
|
+
const { stdout } = await execFileAsync("security", [
|
|
4350
|
+
"find-generic-password",
|
|
4351
|
+
"-a",
|
|
4352
|
+
keychainAccount(),
|
|
4353
|
+
"-w",
|
|
4354
|
+
"-s",
|
|
4355
|
+
keychainServiceName()
|
|
4356
|
+
]);
|
|
4357
|
+
return parseStoredOAuth(stdout);
|
|
4358
|
+
} catch {
|
|
4359
|
+
return null;
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
async function readPlainTextToken() {
|
|
4363
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ?? join6(homedir10(), ".claude");
|
|
4364
|
+
const path7 = join6(configDir, ".credentials.json");
|
|
4365
|
+
try {
|
|
4366
|
+
const raw = await readFile4(path7, "utf8");
|
|
4367
|
+
return parseStoredOAuth(raw);
|
|
4368
|
+
} catch {
|
|
4369
|
+
return null;
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
function parseStoredOAuth(raw) {
|
|
4373
|
+
const trimmed = raw.trim();
|
|
4374
|
+
if (!trimmed)
|
|
4375
|
+
return null;
|
|
4376
|
+
try {
|
|
4377
|
+
const parsed = JSON.parse(trimmed);
|
|
4378
|
+
const tok = parsed.claudeAiOauth;
|
|
4379
|
+
if (!tok || typeof tok.accessToken !== "string" || tok.accessToken.length === 0)
|
|
4380
|
+
return null;
|
|
4381
|
+
return tok;
|
|
4382
|
+
} catch {
|
|
4383
|
+
return null;
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
async function loadToken() {
|
|
4387
|
+
return await readKeychainToken() ?? await readPlainTextToken();
|
|
4388
|
+
}
|
|
4389
|
+
function normalizeBucket(b) {
|
|
4390
|
+
if (!b)
|
|
4391
|
+
return null;
|
|
4392
|
+
return {
|
|
4393
|
+
utilization: typeof b.utilization === "number" && Number.isFinite(b.utilization) ? b.utilization : null,
|
|
4394
|
+
resetsAt: typeof b.resets_at === "string" && b.resets_at.length > 0 ? b.resets_at : null
|
|
4395
|
+
};
|
|
4396
|
+
}
|
|
4397
|
+
async function fetchPlanUsage(now = Date.now()) {
|
|
4398
|
+
const token = await loadToken();
|
|
4399
|
+
if (!token)
|
|
4400
|
+
return null;
|
|
4401
|
+
if (typeof token.expiresAt === "number" && token.expiresAt > 0 && token.expiresAt < now)
|
|
4402
|
+
return null;
|
|
4403
|
+
const ctrl = new AbortController;
|
|
4404
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
4405
|
+
try {
|
|
4406
|
+
const res = await fetch(USAGE_URL, {
|
|
4407
|
+
signal: ctrl.signal,
|
|
4408
|
+
headers: {
|
|
4409
|
+
accept: "application/json",
|
|
4410
|
+
"content-type": "application/json",
|
|
4411
|
+
"user-agent": "kobe-plan-usage",
|
|
4412
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
4413
|
+
authorization: `Bearer ${token.accessToken}`
|
|
4414
|
+
}
|
|
4415
|
+
});
|
|
4416
|
+
if (!res.ok)
|
|
4417
|
+
return null;
|
|
4418
|
+
const body = await res.json();
|
|
4419
|
+
return {
|
|
4420
|
+
fiveHour: normalizeBucket(body.five_hour),
|
|
4421
|
+
sevenDay: normalizeBucket(body.seven_day),
|
|
4422
|
+
sevenDayOpus: normalizeBucket(body.seven_day_opus),
|
|
4423
|
+
sevenDaySonnet: normalizeBucket(body.seven_day_sonnet),
|
|
4424
|
+
fetchedAt: new Date(now).toISOString()
|
|
4425
|
+
};
|
|
4426
|
+
} catch {
|
|
4427
|
+
return null;
|
|
4428
|
+
} finally {
|
|
4429
|
+
clearTimeout(timer);
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
// src/daemon/plan-usage-poller.ts
|
|
4434
|
+
var DEFAULT_INTERVAL_MS = 60000;
|
|
4435
|
+
function createPlanUsagePoller(options) {
|
|
4436
|
+
const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
4437
|
+
const fetcher = options.fetcher ?? fetchPlanUsage;
|
|
4438
|
+
let timer = null;
|
|
4439
|
+
let last = null;
|
|
4440
|
+
let inflight = false;
|
|
4441
|
+
async function tick() {
|
|
4442
|
+
if (inflight)
|
|
4443
|
+
return;
|
|
4444
|
+
inflight = true;
|
|
4445
|
+
try {
|
|
4446
|
+
const usage = await fetcher();
|
|
4447
|
+
if (usage) {
|
|
4448
|
+
last = usage;
|
|
4449
|
+
options.onUpdate(usage);
|
|
4450
|
+
}
|
|
4451
|
+
} finally {
|
|
4452
|
+
inflight = false;
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
return {
|
|
4456
|
+
start() {
|
|
4457
|
+
if (timer)
|
|
4458
|
+
return;
|
|
4459
|
+
tick();
|
|
4460
|
+
timer = setInterval(() => void tick(), intervalMs);
|
|
4461
|
+
timer.unref?.();
|
|
4462
|
+
},
|
|
4463
|
+
stop() {
|
|
4464
|
+
if (timer) {
|
|
4465
|
+
clearInterval(timer);
|
|
4466
|
+
timer = null;
|
|
4467
|
+
}
|
|
4468
|
+
},
|
|
4469
|
+
current() {
|
|
4470
|
+
return last;
|
|
4471
|
+
},
|
|
4472
|
+
async refresh() {
|
|
4473
|
+
await tick();
|
|
4474
|
+
}
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4477
|
+
|
|
4478
|
+
// src/daemon/server.ts
|
|
4300
4479
|
async function startDaemonServer(orch, options = {}) {
|
|
4301
4480
|
const socketPath = options.socketPath ?? defaultDaemonSocketPath(options.homeDir);
|
|
4302
4481
|
const pidPath = options.pidPath ?? defaultDaemonPidPath(options.homeDir);
|
|
@@ -4327,12 +4506,16 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
4327
4506
|
clients.delete(client);
|
|
4328
4507
|
});
|
|
4329
4508
|
});
|
|
4509
|
+
const planUsagePoller = options.planUsagePoller ?? createPlanUsagePoller({
|
|
4510
|
+
onUpdate: (usage) => broadcast(clients, { type: "event", name: "plan.usage", payload: { usage } })
|
|
4511
|
+
});
|
|
4330
4512
|
const serverApi = {
|
|
4331
4513
|
socketPath,
|
|
4332
4514
|
pidPath,
|
|
4333
4515
|
startedAt,
|
|
4334
4516
|
clients,
|
|
4335
4517
|
async close() {
|
|
4518
|
+
planUsagePoller.stop();
|
|
4336
4519
|
broadcast(clients, { type: "event", name: "daemon.stopping", payload: {} });
|
|
4337
4520
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
4338
4521
|
for (const client of Array.from(clients)) {
|
|
@@ -4345,6 +4528,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
4345
4528
|
await unlink4(pidPath).catch(() => {});
|
|
4346
4529
|
}
|
|
4347
4530
|
};
|
|
4531
|
+
planUsagePoller.start();
|
|
4348
4532
|
await new Promise((resolve, reject) => {
|
|
4349
4533
|
server.once("error", reject);
|
|
4350
4534
|
server.listen(socketPath, () => {
|
|
@@ -4380,7 +4564,8 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
4380
4564
|
clientId: client.id,
|
|
4381
4565
|
tasks: tasks.map(serializeTask),
|
|
4382
4566
|
pending,
|
|
4383
|
-
runState
|
|
4567
|
+
runState,
|
|
4568
|
+
planUsage: planUsagePoller.current()
|
|
4384
4569
|
};
|
|
4385
4570
|
}
|
|
4386
4571
|
case "daemon.status":
|
|
@@ -4606,7 +4791,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
4606
4791
|
}
|
|
4607
4792
|
async function readPidFile(pidPath) {
|
|
4608
4793
|
try {
|
|
4609
|
-
const raw = await
|
|
4794
|
+
const raw = await readFile5(pidPath, "utf8");
|
|
4610
4795
|
const pid = Number(raw.trim());
|
|
4611
4796
|
return Number.isFinite(pid) ? pid : null;
|
|
4612
4797
|
} catch {
|
package/dist/cli/index.js
CHANGED
|
@@ -8825,7 +8825,7 @@ var init_package = __esm(() => {
|
|
|
8825
8825
|
package_default = {
|
|
8826
8826
|
$schema: "https://json.schemastore.org/package.json",
|
|
8827
8827
|
name: "@sma1lboy/kobe",
|
|
8828
|
-
version: "0.5.
|
|
8828
|
+
version: "0.5.2",
|
|
8829
8829
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
8830
8830
|
type: "module",
|
|
8831
8831
|
packageManager: "bun@1.3.13",
|
|
@@ -10584,6 +10584,9 @@ class Orchestrator {
|
|
|
10584
10584
|
tasksSignal() {
|
|
10585
10585
|
return this.tasksAcc;
|
|
10586
10586
|
}
|
|
10587
|
+
planUsageSignal() {
|
|
10588
|
+
return () => null;
|
|
10589
|
+
}
|
|
10587
10590
|
subscribeTasks(listener2) {
|
|
10588
10591
|
return this.store.subscribe(listener2);
|
|
10589
10592
|
}
|
|
@@ -11287,16 +11290,21 @@ class RemoteOrchestrator {
|
|
|
11287
11290
|
setTasks;
|
|
11288
11291
|
runStateAcc;
|
|
11289
11292
|
setRunState;
|
|
11293
|
+
planUsageAcc;
|
|
11294
|
+
setPlanUsage;
|
|
11290
11295
|
subscribers = new Map;
|
|
11291
11296
|
pendingInputBroker = new InMemoryPendingInputBroker;
|
|
11292
11297
|
constructor(client) {
|
|
11293
11298
|
this.client = client;
|
|
11294
11299
|
const [tasks, setTasks] = createSignal([]);
|
|
11295
11300
|
const [runState, setRunState] = createSignal(new Map);
|
|
11301
|
+
const [planUsage, setPlanUsage] = createSignal(null);
|
|
11296
11302
|
this.tasksAcc = tasks;
|
|
11297
11303
|
this.setTasks = (next) => setTasks(() => next);
|
|
11298
11304
|
this.runStateAcc = runState;
|
|
11299
11305
|
this.setRunState = (next) => setRunState(() => next);
|
|
11306
|
+
this.planUsageAcc = planUsage;
|
|
11307
|
+
this.setPlanUsage = (next) => setPlanUsage(() => next);
|
|
11300
11308
|
this.client.on("*", (frame) => this.handleEvent(frame.name, frame.payload));
|
|
11301
11309
|
}
|
|
11302
11310
|
async init() {
|
|
@@ -11316,6 +11324,8 @@ class RemoteOrchestrator {
|
|
|
11316
11324
|
if (seed.size > 0)
|
|
11317
11325
|
this.setRunState(seed);
|
|
11318
11326
|
}
|
|
11327
|
+
if (hello.planUsage)
|
|
11328
|
+
this.setPlanUsage(hello.planUsage);
|
|
11319
11329
|
await this.client.request("subscribe", { taskIds: "all" });
|
|
11320
11330
|
if (hello.pending) {
|
|
11321
11331
|
for (const [taskId, entries] of Object.entries(hello.pending)) {
|
|
@@ -11345,6 +11355,9 @@ class RemoteOrchestrator {
|
|
|
11345
11355
|
chatRunStateSignal() {
|
|
11346
11356
|
return this.runStateAcc;
|
|
11347
11357
|
}
|
|
11358
|
+
planUsageSignal() {
|
|
11359
|
+
return this.planUsageAcc;
|
|
11360
|
+
}
|
|
11348
11361
|
listTasks() {
|
|
11349
11362
|
return this.tasksAcc().slice();
|
|
11350
11363
|
}
|
|
@@ -11477,6 +11490,11 @@ class RemoteOrchestrator {
|
|
|
11477
11490
|
}
|
|
11478
11491
|
return;
|
|
11479
11492
|
}
|
|
11493
|
+
if (name === "plan.usage") {
|
|
11494
|
+
const usage = obj.usage;
|
|
11495
|
+
this.setPlanUsage(usage ?? null);
|
|
11496
|
+
return;
|
|
11497
|
+
}
|
|
11480
11498
|
const taskId = obj.taskId;
|
|
11481
11499
|
const tabId = obj.tabId;
|
|
11482
11500
|
if (!taskId || !tabId)
|
|
@@ -13179,8 +13197,10 @@ var init_resizable_edge = __esm(() => {
|
|
|
13179
13197
|
// src/tui/context/focus.tsx
|
|
13180
13198
|
function FocusProvider(props) {
|
|
13181
13199
|
const [focused, setFocusedSignal] = createSignal(props.initial ?? "sidebar");
|
|
13200
|
+
const [refocusTick, setRefocusTick] = createSignal(0);
|
|
13182
13201
|
const renderer = useRenderer();
|
|
13183
13202
|
function setFocused(pane) {
|
|
13203
|
+
setRefocusTick((t) => t + 1);
|
|
13184
13204
|
if (focused() === pane)
|
|
13185
13205
|
return;
|
|
13186
13206
|
const current = renderer?.currentFocusedRenderable;
|
|
@@ -13209,7 +13229,8 @@ function FocusProvider(props) {
|
|
|
13209
13229
|
focused,
|
|
13210
13230
|
is,
|
|
13211
13231
|
setFocused,
|
|
13212
|
-
cycle
|
|
13232
|
+
cycle,
|
|
13233
|
+
refocusTick
|
|
13213
13234
|
};
|
|
13214
13235
|
return createComponent2(FocusContext.Provider, {
|
|
13215
13236
|
value,
|
|
@@ -14704,7 +14725,24 @@ function extractMessage(record, fallbackSessionId) {
|
|
|
14704
14725
|
const content = inner.content;
|
|
14705
14726
|
const ts = typeof record.timestamp === "string" ? record.timestamp : new Date().toISOString();
|
|
14706
14727
|
const sid = typeof record.sessionId === "string" ? record.sessionId : fallbackSessionId;
|
|
14707
|
-
|
|
14728
|
+
const usage = extractUsage(inner.usage);
|
|
14729
|
+
return usage ? { role, content, timestamp: ts, sessionId: sid, usage } : { role, content, timestamp: ts, sessionId: sid };
|
|
14730
|
+
}
|
|
14731
|
+
function extractUsage(v) {
|
|
14732
|
+
if (!isObject(v))
|
|
14733
|
+
return;
|
|
14734
|
+
const inTok = typeof v.input_tokens === "number" ? v.input_tokens : undefined;
|
|
14735
|
+
const outTok = typeof v.output_tokens === "number" ? v.output_tokens : undefined;
|
|
14736
|
+
if (inTok === undefined || outTok === undefined)
|
|
14737
|
+
return;
|
|
14738
|
+
const cacheRead = typeof v.cache_read_input_tokens === "number" ? v.cache_read_input_tokens : undefined;
|
|
14739
|
+
const cacheCreate = typeof v.cache_creation_input_tokens === "number" ? v.cache_creation_input_tokens : undefined;
|
|
14740
|
+
return {
|
|
14741
|
+
input_tokens: inTok,
|
|
14742
|
+
output_tokens: outTok,
|
|
14743
|
+
...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {},
|
|
14744
|
+
...cacheCreate !== undefined ? { cache_creation_input_tokens: cacheCreate } : {}
|
|
14745
|
+
};
|
|
14708
14746
|
}
|
|
14709
14747
|
function isObject(v) {
|
|
14710
14748
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -15598,6 +15636,27 @@ var init_engine_bootstrap = __esm(() => {
|
|
|
15598
15636
|
init_claude_code_local();
|
|
15599
15637
|
});
|
|
15600
15638
|
|
|
15639
|
+
// src/tui/lib/format-plan-usage.ts
|
|
15640
|
+
function pct(value) {
|
|
15641
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
15642
|
+
return null;
|
|
15643
|
+
return `${Math.round(value)}%`;
|
|
15644
|
+
}
|
|
15645
|
+
function formatPlanUsageCompact(usage) {
|
|
15646
|
+
if (!usage)
|
|
15647
|
+
return null;
|
|
15648
|
+
const fiveHour = pct(usage.fiveHour?.utilization);
|
|
15649
|
+
const sevenDay = pct(usage.sevenDay?.utilization);
|
|
15650
|
+
const parts = [];
|
|
15651
|
+
if (fiveHour)
|
|
15652
|
+
parts.push(`5h ${fiveHour}`);
|
|
15653
|
+
if (sevenDay)
|
|
15654
|
+
parts.push(`7d ${sevenDay}`);
|
|
15655
|
+
if (parts.length === 0)
|
|
15656
|
+
return null;
|
|
15657
|
+
return `Plan ${parts.join(" \xB7 ")}`;
|
|
15658
|
+
}
|
|
15659
|
+
|
|
15601
15660
|
// src/tui/lib/use-pane-sizes.ts
|
|
15602
15661
|
function usePaneSizes(kv) {
|
|
15603
15662
|
const dims = useTerminalDimensions();
|
|
@@ -17020,6 +17079,7 @@ function Composer(props) {
|
|
|
17020
17079
|
const {
|
|
17021
17080
|
theme
|
|
17022
17081
|
} = useTheme();
|
|
17082
|
+
const focusCtx = useFocus();
|
|
17023
17083
|
let textareaRef;
|
|
17024
17084
|
let historyIndex = null;
|
|
17025
17085
|
let liveDraftSnapshot = "";
|
|
@@ -17054,6 +17114,7 @@ function Composer(props) {
|
|
|
17054
17114
|
setSlashCursor((cur) => len === 0 ? 0 : Math.min(cur, len - 1));
|
|
17055
17115
|
});
|
|
17056
17116
|
createEffect(() => {
|
|
17117
|
+
focusCtx.refocusTick();
|
|
17057
17118
|
const ref = textareaRef;
|
|
17058
17119
|
if (!ref)
|
|
17059
17120
|
return;
|
|
@@ -17888,6 +17949,7 @@ var init_Composer = __esm(() => {
|
|
|
17888
17949
|
init_solid();
|
|
17889
17950
|
init_dev();
|
|
17890
17951
|
init_border();
|
|
17952
|
+
init_focus();
|
|
17891
17953
|
init_theme();
|
|
17892
17954
|
init_clipboard_image();
|
|
17893
17955
|
init_history2();
|
|
@@ -20349,7 +20411,7 @@ var init_user_slashes = () => {};
|
|
|
20349
20411
|
|
|
20350
20412
|
// src/tui/panes/chat/context-meter.ts
|
|
20351
20413
|
function totalContextTokens(u) {
|
|
20352
|
-
return u.input_tokens +
|
|
20414
|
+
return u.input_tokens + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
|
|
20353
20415
|
}
|
|
20354
20416
|
function contextWindowTokensForModel(modelId) {
|
|
20355
20417
|
const id = modelId ?? resolveDefaultModelId();
|
|
@@ -20378,8 +20440,8 @@ function formatContextUsageCompact(u, modelId) {
|
|
|
20378
20440
|
const total = totalContextTokens(u);
|
|
20379
20441
|
if (total <= 0 || window <= 0)
|
|
20380
20442
|
return null;
|
|
20381
|
-
const
|
|
20382
|
-
return `${
|
|
20443
|
+
const pct2 = Math.min(100, Math.max(0, Math.round(total / window * 100)));
|
|
20444
|
+
return `${pct2}% \xB7 ${formatTokShort(total)}/${formatTokShort(window)}`;
|
|
20383
20445
|
}
|
|
20384
20446
|
var LONG_CTX = 1e6, STD_CTX = 200000;
|
|
20385
20447
|
var init_context_meter = __esm(() => {
|
|
@@ -20431,7 +20493,19 @@ function setMessagesFromHistory(state, past) {
|
|
|
20431
20493
|
for (const m of past) {
|
|
20432
20494
|
appendRowsFromMessage(rows, toolIndexById, m);
|
|
20433
20495
|
}
|
|
20434
|
-
|
|
20496
|
+
let latestUsage;
|
|
20497
|
+
for (let i = past.length - 1;i >= 0; i--) {
|
|
20498
|
+
const u = past[i]?.usage;
|
|
20499
|
+
if (u) {
|
|
20500
|
+
latestUsage = u;
|
|
20501
|
+
break;
|
|
20502
|
+
}
|
|
20503
|
+
}
|
|
20504
|
+
return {
|
|
20505
|
+
...state,
|
|
20506
|
+
messages: capMessages(rows, new Date().toISOString()),
|
|
20507
|
+
...latestUsage ? { lastUsage: latestUsage } : {}
|
|
20508
|
+
};
|
|
20435
20509
|
}
|
|
20436
20510
|
function enqueuePrompt(state, prompt, nowIso = new Date().toISOString()) {
|
|
20437
20511
|
if (state.queue.length >= QUEUE_SOFT_CAP)
|
|
@@ -22305,13 +22379,15 @@ __export(exports_bridge, {
|
|
|
22305
22379
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
22306
22380
|
import { homedir as homedir11 } from "os";
|
|
22307
22381
|
import { join as join14 } from "path";
|
|
22382
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
22308
22383
|
async function startBridge(orch, opts = {}) {
|
|
22309
22384
|
const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir11();
|
|
22310
22385
|
const runDir = join14(home, ".kobe", "run");
|
|
22311
22386
|
const socketPath = join14(runDir, `bridge-${process.pid}.sock`);
|
|
22312
22387
|
const mcpConfigPath = join14(runDir, `mcp-${process.pid}.json`);
|
|
22313
22388
|
const server = await startBridgeServer(orch, socketPath);
|
|
22314
|
-
const
|
|
22389
|
+
const moduleExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
|
|
22390
|
+
const entry = fileURLToPath2(new URL(`../../cli/index${moduleExt}`, import.meta.url));
|
|
22315
22391
|
const mcpConfig = {
|
|
22316
22392
|
mcpServers: {
|
|
22317
22393
|
kobe: {
|
|
@@ -22351,6 +22427,8 @@ function Shell(props) {
|
|
|
22351
22427
|
const [selectedId, setSelectedId] = createSignal(null);
|
|
22352
22428
|
const [pendingPrompt, setPendingPrompt] = createSignal(null);
|
|
22353
22429
|
const [workspaceContextAside, setWorkspaceContextAside] = createSignal(null);
|
|
22430
|
+
const planUsageAcc = props.orchestrator.planUsageSignal();
|
|
22431
|
+
const workspacePlanAside = createMemo(() => formatPlanUsageCompact(planUsageAcc()));
|
|
22354
22432
|
const [updateInfo, setUpdateInfo] = createSignal(null);
|
|
22355
22433
|
onMount(() => {
|
|
22356
22434
|
checkLatestVersion().then((info) => {
|
|
@@ -22433,6 +22511,14 @@ function Shell(props) {
|
|
|
22433
22511
|
selectFileTab,
|
|
22434
22512
|
closeFileTab
|
|
22435
22513
|
} = workspaceTabs;
|
|
22514
|
+
const workspaceAsideRight = createMemo(() => {
|
|
22515
|
+
const plan = workspacePlanAside();
|
|
22516
|
+
const ctx3 = isChatTabActive() ? workspaceContextAside() : null;
|
|
22517
|
+
const parts = [plan, ctx3].filter((v) => Boolean(v));
|
|
22518
|
+
if (parts.length === 0)
|
|
22519
|
+
return;
|
|
22520
|
+
return parts.join(" \u2022 ");
|
|
22521
|
+
});
|
|
22436
22522
|
let pendingPersistedId = persistedSelectedId ?? null;
|
|
22437
22523
|
createEffect(() => {
|
|
22438
22524
|
const tasks = tasksAcc();
|
|
@@ -22565,7 +22651,7 @@ function Shell(props) {
|
|
|
22565
22651
|
return activeTask()?.title ?? "no task";
|
|
22566
22652
|
},
|
|
22567
22653
|
get asideRight() {
|
|
22568
|
-
return
|
|
22654
|
+
return workspaceAsideRight();
|
|
22569
22655
|
},
|
|
22570
22656
|
get focused() {
|
|
22571
22657
|
return focusedPane() === "workspace";
|
package/package.json
CHANGED