@sma1lboy/kobe 0.5.18 → 0.5.20
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 +665 -173
- package/dist/cli/index.js +2482 -1003
- package/package.json +4 -4
package/dist/bin/kobed.js
CHANGED
|
@@ -65,6 +65,9 @@ function fitSocketPath(naturalPath, homeDir, role, pidTag) {
|
|
|
65
65
|
throw new Error(`kobe socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
|
|
66
66
|
}
|
|
67
67
|
function defaultDaemonSocketPath(homeDir) {
|
|
68
|
+
const override = process.env.KOBE_DAEMON_SOCKET_PATH;
|
|
69
|
+
if (override && override.length > 0)
|
|
70
|
+
return override;
|
|
68
71
|
const explicit = homeDir ?? process.env.KOBE_HOME_DIR;
|
|
69
72
|
if (explicit && explicit.length > 0) {
|
|
70
73
|
return fitSocketPath(join(explicit, ".kobe", "daemon.sock"), explicit, "daemon");
|
|
@@ -77,6 +80,9 @@ function defaultDaemonSocketPath(homeDir) {
|
|
|
77
80
|
return fitSocketPath(join(home, ".kobe", "daemon.sock"), home, "daemon");
|
|
78
81
|
}
|
|
79
82
|
function defaultDaemonPidPath(homeDir = process.env.KOBE_HOME_DIR ?? homedir()) {
|
|
83
|
+
const override = process.env.KOBE_DAEMON_PID_PATH;
|
|
84
|
+
if (override && override.length > 0)
|
|
85
|
+
return override;
|
|
80
86
|
return join(homeDir, ".kobe", "daemon.pid");
|
|
81
87
|
}
|
|
82
88
|
var SOCKET_PATH_SAFETY_LIMIT = 100;
|
|
@@ -180,7 +186,11 @@ class KobeDaemonClient {
|
|
|
180
186
|
this.socket = null;
|
|
181
187
|
}
|
|
182
188
|
forceDisconnect() {
|
|
183
|
-
this.socket
|
|
189
|
+
const socket = this.socket;
|
|
190
|
+
if (!socket)
|
|
191
|
+
return;
|
|
192
|
+
this.socket = null;
|
|
193
|
+
socket.destroy();
|
|
184
194
|
}
|
|
185
195
|
on(name, handler) {
|
|
186
196
|
let set = this.handlers.get(name);
|
|
@@ -241,7 +251,7 @@ class KobeDaemonClient {
|
|
|
241
251
|
});
|
|
242
252
|
}
|
|
243
253
|
onSocketClose(which) {
|
|
244
|
-
if (this.socket !== which
|
|
254
|
+
if (this.socket !== which)
|
|
245
255
|
return;
|
|
246
256
|
this.socket = null;
|
|
247
257
|
for (const pending of this.pending.values())
|
|
@@ -300,6 +310,8 @@ var init_client = () => {};
|
|
|
300
310
|
// src/client/daemon-process.ts
|
|
301
311
|
import { spawn } from "child_process";
|
|
302
312
|
import { existsSync } from "fs";
|
|
313
|
+
import { unlink } from "fs/promises";
|
|
314
|
+
import { homedir as homedir2 } from "os";
|
|
303
315
|
import { dirname, join as join2, resolve } from "path";
|
|
304
316
|
import { fileURLToPath } from "url";
|
|
305
317
|
async function ensureDaemonReachable() {
|
|
@@ -332,6 +344,53 @@ async function connectOrStartDaemon() {
|
|
|
332
344
|
await client.connect();
|
|
333
345
|
return client;
|
|
334
346
|
}
|
|
347
|
+
async function connectOrStartOwnedDaemon() {
|
|
348
|
+
const homeDir = process.env.KOBE_HOME_DIR ?? homedir2();
|
|
349
|
+
const socketPath = fitSocketPath(join2(homeDir, ".kobe", `daemon-${process.pid}.sock`), homeDir, "daemon", process.pid);
|
|
350
|
+
const pidPath = join2(homeDir, ".kobe", `daemon-${process.pid}.pid`);
|
|
351
|
+
await ensureOwnedDaemonReachable(socketPath, pidPath);
|
|
352
|
+
const client = new KobeDaemonClient(socketPath);
|
|
353
|
+
await client.connect();
|
|
354
|
+
return {
|
|
355
|
+
client,
|
|
356
|
+
socketPath,
|
|
357
|
+
pidPath,
|
|
358
|
+
stop: async () => {
|
|
359
|
+
try {
|
|
360
|
+
await client.request("daemon.stop");
|
|
361
|
+
} catch {} finally {
|
|
362
|
+
client.close();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
async function ensureOwnedDaemonReachable(socketPath, pidPath) {
|
|
368
|
+
await unlink(socketPath).catch(() => {});
|
|
369
|
+
const { entry, runWithBun } = resolveKobedEntry();
|
|
370
|
+
const env = {
|
|
371
|
+
...process.env,
|
|
372
|
+
KOBE_DAEMON_SOCKET_PATH: socketPath,
|
|
373
|
+
KOBE_DAEMON_PID_PATH: pidPath
|
|
374
|
+
};
|
|
375
|
+
const child = runWithBun ? spawn(process.execPath, [entry, "start"], {
|
|
376
|
+
detached: true,
|
|
377
|
+
stdio: "ignore",
|
|
378
|
+
env
|
|
379
|
+
}) : spawn(entry, ["start"], {
|
|
380
|
+
detached: true,
|
|
381
|
+
stdio: "ignore",
|
|
382
|
+
env
|
|
383
|
+
});
|
|
384
|
+
child.unref();
|
|
385
|
+
const deadline = Date.now() + 5000;
|
|
386
|
+
while (Date.now() < deadline) {
|
|
387
|
+
if (await testCanConnect(socketPath)) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
await new Promise((resolveTimer) => setTimeout(resolveTimer, 100));
|
|
391
|
+
}
|
|
392
|
+
throw new Error(`kobe: owned daemon did not start at ${socketPath}`);
|
|
393
|
+
}
|
|
335
394
|
async function testCanConnect(socketPath) {
|
|
336
395
|
const probe = new KobeDaemonClient(socketPath);
|
|
337
396
|
try {
|
|
@@ -370,6 +429,8 @@ var init_daemon_process = __esm(() => {
|
|
|
370
429
|
|
|
371
430
|
// src/session/usage-metrics.ts
|
|
372
431
|
function totalContextTokens(u) {
|
|
432
|
+
if (typeof u.context_tokens === "number" && Number.isFinite(u.context_tokens))
|
|
433
|
+
return Math.max(0, u.context_tokens);
|
|
373
434
|
return u.input_tokens + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
|
|
374
435
|
}
|
|
375
436
|
function parseTimestampMs(value) {
|
|
@@ -451,7 +512,7 @@ function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
|
|
|
451
512
|
// src/engine/claude-code-local/binary.ts
|
|
452
513
|
import { spawnSync } from "child_process";
|
|
453
514
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
454
|
-
import { homedir as
|
|
515
|
+
import { homedir as homedir3 } from "os";
|
|
455
516
|
import path from "path";
|
|
456
517
|
async function findClaudeBinary(deps = defaultDeps) {
|
|
457
518
|
const checked = [];
|
|
@@ -524,7 +585,7 @@ var init_binary = __esm(() => {
|
|
|
524
585
|
return process.env[name];
|
|
525
586
|
},
|
|
526
587
|
home() {
|
|
527
|
-
return
|
|
588
|
+
return homedir3();
|
|
528
589
|
},
|
|
529
590
|
which(name) {
|
|
530
591
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -614,7 +675,7 @@ var init_models = __esm(() => {
|
|
|
614
675
|
|
|
615
676
|
// src/engine/claude-code-local/settings.ts
|
|
616
677
|
import { readFileSync } from "fs";
|
|
617
|
-
import { homedir as
|
|
678
|
+
import { homedir as homedir4 } from "os";
|
|
618
679
|
import { join as join3 } from "path";
|
|
619
680
|
function readClaudeSettings() {
|
|
620
681
|
if (cached !== undefined)
|
|
@@ -643,7 +704,7 @@ function resolveClaudeDefaultModelId() {
|
|
|
643
704
|
}
|
|
644
705
|
var SETTINGS_PATH, cached, CLAUDE_FALLBACK_DEFAULT_MODEL_ID = "claude-opus-4-7[1m]";
|
|
645
706
|
var init_settings = __esm(() => {
|
|
646
|
-
SETTINGS_PATH = join3(
|
|
707
|
+
SETTINGS_PATH = join3(homedir4(), ".claude", "settings.json");
|
|
647
708
|
});
|
|
648
709
|
|
|
649
710
|
// src/engine/claude-code-local/capabilities.ts
|
|
@@ -719,8 +780,8 @@ function normalizeClaudeContent(content) {
|
|
|
719
780
|
|
|
720
781
|
// src/engine/claude-code-local/history.ts
|
|
721
782
|
import { randomUUID } from "crypto";
|
|
722
|
-
import { appendFile, mkdir, readFile, readdir, unlink, writeFile } from "fs/promises";
|
|
723
|
-
import { homedir as
|
|
783
|
+
import { appendFile, mkdir, readFile, readdir, unlink as unlink2, writeFile } from "fs/promises";
|
|
784
|
+
import { homedir as homedir5 } from "os";
|
|
724
785
|
import path2 from "path";
|
|
725
786
|
function encodeCwd(cwd) {
|
|
726
787
|
return cwd.replace(/[/.]/g, "-");
|
|
@@ -746,7 +807,7 @@ async function deleteHistory(sessionId, deps = defaultDeps2) {
|
|
|
746
807
|
for (const dir of projectDirs) {
|
|
747
808
|
const candidate = path2.join(root, dir, `${sessionId}.jsonl`);
|
|
748
809
|
try {
|
|
749
|
-
await
|
|
810
|
+
await unlink2(candidate);
|
|
750
811
|
} catch (err) {
|
|
751
812
|
if (err.code === "ENOENT")
|
|
752
813
|
continue;
|
|
@@ -890,7 +951,7 @@ var defaultDeps2;
|
|
|
890
951
|
var init_history = __esm(() => {
|
|
891
952
|
defaultDeps2 = {
|
|
892
953
|
projectsDir() {
|
|
893
|
-
return path2.join(
|
|
954
|
+
return path2.join(homedir5(), ".claude", "projects");
|
|
894
955
|
},
|
|
895
956
|
async readdir(p) {
|
|
896
957
|
try {
|
|
@@ -979,7 +1040,7 @@ function delay(ms) {
|
|
|
979
1040
|
|
|
980
1041
|
// src/engine/claude-code-local/sessions.ts
|
|
981
1042
|
import { readFile as readFile2, readdir as readdir2, stat } from "fs/promises";
|
|
982
|
-
import { homedir as
|
|
1043
|
+
import { homedir as homedir6 } from "os";
|
|
983
1044
|
import path3 from "path";
|
|
984
1045
|
async function listSessionsForCwd(cwd, deps = defaultDeps3) {
|
|
985
1046
|
const projectDir = path3.join(deps.projectsDir(), encodeCwd(cwd));
|
|
@@ -1047,7 +1108,7 @@ var init_sessions = __esm(() => {
|
|
|
1047
1108
|
init_history();
|
|
1048
1109
|
defaultDeps3 = {
|
|
1049
1110
|
projectsDir() {
|
|
1050
|
-
return path3.join(
|
|
1111
|
+
return path3.join(homedir6(), ".claude", "projects");
|
|
1051
1112
|
},
|
|
1052
1113
|
async readdir(p) {
|
|
1053
1114
|
try {
|
|
@@ -1450,10 +1511,363 @@ var init_claude_code_local = __esm(() => {
|
|
|
1450
1511
|
init_spawn();
|
|
1451
1512
|
});
|
|
1452
1513
|
|
|
1514
|
+
// src/env.ts
|
|
1515
|
+
import { homedir as homedir7 } from "os";
|
|
1516
|
+
import { join as join4 } from "path";
|
|
1517
|
+
function isDev() {
|
|
1518
|
+
return process.env.KOBE_DEV === "1";
|
|
1519
|
+
}
|
|
1520
|
+
function homeDir() {
|
|
1521
|
+
return process.env.KOBE_HOME_DIR ?? homedir7();
|
|
1522
|
+
}
|
|
1523
|
+
function kobeStateDir() {
|
|
1524
|
+
return join4(homeDir(), ".kobe");
|
|
1525
|
+
}
|
|
1526
|
+
function kvStatePath() {
|
|
1527
|
+
return join4(homeDir(), ".config", "kobe", "state.json");
|
|
1528
|
+
}
|
|
1529
|
+
var init_env = () => {};
|
|
1530
|
+
|
|
1531
|
+
// src/engine/codex-local/app-server.ts
|
|
1532
|
+
import { spawn as spawn3 } from "child_process";
|
|
1533
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1534
|
+
function resolveCodexBackend(env = process.env, readPreference = readCodexBackendPreference) {
|
|
1535
|
+
if (env.KOBE_CODEX_BACKEND === "exec")
|
|
1536
|
+
return "exec";
|
|
1537
|
+
if (env.KOBE_CODEX_BACKEND === "app-server" || env.KOBE_CODEX_APP_SERVER === "1")
|
|
1538
|
+
return "app-server";
|
|
1539
|
+
return readPreference() ?? "app-server";
|
|
1540
|
+
}
|
|
1541
|
+
function readCodexBackendPreference() {
|
|
1542
|
+
try {
|
|
1543
|
+
const parsed = JSON.parse(readFileSync2(kvStatePath(), "utf8"));
|
|
1544
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
1545
|
+
return;
|
|
1546
|
+
const raw = parsed[CODEX_BACKEND_KV_KEY];
|
|
1547
|
+
if (raw === "exec" || raw === "app-server")
|
|
1548
|
+
return raw;
|
|
1549
|
+
} catch {}
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
function spawnCodexAppServerTurn(opts) {
|
|
1553
|
+
const args = buildAppServerArgs(opts);
|
|
1554
|
+
const proc = spawn3(opts.binaryPath, args, {
|
|
1555
|
+
cwd: opts.cwd,
|
|
1556
|
+
env: { ...process.env, ...opts.env ?? {} },
|
|
1557
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1558
|
+
});
|
|
1559
|
+
const rpc = new AppServerRpc(proc, opts);
|
|
1560
|
+
rpc.run();
|
|
1561
|
+
return {
|
|
1562
|
+
proc,
|
|
1563
|
+
stdout: proc.stdout,
|
|
1564
|
+
stderr: proc.stderr,
|
|
1565
|
+
args,
|
|
1566
|
+
ready: rpc.ready,
|
|
1567
|
+
closed: rpc.closed
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
function buildAppServerArgs(opts) {
|
|
1571
|
+
const mode = permissionPayloads(opts.permissionMode);
|
|
1572
|
+
return ["app-server", "-c", `approval_policy="${mode.approvalPolicy}"`, "-c", `sandbox_mode="${mode.threadSandbox}"`];
|
|
1573
|
+
}
|
|
1574
|
+
function codexAppServerUsageToSnapshot(params) {
|
|
1575
|
+
const root = asObject(params);
|
|
1576
|
+
const tokenUsage = asObject(root?.tokenUsage ?? root?.token_usage);
|
|
1577
|
+
const last = asObject(tokenUsage?.last);
|
|
1578
|
+
if (!last)
|
|
1579
|
+
return null;
|
|
1580
|
+
const totalTokens = numberOr(last.totalTokens ?? last.total_tokens, 0);
|
|
1581
|
+
const inputTokens = numberOr(last.inputTokens ?? last.input_tokens, 0);
|
|
1582
|
+
const cachedInputTokens = numberOr(last.cachedInputTokens ?? last.cached_input_tokens, 0);
|
|
1583
|
+
const outputTokens = numberOr(last.outputTokens ?? last.output_tokens, 0);
|
|
1584
|
+
const contextWindow = numberOr(tokenUsage?.modelContextWindow ?? tokenUsage?.model_context_window, 0);
|
|
1585
|
+
if (totalTokens <= 0 && inputTokens <= 0 && outputTokens <= 0 && cachedInputTokens <= 0)
|
|
1586
|
+
return null;
|
|
1587
|
+
return {
|
|
1588
|
+
type: "usage",
|
|
1589
|
+
input_tokens: Math.max(0, inputTokens - cachedInputTokens),
|
|
1590
|
+
output_tokens: outputTokens,
|
|
1591
|
+
...cachedInputTokens > 0 ? { cache_read_input_tokens: cachedInputTokens } : {},
|
|
1592
|
+
...totalTokens > 0 ? { context_tokens: totalTokens } : {},
|
|
1593
|
+
...contextWindow > 0 ? { context_window_tokens: contextWindow } : {}
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
function codexAppServerItemNotificationToEvents(method, params) {
|
|
1597
|
+
if (method !== "item/started" && method !== "item/completed")
|
|
1598
|
+
return [];
|
|
1599
|
+
const p = asObject(params);
|
|
1600
|
+
const item = asObject(p?.item);
|
|
1601
|
+
const itemType = typeof item?.type === "string" ? item.type : "tool";
|
|
1602
|
+
if (isNonToolTranscriptItem(itemType))
|
|
1603
|
+
return [];
|
|
1604
|
+
const payload = stripItemHousekeeping(item ?? {});
|
|
1605
|
+
if (method === "item/started")
|
|
1606
|
+
return [{ type: "tool.start", name: itemType, input: payload }];
|
|
1607
|
+
return [{ type: "tool.result", name: itemType, output: payload }];
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
class AppServerRpc {
|
|
1611
|
+
proc;
|
|
1612
|
+
opts;
|
|
1613
|
+
pending = new Map;
|
|
1614
|
+
readBuffer = "";
|
|
1615
|
+
nextId = 1;
|
|
1616
|
+
readyResolve = () => {};
|
|
1617
|
+
readyReject = () => {};
|
|
1618
|
+
ready = new Promise((resolve2, reject) => {
|
|
1619
|
+
this.readyResolve = resolve2;
|
|
1620
|
+
this.readyReject = reject;
|
|
1621
|
+
});
|
|
1622
|
+
closed;
|
|
1623
|
+
constructor(proc, opts) {
|
|
1624
|
+
this.proc = proc;
|
|
1625
|
+
this.opts = opts;
|
|
1626
|
+
this.closed = new Promise((resolve2) => {
|
|
1627
|
+
proc.once("exit", (code, signal) => resolve2({ code, signal }));
|
|
1628
|
+
});
|
|
1629
|
+
proc.stdout.setEncoding("utf8");
|
|
1630
|
+
proc.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
1631
|
+
proc.stdout.on("error", () => {});
|
|
1632
|
+
proc.stderr.on("error", () => {});
|
|
1633
|
+
proc.once("error", (err) => {
|
|
1634
|
+
this.readyReject(err);
|
|
1635
|
+
this.rejectAll(err);
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
async run() {
|
|
1639
|
+
try {
|
|
1640
|
+
await this.call("initialize", {
|
|
1641
|
+
clientInfo: {
|
|
1642
|
+
name: "kobe",
|
|
1643
|
+
title: "kobe",
|
|
1644
|
+
version: "0.0.0"
|
|
1645
|
+
},
|
|
1646
|
+
capabilities: { experimentalApi: true }
|
|
1647
|
+
});
|
|
1648
|
+
this.notify("initialized", undefined);
|
|
1649
|
+
const sessionId = this.opts.resumeSessionId ? await this.resumeThread(this.opts.resumeSessionId) : await this.startThread();
|
|
1650
|
+
this.opts.onSessionId(sessionId);
|
|
1651
|
+
this.readyResolve(sessionId);
|
|
1652
|
+
await this.startTurn(sessionId);
|
|
1653
|
+
} catch (err) {
|
|
1654
|
+
this.readyReject(err);
|
|
1655
|
+
this.opts.onEvent({ type: "error", message: `codex app-server failure: ${stringifyErr2(err)}` });
|
|
1656
|
+
this.close();
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
async startThread() {
|
|
1660
|
+
const mode = permissionPayloads(this.opts.permissionMode);
|
|
1661
|
+
const result = asObject(await this.call("thread/start", {
|
|
1662
|
+
cwd: this.opts.cwd,
|
|
1663
|
+
model: this.opts.model ?? null,
|
|
1664
|
+
approvalPolicy: mode.approvalPolicy,
|
|
1665
|
+
sandbox: mode.threadSandbox,
|
|
1666
|
+
ephemeral: false
|
|
1667
|
+
}));
|
|
1668
|
+
const thread = asObject(result?.thread);
|
|
1669
|
+
const id = typeof thread?.id === "string" ? thread.id : undefined;
|
|
1670
|
+
if (!id)
|
|
1671
|
+
throw new Error("thread/start did not return a thread id");
|
|
1672
|
+
return id;
|
|
1673
|
+
}
|
|
1674
|
+
async resumeThread(sessionId) {
|
|
1675
|
+
const mode = permissionPayloads(this.opts.permissionMode);
|
|
1676
|
+
const result = asObject(await this.call("thread/resume", {
|
|
1677
|
+
threadId: sessionId,
|
|
1678
|
+
cwd: this.opts.cwd,
|
|
1679
|
+
model: this.opts.model ?? null,
|
|
1680
|
+
approvalPolicy: mode.approvalPolicy,
|
|
1681
|
+
sandbox: mode.threadSandbox
|
|
1682
|
+
}));
|
|
1683
|
+
const thread = asObject(result?.thread);
|
|
1684
|
+
const id = typeof thread?.id === "string" ? thread.id : undefined;
|
|
1685
|
+
if (!id)
|
|
1686
|
+
throw new Error("thread/resume did not return a thread id");
|
|
1687
|
+
return id;
|
|
1688
|
+
}
|
|
1689
|
+
async startTurn(sessionId) {
|
|
1690
|
+
const mode = permissionPayloads(this.opts.permissionMode);
|
|
1691
|
+
await this.call("turn/start", {
|
|
1692
|
+
threadId: sessionId,
|
|
1693
|
+
input: [{ type: "text", text: this.opts.prompt, text_elements: [] }],
|
|
1694
|
+
cwd: this.opts.cwd,
|
|
1695
|
+
model: this.opts.model ?? null,
|
|
1696
|
+
effort: this.opts.modelEffort ?? null,
|
|
1697
|
+
approvalPolicy: mode.approvalPolicy,
|
|
1698
|
+
sandboxPolicy: mode.turnSandboxPolicy
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
call(method, params) {
|
|
1702
|
+
const id = this.nextId++;
|
|
1703
|
+
this.send({ jsonrpc: "2.0", id, method, params });
|
|
1704
|
+
return new Promise((resolve2, reject) => {
|
|
1705
|
+
this.pending.set(id, { resolve: resolve2, reject });
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
notify(method, params) {
|
|
1709
|
+
this.send(params === undefined ? { jsonrpc: "2.0", method } : { jsonrpc: "2.0", method, params });
|
|
1710
|
+
}
|
|
1711
|
+
send(payload) {
|
|
1712
|
+
this.proc.stdin.write(`${JSON.stringify(payload)}
|
|
1713
|
+
`);
|
|
1714
|
+
}
|
|
1715
|
+
onStdout(chunk) {
|
|
1716
|
+
this.readBuffer += chunk;
|
|
1717
|
+
let lineEnd = this.readBuffer.indexOf(`
|
|
1718
|
+
`);
|
|
1719
|
+
while (lineEnd !== -1) {
|
|
1720
|
+
const line = this.readBuffer.slice(0, lineEnd).trim();
|
|
1721
|
+
this.readBuffer = this.readBuffer.slice(lineEnd + 1);
|
|
1722
|
+
if (line.length > 0)
|
|
1723
|
+
this.handleLine(line);
|
|
1724
|
+
lineEnd = this.readBuffer.indexOf(`
|
|
1725
|
+
`);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
handleLine(line) {
|
|
1729
|
+
let msg;
|
|
1730
|
+
try {
|
|
1731
|
+
msg = JSON.parse(line);
|
|
1732
|
+
} catch {
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
const record = asObject(msg);
|
|
1736
|
+
if (!record)
|
|
1737
|
+
return;
|
|
1738
|
+
const id = typeof record.id === "number" ? record.id : undefined;
|
|
1739
|
+
const method = typeof record.method === "string" ? record.method : undefined;
|
|
1740
|
+
if (id !== undefined && this.pending.has(id)) {
|
|
1741
|
+
const pending = this.pending.get(id);
|
|
1742
|
+
this.pending.delete(id);
|
|
1743
|
+
if (!pending)
|
|
1744
|
+
return;
|
|
1745
|
+
const error = asObject(record.error);
|
|
1746
|
+
if (error)
|
|
1747
|
+
pending.reject(new Error(typeof error.message === "string" ? error.message : "app-server request failed"));
|
|
1748
|
+
else
|
|
1749
|
+
pending.resolve(record.result);
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
if (id !== undefined && method) {
|
|
1753
|
+
this.rejectServerRequest(id, method);
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
if (!method)
|
|
1757
|
+
return;
|
|
1758
|
+
this.handleNotification(method, record.params);
|
|
1759
|
+
}
|
|
1760
|
+
handleNotification(method, params) {
|
|
1761
|
+
if (method === "thread/tokenUsage/updated") {
|
|
1762
|
+
const usage = codexAppServerUsageToSnapshot(params);
|
|
1763
|
+
if (usage)
|
|
1764
|
+
this.opts.onEvent(usage);
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
if (method === "item/agentMessage/delta") {
|
|
1768
|
+
const p = asObject(params);
|
|
1769
|
+
const text = typeof p?.delta === "string" ? p.delta : typeof p?.text === "string" ? p.text : "";
|
|
1770
|
+
if (text)
|
|
1771
|
+
this.opts.onEvent({ type: "assistant.delta", text });
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
if (method === "item/started" || method === "item/completed") {
|
|
1775
|
+
for (const event of codexAppServerItemNotificationToEvents(method, params))
|
|
1776
|
+
this.opts.onEvent(event);
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
if (method === "turn/completed") {
|
|
1780
|
+
const p = asObject(params);
|
|
1781
|
+
const turn = asObject(p?.turn);
|
|
1782
|
+
const error = turn?.error;
|
|
1783
|
+
if (error) {
|
|
1784
|
+
this.opts.onEvent({ type: "error", message: stringifyErr2(error) });
|
|
1785
|
+
} else {
|
|
1786
|
+
this.opts.onEvent({ type: "done" });
|
|
1787
|
+
}
|
|
1788
|
+
this.close();
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
if (method === "error") {
|
|
1792
|
+
const p = asObject(params);
|
|
1793
|
+
const message = typeof p?.message === "string" ? p.message : "codex app-server emitted an error";
|
|
1794
|
+
this.opts.onEvent({ type: "error", message });
|
|
1795
|
+
this.close();
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
rejectServerRequest(id, method) {
|
|
1799
|
+
this.send({
|
|
1800
|
+
jsonrpc: "2.0",
|
|
1801
|
+
id,
|
|
1802
|
+
error: {
|
|
1803
|
+
code: -32000,
|
|
1804
|
+
message: `kobe app-server backend does not handle server request ${method}`
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
close() {
|
|
1809
|
+
try {
|
|
1810
|
+
this.proc.stdin.end();
|
|
1811
|
+
} catch {}
|
|
1812
|
+
setTimeout(() => {
|
|
1813
|
+
if (!this.proc.killed) {
|
|
1814
|
+
try {
|
|
1815
|
+
this.proc.kill("SIGTERM");
|
|
1816
|
+
} catch {}
|
|
1817
|
+
}
|
|
1818
|
+
}, 50).unref();
|
|
1819
|
+
}
|
|
1820
|
+
rejectAll(err) {
|
|
1821
|
+
for (const pending of this.pending.values())
|
|
1822
|
+
pending.reject(err);
|
|
1823
|
+
this.pending.clear();
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
function permissionPayloads(mode) {
|
|
1827
|
+
if (mode === "plan") {
|
|
1828
|
+
return {
|
|
1829
|
+
approvalPolicy: "never",
|
|
1830
|
+
threadSandbox: "read-only",
|
|
1831
|
+
turnSandboxPolicy: { type: "readOnly", networkAccess: true }
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
return {
|
|
1835
|
+
approvalPolicy: "never",
|
|
1836
|
+
threadSandbox: "danger-full-access",
|
|
1837
|
+
turnSandboxPolicy: { type: "dangerFullAccess" }
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
function asObject(v) {
|
|
1841
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : null;
|
|
1842
|
+
}
|
|
1843
|
+
function numberOr(v, fallback) {
|
|
1844
|
+
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
1845
|
+
}
|
|
1846
|
+
function stripItemHousekeeping(item) {
|
|
1847
|
+
const { id: _id, type: _type, ...rest } = item;
|
|
1848
|
+
return rest;
|
|
1849
|
+
}
|
|
1850
|
+
function isNonToolTranscriptItem(itemType) {
|
|
1851
|
+
return itemType === "agentMessage" || itemType === "agent_message" || itemType === "userMessage" || itemType === "user_message";
|
|
1852
|
+
}
|
|
1853
|
+
function stringifyErr2(err) {
|
|
1854
|
+
if (err instanceof Error)
|
|
1855
|
+
return err.message;
|
|
1856
|
+
try {
|
|
1857
|
+
return JSON.stringify(err);
|
|
1858
|
+
} catch {
|
|
1859
|
+
return String(err);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
var CODEX_BACKEND_KV_KEY = "codex.backend";
|
|
1863
|
+
var init_app_server = __esm(() => {
|
|
1864
|
+
init_env();
|
|
1865
|
+
});
|
|
1866
|
+
|
|
1453
1867
|
// src/engine/codex-local/binary.ts
|
|
1454
1868
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1455
1869
|
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
1456
|
-
import { homedir as
|
|
1870
|
+
import { homedir as homedir8 } from "os";
|
|
1457
1871
|
import path4 from "path";
|
|
1458
1872
|
async function findCodexBinary(deps = defaultDeps4) {
|
|
1459
1873
|
const checked = [];
|
|
@@ -1510,7 +1924,7 @@ var init_binary2 = __esm(() => {
|
|
|
1510
1924
|
return process.env[name];
|
|
1511
1925
|
},
|
|
1512
1926
|
home() {
|
|
1513
|
-
return
|
|
1927
|
+
return homedir8();
|
|
1514
1928
|
},
|
|
1515
1929
|
which(name) {
|
|
1516
1930
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -1540,9 +1954,9 @@ var init_binary2 = __esm(() => {
|
|
|
1540
1954
|
|
|
1541
1955
|
// src/engine/codex-local/models.ts
|
|
1542
1956
|
function codexContextWindowFor(_modelId) {
|
|
1543
|
-
return
|
|
1957
|
+
return 0;
|
|
1544
1958
|
}
|
|
1545
|
-
var CODEX_GPT55_EFFORT_LEVELS, CODEX_MODELS, CODEX_FALLBACK_DEFAULT_MODEL_ID = "gpt-5.4-mini"
|
|
1959
|
+
var CODEX_GPT55_EFFORT_LEVELS, CODEX_MODELS, CODEX_FALLBACK_DEFAULT_MODEL_ID = "gpt-5.4-mini";
|
|
1546
1960
|
var init_models2 = __esm(() => {
|
|
1547
1961
|
CODEX_GPT55_EFFORT_LEVELS = [
|
|
1548
1962
|
"none",
|
|
@@ -1567,12 +1981,12 @@ var init_models2 = __esm(() => {
|
|
|
1567
1981
|
});
|
|
1568
1982
|
|
|
1569
1983
|
// src/engine/codex-local/settings.ts
|
|
1570
|
-
import { readFileSync as
|
|
1571
|
-
import { homedir as
|
|
1572
|
-
import { join as
|
|
1984
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1985
|
+
import { homedir as homedir9 } from "os";
|
|
1986
|
+
import { join as join5 } from "path";
|
|
1573
1987
|
function readModelFromConfig() {
|
|
1574
1988
|
try {
|
|
1575
|
-
const raw =
|
|
1989
|
+
const raw = readFileSync3(CONFIG_PATH, "utf8");
|
|
1576
1990
|
let inTable = false;
|
|
1577
1991
|
for (const line of raw.split(`
|
|
1578
1992
|
`)) {
|
|
@@ -1600,7 +2014,7 @@ function resolveCodexDefaultModelId() {
|
|
|
1600
2014
|
var CONFIG_PATH, cached2;
|
|
1601
2015
|
var init_settings2 = __esm(() => {
|
|
1602
2016
|
init_models2();
|
|
1603
|
-
CONFIG_PATH =
|
|
2017
|
+
CONFIG_PATH = join5(homedir9(), ".codex", "config.toml");
|
|
1604
2018
|
});
|
|
1605
2019
|
|
|
1606
2020
|
// src/engine/codex-local/capabilities.ts
|
|
@@ -1690,9 +2104,31 @@ function isInstructionsEnvelope(text) {
|
|
|
1690
2104
|
}
|
|
1691
2105
|
var init_synthetic = () => {};
|
|
1692
2106
|
|
|
2107
|
+
// src/engine/codex-local/usage.ts
|
|
2108
|
+
function codexUsageToSnapshot(usage, opts = {}) {
|
|
2109
|
+
const totalInput = numberOr2(usage.input_tokens, 0);
|
|
2110
|
+
const cachedInput = numberOr2(usage.cached_input_tokens, 0);
|
|
2111
|
+
const output = numberOr2(usage.output_tokens, 0);
|
|
2112
|
+
const nonCachedInput = Math.max(0, totalInput - cachedInput);
|
|
2113
|
+
if (totalInput <= 0 && output <= 0 && cachedInput <= 0)
|
|
2114
|
+
return;
|
|
2115
|
+
return {
|
|
2116
|
+
input_tokens: nonCachedInput,
|
|
2117
|
+
output_tokens: output,
|
|
2118
|
+
...cachedInput > 0 ? { cache_read_input_tokens: cachedInput } : {},
|
|
2119
|
+
...validPositive(opts.contextWindowTokens) ? { context_window_tokens: opts.contextWindowTokens } : {}
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
function numberOr2(v, fallback) {
|
|
2123
|
+
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
2124
|
+
}
|
|
2125
|
+
function validPositive(v) {
|
|
2126
|
+
return typeof v === "number" && Number.isFinite(v) && v > 0;
|
|
2127
|
+
}
|
|
2128
|
+
|
|
1693
2129
|
// src/engine/codex-local/history.ts
|
|
1694
|
-
import { readFile as readFile3, readdir as readdir3, unlink as
|
|
1695
|
-
import { homedir as
|
|
2130
|
+
import { readFile as readFile3, readdir as readdir3, unlink as unlink3 } from "fs/promises";
|
|
2131
|
+
import { homedir as homedir10 } from "os";
|
|
1696
2132
|
import path5 from "path";
|
|
1697
2133
|
async function listRolloutFiles(deps = defaultDeps5) {
|
|
1698
2134
|
const root = deps.sessionsDir();
|
|
@@ -1742,7 +2178,7 @@ async function deleteHistory2(sessionId, deps = defaultDeps5) {
|
|
|
1742
2178
|
if (!file)
|
|
1743
2179
|
return;
|
|
1744
2180
|
try {
|
|
1745
|
-
await
|
|
2181
|
+
await unlink3(file);
|
|
1746
2182
|
} catch (err) {
|
|
1747
2183
|
if (err.code === "ENOENT")
|
|
1748
2184
|
return;
|
|
@@ -1794,10 +2230,6 @@ function parseJsonl2(raw, sessionId) {
|
|
|
1794
2230
|
function deriveCodexUsageMetrics(raw) {
|
|
1795
2231
|
let latestUsage;
|
|
1796
2232
|
let latestUsageTimestampMs = null;
|
|
1797
|
-
let lastUserTimestampMs = null;
|
|
1798
|
-
let inputTokens = 0;
|
|
1799
|
-
let outputTokens = 0;
|
|
1800
|
-
const intervals = [];
|
|
1801
2233
|
for (const line of raw.split(`
|
|
1802
2234
|
`)) {
|
|
1803
2235
|
const trimmed = line.trim();
|
|
@@ -1812,15 +2244,8 @@ function deriveCodexUsageMetrics(raw) {
|
|
|
1812
2244
|
if (!isObject5(parsed))
|
|
1813
2245
|
continue;
|
|
1814
2246
|
const timestampMs = typeof parsed.timestamp === "string" ? parseTimestampMs2(parsed.timestamp) : null;
|
|
1815
|
-
if (parsed.type === "response_item")
|
|
1816
|
-
const payload = isObject5(parsed.payload) ? parsed.payload : undefined;
|
|
1817
|
-
if (payload?.type === "message" && payload.role === "user" && timestampMs !== null) {
|
|
1818
|
-
const blocks = normalizeCodexContent(payload.content);
|
|
1819
|
-
if (!isSyntheticCodexUserRow(blocks))
|
|
1820
|
-
lastUserTimestampMs = timestampMs;
|
|
1821
|
-
}
|
|
2247
|
+
if (parsed.type === "response_item")
|
|
1822
2248
|
continue;
|
|
1823
|
-
}
|
|
1824
2249
|
if (parsed.type !== "turn.completed")
|
|
1825
2250
|
continue;
|
|
1826
2251
|
const usage = isObject5(parsed.usage) ? parsed.usage : undefined;
|
|
@@ -1835,63 +2260,13 @@ function deriveCodexUsageMetrics(raw) {
|
|
|
1835
2260
|
} else if (latestUsage === undefined) {
|
|
1836
2261
|
latestUsage = snapshot;
|
|
1837
2262
|
}
|
|
1838
|
-
inputTokens += snapshot.input_tokens;
|
|
1839
|
-
outputTokens += snapshot.output_tokens;
|
|
1840
|
-
if (timestampMs !== null && lastUserTimestampMs !== null && timestampMs > lastUserTimestampMs) {
|
|
1841
|
-
intervals.push({ startMs: lastUserTimestampMs, endMs: timestampMs });
|
|
1842
|
-
}
|
|
1843
2263
|
}
|
|
1844
|
-
|
|
1845
|
-
return;
|
|
1846
|
-
const durationMs2 = mergedDurationMs(intervals);
|
|
1847
|
-
if (durationMs2 <= 0)
|
|
1848
|
-
return latestUsage;
|
|
1849
|
-
return {
|
|
1850
|
-
...latestUsage,
|
|
1851
|
-
total_speed_tokens_per_second: (inputTokens + outputTokens) / (durationMs2 / 1000)
|
|
1852
|
-
};
|
|
1853
|
-
}
|
|
1854
|
-
function codexUsageToSnapshot(usage) {
|
|
1855
|
-
const input = numberOr(usage.input_tokens, 0);
|
|
1856
|
-
const output = numberOr(usage.output_tokens, 0) + numberOr(usage.reasoning_output_tokens, 0);
|
|
1857
|
-
const cacheRead = typeof usage.cached_input_tokens === "number" ? usage.cached_input_tokens : undefined;
|
|
1858
|
-
if (input <= 0 && output <= 0 && cacheRead === undefined)
|
|
1859
|
-
return;
|
|
1860
|
-
return {
|
|
1861
|
-
input_tokens: input,
|
|
1862
|
-
output_tokens: output,
|
|
1863
|
-
...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {}
|
|
1864
|
-
};
|
|
2264
|
+
return latestUsage;
|
|
1865
2265
|
}
|
|
1866
2266
|
function parseTimestampMs2(value) {
|
|
1867
2267
|
const ms = new Date(value).getTime();
|
|
1868
2268
|
return Number.isFinite(ms) ? ms : null;
|
|
1869
2269
|
}
|
|
1870
|
-
function mergedDurationMs(intervals) {
|
|
1871
|
-
if (intervals.length === 0)
|
|
1872
|
-
return 0;
|
|
1873
|
-
const sorted = [...intervals].sort((a, b) => a.startMs - b.startMs);
|
|
1874
|
-
let total = 0;
|
|
1875
|
-
let current = sorted[0];
|
|
1876
|
-
if (!current)
|
|
1877
|
-
return 0;
|
|
1878
|
-
for (let i = 1;i < sorted.length; i++) {
|
|
1879
|
-
const next = sorted[i];
|
|
1880
|
-
if (!next)
|
|
1881
|
-
continue;
|
|
1882
|
-
if (next.startMs <= current.endMs) {
|
|
1883
|
-
current = { startMs: current.startMs, endMs: Math.max(current.endMs, next.endMs) };
|
|
1884
|
-
} else {
|
|
1885
|
-
total += current.endMs - current.startMs;
|
|
1886
|
-
current = next;
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
total += current.endMs - current.startMs;
|
|
1890
|
-
return total;
|
|
1891
|
-
}
|
|
1892
|
-
function numberOr(v, fallback) {
|
|
1893
|
-
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
1894
|
-
}
|
|
1895
2270
|
function isObject5(v) {
|
|
1896
2271
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1897
2272
|
}
|
|
@@ -1900,7 +2275,7 @@ var init_history2 = __esm(() => {
|
|
|
1900
2275
|
init_synthetic();
|
|
1901
2276
|
defaultDeps5 = {
|
|
1902
2277
|
sessionsDir() {
|
|
1903
|
-
return path5.join(
|
|
2278
|
+
return path5.join(homedir10(), ".codex", "sessions");
|
|
1904
2279
|
},
|
|
1905
2280
|
async readdir(p) {
|
|
1906
2281
|
try {
|
|
@@ -1915,6 +2290,52 @@ var init_history2 = __esm(() => {
|
|
|
1915
2290
|
};
|
|
1916
2291
|
});
|
|
1917
2292
|
|
|
2293
|
+
// src/engine/codex-local/openrouter.ts
|
|
2294
|
+
async function resolveOpenRouterContextWindow(modelId) {
|
|
2295
|
+
const id = openRouterModelId(modelId);
|
|
2296
|
+
if (!id)
|
|
2297
|
+
return;
|
|
2298
|
+
const models = await loadOpenRouterModels();
|
|
2299
|
+
return models.get(id)?.contextLength;
|
|
2300
|
+
}
|
|
2301
|
+
function openRouterModelId(modelId) {
|
|
2302
|
+
const id = modelId?.trim();
|
|
2303
|
+
if (!id)
|
|
2304
|
+
return null;
|
|
2305
|
+
return id.includes("/") ? id : `openai/${id}`;
|
|
2306
|
+
}
|
|
2307
|
+
async function loadOpenRouterModels() {
|
|
2308
|
+
cachePromise ??= fetchOpenRouterModels().catch(() => new Map);
|
|
2309
|
+
return cachePromise;
|
|
2310
|
+
}
|
|
2311
|
+
async function fetchOpenRouterModels() {
|
|
2312
|
+
const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
2313
|
+
const res = await fetch(MODELS_URL, {
|
|
2314
|
+
signal,
|
|
2315
|
+
headers: { "user-agent": "kobe-codex-openrouter-context" }
|
|
2316
|
+
});
|
|
2317
|
+
if (!res.ok)
|
|
2318
|
+
return new Map;
|
|
2319
|
+
const body = await res.json();
|
|
2320
|
+
if (!isObject6(body) || !Array.isArray(body.data))
|
|
2321
|
+
return new Map;
|
|
2322
|
+
const out = new Map;
|
|
2323
|
+
for (const item of body.data) {
|
|
2324
|
+
if (!isObject6(item))
|
|
2325
|
+
continue;
|
|
2326
|
+
const id = typeof item.id === "string" ? item.id : undefined;
|
|
2327
|
+
const contextLength = typeof item.context_length === "number" ? item.context_length : undefined;
|
|
2328
|
+
if (!id || !contextLength || !Number.isFinite(contextLength) || contextLength <= 0)
|
|
2329
|
+
continue;
|
|
2330
|
+
out.set(id, { id, contextLength });
|
|
2331
|
+
}
|
|
2332
|
+
return out;
|
|
2333
|
+
}
|
|
2334
|
+
function isObject6(v) {
|
|
2335
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2336
|
+
}
|
|
2337
|
+
var MODELS_URL = "https://openrouter.ai/api/v1/models", FETCH_TIMEOUT_MS = 2500, cachePromise;
|
|
2338
|
+
|
|
1918
2339
|
// src/engine/codex-local/sessions.ts
|
|
1919
2340
|
import { open, stat as stat2 } from "fs/promises";
|
|
1920
2341
|
async function listSessionsForCwd2(cwd, deps) {
|
|
@@ -1958,7 +2379,7 @@ async function tryReadMeta(file) {
|
|
|
1958
2379
|
return;
|
|
1959
2380
|
if (parsed.type === "session_meta") {
|
|
1960
2381
|
const payload = parsed.payload;
|
|
1961
|
-
if (
|
|
2382
|
+
if (isObject7(payload)) {
|
|
1962
2383
|
if (typeof payload.id === "string")
|
|
1963
2384
|
sessionId = payload.id;
|
|
1964
2385
|
if (typeof payload.cwd === "string")
|
|
@@ -1966,7 +2387,7 @@ async function tryReadMeta(file) {
|
|
|
1966
2387
|
}
|
|
1967
2388
|
return;
|
|
1968
2389
|
}
|
|
1969
|
-
if (parsed.type === "response_item" &&
|
|
2390
|
+
if (parsed.type === "response_item" && isObject7(parsed.payload)) {
|
|
1970
2391
|
const p = parsed.payload;
|
|
1971
2392
|
if (p.type === "message") {
|
|
1972
2393
|
messageCount++;
|
|
@@ -2012,12 +2433,12 @@ function safeParse(line) {
|
|
|
2012
2433
|
return null;
|
|
2013
2434
|
try {
|
|
2014
2435
|
const v = JSON.parse(t);
|
|
2015
|
-
return
|
|
2436
|
+
return isObject7(v) ? v : null;
|
|
2016
2437
|
} catch {
|
|
2017
2438
|
return null;
|
|
2018
2439
|
}
|
|
2019
2440
|
}
|
|
2020
|
-
function
|
|
2441
|
+
function isObject7(v) {
|
|
2021
2442
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2022
2443
|
}
|
|
2023
2444
|
var PREVIEW_CHAR_CAP = 200;
|
|
@@ -2027,10 +2448,10 @@ var init_sessions2 = __esm(() => {
|
|
|
2027
2448
|
});
|
|
2028
2449
|
|
|
2029
2450
|
// src/engine/codex-local/spawn.ts
|
|
2030
|
-
import { spawn as
|
|
2451
|
+
import { spawn as spawn4 } from "child_process";
|
|
2031
2452
|
function spawnCodexProcess(opts) {
|
|
2032
2453
|
const args = buildArgs2(opts);
|
|
2033
|
-
const proc =
|
|
2454
|
+
const proc = spawn4(opts.binaryPath, args, {
|
|
2034
2455
|
cwd: opts.cwd,
|
|
2035
2456
|
env: { ...process.env, ...opts.env ?? {} },
|
|
2036
2457
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2087,10 +2508,10 @@ async function* parseStreamJson2(lines, opts = {}) {
|
|
|
2087
2508
|
try {
|
|
2088
2509
|
msg = JSON.parse(line);
|
|
2089
2510
|
} catch (err) {
|
|
2090
|
-
yield { type: "error", message: `codex stream-json parse failed: ${
|
|
2511
|
+
yield { type: "error", message: `codex stream-json parse failed: ${stringifyErr3(err)}` };
|
|
2091
2512
|
continue;
|
|
2092
2513
|
}
|
|
2093
|
-
if (!
|
|
2514
|
+
if (!isObject8(msg))
|
|
2094
2515
|
continue;
|
|
2095
2516
|
const type = typeof msg.type === "string" ? msg.type : undefined;
|
|
2096
2517
|
if (!type)
|
|
@@ -2106,7 +2527,7 @@ async function* parseStreamJson2(lines, opts = {}) {
|
|
|
2106
2527
|
if (type === "turn.started")
|
|
2107
2528
|
continue;
|
|
2108
2529
|
if (type === "item.started" || type === "item.completed") {
|
|
2109
|
-
const item =
|
|
2530
|
+
const item = isObject8(msg.item) ? msg.item : undefined;
|
|
2110
2531
|
if (!item)
|
|
2111
2532
|
continue;
|
|
2112
2533
|
const itemId = typeof item.id === "string" ? item.id : undefined;
|
|
@@ -2140,17 +2561,12 @@ async function* parseStreamJson2(lines, opts = {}) {
|
|
|
2140
2561
|
continue;
|
|
2141
2562
|
}
|
|
2142
2563
|
if (type === "turn.completed") {
|
|
2143
|
-
const usage =
|
|
2564
|
+
const usage = isObject8(msg.usage) ? msg.usage : undefined;
|
|
2144
2565
|
if (usage) {
|
|
2145
|
-
const
|
|
2146
|
-
const
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
type: "usage",
|
|
2150
|
-
input_tokens: inTok,
|
|
2151
|
-
output_tokens: outTok,
|
|
2152
|
-
...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {}
|
|
2153
|
-
};
|
|
2566
|
+
const contextWindowTokens = await opts.contextWindowTokens?.();
|
|
2567
|
+
const snapshot = codexUsageToSnapshot(usage, { contextWindowTokens });
|
|
2568
|
+
if (snapshot)
|
|
2569
|
+
yield { type: "usage", ...snapshot };
|
|
2154
2570
|
}
|
|
2155
2571
|
yield { type: "done" };
|
|
2156
2572
|
return;
|
|
@@ -2179,26 +2595,23 @@ async function* readLines2(stream) {
|
|
|
2179
2595
|
if (buf.length > 0)
|
|
2180
2596
|
yield buf;
|
|
2181
2597
|
}
|
|
2182
|
-
function
|
|
2598
|
+
function isObject8(v) {
|
|
2183
2599
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2184
2600
|
}
|
|
2185
2601
|
function codexSessionId(msg) {
|
|
2186
2602
|
if (msg.type === "session_meta") {
|
|
2187
|
-
const payload =
|
|
2603
|
+
const payload = isObject8(msg.payload) ? msg.payload : undefined;
|
|
2188
2604
|
const id2 = payload?.id;
|
|
2189
2605
|
return typeof id2 === "string" && id2.length > 0 ? id2 : undefined;
|
|
2190
2606
|
}
|
|
2191
2607
|
const id = msg.thread_id;
|
|
2192
2608
|
return typeof id === "string" && id.length > 0 ? id : undefined;
|
|
2193
2609
|
}
|
|
2194
|
-
function numberOr2(v, fallback) {
|
|
2195
|
-
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
2196
|
-
}
|
|
2197
2610
|
function stripIdAndType(item) {
|
|
2198
2611
|
const { id: _id, type: _type, ...rest } = item;
|
|
2199
2612
|
return rest;
|
|
2200
2613
|
}
|
|
2201
|
-
function
|
|
2614
|
+
function stringifyErr3(err) {
|
|
2202
2615
|
if (err instanceof Error)
|
|
2203
2616
|
return err.message;
|
|
2204
2617
|
try {
|
|
@@ -2207,6 +2620,7 @@ function stringifyErr2(err) {
|
|
|
2207
2620
|
return String(err);
|
|
2208
2621
|
}
|
|
2209
2622
|
}
|
|
2623
|
+
var init_stream = () => {};
|
|
2210
2624
|
|
|
2211
2625
|
// src/engine/codex-local/index.ts
|
|
2212
2626
|
class CodexLocal {
|
|
@@ -2216,9 +2630,11 @@ class CodexLocal {
|
|
|
2216
2630
|
running = new Map;
|
|
2217
2631
|
binaryPathResolver;
|
|
2218
2632
|
stopGraceMs;
|
|
2633
|
+
backend;
|
|
2219
2634
|
constructor(opts = {}) {
|
|
2220
2635
|
this.binaryPathResolver = opts.binaryPathResolver ?? findCodexBinary;
|
|
2221
2636
|
this.stopGraceMs = opts.stopGraceMs ?? 5000;
|
|
2637
|
+
this.backend = opts.backend ?? resolveCodexBackend();
|
|
2222
2638
|
}
|
|
2223
2639
|
async spawn(cwd, prompt, opts) {
|
|
2224
2640
|
return this.start({ cwd, prompt, opts });
|
|
@@ -2273,7 +2689,100 @@ class CodexLocal {
|
|
|
2273
2689
|
}
|
|
2274
2690
|
}
|
|
2275
2691
|
async start(args) {
|
|
2692
|
+
if (this.backend === "app-server")
|
|
2693
|
+
return this.startAppServer(args);
|
|
2694
|
+
return this.startExec(args);
|
|
2695
|
+
}
|
|
2696
|
+
async startAppServer(args) {
|
|
2697
|
+
const binaryPath = await this.binaryPathResolver();
|
|
2698
|
+
const queue = [];
|
|
2699
|
+
let session;
|
|
2700
|
+
let bound = false;
|
|
2701
|
+
let terminalSeen = false;
|
|
2702
|
+
let stderrTail = "";
|
|
2703
|
+
let resolveHandle = () => {};
|
|
2704
|
+
let rejectHandle = () => {};
|
|
2705
|
+
const handlePromise = new Promise((res, rej) => {
|
|
2706
|
+
resolveHandle = res;
|
|
2707
|
+
rejectHandle = rej;
|
|
2708
|
+
});
|
|
2709
|
+
const bind = (sessionId) => {
|
|
2710
|
+
if (bound)
|
|
2711
|
+
return;
|
|
2712
|
+
bound = true;
|
|
2713
|
+
session = {
|
|
2714
|
+
sessionId,
|
|
2715
|
+
cwd: args.cwd,
|
|
2716
|
+
spawned,
|
|
2717
|
+
queue,
|
|
2718
|
+
waiters: [],
|
|
2719
|
+
closed: false,
|
|
2720
|
+
spawnedAtIso: new Date().toISOString()
|
|
2721
|
+
};
|
|
2722
|
+
this.running.set(sessionId, session);
|
|
2723
|
+
this.registry.register({
|
|
2724
|
+
sessionId,
|
|
2725
|
+
cwd: args.cwd,
|
|
2726
|
+
proc: spawned.proc,
|
|
2727
|
+
startedAt: Date.now(),
|
|
2728
|
+
prompt: args.prompt
|
|
2729
|
+
});
|
|
2730
|
+
resolveHandle({ sessionId, cwd: args.cwd });
|
|
2731
|
+
};
|
|
2732
|
+
const emit = (ev) => {
|
|
2733
|
+
if (ev.type === "done" || ev.type === "error")
|
|
2734
|
+
terminalSeen = true;
|
|
2735
|
+
queue.push(ev);
|
|
2736
|
+
if (session) {
|
|
2737
|
+
this.notify(session);
|
|
2738
|
+
if (ev.type === "done" || ev.type === "error") {
|
|
2739
|
+
this.registry.unregister(session.sessionId, spawned.proc);
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
const spawned = spawnCodexAppServerTurn({
|
|
2744
|
+
binaryPath,
|
|
2745
|
+
cwd: args.cwd,
|
|
2746
|
+
prompt: args.prompt,
|
|
2747
|
+
model: args.opts?.model,
|
|
2748
|
+
modelEffort: args.opts?.modelEffort,
|
|
2749
|
+
permissionMode: args.opts?.permissionMode,
|
|
2750
|
+
env: args.opts?.env,
|
|
2751
|
+
resumeSessionId: args.resumeSessionId,
|
|
2752
|
+
onSessionId: bind,
|
|
2753
|
+
onEvent: emit
|
|
2754
|
+
});
|
|
2755
|
+
captureStderrTail(spawned.stderr, (chunk) => {
|
|
2756
|
+
stderrTail = (stderrTail + chunk).slice(-STDERR_TAIL_CAP);
|
|
2757
|
+
});
|
|
2758
|
+
spawned.ready.catch((err) => {
|
|
2759
|
+
if (!bound)
|
|
2760
|
+
rejectHandle(err);
|
|
2761
|
+
});
|
|
2762
|
+
spawned.closed.then(({ code, signal }) => {
|
|
2763
|
+
if (session) {
|
|
2764
|
+
if (!terminalSeen && typeof code === "number" && code !== 0) {
|
|
2765
|
+
queue.push({
|
|
2766
|
+
type: "error",
|
|
2767
|
+
message: formatExitMsg("codex app-server exited", code, signal, stderrTail)
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2770
|
+
session.closed = true;
|
|
2771
|
+
this.notify(session);
|
|
2772
|
+
this.registry.unregister(session.sessionId, spawned.proc);
|
|
2773
|
+
if (this.running.get(session.sessionId) === session) {
|
|
2774
|
+
this.running.delete(session.sessionId);
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
if (!bound) {
|
|
2778
|
+
rejectHandle(new Error(formatExitMsg("codex app-server exited before session id was captured", code, signal, stderrTail)));
|
|
2779
|
+
}
|
|
2780
|
+
});
|
|
2781
|
+
return handlePromise;
|
|
2782
|
+
}
|
|
2783
|
+
async startExec(args) {
|
|
2276
2784
|
const binaryPath = await this.binaryPathResolver();
|
|
2785
|
+
const modelId = args.opts?.model ?? codexCapabilities.defaultModelId();
|
|
2277
2786
|
const spawned = spawnCodexProcess({
|
|
2278
2787
|
binaryPath,
|
|
2279
2788
|
cwd: args.cwd,
|
|
@@ -2294,6 +2803,7 @@ class CodexLocal {
|
|
|
2294
2803
|
let session;
|
|
2295
2804
|
let bound = false;
|
|
2296
2805
|
let stderrTail = "";
|
|
2806
|
+
const contextWindowPromise = resolveOpenRouterContextWindow(modelId);
|
|
2297
2807
|
const bind = (sessionId) => {
|
|
2298
2808
|
if (bound) {
|
|
2299
2809
|
if (session && session.sessionId !== sessionId) {
|
|
@@ -2357,15 +2867,15 @@ class CodexLocal {
|
|
|
2357
2867
|
});
|
|
2358
2868
|
(async () => {
|
|
2359
2869
|
const events = parseStreamJson2(readLines2(spawned.stdout), {
|
|
2360
|
-
onSessionId: (sid) => bind(sid)
|
|
2870
|
+
onSessionId: (sid) => bind(sid),
|
|
2871
|
+
contextWindowTokens: () => contextWindowPromise
|
|
2361
2872
|
});
|
|
2362
2873
|
try {
|
|
2363
2874
|
for await (const ev of events) {
|
|
2364
|
-
|
|
2365
|
-
queue.push(enriched);
|
|
2875
|
+
queue.push(ev);
|
|
2366
2876
|
if (session)
|
|
2367
2877
|
this.notify(session);
|
|
2368
|
-
if ((
|
|
2878
|
+
if ((ev.type === "done" || ev.type === "error") && session) {
|
|
2369
2879
|
this.registry.unregister(session.sessionId, spawned.proc);
|
|
2370
2880
|
}
|
|
2371
2881
|
}
|
|
@@ -2431,28 +2941,25 @@ function formatExitMsg(prefix, code, signal, stderrTail) {
|
|
|
2431
2941
|
parts.push(`: ${detail}`);
|
|
2432
2942
|
return parts.join(" ").replace(/ : /, ": ");
|
|
2433
2943
|
}
|
|
2434
|
-
function enrichUsageEvent2(ev, startedAtIso) {
|
|
2435
|
-
if (ev.type !== "usage")
|
|
2436
|
-
return ev;
|
|
2437
|
-
return { type: "usage", ...withTotalSpeedForTurn(ev, startedAtIso, new Date().toISOString()) };
|
|
2438
|
-
}
|
|
2439
2944
|
var STDERR_TAIL_CAP;
|
|
2440
2945
|
var init_codex_local = __esm(() => {
|
|
2946
|
+
init_app_server();
|
|
2441
2947
|
init_binary2();
|
|
2442
2948
|
init_capabilities2();
|
|
2443
2949
|
init_history2();
|
|
2444
2950
|
init_sessions2();
|
|
2445
2951
|
init_spawn2();
|
|
2952
|
+
init_stream();
|
|
2446
2953
|
STDERR_TAIL_CAP = 4 * 1024;
|
|
2447
2954
|
});
|
|
2448
2955
|
|
|
2449
2956
|
// src/orchestrator/bridge/server.ts
|
|
2450
|
-
import { mkdir as mkdir2, unlink as
|
|
2957
|
+
import { mkdir as mkdir2, unlink as unlink4 } from "fs/promises";
|
|
2451
2958
|
import { createServer } from "net";
|
|
2452
2959
|
import { dirname as dirname2 } from "path";
|
|
2453
2960
|
async function startBridgeServer(orch, socketPath) {
|
|
2454
2961
|
await mkdir2(dirname2(socketPath), { recursive: true });
|
|
2455
|
-
await
|
|
2962
|
+
await unlink4(socketPath).catch(() => {});
|
|
2456
2963
|
const conns = new Set;
|
|
2457
2964
|
const server = createServer((conn) => {
|
|
2458
2965
|
conns.add(conn);
|
|
@@ -2493,7 +3000,7 @@ async function startBridgeServer(orch, socketPath) {
|
|
|
2493
3000
|
conn.destroy();
|
|
2494
3001
|
conns.clear();
|
|
2495
3002
|
await new Promise((resolve2) => server.close(() => resolve2()));
|
|
2496
|
-
await
|
|
3003
|
+
await unlink4(socketPath).catch(() => {});
|
|
2497
3004
|
}
|
|
2498
3005
|
};
|
|
2499
3006
|
}
|
|
@@ -2585,19 +3092,19 @@ __export(exports_bridge, {
|
|
|
2585
3092
|
startBridge: () => startBridge,
|
|
2586
3093
|
bridgeSocketPathForHome: () => bridgeSocketPathForHome
|
|
2587
3094
|
});
|
|
2588
|
-
import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
|
|
2589
|
-
import { homedir as
|
|
2590
|
-
import { join as
|
|
3095
|
+
import { mkdir as mkdir3, unlink as unlink5, writeFile as writeFile2 } from "fs/promises";
|
|
3096
|
+
import { homedir as homedir11 } from "os";
|
|
3097
|
+
import { join as join6 } from "path";
|
|
2591
3098
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2592
3099
|
function bridgeSocketPathForHome(home, pid = process.pid) {
|
|
2593
|
-
const runDir =
|
|
2594
|
-
return fitSocketPath(
|
|
3100
|
+
const runDir = join6(home, ".kobe", "run");
|
|
3101
|
+
return fitSocketPath(join6(runDir, `bridge-${pid}.sock`), home, "bridge", pid);
|
|
2595
3102
|
}
|
|
2596
3103
|
async function startBridge(orch, opts = {}) {
|
|
2597
|
-
const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ??
|
|
2598
|
-
const runDir =
|
|
3104
|
+
const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir11();
|
|
3105
|
+
const runDir = join6(home, ".kobe", "run");
|
|
2599
3106
|
const socketPath = bridgeSocketPathForHome(home);
|
|
2600
|
-
const mcpConfigPath =
|
|
3107
|
+
const mcpConfigPath = join6(runDir, `mcp-${process.pid}.json`);
|
|
2601
3108
|
await mkdir3(runDir, { recursive: true });
|
|
2602
3109
|
const server = await startBridgeServer(orch, socketPath);
|
|
2603
3110
|
await mkdir3(runDir, { recursive: true });
|
|
@@ -2618,6 +3125,7 @@ async function startBridge(orch, opts = {}) {
|
|
|
2618
3125
|
mcpConfigPath,
|
|
2619
3126
|
async close() {
|
|
2620
3127
|
await server.close();
|
|
3128
|
+
await unlink5(mcpConfigPath).catch(() => {});
|
|
2621
3129
|
}
|
|
2622
3130
|
};
|
|
2623
3131
|
}
|
|
@@ -3925,23 +4433,6 @@ var init_registry = __esm(() => {
|
|
|
3925
4433
|
defaultIdentity = claudeIdentity;
|
|
3926
4434
|
});
|
|
3927
4435
|
|
|
3928
|
-
// src/env.ts
|
|
3929
|
-
import { homedir as homedir10 } from "os";
|
|
3930
|
-
import { join as join6 } from "path";
|
|
3931
|
-
function isDev() {
|
|
3932
|
-
return process.env.KOBE_DEV === "1";
|
|
3933
|
-
}
|
|
3934
|
-
function homeDir() {
|
|
3935
|
-
return process.env.KOBE_HOME_DIR ?? homedir10();
|
|
3936
|
-
}
|
|
3937
|
-
function kobeStateDir() {
|
|
3938
|
-
return join6(homeDir(), ".kobe");
|
|
3939
|
-
}
|
|
3940
|
-
function kvStatePath() {
|
|
3941
|
-
return join6(homeDir(), ".config", "kobe", "state.json");
|
|
3942
|
-
}
|
|
3943
|
-
var init_env = () => {};
|
|
3944
|
-
|
|
3945
4436
|
// src/state/repos.ts
|
|
3946
4437
|
var exports_repos = {};
|
|
3947
4438
|
__export(exports_repos, {
|
|
@@ -3954,7 +4445,7 @@ __export(exports_repos, {
|
|
|
3954
4445
|
addSavedRepo: () => addSavedRepo
|
|
3955
4446
|
});
|
|
3956
4447
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
3957
|
-
import { mkdirSync, readFileSync as
|
|
4448
|
+
import { mkdirSync, readFileSync as readFileSync4, realpathSync, renameSync, writeFileSync } from "fs";
|
|
3958
4449
|
import { dirname as dirname3 } from "path";
|
|
3959
4450
|
function resolveRepoRoot(absPath) {
|
|
3960
4451
|
const r = spawnSync3("git", ["rev-parse", "--show-toplevel"], {
|
|
@@ -3991,7 +4482,7 @@ function statePath() {
|
|
|
3991
4482
|
}
|
|
3992
4483
|
function load() {
|
|
3993
4484
|
try {
|
|
3994
|
-
const text =
|
|
4485
|
+
const text = readFileSync4(statePath(), "utf8");
|
|
3995
4486
|
const parsed = JSON.parse(text);
|
|
3996
4487
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3997
4488
|
return parsed;
|
|
@@ -4138,7 +4629,7 @@ var init_ulid = __esm(() => {
|
|
|
4138
4629
|
});
|
|
4139
4630
|
|
|
4140
4631
|
// src/orchestrator/metadata-suggester.ts
|
|
4141
|
-
import { spawn as
|
|
4632
|
+
import { spawn as spawn5 } from "child_process";
|
|
4142
4633
|
|
|
4143
4634
|
class MetadataSuggester {
|
|
4144
4635
|
binaryPromise = null;
|
|
@@ -4167,7 +4658,7 @@ class MetadataSuggester {
|
|
|
4167
4658
|
return new Promise((resolve2) => {
|
|
4168
4659
|
let proc;
|
|
4169
4660
|
try {
|
|
4170
|
-
proc =
|
|
4661
|
+
proc = spawn5(binary, ["-p", builder(trimmed)], {
|
|
4171
4662
|
stdio: ["ignore", "pipe", "ignore"],
|
|
4172
4663
|
env: process.env
|
|
4173
4664
|
});
|
|
@@ -6018,8 +6509,8 @@ var init_core = __esm(() => {
|
|
|
6018
6509
|
});
|
|
6019
6510
|
|
|
6020
6511
|
// src/orchestrator/index/store.ts
|
|
6021
|
-
import { mkdir as mkdir4, open as open2, readFile as readFile4, rename, unlink as
|
|
6022
|
-
import { homedir as
|
|
6512
|
+
import { mkdir as mkdir4, open as open2, readFile as readFile4, rename, unlink as unlink6 } from "fs/promises";
|
|
6513
|
+
import { homedir as homedir12 } from "os";
|
|
6023
6514
|
import { dirname as dirname4, join as join7 } from "path";
|
|
6024
6515
|
|
|
6025
6516
|
class TaskIndexStore {
|
|
@@ -6032,7 +6523,7 @@ class TaskIndexStore {
|
|
|
6032
6523
|
listeners = new Set;
|
|
6033
6524
|
saveChain = Promise.resolve();
|
|
6034
6525
|
constructor(options = {}) {
|
|
6035
|
-
this.homeDir = options.homeDir ??
|
|
6526
|
+
this.homeDir = options.homeDir ?? homedir12();
|
|
6036
6527
|
this.kobeDir = join7(this.homeDir, ".kobe");
|
|
6037
6528
|
this.path = join7(this.kobeDir, "tasks.json");
|
|
6038
6529
|
this.tmpPath = `${this.path}.tmp`;
|
|
@@ -6201,13 +6692,13 @@ class TaskIndexStore {
|
|
|
6201
6692
|
}
|
|
6202
6693
|
async _unlinkForTests() {
|
|
6203
6694
|
try {
|
|
6204
|
-
await
|
|
6695
|
+
await unlink6(this.path);
|
|
6205
6696
|
} catch (err) {
|
|
6206
6697
|
if (err.code !== "ENOENT")
|
|
6207
6698
|
throw err;
|
|
6208
6699
|
}
|
|
6209
6700
|
try {
|
|
6210
|
-
await
|
|
6701
|
+
await unlink6(this.tmpPath);
|
|
6211
6702
|
} catch (err) {
|
|
6212
6703
|
if (err.code !== "ENOENT")
|
|
6213
6704
|
throw err;
|
|
@@ -6605,7 +7096,7 @@ var init_manager = __esm(() => {
|
|
|
6605
7096
|
// src/bin/kobed.ts
|
|
6606
7097
|
init_daemon_process();
|
|
6607
7098
|
init_client();
|
|
6608
|
-
import { unlink as
|
|
7099
|
+
import { unlink as unlink8 } from "fs/promises";
|
|
6609
7100
|
|
|
6610
7101
|
// src/core/index.ts
|
|
6611
7102
|
init_claude_code_local();
|
|
@@ -6614,9 +7105,9 @@ init_bridge();
|
|
|
6614
7105
|
init_core();
|
|
6615
7106
|
init_store();
|
|
6616
7107
|
init_manager();
|
|
6617
|
-
import { homedir as
|
|
7108
|
+
import { homedir as homedir13 } from "os";
|
|
6618
7109
|
async function createKobeCore(options = {}) {
|
|
6619
|
-
const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ??
|
|
7110
|
+
const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir13();
|
|
6620
7111
|
const store = new TaskIndexStore({ homeDir: homeDir2 });
|
|
6621
7112
|
await store.load();
|
|
6622
7113
|
const worktrees = new GitWorktreeManager;
|
|
@@ -6645,7 +7136,7 @@ init_paths();
|
|
|
6645
7136
|
// src/daemon/server.ts
|
|
6646
7137
|
init_repos();
|
|
6647
7138
|
init_paths();
|
|
6648
|
-
import { mkdir as mkdir5, readFile as readFile6, unlink as
|
|
7139
|
+
import { mkdir as mkdir5, readFile as readFile6, unlink as unlink7, writeFile as writeFile4 } from "fs/promises";
|
|
6649
7140
|
import { createServer as createServer2 } from "net";
|
|
6650
7141
|
import { dirname as dirname5 } from "path";
|
|
6651
7142
|
|
|
@@ -6653,12 +7144,12 @@ import { dirname as dirname5 } from "path";
|
|
|
6653
7144
|
import { execFile } from "child_process";
|
|
6654
7145
|
import { createHash as createHash2 } from "crypto";
|
|
6655
7146
|
import { readFile as readFile5 } from "fs/promises";
|
|
6656
|
-
import { homedir as
|
|
7147
|
+
import { homedir as homedir14, userInfo } from "os";
|
|
6657
7148
|
import { join as join8 } from "path";
|
|
6658
7149
|
import { promisify } from "util";
|
|
6659
7150
|
var execFileAsync = promisify(execFile);
|
|
6660
7151
|
var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
6661
|
-
var
|
|
7152
|
+
var FETCH_TIMEOUT_MS2 = 5000;
|
|
6662
7153
|
var KEYCHAIN_BASE = "Claude Code";
|
|
6663
7154
|
var KEYCHAIN_SUFFIX = "-credentials";
|
|
6664
7155
|
function keychainServiceName() {
|
|
@@ -6689,7 +7180,7 @@ async function readKeychainToken() {
|
|
|
6689
7180
|
}
|
|
6690
7181
|
}
|
|
6691
7182
|
async function readPlainTextToken() {
|
|
6692
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR ?? join8(
|
|
7183
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ?? join8(homedir14(), ".claude");
|
|
6693
7184
|
const path9 = join8(configDir, ".credentials.json");
|
|
6694
7185
|
try {
|
|
6695
7186
|
const raw = await readFile5(path9, "utf8");
|
|
@@ -6730,7 +7221,7 @@ async function fetchPlanUsage(now = Date.now()) {
|
|
|
6730
7221
|
if (typeof token.expiresAt === "number" && token.expiresAt > 0 && token.expiresAt < now)
|
|
6731
7222
|
return null;
|
|
6732
7223
|
const ctrl = new AbortController;
|
|
6733
|
-
const timer = setTimeout(() => ctrl.abort(),
|
|
7224
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS2);
|
|
6734
7225
|
try {
|
|
6735
7226
|
const res = await fetch(USAGE_URL, {
|
|
6736
7227
|
signal: ctrl.signal,
|
|
@@ -6805,7 +7296,7 @@ function createPlanUsagePoller(options) {
|
|
|
6805
7296
|
}
|
|
6806
7297
|
// src/daemon/rc-bridge.ts
|
|
6807
7298
|
init_binary();
|
|
6808
|
-
import { spawn as
|
|
7299
|
+
import { spawn as spawn6 } from "child_process";
|
|
6809
7300
|
var ENV_ID_RE = /Environment ID:\s*(env_[A-Za-z0-9]+)/;
|
|
6810
7301
|
var DEEPLINK_RE = /https:\/\/claude\.ai\/code\?environment=([A-Za-z0-9_]+)/;
|
|
6811
7302
|
var ANSI_RE = /\x1b\[[0-9;?]*[A-Za-z]/g;
|
|
@@ -6813,7 +7304,7 @@ function createRcBridge(options = {}) {
|
|
|
6813
7304
|
const stopGraceMs = options.stopGraceMs ?? 5000;
|
|
6814
7305
|
const readyTimeoutMs = options.readyTimeoutMs ?? 30000;
|
|
6815
7306
|
const binaryPathResolver = options.binaryPathResolver ?? findClaudeBinary;
|
|
6816
|
-
const spawner = options.spawner ?? ((cmd, args, cwd) =>
|
|
7307
|
+
const spawner = options.spawner ?? ((cmd, args, cwd) => spawn6(cmd, [...args], {
|
|
6817
7308
|
cwd,
|
|
6818
7309
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6819
7310
|
env: { ...process.env }
|
|
@@ -6987,7 +7478,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
6987
7478
|
let nextClientId = 1;
|
|
6988
7479
|
await mkdir5(dirname5(socketPath), { recursive: true });
|
|
6989
7480
|
await mkdir5(dirname5(pidPath), { recursive: true });
|
|
6990
|
-
await
|
|
7481
|
+
await unlink7(socketPath).catch(() => {});
|
|
6991
7482
|
const server = createServer2((socket) => {
|
|
6992
7483
|
const client = {
|
|
6993
7484
|
id: nextClientId++,
|
|
@@ -7032,8 +7523,8 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7032
7523
|
client.socket.destroy();
|
|
7033
7524
|
}
|
|
7034
7525
|
await new Promise((resolve2) => server.close(() => resolve2()));
|
|
7035
|
-
await
|
|
7036
|
-
await
|
|
7526
|
+
await unlink7(socketPath).catch(() => {});
|
|
7527
|
+
await unlink7(pidPath).catch(() => {});
|
|
7037
7528
|
}
|
|
7038
7529
|
};
|
|
7039
7530
|
planUsagePoller.start();
|
|
@@ -7251,6 +7742,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7251
7742
|
const tabId = optionalString2(payload, "tabId");
|
|
7252
7743
|
const text = optionalString2(payload, "text");
|
|
7253
7744
|
await orch.runTask(taskId, text, tabId);
|
|
7745
|
+
broadcastTaskUpdated(orch, clients, taskId);
|
|
7254
7746
|
const task = orch.getTask(taskId);
|
|
7255
7747
|
if (task)
|
|
7256
7748
|
broadcast(clients, {
|
|
@@ -7534,7 +8026,7 @@ async function main() {
|
|
|
7534
8026
|
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
7535
8027
|
} catch {}
|
|
7536
8028
|
}
|
|
7537
|
-
await
|
|
8029
|
+
await unlink8(socketPath).catch(() => {});
|
|
7538
8030
|
const next = await connectOrStartDaemon();
|
|
7539
8031
|
next.close();
|
|
7540
8032
|
console.log(`kobed: restarted, listening on ${socketPath}`);
|