@sma1lboy/kobe 0.5.15 → 0.5.16
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 +1554 -310
- package/dist/cli/index.js +2404 -762
- package/package.json +3 -1
package/dist/cli/index.js
CHANGED
|
@@ -14755,6 +14755,234 @@ var init_binary = __esm(() => {
|
|
|
14755
14755
|
};
|
|
14756
14756
|
});
|
|
14757
14757
|
|
|
14758
|
+
// src/engine/claude-code-local/models.ts
|
|
14759
|
+
function parseContextWindowSize(modelIdentifier) {
|
|
14760
|
+
const delimitedMatch = /(?:\(|\[)\s*(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])\s*(?:\)|\])/i.exec(modelIdentifier);
|
|
14761
|
+
if (delimitedMatch?.[1] && delimitedMatch[2]) {
|
|
14762
|
+
const parsed2 = Number.parseFloat(delimitedMatch[1].replace(/[,_]/g, ""));
|
|
14763
|
+
if (Number.isFinite(parsed2) && parsed2 > 0) {
|
|
14764
|
+
return Math.round(parsed2 * (delimitedMatch[2].toLowerCase() === "m" ? 1e6 : 1000));
|
|
14765
|
+
}
|
|
14766
|
+
}
|
|
14767
|
+
const contextMatch = /\b(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])(?:\s*(?:token\s*)?context)?\b/i.exec(modelIdentifier);
|
|
14768
|
+
if (!contextMatch?.[1] || !contextMatch[2])
|
|
14769
|
+
return null;
|
|
14770
|
+
const parsed = Number.parseFloat(contextMatch[1].replace(/[,_]/g, ""));
|
|
14771
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
14772
|
+
return null;
|
|
14773
|
+
return Math.round(parsed * (contextMatch[2].toLowerCase() === "m" ? 1e6 : 1000));
|
|
14774
|
+
}
|
|
14775
|
+
function claudeContextWindowFor(modelId) {
|
|
14776
|
+
const parsed = parseContextWindowSize(modelId);
|
|
14777
|
+
if (parsed !== null)
|
|
14778
|
+
return parsed;
|
|
14779
|
+
if (modelId.includes("[1m]"))
|
|
14780
|
+
return LONG_CTX;
|
|
14781
|
+
const inCatalog = CLAUDE_MODELS.some((m2) => m2.id === modelId);
|
|
14782
|
+
if (inCatalog)
|
|
14783
|
+
return STD_CTX;
|
|
14784
|
+
if (modelId.includes("1m") || modelId.includes("[1M]"))
|
|
14785
|
+
return LONG_CTX;
|
|
14786
|
+
return STD_CTX;
|
|
14787
|
+
}
|
|
14788
|
+
var CLAUDE_MODELS, LONG_CTX = 1e6, STD_CTX = 200000;
|
|
14789
|
+
var init_models = __esm(() => {
|
|
14790
|
+
CLAUDE_MODELS = [
|
|
14791
|
+
{ vendor: "claude", id: "claude-opus-4-7[1m]", label: "Opus 4.7 1M", hint: "long context, default" },
|
|
14792
|
+
{ vendor: "claude", id: "claude-opus-4-7", label: "Opus 4.7", hint: "most capable, slowest" },
|
|
14793
|
+
{ vendor: "claude", id: "claude-sonnet-4-6[1m]", label: "sonnet 4.6 (1M)", hint: "long context" },
|
|
14794
|
+
{ vendor: "claude", id: "claude-sonnet-4-6", label: "sonnet 4.6" },
|
|
14795
|
+
{ vendor: "claude", id: "claude-haiku-4-5-20251001", label: "haiku 4.5", hint: "fastest, cheapest" }
|
|
14796
|
+
];
|
|
14797
|
+
});
|
|
14798
|
+
|
|
14799
|
+
// src/engine/claude-code-local/settings.ts
|
|
14800
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
14801
|
+
import { homedir as homedir3 } from "os";
|
|
14802
|
+
import { join as join2 } from "path";
|
|
14803
|
+
function readClaudeSettings() {
|
|
14804
|
+
if (cached !== undefined)
|
|
14805
|
+
return cached;
|
|
14806
|
+
try {
|
|
14807
|
+
const raw = readFileSync2(SETTINGS_PATH, "utf8");
|
|
14808
|
+
const parsed = JSON.parse(raw);
|
|
14809
|
+
if (!parsed || typeof parsed !== "object") {
|
|
14810
|
+
cached = null;
|
|
14811
|
+
return null;
|
|
14812
|
+
}
|
|
14813
|
+
const obj = parsed;
|
|
14814
|
+
const model = typeof obj.model === "string" && obj.model.length > 0 ? obj.model : undefined;
|
|
14815
|
+
cached = { model };
|
|
14816
|
+
return cached;
|
|
14817
|
+
} catch {
|
|
14818
|
+
cached = null;
|
|
14819
|
+
return null;
|
|
14820
|
+
}
|
|
14821
|
+
}
|
|
14822
|
+
function resolveClaudeDefaultModelId() {
|
|
14823
|
+
const settings = readClaudeSettings();
|
|
14824
|
+
if (settings?.model && settings.model.length > 0)
|
|
14825
|
+
return settings.model;
|
|
14826
|
+
return CLAUDE_FALLBACK_DEFAULT_MODEL_ID;
|
|
14827
|
+
}
|
|
14828
|
+
var SETTINGS_PATH, cached, CLAUDE_FALLBACK_DEFAULT_MODEL_ID = "claude-opus-4-7[1m]";
|
|
14829
|
+
var init_settings = __esm(() => {
|
|
14830
|
+
SETTINGS_PATH = join2(homedir3(), ".claude", "settings.json");
|
|
14831
|
+
});
|
|
14832
|
+
|
|
14833
|
+
// src/engine/claude-code-local/capabilities.ts
|
|
14834
|
+
var claudeCapabilities, claudeIdentity;
|
|
14835
|
+
var init_capabilities = __esm(() => {
|
|
14836
|
+
init_models();
|
|
14837
|
+
init_settings();
|
|
14838
|
+
claudeCapabilities = {
|
|
14839
|
+
vendorId: "claude",
|
|
14840
|
+
label: "Claude Code",
|
|
14841
|
+
models: CLAUDE_MODELS,
|
|
14842
|
+
defaultModelId: resolveClaudeDefaultModelId,
|
|
14843
|
+
contextWindowFor: claudeContextWindowFor
|
|
14844
|
+
};
|
|
14845
|
+
claudeIdentity = {
|
|
14846
|
+
vendorId: "claude",
|
|
14847
|
+
productName: "Claude Code",
|
|
14848
|
+
shortName: "Claude",
|
|
14849
|
+
assistantName: "Claude",
|
|
14850
|
+
inputPlaceholder: "Ask Claude\u2026"
|
|
14851
|
+
};
|
|
14852
|
+
});
|
|
14853
|
+
|
|
14854
|
+
// src/engine/codex-local/models.ts
|
|
14855
|
+
function codexContextWindowFor(_modelId) {
|
|
14856
|
+
return DEFAULT_CTX;
|
|
14857
|
+
}
|
|
14858
|
+
var CODEX_MODELS, CODEX_FALLBACK_DEFAULT_MODEL_ID = "gpt-5.4-mini", DEFAULT_CTX = 400000;
|
|
14859
|
+
var init_models2 = __esm(() => {
|
|
14860
|
+
CODEX_MODELS = [
|
|
14861
|
+
{ vendor: "codex", id: "gpt-5.5", label: "GPT-5.5", hint: "latest, ChatGPT-account compatible" },
|
|
14862
|
+
{ vendor: "codex", id: "gpt-5.4", label: "GPT-5.4", hint: "stable" },
|
|
14863
|
+
{ vendor: "codex", id: "gpt-5.4-mini", label: "GPT-5.4 mini", hint: "fastest, always supported" }
|
|
14864
|
+
];
|
|
14865
|
+
});
|
|
14866
|
+
|
|
14867
|
+
// src/engine/codex-local/settings.ts
|
|
14868
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
14869
|
+
import { homedir as homedir4 } from "os";
|
|
14870
|
+
import { join as join3 } from "path";
|
|
14871
|
+
function readModelFromConfig() {
|
|
14872
|
+
try {
|
|
14873
|
+
const raw = readFileSync3(CONFIG_PATH, "utf8");
|
|
14874
|
+
let inTable = false;
|
|
14875
|
+
for (const line of raw.split(`
|
|
14876
|
+
`)) {
|
|
14877
|
+
const t2 = line.trim();
|
|
14878
|
+
if (t2.startsWith("[") && t2.endsWith("]")) {
|
|
14879
|
+
inTable = true;
|
|
14880
|
+
continue;
|
|
14881
|
+
}
|
|
14882
|
+
if (inTable)
|
|
14883
|
+
continue;
|
|
14884
|
+
const m2 = /^model\s*=\s*"([^"]+)"/.exec(t2);
|
|
14885
|
+
if (m2)
|
|
14886
|
+
return m2[1] ?? null;
|
|
14887
|
+
}
|
|
14888
|
+
return null;
|
|
14889
|
+
} catch {
|
|
14890
|
+
return null;
|
|
14891
|
+
}
|
|
14892
|
+
}
|
|
14893
|
+
function resolveCodexDefaultModelId() {
|
|
14894
|
+
if (cached2 === undefined)
|
|
14895
|
+
cached2 = readModelFromConfig();
|
|
14896
|
+
return cached2 ?? CODEX_FALLBACK_DEFAULT_MODEL_ID;
|
|
14897
|
+
}
|
|
14898
|
+
var CONFIG_PATH, cached2;
|
|
14899
|
+
var init_settings2 = __esm(() => {
|
|
14900
|
+
init_models2();
|
|
14901
|
+
CONFIG_PATH = join3(homedir4(), ".codex", "config.toml");
|
|
14902
|
+
});
|
|
14903
|
+
|
|
14904
|
+
// src/engine/codex-local/capabilities.ts
|
|
14905
|
+
var codexCapabilities, codexIdentity;
|
|
14906
|
+
var init_capabilities2 = __esm(() => {
|
|
14907
|
+
init_models2();
|
|
14908
|
+
init_settings2();
|
|
14909
|
+
codexCapabilities = {
|
|
14910
|
+
vendorId: "codex",
|
|
14911
|
+
label: "Codex",
|
|
14912
|
+
models: CODEX_MODELS,
|
|
14913
|
+
defaultModelId: resolveCodexDefaultModelId,
|
|
14914
|
+
contextWindowFor: codexContextWindowFor
|
|
14915
|
+
};
|
|
14916
|
+
codexIdentity = {
|
|
14917
|
+
vendorId: "codex",
|
|
14918
|
+
productName: "Codex",
|
|
14919
|
+
shortName: "Codex",
|
|
14920
|
+
assistantName: "Codex",
|
|
14921
|
+
inputPlaceholder: "Ask Codex\u2026"
|
|
14922
|
+
};
|
|
14923
|
+
});
|
|
14924
|
+
|
|
14925
|
+
// src/engine/registry.ts
|
|
14926
|
+
function getCapabilities(vendor) {
|
|
14927
|
+
return ENGINE_REGISTRY[vendor] ?? defaultCapabilities;
|
|
14928
|
+
}
|
|
14929
|
+
function getIdentity(vendor) {
|
|
14930
|
+
return ENGINE_IDENTITIES[vendor] ?? defaultIdentity;
|
|
14931
|
+
}
|
|
14932
|
+
function capabilitiesForModelId(modelId) {
|
|
14933
|
+
if (!modelId)
|
|
14934
|
+
return defaultCapabilities;
|
|
14935
|
+
for (const caps of Object.values(ENGINE_REGISTRY)) {
|
|
14936
|
+
if (!caps)
|
|
14937
|
+
continue;
|
|
14938
|
+
if (caps.models.some((m2) => m2.id === modelId))
|
|
14939
|
+
return caps;
|
|
14940
|
+
}
|
|
14941
|
+
return defaultCapabilities;
|
|
14942
|
+
}
|
|
14943
|
+
function allModels() {
|
|
14944
|
+
const seen = new Set;
|
|
14945
|
+
const out = [];
|
|
14946
|
+
for (const caps of Object.values(ENGINE_REGISTRY)) {
|
|
14947
|
+
if (!caps)
|
|
14948
|
+
continue;
|
|
14949
|
+
for (const m2 of caps.models) {
|
|
14950
|
+
const key = `${m2.vendor}:${m2.id}`;
|
|
14951
|
+
if (seen.has(key))
|
|
14952
|
+
continue;
|
|
14953
|
+
seen.add(key);
|
|
14954
|
+
out.push(m2);
|
|
14955
|
+
}
|
|
14956
|
+
}
|
|
14957
|
+
return out;
|
|
14958
|
+
}
|
|
14959
|
+
function modelLabelFor(modelId) {
|
|
14960
|
+
const resolved = modelId ?? defaultCapabilities.defaultModelId();
|
|
14961
|
+
for (const caps of Object.values(ENGINE_REGISTRY)) {
|
|
14962
|
+
if (!caps)
|
|
14963
|
+
continue;
|
|
14964
|
+
const match = caps.models.find((m2) => m2.id === resolved);
|
|
14965
|
+
if (match)
|
|
14966
|
+
return match.label;
|
|
14967
|
+
}
|
|
14968
|
+
return resolved;
|
|
14969
|
+
}
|
|
14970
|
+
var ENGINE_REGISTRY, ENGINE_IDENTITIES, defaultCapabilities, defaultIdentity;
|
|
14971
|
+
var init_registry2 = __esm(() => {
|
|
14972
|
+
init_capabilities();
|
|
14973
|
+
init_capabilities2();
|
|
14974
|
+
ENGINE_REGISTRY = {
|
|
14975
|
+
claude: claudeCapabilities,
|
|
14976
|
+
codex: codexCapabilities
|
|
14977
|
+
};
|
|
14978
|
+
ENGINE_IDENTITIES = {
|
|
14979
|
+
claude: claudeIdentity,
|
|
14980
|
+
codex: codexIdentity
|
|
14981
|
+
};
|
|
14982
|
+
defaultCapabilities = claudeCapabilities;
|
|
14983
|
+
defaultIdentity = claudeIdentity;
|
|
14984
|
+
});
|
|
14985
|
+
|
|
14758
14986
|
// src/types/task.ts
|
|
14759
14987
|
function nextChatTabSeq(tabs) {
|
|
14760
14988
|
let max = 0;
|
|
@@ -14763,7 +14991,7 @@ function nextChatTabSeq(tabs) {
|
|
|
14763
14991
|
max = t2.seq;
|
|
14764
14992
|
return max + 1;
|
|
14765
14993
|
}
|
|
14766
|
-
var toTaskId = (id) => id;
|
|
14994
|
+
var toTaskId = (id) => id, DEFAULT_TASK_VENDOR = "claude";
|
|
14767
14995
|
|
|
14768
14996
|
// src/orchestrator/index/ulid.ts
|
|
14769
14997
|
function encodeTime(now, len) {
|
|
@@ -14826,8 +15054,8 @@ var init_ulid = __esm(() => {
|
|
|
14826
15054
|
|
|
14827
15055
|
// src/orchestrator/index/store.ts
|
|
14828
15056
|
import { mkdir, open as open2, readFile as readFile2, rename, unlink } from "fs/promises";
|
|
14829
|
-
import { homedir as
|
|
14830
|
-
import { dirname as dirname2, join as
|
|
15057
|
+
import { homedir as homedir5 } from "os";
|
|
15058
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
14831
15059
|
|
|
14832
15060
|
class TaskIndexStore {
|
|
14833
15061
|
homeDir;
|
|
@@ -14839,9 +15067,9 @@ class TaskIndexStore {
|
|
|
14839
15067
|
listeners = new Set;
|
|
14840
15068
|
saveChain = Promise.resolve();
|
|
14841
15069
|
constructor(options = {}) {
|
|
14842
|
-
this.homeDir = options.homeDir ??
|
|
14843
|
-
this.kobeDir =
|
|
14844
|
-
this.path =
|
|
15070
|
+
this.homeDir = options.homeDir ?? homedir5();
|
|
15071
|
+
this.kobeDir = join4(this.homeDir, ".kobe");
|
|
15072
|
+
this.path = join4(this.kobeDir, "tasks.json");
|
|
14845
15073
|
this.tmpPath = `${this.path}.tmp`;
|
|
14846
15074
|
}
|
|
14847
15075
|
subscribe(listener2) {
|
|
@@ -14931,7 +15159,16 @@ class TaskIndexStore {
|
|
|
14931
15159
|
activeTabId = activeIn && tabsIn.some((t2) => t2.id === activeIn) ? activeIn : tabsIn[0]?.id ?? "";
|
|
14932
15160
|
} else {
|
|
14933
15161
|
const tabId = ulid();
|
|
14934
|
-
tabs = [
|
|
15162
|
+
tabs = [
|
|
15163
|
+
{
|
|
15164
|
+
id: tabId,
|
|
15165
|
+
sessionId: sessionId ?? null,
|
|
15166
|
+
seq: 1,
|
|
15167
|
+
createdAt: now,
|
|
15168
|
+
...rest.model ? { model: rest.model } : {},
|
|
15169
|
+
vendor: rest.vendor ?? DEFAULT_TASK_VENDOR
|
|
15170
|
+
}
|
|
15171
|
+
];
|
|
14935
15172
|
activeTabId = tabId;
|
|
14936
15173
|
}
|
|
14937
15174
|
const firstSession = tabs[0]?.sessionId ?? null;
|
|
@@ -15094,7 +15331,9 @@ function coerceTask(value) {
|
|
|
15094
15331
|
sessionId: tt.sessionId ?? null,
|
|
15095
15332
|
seq,
|
|
15096
15333
|
createdAt: tt.createdAt,
|
|
15097
|
-
...typeof tt.title === "string" ? { title: tt.title } : {}
|
|
15334
|
+
...typeof tt.title === "string" ? { title: tt.title } : {},
|
|
15335
|
+
...typeof tt.model === "string" ? { model: tt.model } : {},
|
|
15336
|
+
...isVendorId(tt.vendor) ? { vendor: tt.vendor } : {}
|
|
15098
15337
|
};
|
|
15099
15338
|
tabs.push(tab);
|
|
15100
15339
|
}
|
|
@@ -15127,6 +15366,7 @@ function coerceTask(value) {
|
|
|
15127
15366
|
kind: v2.kind === "main" ? "main" : "task",
|
|
15128
15367
|
permissionMode: isPermissionMode(v2.permissionMode) ? v2.permissionMode : undefined,
|
|
15129
15368
|
model: typeof v2.model === "string" ? v2.model : undefined,
|
|
15369
|
+
vendor: resolveTaskVendor(v2.vendor, typeof v2.model === "string" ? v2.model : undefined),
|
|
15130
15370
|
createdAt: v2.createdAt,
|
|
15131
15371
|
updatedAt: v2.updatedAt
|
|
15132
15372
|
};
|
|
@@ -15134,10 +15374,23 @@ function coerceTask(value) {
|
|
|
15134
15374
|
function isPermissionMode(v2) {
|
|
15135
15375
|
return v2 === "default" || v2 === "plan";
|
|
15136
15376
|
}
|
|
15377
|
+
function isVendorId(v2) {
|
|
15378
|
+
return typeof v2 === "string" && v2 in ENGINE_REGISTRY;
|
|
15379
|
+
}
|
|
15380
|
+
function resolveTaskVendor(rawVendor, modelId) {
|
|
15381
|
+
const stored = isVendorId(rawVendor) ? rawVendor : DEFAULT_TASK_VENDOR;
|
|
15382
|
+
if (!modelId)
|
|
15383
|
+
return stored;
|
|
15384
|
+
const matched = Object.values(ENGINE_REGISTRY).some((caps) => caps?.models.some((m2) => m2.id === modelId));
|
|
15385
|
+
if (!matched)
|
|
15386
|
+
return stored;
|
|
15387
|
+
return capabilitiesForModelId(modelId).vendorId;
|
|
15388
|
+
}
|
|
15137
15389
|
function isTaskStatus(s2) {
|
|
15138
15390
|
return s2 === "backlog" || s2 === "in_progress" || s2 === "in_review" || s2 === "done" || s2 === "canceled" || s2 === "error";
|
|
15139
15391
|
}
|
|
15140
15392
|
var init_store = __esm(() => {
|
|
15393
|
+
init_registry2();
|
|
15141
15394
|
init_ulid();
|
|
15142
15395
|
});
|
|
15143
15396
|
|
|
@@ -15180,7 +15433,7 @@ var init_package = __esm(() => {
|
|
|
15180
15433
|
package_default = {
|
|
15181
15434
|
$schema: "https://json.schemastore.org/package.json",
|
|
15182
15435
|
name: "@sma1lboy/kobe",
|
|
15183
|
-
version: "0.5.
|
|
15436
|
+
version: "0.5.16",
|
|
15184
15437
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
15185
15438
|
type: "module",
|
|
15186
15439
|
packageManager: "bun@1.3.13",
|
|
@@ -15207,6 +15460,8 @@ var init_package = __esm(() => {
|
|
|
15207
15460
|
dev: "KOBE_DEV=1 bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
|
|
15208
15461
|
"dev:test": "bun run scripts/dev-fixture.ts && KOBE_DEV=1 KOBE_TEST_ENGINE=dev-fake KOBE_HOME_DIR=$PWD/.dev-fixture/home bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
|
|
15209
15462
|
"dev:test:reset": "bun run scripts/dev-fixture.ts --reset",
|
|
15463
|
+
"dev:sandbox": "mkdir -p .dev-sandbox/home && KOBE_DEV=1 KOBE_HOME_DIR=$PWD/.dev-sandbox/home bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
|
|
15464
|
+
"dev:sandbox:reset": "bun run scripts/dev-sandbox.ts --reset",
|
|
15210
15465
|
build: "bun run scripts/build.ts",
|
|
15211
15466
|
compile: "bun run scripts/compile.ts",
|
|
15212
15467
|
typecheck: "tsc --noEmit",
|
|
@@ -15354,8 +15609,8 @@ __export(exports_diagnose, {
|
|
|
15354
15609
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
15355
15610
|
import { existsSync as existsSync2, readdirSync, statSync as statSync2 } from "fs";
|
|
15356
15611
|
import { stat } from "fs/promises";
|
|
15357
|
-
import { homedir as
|
|
15358
|
-
import { join as
|
|
15612
|
+
import { homedir as homedir6, arch as osArch, platform as osPlatform, release as osRelease } from "os";
|
|
15613
|
+
import { join as join5 } from "path";
|
|
15359
15614
|
function formatKv(key, value) {
|
|
15360
15615
|
const k2 = key.length >= KEY_PAD ? key : key.padEnd(KEY_PAD, " ");
|
|
15361
15616
|
return `${k2}${value}`;
|
|
@@ -15398,7 +15653,7 @@ function reconcileWorktrees(tasks, onDiskByRepo) {
|
|
|
15398
15653
|
for (const [repo, names] of onDiskByRepo) {
|
|
15399
15654
|
const root = worktreeRootFor(repo);
|
|
15400
15655
|
for (const n2 of names)
|
|
15401
|
-
onDiskAbs.add(
|
|
15656
|
+
onDiskAbs.add(join5(root, n2));
|
|
15402
15657
|
}
|
|
15403
15658
|
const referenced = new Set;
|
|
15404
15659
|
for (const t2 of tasks)
|
|
@@ -15454,7 +15709,7 @@ function dirSize(root) {
|
|
|
15454
15709
|
continue;
|
|
15455
15710
|
}
|
|
15456
15711
|
for (const name of entries) {
|
|
15457
|
-
const full =
|
|
15712
|
+
const full = join5(dir, name);
|
|
15458
15713
|
try {
|
|
15459
15714
|
const st = statSync2(full);
|
|
15460
15715
|
if (st.isSymbolicLink())
|
|
@@ -15472,7 +15727,7 @@ function listWorktreeDirs(root) {
|
|
|
15472
15727
|
try {
|
|
15473
15728
|
return readdirSync(root).filter((n2) => {
|
|
15474
15729
|
try {
|
|
15475
|
-
return statSync2(
|
|
15730
|
+
return statSync2(join5(root, n2)).isDirectory();
|
|
15476
15731
|
} catch {
|
|
15477
15732
|
return false;
|
|
15478
15733
|
}
|
|
@@ -15543,7 +15798,7 @@ async function buildDiagnoseReport() {
|
|
|
15543
15798
|
let tasks = [];
|
|
15544
15799
|
let taskLoadError = null;
|
|
15545
15800
|
try {
|
|
15546
|
-
const store2 = new TaskIndexStore({ homeDir:
|
|
15801
|
+
const store2 = new TaskIndexStore({ homeDir: homedir6() });
|
|
15547
15802
|
await store2.load();
|
|
15548
15803
|
tasks = store2.list();
|
|
15549
15804
|
} catch (err) {
|
|
@@ -15555,8 +15810,8 @@ async function buildDiagnoseReport() {
|
|
|
15555
15810
|
lines.push(formatKv("tasks.json:", formatTaskBreakdown(tasks)));
|
|
15556
15811
|
}
|
|
15557
15812
|
section("config dir (~/.config/kobe/)");
|
|
15558
|
-
const configDir =
|
|
15559
|
-
const stateJsonPath =
|
|
15813
|
+
const configDir = join5(homedir6(), ".config", "kobe");
|
|
15814
|
+
const stateJsonPath = join5(configDir, "state.json");
|
|
15560
15815
|
lines.push(formatKv("path:", configDir));
|
|
15561
15816
|
if (existsSync2(configDir)) {
|
|
15562
15817
|
lines.push(formatKv("exists:", "yes"));
|
|
@@ -15613,7 +15868,7 @@ async function buildDiagnoseReport() {
|
|
|
15613
15868
|
}
|
|
15614
15869
|
}
|
|
15615
15870
|
section("recent errors");
|
|
15616
|
-
const errorLogPath =
|
|
15871
|
+
const errorLogPath = join5(stateDir, "crash-logs", "latest.log");
|
|
15617
15872
|
if (existsSync2(errorLogPath)) {
|
|
15618
15873
|
const tail = tailLines(errorLogPath, 5);
|
|
15619
15874
|
if (tail.length === 0) {
|
|
@@ -15782,10 +16037,10 @@ function validateTheme(value) {
|
|
|
15782
16037
|
var init_schema = () => {};
|
|
15783
16038
|
|
|
15784
16039
|
// src/tui/context/theme/loader.ts
|
|
15785
|
-
import { readFileSync as
|
|
15786
|
-
import { join as
|
|
16040
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
|
|
16041
|
+
import { join as join6 } from "path";
|
|
15787
16042
|
function userThemesDir() {
|
|
15788
|
-
return
|
|
16043
|
+
return join6(kobeStateDir(), "themes");
|
|
15789
16044
|
}
|
|
15790
16045
|
function loadUserThemes() {
|
|
15791
16046
|
const dir = userThemesDir();
|
|
@@ -15799,10 +16054,10 @@ function loadUserThemes() {
|
|
|
15799
16054
|
for (const file of entries) {
|
|
15800
16055
|
if (!file.endsWith(".json"))
|
|
15801
16056
|
continue;
|
|
15802
|
-
const path5 =
|
|
16057
|
+
const path5 = join6(dir, file);
|
|
15803
16058
|
let parsed;
|
|
15804
16059
|
try {
|
|
15805
|
-
const text =
|
|
16060
|
+
const text = readFileSync4(path5, "utf8");
|
|
15806
16061
|
parsed = JSON.parse(text);
|
|
15807
16062
|
} catch (err) {
|
|
15808
16063
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -15829,8 +16084,8 @@ var exports_theme = {};
|
|
|
15829
16084
|
__export(exports_theme, {
|
|
15830
16085
|
runThemeSubcommand: () => runThemeSubcommand
|
|
15831
16086
|
});
|
|
15832
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as
|
|
15833
|
-
import { basename, join as
|
|
16087
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, readdirSync as readdirSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
16088
|
+
import { basename, join as join7, resolve } from "path";
|
|
15834
16089
|
function fail2(message) {
|
|
15835
16090
|
process.stderr.write(`kobe theme: ${message}
|
|
15836
16091
|
`);
|
|
@@ -15854,7 +16109,7 @@ function listThemes2() {
|
|
|
15854
16109
|
} else {
|
|
15855
16110
|
for (const f2 of userFiles) {
|
|
15856
16111
|
const name = f2.slice(0, -".json".length);
|
|
15857
|
-
const path5 =
|
|
16112
|
+
const path5 = join7(dir, f2);
|
|
15858
16113
|
const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
|
|
15859
16114
|
lines.push(` ${name}${overridesBundled} ${path5}`);
|
|
15860
16115
|
}
|
|
@@ -15883,7 +16138,7 @@ async function readSource(source) {
|
|
|
15883
16138
|
const abs = resolve(process.cwd(), source);
|
|
15884
16139
|
let text;
|
|
15885
16140
|
try {
|
|
15886
|
-
text =
|
|
16141
|
+
text = readFileSync5(abs, "utf8");
|
|
15887
16142
|
} catch (err) {
|
|
15888
16143
|
fail2(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
|
|
15889
16144
|
}
|
|
@@ -15944,7 +16199,7 @@ async function addTheme2(args) {
|
|
|
15944
16199
|
}
|
|
15945
16200
|
const dir = userThemesDir();
|
|
15946
16201
|
mkdirSync2(dir, { recursive: true });
|
|
15947
|
-
const dest =
|
|
16202
|
+
const dest = join7(dir, `${name}.json`);
|
|
15948
16203
|
if (existsSync3(dest) && !opts.force) {
|
|
15949
16204
|
fail2(`${dest} already exists (pass --force to overwrite)`);
|
|
15950
16205
|
}
|
|
@@ -15962,7 +16217,7 @@ function removeTheme(args) {
|
|
|
15962
16217
|
if (BUNDLED_NAMES.includes(name)) {
|
|
15963
16218
|
fail2(`"${name}" is a built-in theme and cannot be removed`);
|
|
15964
16219
|
}
|
|
15965
|
-
const dest =
|
|
16220
|
+
const dest = join7(userThemesDir(), `${name}.json`);
|
|
15966
16221
|
if (!existsSync3(dest)) {
|
|
15967
16222
|
fail2(`no user theme named "${name}" (looked for ${dest})`);
|
|
15968
16223
|
}
|
|
@@ -16257,17 +16512,38 @@ var init_mcp_bridge = __esm(() => {
|
|
|
16257
16512
|
});
|
|
16258
16513
|
|
|
16259
16514
|
// src/daemon/paths.ts
|
|
16260
|
-
import {
|
|
16261
|
-
import {
|
|
16262
|
-
|
|
16515
|
+
import { createHash } from "crypto";
|
|
16516
|
+
import { homedir as homedir7, tmpdir } from "os";
|
|
16517
|
+
import { join as join8 } from "path";
|
|
16518
|
+
function shortHomeTag(homeDir2) {
|
|
16519
|
+
return createHash("sha1").update(homeDir2).digest("hex").slice(0, 8);
|
|
16520
|
+
}
|
|
16521
|
+
function fitSocketPath(naturalPath, homeDir2, role, pidTag) {
|
|
16522
|
+
if (Buffer.byteLength(naturalPath, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
|
|
16523
|
+
return naturalPath;
|
|
16524
|
+
const tag = shortHomeTag(homeDir2);
|
|
16525
|
+
const suffix = pidTag === undefined ? "" : `-${pidTag}`;
|
|
16526
|
+
const fallback = join8(tmpdir(), `kobe-${tag}-${role}${suffix}.sock`);
|
|
16527
|
+
if (Buffer.byteLength(fallback, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
|
|
16528
|
+
return fallback;
|
|
16529
|
+
throw new Error(`kobe socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
|
|
16530
|
+
}
|
|
16531
|
+
function defaultDaemonSocketPath(homeDir2) {
|
|
16532
|
+
const explicit = homeDir2 ?? process.env.KOBE_HOME_DIR;
|
|
16533
|
+
if (explicit && explicit.length > 0) {
|
|
16534
|
+
return fitSocketPath(join8(explicit, ".kobe", "daemon.sock"), explicit, "daemon");
|
|
16535
|
+
}
|
|
16263
16536
|
const runtimeDir = process.env.XDG_RUNTIME_DIR;
|
|
16264
|
-
if (runtimeDir && runtimeDir.length > 0)
|
|
16265
|
-
return
|
|
16266
|
-
|
|
16537
|
+
if (runtimeDir && runtimeDir.length > 0) {
|
|
16538
|
+
return fitSocketPath(join8(runtimeDir, "kobe.sock"), runtimeDir, "daemon");
|
|
16539
|
+
}
|
|
16540
|
+
const home = homedir7();
|
|
16541
|
+
return fitSocketPath(join8(home, ".kobe", "daemon.sock"), home, "daemon");
|
|
16267
16542
|
}
|
|
16268
|
-
function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ??
|
|
16269
|
-
return
|
|
16543
|
+
function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir7()) {
|
|
16544
|
+
return join8(homeDir2, ".kobe", "daemon.pid");
|
|
16270
16545
|
}
|
|
16546
|
+
var SOCKET_PATH_SAFETY_LIMIT = 100;
|
|
16271
16547
|
var init_paths2 = () => {};
|
|
16272
16548
|
|
|
16273
16549
|
// src/daemon/protocol.ts
|
|
@@ -16287,6 +16563,7 @@ function serializeTask(task) {
|
|
|
16287
16563
|
pinned: task.pinned ?? false,
|
|
16288
16564
|
permissionMode: task.permissionMode,
|
|
16289
16565
|
model: task.model,
|
|
16566
|
+
vendor: task.vendor,
|
|
16290
16567
|
createdAt: task.createdAt,
|
|
16291
16568
|
updatedAt: task.updatedAt
|
|
16292
16569
|
};
|
|
@@ -16486,7 +16763,7 @@ var init_client = () => {};
|
|
|
16486
16763
|
// src/client/daemon-process.ts
|
|
16487
16764
|
import { spawn as spawn3 } from "child_process";
|
|
16488
16765
|
import { existsSync as existsSync4 } from "fs";
|
|
16489
|
-
import { dirname as dirname3, join as
|
|
16766
|
+
import { dirname as dirname3, join as join9, resolve as resolve2 } from "path";
|
|
16490
16767
|
import { fileURLToPath } from "url";
|
|
16491
16768
|
async function ensureDaemonReachable() {
|
|
16492
16769
|
const socketPath = defaultDaemonSocketPath();
|
|
@@ -16534,7 +16811,7 @@ function resolveKobedEntry() {
|
|
|
16534
16811
|
if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
|
|
16535
16812
|
const exeDir = dirname3(process.execPath);
|
|
16536
16813
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
16537
|
-
const sibling =
|
|
16814
|
+
const sibling = join9(exeDir, `kobed${ext}`);
|
|
16538
16815
|
if (!existsSync4(sibling)) {
|
|
16539
16816
|
throw new Error(`kobe: standalone build expected sibling kobed binary at ${sibling}; extract the full release tarball.`);
|
|
16540
16817
|
}
|
|
@@ -16554,120 +16831,6 @@ var init_daemon_process = __esm(() => {
|
|
|
16554
16831
|
init_client();
|
|
16555
16832
|
});
|
|
16556
16833
|
|
|
16557
|
-
// src/engine/claude-settings.ts
|
|
16558
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
16559
|
-
import { homedir as homedir6 } from "os";
|
|
16560
|
-
import { join as join8 } from "path";
|
|
16561
|
-
function readClaudeSettings() {
|
|
16562
|
-
if (cached !== undefined)
|
|
16563
|
-
return cached;
|
|
16564
|
-
try {
|
|
16565
|
-
const raw = readFileSync4(SETTINGS_PATH, "utf8");
|
|
16566
|
-
const parsed = JSON.parse(raw);
|
|
16567
|
-
if (!parsed || typeof parsed !== "object") {
|
|
16568
|
-
cached = null;
|
|
16569
|
-
return null;
|
|
16570
|
-
}
|
|
16571
|
-
const obj = parsed;
|
|
16572
|
-
const model = typeof obj.model === "string" && obj.model.length > 0 ? obj.model : undefined;
|
|
16573
|
-
cached = { model };
|
|
16574
|
-
return cached;
|
|
16575
|
-
} catch {
|
|
16576
|
-
cached = null;
|
|
16577
|
-
return null;
|
|
16578
|
-
}
|
|
16579
|
-
}
|
|
16580
|
-
function resolveDefaultModelId() {
|
|
16581
|
-
const settings = readClaudeSettings();
|
|
16582
|
-
if (settings?.model && settings.model.length > 0)
|
|
16583
|
-
return settings.model;
|
|
16584
|
-
return FALLBACK_DEFAULT_MODEL_ID;
|
|
16585
|
-
}
|
|
16586
|
-
var SETTINGS_PATH, cached, FALLBACK_DEFAULT_MODEL_ID = "claude-opus-4-7[1m]";
|
|
16587
|
-
var init_claude_settings = __esm(() => {
|
|
16588
|
-
SETTINGS_PATH = join8(homedir6(), ".claude", "settings.json");
|
|
16589
|
-
});
|
|
16590
|
-
|
|
16591
|
-
// src/session/usage-metrics.ts
|
|
16592
|
-
function totalContextTokens(u2) {
|
|
16593
|
-
return u2.input_tokens + (u2.cache_read_input_tokens ?? 0) + (u2.cache_creation_input_tokens ?? 0);
|
|
16594
|
-
}
|
|
16595
|
-
function parseTimestampMs(value) {
|
|
16596
|
-
const ms = new Date(value).getTime();
|
|
16597
|
-
return Number.isFinite(ms) ? ms : null;
|
|
16598
|
-
}
|
|
16599
|
-
function mergeIntervals(intervals) {
|
|
16600
|
-
if (intervals.length === 0)
|
|
16601
|
-
return [];
|
|
16602
|
-
const sorted = [...intervals].sort((a2, b2) => a2.startMs - b2.startMs);
|
|
16603
|
-
const first = sorted[0];
|
|
16604
|
-
if (!first)
|
|
16605
|
-
return [];
|
|
16606
|
-
const merged = [{ startMs: first.startMs, endMs: first.endMs }];
|
|
16607
|
-
for (let i2 = 1;i2 < sorted.length; i2++) {
|
|
16608
|
-
const current = sorted[i2];
|
|
16609
|
-
const last = merged[merged.length - 1];
|
|
16610
|
-
if (!current || !last)
|
|
16611
|
-
continue;
|
|
16612
|
-
if (current.startMs <= last.endMs) {
|
|
16613
|
-
last.endMs = Math.max(last.endMs, current.endMs);
|
|
16614
|
-
} else {
|
|
16615
|
-
merged.push({ startMs: current.startMs, endMs: current.endMs });
|
|
16616
|
-
}
|
|
16617
|
-
}
|
|
16618
|
-
return merged;
|
|
16619
|
-
}
|
|
16620
|
-
function durationMs(intervals) {
|
|
16621
|
-
return intervals.reduce((total, interval) => total + (interval.endMs - interval.startMs), 0);
|
|
16622
|
-
}
|
|
16623
|
-
function deriveSessionUsageMetrics(past) {
|
|
16624
|
-
let latestUsage;
|
|
16625
|
-
let latestUsageTimestampMs = null;
|
|
16626
|
-
let lastUserTimestampMs = null;
|
|
16627
|
-
let inputTokens = 0;
|
|
16628
|
-
let outputTokens = 0;
|
|
16629
|
-
const intervals = [];
|
|
16630
|
-
for (const message of past) {
|
|
16631
|
-
const timestampMs = parseTimestampMs(message.timestamp);
|
|
16632
|
-
if (message.role === "user" && timestampMs !== null) {
|
|
16633
|
-
lastUserTimestampMs = timestampMs;
|
|
16634
|
-
continue;
|
|
16635
|
-
}
|
|
16636
|
-
if (message.role !== "assistant" || !message.usage)
|
|
16637
|
-
continue;
|
|
16638
|
-
if (timestampMs !== null && (latestUsageTimestampMs === null || timestampMs > latestUsageTimestampMs)) {
|
|
16639
|
-
latestUsageTimestampMs = timestampMs;
|
|
16640
|
-
latestUsage = message.usage;
|
|
16641
|
-
} else if (latestUsage === undefined) {
|
|
16642
|
-
latestUsage = message.usage;
|
|
16643
|
-
}
|
|
16644
|
-
inputTokens += message.usage.input_tokens;
|
|
16645
|
-
outputTokens += message.usage.output_tokens;
|
|
16646
|
-
if (timestampMs !== null && lastUserTimestampMs !== null && timestampMs > lastUserTimestampMs) {
|
|
16647
|
-
intervals.push({ startMs: lastUserTimestampMs, endMs: timestampMs });
|
|
16648
|
-
}
|
|
16649
|
-
}
|
|
16650
|
-
if (!latestUsage)
|
|
16651
|
-
return;
|
|
16652
|
-
const totalDurationMs = durationMs(mergeIntervals(intervals));
|
|
16653
|
-
if (totalDurationMs <= 0)
|
|
16654
|
-
return latestUsage;
|
|
16655
|
-
return {
|
|
16656
|
-
...latestUsage,
|
|
16657
|
-
total_speed_tokens_per_second: (inputTokens + outputTokens) / (totalDurationMs / 1000)
|
|
16658
|
-
};
|
|
16659
|
-
}
|
|
16660
|
-
function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
|
|
16661
|
-
const startMs = startedAtIso ? parseTimestampMs(startedAtIso) : null;
|
|
16662
|
-
const endMs = parseTimestampMs(endedAtIso);
|
|
16663
|
-
if (startMs === null || endMs === null || endMs <= startMs)
|
|
16664
|
-
return usage;
|
|
16665
|
-
return {
|
|
16666
|
-
...usage,
|
|
16667
|
-
total_speed_tokens_per_second: (usage.input_tokens + usage.output_tokens) / ((endMs - startMs) / 1000)
|
|
16668
|
-
};
|
|
16669
|
-
}
|
|
16670
|
-
|
|
16671
16834
|
// src/orchestrator/metadata-suggester.ts
|
|
16672
16835
|
import { spawn as spawn4 } from "child_process";
|
|
16673
16836
|
|
|
@@ -16979,10 +17142,11 @@ class SessionPump {
|
|
|
16979
17142
|
}
|
|
16980
17143
|
async run(taskId, tabId, handle) {
|
|
16981
17144
|
const tabKey = chatRunStateKey(taskId, tabId);
|
|
17145
|
+
const engine3 = this.env.engineFor(taskId, tabId);
|
|
16982
17146
|
let killedForInput = false;
|
|
16983
17147
|
let terminalEvent = null;
|
|
16984
17148
|
try {
|
|
16985
|
-
for await (const ev of
|
|
17149
|
+
for await (const ev of engine3.stream(handle)) {
|
|
16986
17150
|
const inputReq = detectUserInputFromEngineEvent(ev);
|
|
16987
17151
|
if (inputReq) {
|
|
16988
17152
|
this.env.dispatch(taskId, tabId, ev);
|
|
@@ -16996,7 +17160,7 @@ class SessionPump {
|
|
|
16996
17160
|
});
|
|
16997
17161
|
killedForInput = true;
|
|
16998
17162
|
try {
|
|
16999
|
-
await
|
|
17163
|
+
await engine3.stop(handle);
|
|
17000
17164
|
} catch {}
|
|
17001
17165
|
break;
|
|
17002
17166
|
}
|
|
@@ -17009,7 +17173,7 @@ class SessionPump {
|
|
|
17009
17173
|
} finally {
|
|
17010
17174
|
if (!killedForInput) {
|
|
17011
17175
|
try {
|
|
17012
|
-
await
|
|
17176
|
+
await engine3.stop(handle);
|
|
17013
17177
|
} catch {}
|
|
17014
17178
|
}
|
|
17015
17179
|
}
|
|
@@ -17069,7 +17233,8 @@ function summarizeWorktreeError(raw, repo, baseRef) {
|
|
|
17069
17233
|
}
|
|
17070
17234
|
|
|
17071
17235
|
class Orchestrator {
|
|
17072
|
-
|
|
17236
|
+
engines;
|
|
17237
|
+
fallbackEngine;
|
|
17073
17238
|
store;
|
|
17074
17239
|
worktrees;
|
|
17075
17240
|
metadataSuggester;
|
|
@@ -17087,7 +17252,26 @@ class Orchestrator {
|
|
|
17087
17252
|
runStateAcc;
|
|
17088
17253
|
setRunState;
|
|
17089
17254
|
constructor(deps) {
|
|
17090
|
-
|
|
17255
|
+
const built = {};
|
|
17256
|
+
let fallback;
|
|
17257
|
+
if (deps.engines) {
|
|
17258
|
+
for (const [vendor, eng] of Object.entries(deps.engines)) {
|
|
17259
|
+
if (!eng)
|
|
17260
|
+
continue;
|
|
17261
|
+
built[vendor] = eng;
|
|
17262
|
+
fallback ??= eng;
|
|
17263
|
+
}
|
|
17264
|
+
}
|
|
17265
|
+
if (deps.engine) {
|
|
17266
|
+
const v2 = deps.engine.capabilities.vendorId;
|
|
17267
|
+
built[v2] ??= deps.engine;
|
|
17268
|
+
fallback ??= deps.engine;
|
|
17269
|
+
}
|
|
17270
|
+
if (!fallback) {
|
|
17271
|
+
throw new Error("Orchestrator: no usable engine found; both deps.engine and deps.engines were examined but contained no valid engines.");
|
|
17272
|
+
}
|
|
17273
|
+
this.engines = built;
|
|
17274
|
+
this.fallbackEngine = fallback;
|
|
17091
17275
|
this.store = deps.store;
|
|
17092
17276
|
this.worktrees = deps.worktrees;
|
|
17093
17277
|
this.metadataSuggester = deps.metadataSuggester ?? new MetadataSuggester;
|
|
@@ -17101,13 +17285,86 @@ class Orchestrator {
|
|
|
17101
17285
|
this.runStateAcc = runState;
|
|
17102
17286
|
this.setRunState = (next) => setRunState(() => next);
|
|
17103
17287
|
this.sessionPump = new SessionPump({
|
|
17104
|
-
|
|
17288
|
+
engineFor: (taskId, tabId) => this.engineForTaskTabId(taskId, tabId),
|
|
17105
17289
|
broker: this.pendingInputBroker,
|
|
17106
17290
|
dispatch: (taskId, tabId, ev) => this.dispatchEvent(taskId, tabId, ev),
|
|
17107
17291
|
nextRequestId: () => `req-${++this.requestIdCounter}`,
|
|
17108
17292
|
onPendingInputChange: () => this.bumpRunState()
|
|
17109
17293
|
});
|
|
17110
17294
|
}
|
|
17295
|
+
engineForVendor(vendor) {
|
|
17296
|
+
const v2 = vendor ?? DEFAULT_TASK_VENDOR;
|
|
17297
|
+
return this.engines[v2] ?? this.fallbackEngine;
|
|
17298
|
+
}
|
|
17299
|
+
engineForTask(task) {
|
|
17300
|
+
return this.engineForVendor(task.vendor);
|
|
17301
|
+
}
|
|
17302
|
+
vendorForTab(task, tab) {
|
|
17303
|
+
return tab.vendor ?? task.vendor ?? "claude";
|
|
17304
|
+
}
|
|
17305
|
+
modelForTab(task, tab, engine3) {
|
|
17306
|
+
return tab.model ?? task.model ?? engine3.capabilities.defaultModelId();
|
|
17307
|
+
}
|
|
17308
|
+
engineForTab(task, tab) {
|
|
17309
|
+
return this.engineForVendor(this.vendorForTab(task, tab));
|
|
17310
|
+
}
|
|
17311
|
+
async engineForTabRun(task, tab) {
|
|
17312
|
+
if (!tab.sessionId || tab.vendor)
|
|
17313
|
+
return this.engineForTab(task, tab);
|
|
17314
|
+
const resolved = await this.findEngineWithHistory(tab.sessionId, this.vendorForTab(task, tab));
|
|
17315
|
+
if (resolved.vendor && resolved.vendor !== tab.vendor) {
|
|
17316
|
+
await this.updateTab(task.id, tab.id, { vendor: resolved.vendor });
|
|
17317
|
+
}
|
|
17318
|
+
return resolved.engine;
|
|
17319
|
+
}
|
|
17320
|
+
async findEngineWithHistory(sessionId, preferredVendor) {
|
|
17321
|
+
const candidates = [];
|
|
17322
|
+
if (preferredVendor)
|
|
17323
|
+
candidates.push([preferredVendor, this.engineForVendor(preferredVendor)]);
|
|
17324
|
+
for (const [vendor, engine3] of Object.entries(this.engines)) {
|
|
17325
|
+
if (!engine3 || vendor === preferredVendor)
|
|
17326
|
+
continue;
|
|
17327
|
+
candidates.push([vendor, engine3]);
|
|
17328
|
+
}
|
|
17329
|
+
if (candidates.length === 0)
|
|
17330
|
+
candidates.push([undefined, this.fallbackEngine]);
|
|
17331
|
+
let fallback = candidates[0] ?? [undefined, this.fallbackEngine];
|
|
17332
|
+
let fallbackHistory;
|
|
17333
|
+
for (const [vendor, engine3] of candidates) {
|
|
17334
|
+
try {
|
|
17335
|
+
const history = await engine3.readHistory(sessionId);
|
|
17336
|
+
if (!fallbackHistory) {
|
|
17337
|
+
fallback = [vendor, engine3];
|
|
17338
|
+
fallbackHistory = history;
|
|
17339
|
+
}
|
|
17340
|
+
if (history.messages.length > 0 || history.usageMetrics)
|
|
17341
|
+
return { engine: engine3, vendor, history };
|
|
17342
|
+
} catch {}
|
|
17343
|
+
}
|
|
17344
|
+
return { engine: fallback[1], vendor: fallback[0], history: fallbackHistory };
|
|
17345
|
+
}
|
|
17346
|
+
engineForTaskId(taskId) {
|
|
17347
|
+
const task = this.store.get(taskId);
|
|
17348
|
+
return task ? this.engineForTask(task) : this.fallbackEngine;
|
|
17349
|
+
}
|
|
17350
|
+
engineForTaskTabId(taskId, tabId) {
|
|
17351
|
+
const task = this.store.get(taskId);
|
|
17352
|
+
if (!task)
|
|
17353
|
+
return this.fallbackEngine;
|
|
17354
|
+
const tab = task.tabs.find((t2) => t2.id === tabId);
|
|
17355
|
+
return tab ? this.engineForTab(task, tab) : this.engineForTask(task);
|
|
17356
|
+
}
|
|
17357
|
+
engineForSessionId(sessionId) {
|
|
17358
|
+
for (const task of this.store.list()) {
|
|
17359
|
+
for (const tab of task.tabs) {
|
|
17360
|
+
if (tab.sessionId === sessionId)
|
|
17361
|
+
return this.engineForTab(task, tab);
|
|
17362
|
+
}
|
|
17363
|
+
if (task.sessionId === sessionId)
|
|
17364
|
+
return this.engineForTask(task);
|
|
17365
|
+
}
|
|
17366
|
+
return this.fallbackEngine;
|
|
17367
|
+
}
|
|
17111
17368
|
bumpRunState() {
|
|
17112
17369
|
const next = new Map;
|
|
17113
17370
|
for (const tabKey2 of this.pendingInputBroker.awaitingTabKeys()) {
|
|
@@ -17327,10 +17584,11 @@ class Orchestrator {
|
|
|
17327
17584
|
if (prompt && prompt.trim().length > 0) {
|
|
17328
17585
|
this.dispatchEvent(task.id, targetTab.id, { type: "user.inject", text: prompt });
|
|
17329
17586
|
}
|
|
17330
|
-
const
|
|
17587
|
+
const engine3 = targetTab.sessionId ? await this.engineForTabRun(task, targetTab) : this.engineForTab(task, targetTab);
|
|
17588
|
+
const modelToUse = this.modelForTab(task, targetTab, engine3);
|
|
17331
17589
|
let handle;
|
|
17332
17590
|
if (targetTab.sessionId) {
|
|
17333
|
-
handle = await
|
|
17591
|
+
handle = await engine3.resume(targetTab.sessionId, promptToSend, {
|
|
17334
17592
|
cwd: task.worktreePath,
|
|
17335
17593
|
env: { KOBE_RESUME_CWD: task.worktreePath },
|
|
17336
17594
|
permissionMode: task.permissionMode,
|
|
@@ -17343,7 +17601,7 @@ class Orchestrator {
|
|
|
17343
17601
|
});
|
|
17344
17602
|
this.firstSpawnLatches.set(key, latch);
|
|
17345
17603
|
try {
|
|
17346
|
-
handle = await
|
|
17604
|
+
handle = await engine3.spawn(task.worktreePath, promptToSend, {
|
|
17347
17605
|
permissionMode: task.permissionMode,
|
|
17348
17606
|
model: modelToUse
|
|
17349
17607
|
});
|
|
@@ -17418,7 +17676,7 @@ class Orchestrator {
|
|
|
17418
17676
|
text: "(turn interrupted \u2014 sending new prompt)"
|
|
17419
17677
|
});
|
|
17420
17678
|
try {
|
|
17421
|
-
await this.
|
|
17679
|
+
await this.engineForTask(task).stop(handle);
|
|
17422
17680
|
} finally {
|
|
17423
17681
|
this.handles.delete(key);
|
|
17424
17682
|
this.bumpRunState();
|
|
@@ -17460,11 +17718,13 @@ class Orchestrator {
|
|
|
17460
17718
|
return;
|
|
17461
17719
|
await this.store.update(task.id, { permissionMode: mode });
|
|
17462
17720
|
}
|
|
17463
|
-
async setModel(id, model) {
|
|
17721
|
+
async setModel(id, model, tabId) {
|
|
17464
17722
|
const task = this.requireTask(id);
|
|
17465
|
-
|
|
17723
|
+
const tab = this.resolveTab(task, tabId);
|
|
17724
|
+
const vendor = model ? capabilitiesForModelId(model).vendorId : this.vendorForTab(task, tab);
|
|
17725
|
+
if (tab.model === model && this.vendorForTab(task, tab) === vendor)
|
|
17466
17726
|
return;
|
|
17467
|
-
await this.
|
|
17727
|
+
await this.updateTab(task.id, tab.id, { model, vendor });
|
|
17468
17728
|
}
|
|
17469
17729
|
async setTitle(id, title) {
|
|
17470
17730
|
const task = this.requireTask(id);
|
|
@@ -17528,7 +17788,7 @@ class Orchestrator {
|
|
|
17528
17788
|
}
|
|
17529
17789
|
if (task.sessionId) {
|
|
17530
17790
|
try {
|
|
17531
|
-
await this.
|
|
17791
|
+
await this.engineForTask(task).deleteHistory(task.sessionId);
|
|
17532
17792
|
} catch (err) {
|
|
17533
17793
|
console.error(`[kobe orchestrator] deleteTask: deleteHistory failed for ${task.id}:`, err);
|
|
17534
17794
|
}
|
|
@@ -17537,15 +17797,15 @@ class Orchestrator {
|
|
|
17537
17797
|
await this.store.remove(task.id);
|
|
17538
17798
|
}
|
|
17539
17799
|
async readHistory(sessionId) {
|
|
17540
|
-
|
|
17541
|
-
return await this.engine.readHistory(sessionId);
|
|
17542
|
-
} catch {
|
|
17543
|
-
return [];
|
|
17544
|
-
}
|
|
17800
|
+
return (await this.readHistoryWithMetrics(sessionId)).messages;
|
|
17545
17801
|
}
|
|
17546
17802
|
async readHistoryWithMetrics(sessionId) {
|
|
17547
|
-
const
|
|
17548
|
-
const
|
|
17803
|
+
const preferred = this.engineForSessionId(sessionId);
|
|
17804
|
+
const { history } = await this.findEngineWithHistory(sessionId, preferred.capabilities.vendorId);
|
|
17805
|
+
if (!history)
|
|
17806
|
+
return { messages: [] };
|
|
17807
|
+
const messages = [...history.messages];
|
|
17808
|
+
const usageMetrics = history.usageMetrics;
|
|
17549
17809
|
return {
|
|
17550
17810
|
messages,
|
|
17551
17811
|
...usageMetrics ? { usageMetrics } : {}
|
|
@@ -17555,14 +17815,16 @@ class Orchestrator {
|
|
|
17555
17815
|
const task = this.requireTask(id);
|
|
17556
17816
|
if (!task.worktreePath)
|
|
17557
17817
|
return [];
|
|
17818
|
+
const tab = this.resolveTab(task);
|
|
17558
17819
|
try {
|
|
17559
|
-
return await this.
|
|
17820
|
+
return await this.engineForTab(task, tab).listSessions(task.worktreePath);
|
|
17560
17821
|
} catch {
|
|
17561
17822
|
return [];
|
|
17562
17823
|
}
|
|
17563
17824
|
}
|
|
17564
17825
|
async openSessionInTab(id, sessionId, opts = {}) {
|
|
17565
17826
|
const task = this.requireTask(id);
|
|
17827
|
+
const active = this.resolveTab(task);
|
|
17566
17828
|
const existing = task.tabs.find((t2) => t2.sessionId === sessionId);
|
|
17567
17829
|
if (existing) {
|
|
17568
17830
|
await this.setActiveTab(task.id, existing.id);
|
|
@@ -17573,6 +17835,8 @@ class Orchestrator {
|
|
|
17573
17835
|
sessionId,
|
|
17574
17836
|
seq: nextChatTabSeq(task.tabs),
|
|
17575
17837
|
createdAt: new Date().toISOString(),
|
|
17838
|
+
model: active.model ?? task.model,
|
|
17839
|
+
vendor: this.vendorForTab(task, active),
|
|
17576
17840
|
...opts.title ? { title: opts.title } : {}
|
|
17577
17841
|
};
|
|
17578
17842
|
await this.store.update(task.id, { tabs: [...task.tabs, tab], activeTabId: tab.id });
|
|
@@ -17600,11 +17864,14 @@ class Orchestrator {
|
|
|
17600
17864
|
}
|
|
17601
17865
|
async createTab(id, opts = {}) {
|
|
17602
17866
|
const task = this.requireTask(id);
|
|
17867
|
+
const active = this.resolveTab(task);
|
|
17603
17868
|
const tab = {
|
|
17604
17869
|
id: ulid(),
|
|
17605
17870
|
sessionId: null,
|
|
17606
17871
|
seq: nextChatTabSeq(task.tabs),
|
|
17607
17872
|
createdAt: new Date().toISOString(),
|
|
17873
|
+
model: active.model ?? task.model,
|
|
17874
|
+
vendor: this.vendorForTab(task, active),
|
|
17608
17875
|
...opts.title ? { title: opts.title } : {}
|
|
17609
17876
|
};
|
|
17610
17877
|
const tabs = [...task.tabs, tab];
|
|
@@ -17684,7 +17951,7 @@ class Orchestrator {
|
|
|
17684
17951
|
if (!handle)
|
|
17685
17952
|
return;
|
|
17686
17953
|
try {
|
|
17687
|
-
await this.
|
|
17954
|
+
await this.engineForTaskId(taskId).stop(handle);
|
|
17688
17955
|
} finally {
|
|
17689
17956
|
this.handles.delete(key);
|
|
17690
17957
|
this.bumpRunState();
|
|
@@ -17693,12 +17960,13 @@ class Orchestrator {
|
|
|
17693
17960
|
async stopAllTabsForTask(taskId) {
|
|
17694
17961
|
const prefix = `${taskId}:`;
|
|
17695
17962
|
const keys = Array.from(this.handles.keys()).filter((k2) => k2.startsWith(prefix));
|
|
17963
|
+
const engine3 = this.engineForTaskId(taskId);
|
|
17696
17964
|
for (const key of keys) {
|
|
17697
17965
|
const handle = this.handles.get(key);
|
|
17698
17966
|
if (!handle)
|
|
17699
17967
|
continue;
|
|
17700
17968
|
try {
|
|
17701
|
-
await
|
|
17969
|
+
await engine3.stop(handle);
|
|
17702
17970
|
} catch {}
|
|
17703
17971
|
this.handles.delete(key);
|
|
17704
17972
|
}
|
|
@@ -17824,7 +18092,7 @@ function deriveTitleFromPrompt(prompt) {
|
|
|
17824
18092
|
var CONCURRENCY_CAP = 20, PLACEHOLDER_TASK_TITLE = "(new task)", IllegalTransitionError, ConcurrencyCapError, PRPreconditionError, TaskNotFoundError, CannotDeleteMainTaskError, TITLE_CHAR_CAP = 40;
|
|
17825
18093
|
var init_core = __esm(() => {
|
|
17826
18094
|
init_dev();
|
|
17827
|
-
|
|
18095
|
+
init_registry2();
|
|
17828
18096
|
init_repos();
|
|
17829
18097
|
init_ulid();
|
|
17830
18098
|
init_metadata_suggester();
|
|
@@ -18025,8 +18293,8 @@ class RemoteOrchestrator {
|
|
|
18025
18293
|
async setPermissionMode(taskId, mode) {
|
|
18026
18294
|
await this.client.request("task.permissionMode", { taskId, mode });
|
|
18027
18295
|
}
|
|
18028
|
-
async setModel(taskId, model) {
|
|
18029
|
-
await this.client.request("task.model", { taskId, model });
|
|
18296
|
+
async setModel(taskId, model, tabId) {
|
|
18297
|
+
await this.client.request("task.model", { taskId, model, tabId });
|
|
18030
18298
|
}
|
|
18031
18299
|
async createTab(taskId, opts = {}) {
|
|
18032
18300
|
const res = await this.client.request("chat.tab.create", { taskId, title: opts.title });
|
|
@@ -18447,21 +18715,276 @@ var init_manager = __esm(() => {
|
|
|
18447
18715
|
init_paths();
|
|
18448
18716
|
});
|
|
18449
18717
|
|
|
18450
|
-
// src/
|
|
18451
|
-
import {
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
|
|
18718
|
+
// src/engine/codex-local/binary.ts
|
|
18719
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
18720
|
+
import { existsSync as existsSync5, statSync as statSync3 } from "fs";
|
|
18721
|
+
import { homedir as homedir8 } from "os";
|
|
18722
|
+
import path7 from "path";
|
|
18723
|
+
async function findCodexBinary(deps = defaultDeps2) {
|
|
18724
|
+
const checked = [];
|
|
18725
|
+
const tryPath = (p2) => {
|
|
18726
|
+
if (!p2)
|
|
18727
|
+
return;
|
|
18728
|
+
checked.push(p2);
|
|
18729
|
+
return deps.fileExists(p2) ? p2 : undefined;
|
|
18730
|
+
};
|
|
18731
|
+
const whichResult = deps.which("codex");
|
|
18732
|
+
if (whichResult) {
|
|
18733
|
+
checked.push(`which:${whichResult}`);
|
|
18734
|
+
if (deps.fileExists(whichResult))
|
|
18735
|
+
return whichResult;
|
|
18736
|
+
}
|
|
18737
|
+
const home = deps.home();
|
|
18738
|
+
for (const p2 of ["/opt/homebrew/bin/codex", "/usr/local/bin/codex", "/usr/bin/codex", "/bin/codex"]) {
|
|
18739
|
+
const candidate = tryPath(p2);
|
|
18740
|
+
if (candidate)
|
|
18741
|
+
return candidate;
|
|
18742
|
+
}
|
|
18743
|
+
const nvmBin = deps.env("NVM_BIN");
|
|
18744
|
+
if (nvmBin) {
|
|
18745
|
+
const candidate = tryPath(path7.join(nvmBin, "codex"));
|
|
18746
|
+
if (candidate)
|
|
18747
|
+
return candidate;
|
|
18748
|
+
}
|
|
18749
|
+
for (const rel of [".local/bin/codex", ".bun/bin/codex", "bin/codex"]) {
|
|
18750
|
+
const candidate = tryPath(path7.join(home, rel));
|
|
18751
|
+
if (candidate)
|
|
18752
|
+
return candidate;
|
|
18753
|
+
}
|
|
18754
|
+
throw new CodexBinaryNotFoundError(checked);
|
|
18755
|
+
}
|
|
18756
|
+
var CodexBinaryNotFoundError, defaultDeps2;
|
|
18757
|
+
var init_binary2 = __esm(() => {
|
|
18758
|
+
CodexBinaryNotFoundError = class CodexBinaryNotFoundError extends Error {
|
|
18759
|
+
checkedPaths;
|
|
18760
|
+
constructor(checkedPaths) {
|
|
18761
|
+
super(`Codex CLI binary not found. Checked: ${checkedPaths.join(", ")}. Ensure 'codex' is on PATH (e.g. \`brew install codex\` or the official installer).`);
|
|
18762
|
+
this.name = "CodexBinaryNotFoundError";
|
|
18763
|
+
this.checkedPaths = checkedPaths;
|
|
18764
|
+
}
|
|
18765
|
+
};
|
|
18766
|
+
defaultDeps2 = {
|
|
18767
|
+
fileExists(p2) {
|
|
18768
|
+
try {
|
|
18769
|
+
return statSync3(p2).isFile();
|
|
18770
|
+
} catch {
|
|
18771
|
+
return false;
|
|
18772
|
+
}
|
|
18773
|
+
},
|
|
18774
|
+
env(name) {
|
|
18775
|
+
return process.env[name];
|
|
18776
|
+
},
|
|
18777
|
+
home() {
|
|
18778
|
+
return homedir8();
|
|
18779
|
+
},
|
|
18780
|
+
which(name) {
|
|
18781
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
18782
|
+
const out = spawnSync7(cmd, [name], { encoding: "utf8" });
|
|
18783
|
+
if (out.status !== 0)
|
|
18784
|
+
return;
|
|
18785
|
+
const first = out.stdout.split(`
|
|
18786
|
+
`).map((l2) => l2.trim()).filter(Boolean)[0];
|
|
18787
|
+
if (!first)
|
|
18788
|
+
return;
|
|
18789
|
+
if (first.startsWith("codex:") && first.includes("aliased to")) {
|
|
18790
|
+
const aliasTarget = first.split("aliased to")[1]?.trim();
|
|
18791
|
+
return aliasTarget && existsSync5(aliasTarget) ? aliasTarget : undefined;
|
|
18792
|
+
}
|
|
18793
|
+
return first;
|
|
18794
|
+
},
|
|
18795
|
+
readdir(p2) {
|
|
18796
|
+
try {
|
|
18797
|
+
const fs5 = __require("fs");
|
|
18798
|
+
return fs5.readdirSync(p2);
|
|
18799
|
+
} catch {
|
|
18800
|
+
return [];
|
|
18801
|
+
}
|
|
18802
|
+
}
|
|
18803
|
+
};
|
|
18804
|
+
});
|
|
18805
|
+
|
|
18806
|
+
// src/engine/account-detect.ts
|
|
18807
|
+
import { readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
18808
|
+
import { homedir as homedir9 } from "os";
|
|
18809
|
+
import path8 from "path";
|
|
18810
|
+
function claudeGlobalConfigPath(env, home) {
|
|
18811
|
+
const override = env("CLAUDE_CONFIG_DIR")?.trim();
|
|
18812
|
+
if (override)
|
|
18813
|
+
return path8.join(override, ".claude.json");
|
|
18814
|
+
return path8.join(home, ".claude.json");
|
|
18815
|
+
}
|
|
18816
|
+
function codexAuthPath(env, home) {
|
|
18817
|
+
const override = env("CODEX_HOME")?.trim();
|
|
18818
|
+
const dir = override ?? path8.join(home, ".codex");
|
|
18819
|
+
return path8.join(dir, "auth.json");
|
|
18820
|
+
}
|
|
18821
|
+
function decodeJwtPayload(jwt) {
|
|
18822
|
+
const parts = jwt.split(".");
|
|
18823
|
+
if (parts.length !== 3)
|
|
18824
|
+
return null;
|
|
18825
|
+
const payload = parts[1];
|
|
18826
|
+
if (!payload)
|
|
18827
|
+
return null;
|
|
18828
|
+
const b64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
18829
|
+
const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
|
|
18830
|
+
try {
|
|
18831
|
+
const json = Buffer.from(padded, "base64").toString("utf8");
|
|
18832
|
+
const obj = JSON.parse(json);
|
|
18833
|
+
return typeof obj === "object" && obj !== null ? obj : null;
|
|
18834
|
+
} catch {
|
|
18835
|
+
return null;
|
|
18836
|
+
}
|
|
18837
|
+
}
|
|
18838
|
+
async function probeBinary(probe) {
|
|
18839
|
+
try {
|
|
18840
|
+
const p2 = await probe();
|
|
18841
|
+
return { found: true, path: p2 };
|
|
18842
|
+
} catch (err) {
|
|
18843
|
+
if (err instanceof ClaudeBinaryNotFoundError || err instanceof CodexBinaryNotFoundError) {
|
|
18844
|
+
return { found: false, error: "not found on PATH" };
|
|
18845
|
+
}
|
|
18846
|
+
return { found: false, error: err instanceof Error ? err.message : String(err) };
|
|
18847
|
+
}
|
|
18848
|
+
}
|
|
18849
|
+
async function detectClaudeAccount(deps = defaultDeps3) {
|
|
18850
|
+
const binary = await probeBinary(() => deps.findClaudeBinary());
|
|
18851
|
+
const configPath = claudeGlobalConfigPath(deps.env, deps.home());
|
|
18852
|
+
let raw;
|
|
18853
|
+
try {
|
|
18854
|
+
raw = deps.readFile(configPath);
|
|
18855
|
+
} catch (err) {
|
|
18856
|
+
return {
|
|
18857
|
+
binary,
|
|
18858
|
+
account: { kind: "none" },
|
|
18859
|
+
accountError: `read ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
18860
|
+
};
|
|
18861
|
+
}
|
|
18862
|
+
if (raw === null)
|
|
18863
|
+
return { binary, account: { kind: "none" } };
|
|
18864
|
+
let parsed;
|
|
18865
|
+
try {
|
|
18866
|
+
parsed = JSON.parse(raw);
|
|
18867
|
+
} catch (err) {
|
|
18868
|
+
return {
|
|
18869
|
+
binary,
|
|
18870
|
+
account: { kind: "none" },
|
|
18871
|
+
accountError: `parse ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
18872
|
+
};
|
|
18873
|
+
}
|
|
18874
|
+
const oauth = parsed?.oauthAccount;
|
|
18875
|
+
if (!oauth || typeof oauth !== "object")
|
|
18876
|
+
return { binary, account: { kind: "none" } };
|
|
18877
|
+
const o2 = oauth;
|
|
18878
|
+
const email = typeof o2.emailAddress === "string" ? o2.emailAddress : undefined;
|
|
18879
|
+
if (!email)
|
|
18880
|
+
return { binary, account: { kind: "none" } };
|
|
18881
|
+
return {
|
|
18882
|
+
binary,
|
|
18883
|
+
account: {
|
|
18884
|
+
kind: "oauth",
|
|
18885
|
+
email,
|
|
18886
|
+
organization: typeof o2.organizationName === "string" ? o2.organizationName : undefined,
|
|
18887
|
+
displayName: typeof o2.displayName === "string" ? o2.displayName : undefined,
|
|
18888
|
+
billingType: typeof o2.billingType === "string" ? o2.billingType : undefined
|
|
18889
|
+
}
|
|
18890
|
+
};
|
|
18891
|
+
}
|
|
18892
|
+
async function detectCodexAccount(deps = defaultDeps3) {
|
|
18893
|
+
const binary = await probeBinary(() => deps.findCodexBinary());
|
|
18894
|
+
const authPath = codexAuthPath(deps.env, deps.home());
|
|
18895
|
+
let raw;
|
|
18896
|
+
try {
|
|
18897
|
+
raw = deps.readFile(authPath);
|
|
18898
|
+
} catch (err) {
|
|
18899
|
+
return {
|
|
18900
|
+
binary,
|
|
18901
|
+
account: { kind: "none" },
|
|
18902
|
+
accountError: `read ${authPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
18903
|
+
};
|
|
18904
|
+
}
|
|
18905
|
+
if (raw === null)
|
|
18906
|
+
return { binary, account: { kind: "none" } };
|
|
18907
|
+
let parsed;
|
|
18908
|
+
try {
|
|
18909
|
+
parsed = JSON.parse(raw);
|
|
18910
|
+
} catch (err) {
|
|
18911
|
+
return {
|
|
18912
|
+
binary,
|
|
18913
|
+
account: { kind: "none" },
|
|
18914
|
+
accountError: `parse ${authPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
18915
|
+
};
|
|
18916
|
+
}
|
|
18917
|
+
const obj = parsed ?? {};
|
|
18918
|
+
const tokens = obj.tokens;
|
|
18919
|
+
const idToken = typeof tokens?.id_token === "string" ? tokens.id_token : undefined;
|
|
18920
|
+
if (idToken) {
|
|
18921
|
+
const payload = decodeJwtPayload(idToken);
|
|
18922
|
+
if (!payload) {
|
|
18923
|
+
return {
|
|
18924
|
+
binary,
|
|
18925
|
+
account: { kind: "none" },
|
|
18926
|
+
accountError: "codex id_token: malformed JWT"
|
|
18927
|
+
};
|
|
18928
|
+
}
|
|
18929
|
+
const email = typeof payload.email === "string" ? payload.email : undefined;
|
|
18930
|
+
const authClaimRaw = payload["https://api.openai.com/auth"];
|
|
18931
|
+
const authClaim = typeof authClaimRaw === "object" && authClaimRaw !== null && !Array.isArray(authClaimRaw) ? authClaimRaw : undefined;
|
|
18932
|
+
const plan = typeof authClaim?.chatgpt_plan_type === "string" ? authClaim.chatgpt_plan_type : undefined;
|
|
18933
|
+
if (email)
|
|
18934
|
+
return { binary, account: { kind: "chatgpt", email, plan } };
|
|
18935
|
+
return { binary, account: { kind: "none" }, accountError: "codex id_token: no email claim" };
|
|
18936
|
+
}
|
|
18937
|
+
const apiKey = obj.OPENAI_API_KEY;
|
|
18938
|
+
if (typeof apiKey === "string" && apiKey.length > 0) {
|
|
18939
|
+
return { binary, account: { kind: "apikey" } };
|
|
18940
|
+
}
|
|
18941
|
+
return { binary, account: { kind: "none" } };
|
|
18942
|
+
}
|
|
18943
|
+
var defaultDeps3;
|
|
18944
|
+
var init_account_detect = __esm(() => {
|
|
18945
|
+
init_binary();
|
|
18946
|
+
init_binary2();
|
|
18947
|
+
defaultDeps3 = {
|
|
18948
|
+
readFile(p2) {
|
|
18949
|
+
try {
|
|
18950
|
+
statSync4(p2);
|
|
18951
|
+
} catch (err) {
|
|
18952
|
+
if (err.code === "ENOENT")
|
|
18953
|
+
return null;
|
|
18954
|
+
throw err;
|
|
18955
|
+
}
|
|
18956
|
+
return readFileSync6(p2, "utf8");
|
|
18957
|
+
},
|
|
18958
|
+
env(name) {
|
|
18959
|
+
return process.env[name];
|
|
18960
|
+
},
|
|
18961
|
+
home() {
|
|
18962
|
+
return homedir9();
|
|
18963
|
+
},
|
|
18964
|
+
findClaudeBinary() {
|
|
18965
|
+
return findClaudeBinary();
|
|
18966
|
+
},
|
|
18967
|
+
findCodexBinary() {
|
|
18968
|
+
return findCodexBinary();
|
|
18969
|
+
}
|
|
18970
|
+
};
|
|
18971
|
+
});
|
|
18972
|
+
|
|
18973
|
+
// src/tui/ui/dialog-confirm.tsx
|
|
18974
|
+
import { TextAttributes as TextAttributes6 } from "@opentui/core";
|
|
18975
|
+
function titlecase(s2) {
|
|
18976
|
+
if (!s2)
|
|
18977
|
+
return s2;
|
|
18978
|
+
return s2.charAt(0).toUpperCase() + s2.slice(1);
|
|
18979
|
+
}
|
|
18980
|
+
function DialogConfirm(props) {
|
|
18981
|
+
const dialog = useDialog();
|
|
18982
|
+
const {
|
|
18983
|
+
theme
|
|
18984
|
+
} = useTheme();
|
|
18985
|
+
const [store2, setStore2] = createStore({
|
|
18986
|
+
active: "confirm"
|
|
18987
|
+
});
|
|
18465
18988
|
useBindings(() => ({
|
|
18466
18989
|
bindings: [{
|
|
18467
18990
|
key: "return",
|
|
@@ -18573,7 +19096,7 @@ var init_dialog_confirm = __esm(() => {
|
|
|
18573
19096
|
|
|
18574
19097
|
// src/tui/component/settings-dialog.tsx
|
|
18575
19098
|
import { unlinkSync as unlinkSync2 } from "fs";
|
|
18576
|
-
import { join as
|
|
19099
|
+
import { join as join10 } from "path";
|
|
18577
19100
|
import { TextAttributes as TextAttributes7 } from "@opentui/core";
|
|
18578
19101
|
function SettingsDialog(props) {
|
|
18579
19102
|
const dialog = useDialog();
|
|
@@ -18589,6 +19112,34 @@ function SettingsDialog(props) {
|
|
|
18589
19112
|
const themeNames = createMemo(() => themeCtx.all().slice().sort());
|
|
18590
19113
|
const [themeCursor, setThemeCursor] = createSignal(Math.max(0, themeNames().findIndex((n2) => n2 === themeCtx.selected)));
|
|
18591
19114
|
const hasDaemon = props.orchestrator instanceof RemoteOrchestrator;
|
|
19115
|
+
const [claudeStatus, setClaudeStatus] = createSignal(null);
|
|
19116
|
+
const [codexStatus, setCodexStatus] = createSignal(null);
|
|
19117
|
+
onMount(() => {
|
|
19118
|
+
detectClaudeAccount().then(setClaudeStatus).catch((err) => {
|
|
19119
|
+
console.error("kobe: detectClaudeAccount threw:", err);
|
|
19120
|
+
setClaudeStatus({
|
|
19121
|
+
binary: {
|
|
19122
|
+
found: false,
|
|
19123
|
+
error: String(err)
|
|
19124
|
+
},
|
|
19125
|
+
account: {
|
|
19126
|
+
kind: "none"
|
|
19127
|
+
}
|
|
19128
|
+
});
|
|
19129
|
+
});
|
|
19130
|
+
detectCodexAccount().then(setCodexStatus).catch((err) => {
|
|
19131
|
+
console.error("kobe: detectCodexAccount threw:", err);
|
|
19132
|
+
setCodexStatus({
|
|
19133
|
+
binary: {
|
|
19134
|
+
found: false,
|
|
19135
|
+
error: String(err)
|
|
19136
|
+
},
|
|
19137
|
+
account: {
|
|
19138
|
+
kind: "none"
|
|
19139
|
+
}
|
|
19140
|
+
});
|
|
19141
|
+
});
|
|
19142
|
+
});
|
|
18592
19143
|
function devRowCount() {
|
|
18593
19144
|
return hasDaemon ? 2 : 1;
|
|
18594
19145
|
}
|
|
@@ -18597,6 +19148,8 @@ function SettingsDialog(props) {
|
|
|
18597
19148
|
return themeNames().length + 1 + FOCUS_ACCENT_SLOTS.length + 2;
|
|
18598
19149
|
if (section() === "dev")
|
|
18599
19150
|
return devRowCount();
|
|
19151
|
+
if (section() === "accounts")
|
|
19152
|
+
return 0;
|
|
18600
19153
|
return 0;
|
|
18601
19154
|
}
|
|
18602
19155
|
function isTransparentRow() {
|
|
@@ -18665,7 +19218,7 @@ function SettingsDialog(props) {
|
|
|
18665
19218
|
return;
|
|
18666
19219
|
props.kv.clear();
|
|
18667
19220
|
try {
|
|
18668
|
-
unlinkSync2(
|
|
19221
|
+
unlinkSync2(join10(homeDir(), ".kobe", "tasks.json"));
|
|
18669
19222
|
} catch (err) {
|
|
18670
19223
|
if (err.code !== "ENOENT") {
|
|
18671
19224
|
console.error("kobe: failed to delete tasks.json during reset:", err);
|
|
@@ -18805,10 +19358,10 @@ function SettingsDialog(props) {
|
|
|
18805
19358
|
]
|
|
18806
19359
|
}));
|
|
18807
19360
|
return (() => {
|
|
18808
|
-
var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("box"), _el$9 = createElement("box"), _el$
|
|
19361
|
+
var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("box"), _el$9 = createElement("box"), _el$62 = createElement("box"), _el$63 = createElement("text");
|
|
18809
19362
|
insertNode(_el$, _el$2);
|
|
18810
19363
|
insertNode(_el$, _el$7);
|
|
18811
|
-
insertNode(_el$, _el$
|
|
19364
|
+
insertNode(_el$, _el$62);
|
|
18812
19365
|
setProp(_el$, "paddingLeft", 2);
|
|
18813
19366
|
setProp(_el$, "paddingRight", 2);
|
|
18814
19367
|
setProp(_el$, "paddingBottom", 1);
|
|
@@ -18834,28 +19387,28 @@ function SettingsDialog(props) {
|
|
|
18834
19387
|
const isSection = () => i2() === cursor();
|
|
18835
19388
|
const isSidebarFocused = () => isSection() && level() === "sidebar";
|
|
18836
19389
|
return (() => {
|
|
18837
|
-
var _el$
|
|
18838
|
-
insertNode(_el$
|
|
18839
|
-
setProp(_el$
|
|
18840
|
-
setProp(_el$
|
|
18841
|
-
setProp(_el$
|
|
19390
|
+
var _el$65 = createElement("box"), _el$66 = createElement("text");
|
|
19391
|
+
insertNode(_el$65, _el$66);
|
|
19392
|
+
setProp(_el$65, "paddingLeft", 1);
|
|
19393
|
+
setProp(_el$65, "paddingRight", 1);
|
|
19394
|
+
setProp(_el$65, "onMouseUp", () => {
|
|
18842
19395
|
switchSection(s2.id);
|
|
18843
19396
|
setLevel("sidebar");
|
|
18844
19397
|
});
|
|
18845
|
-
setProp(_el$
|
|
18846
|
-
insert(_el$
|
|
19398
|
+
setProp(_el$66, "wrapMode", "none");
|
|
19399
|
+
insert(_el$66, () => s2.label);
|
|
18847
19400
|
effect((_p$) => {
|
|
18848
|
-
var _v$
|
|
18849
|
-
_v$
|
|
18850
|
-
_v$
|
|
18851
|
-
_v$
|
|
19401
|
+
var _v$37 = isSidebarFocused() ? theme.primary : undefined, _v$38 = isSidebarFocused() ? theme.selectedListItemText : isSection() ? theme.accent : theme.textMuted, _v$39 = isSection() ? TextAttributes7.BOLD : undefined;
|
|
19402
|
+
_v$37 !== _p$.e && (_p$.e = setProp(_el$65, "backgroundColor", _v$37, _p$.e));
|
|
19403
|
+
_v$38 !== _p$.t && (_p$.t = setProp(_el$66, "fg", _v$38, _p$.t));
|
|
19404
|
+
_v$39 !== _p$.a && (_p$.a = setProp(_el$66, "attributes", _v$39, _p$.a));
|
|
18852
19405
|
return _p$;
|
|
18853
19406
|
}, {
|
|
18854
19407
|
e: undefined,
|
|
18855
19408
|
t: undefined,
|
|
18856
19409
|
a: undefined
|
|
18857
19410
|
});
|
|
18858
|
-
return _el$
|
|
19411
|
+
return _el$65;
|
|
18859
19412
|
})();
|
|
18860
19413
|
}
|
|
18861
19414
|
}));
|
|
@@ -18889,33 +19442,33 @@ function SettingsDialog(props) {
|
|
|
18889
19442
|
const isCursor = () => level() === "body" && bodyRow() === i2();
|
|
18890
19443
|
const isSelected = () => name === themeCtx.selected;
|
|
18891
19444
|
return (() => {
|
|
18892
|
-
var _el$
|
|
18893
|
-
insertNode(_el$
|
|
18894
|
-
setProp(_el$
|
|
18895
|
-
setProp(_el$
|
|
18896
|
-
setProp(_el$
|
|
18897
|
-
setProp(_el$
|
|
18898
|
-
setProp(_el$
|
|
19445
|
+
var _el$67 = createElement("box"), _el$68 = createElement("text");
|
|
19446
|
+
insertNode(_el$67, _el$68);
|
|
19447
|
+
setProp(_el$67, "flexDirection", "row");
|
|
19448
|
+
setProp(_el$67, "gap", 1);
|
|
19449
|
+
setProp(_el$67, "paddingLeft", 1);
|
|
19450
|
+
setProp(_el$67, "paddingRight", 1);
|
|
19451
|
+
setProp(_el$67, "onMouseUp", () => {
|
|
18899
19452
|
setLevel("body");
|
|
18900
19453
|
setBodyRow(i2());
|
|
18901
19454
|
setThemeCursor(i2());
|
|
18902
19455
|
themeCtx.set(name);
|
|
18903
19456
|
});
|
|
18904
|
-
setProp(_el$
|
|
18905
|
-
insert(_el$
|
|
18906
|
-
insert(_el$
|
|
19457
|
+
setProp(_el$68, "wrapMode", "none");
|
|
19458
|
+
insert(_el$68, () => isSelected() ? "\u25CF " : " ", null);
|
|
19459
|
+
insert(_el$68, name, null);
|
|
18907
19460
|
effect((_p$) => {
|
|
18908
|
-
var _v$
|
|
18909
|
-
_v$
|
|
18910
|
-
_v$
|
|
18911
|
-
_v$
|
|
19461
|
+
var _v$40 = isCursor() ? theme.primary : undefined, _v$41 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.accent : theme.text, _v$42 = isCursor() || isSelected() ? TextAttributes7.BOLD : undefined;
|
|
19462
|
+
_v$40 !== _p$.e && (_p$.e = setProp(_el$67, "backgroundColor", _v$40, _p$.e));
|
|
19463
|
+
_v$41 !== _p$.t && (_p$.t = setProp(_el$68, "fg", _v$41, _p$.t));
|
|
19464
|
+
_v$42 !== _p$.a && (_p$.a = setProp(_el$68, "attributes", _v$42, _p$.a));
|
|
18912
19465
|
return _p$;
|
|
18913
19466
|
}, {
|
|
18914
19467
|
e: undefined,
|
|
18915
19468
|
t: undefined,
|
|
18916
19469
|
a: undefined
|
|
18917
19470
|
});
|
|
18918
|
-
return _el$
|
|
19471
|
+
return _el$67;
|
|
18919
19472
|
})();
|
|
18920
19473
|
}
|
|
18921
19474
|
}));
|
|
@@ -18954,32 +19507,32 @@ function SettingsDialog(props) {
|
|
|
18954
19507
|
const isCursor = () => level() === "body" && bodyRow() === rowIndex();
|
|
18955
19508
|
const isSelected = () => themeCtx.focusAccent === slot;
|
|
18956
19509
|
return (() => {
|
|
18957
|
-
var _el$
|
|
18958
|
-
insertNode(_el$
|
|
18959
|
-
setProp(_el$
|
|
18960
|
-
setProp(_el$
|
|
18961
|
-
setProp(_el$
|
|
18962
|
-
setProp(_el$
|
|
18963
|
-
setProp(_el$
|
|
19510
|
+
var _el$69 = createElement("box"), _el$70 = createElement("text");
|
|
19511
|
+
insertNode(_el$69, _el$70);
|
|
19512
|
+
setProp(_el$69, "flexDirection", "row");
|
|
19513
|
+
setProp(_el$69, "gap", 1);
|
|
19514
|
+
setProp(_el$69, "paddingLeft", 1);
|
|
19515
|
+
setProp(_el$69, "paddingRight", 1);
|
|
19516
|
+
setProp(_el$69, "onMouseUp", () => {
|
|
18964
19517
|
setLevel("body");
|
|
18965
19518
|
setBodyRow(rowIndex());
|
|
18966
19519
|
themeCtx.setFocusAccent(slot);
|
|
18967
19520
|
});
|
|
18968
|
-
setProp(_el$
|
|
18969
|
-
insert(_el$
|
|
18970
|
-
insert(_el$
|
|
19521
|
+
setProp(_el$70, "wrapMode", "none");
|
|
19522
|
+
insert(_el$70, () => isSelected() ? "\u25CF " : " ", null);
|
|
19523
|
+
insert(_el$70, () => FOCUS_ACCENT_LABEL[slot], null);
|
|
18971
19524
|
effect((_p$) => {
|
|
18972
|
-
var _v$
|
|
18973
|
-
_v$
|
|
18974
|
-
_v$
|
|
18975
|
-
_v$
|
|
19525
|
+
var _v$43 = isCursor() ? theme.primary : undefined, _v$44 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.focusAccent : theme.text, _v$45 = isCursor() || isSelected() ? TextAttributes7.BOLD : undefined;
|
|
19526
|
+
_v$43 !== _p$.e && (_p$.e = setProp(_el$69, "backgroundColor", _v$43, _p$.e));
|
|
19527
|
+
_v$44 !== _p$.t && (_p$.t = setProp(_el$70, "fg", _v$44, _p$.t));
|
|
19528
|
+
_v$45 !== _p$.a && (_p$.a = setProp(_el$70, "attributes", _v$45, _p$.a));
|
|
18976
19529
|
return _p$;
|
|
18977
19530
|
}, {
|
|
18978
19531
|
e: undefined,
|
|
18979
19532
|
t: undefined,
|
|
18980
19533
|
a: undefined
|
|
18981
19534
|
});
|
|
18982
|
-
return _el$
|
|
19535
|
+
return _el$69;
|
|
18983
19536
|
})();
|
|
18984
19537
|
}
|
|
18985
19538
|
}), null);
|
|
@@ -19071,122 +19624,294 @@ function SettingsDialog(props) {
|
|
|
19071
19624
|
}), null);
|
|
19072
19625
|
insert(_el$9, createComponent2(Show, {
|
|
19073
19626
|
get when() {
|
|
19074
|
-
return section() === "
|
|
19627
|
+
return section() === "accounts";
|
|
19075
19628
|
},
|
|
19076
19629
|
get children() {
|
|
19077
|
-
var _el$37 = createElement("box"), _el$38 = createElement("text"), _el$40 = createElement("text");
|
|
19630
|
+
var _el$37 = createElement("box"), _el$38 = createElement("text"), _el$40 = createElement("text"), _el$42 = createElement("box"), _el$43 = createElement("text"), _el$47 = createElement("box"), _el$48 = createElement("text");
|
|
19078
19631
|
insertNode(_el$37, _el$38);
|
|
19079
19632
|
insertNode(_el$37, _el$40);
|
|
19633
|
+
insertNode(_el$37, _el$42);
|
|
19634
|
+
insertNode(_el$37, _el$47);
|
|
19080
19635
|
setProp(_el$37, "flexDirection", "column");
|
|
19081
19636
|
setProp(_el$37, "gap", 1);
|
|
19082
|
-
insertNode(_el$38, createTextNode(`
|
|
19083
|
-
insertNode(_el$40, createTextNode(`
|
|
19637
|
+
insertNode(_el$38, createTextNode(`Accounts`));
|
|
19638
|
+
insertNode(_el$40, createTextNode(`Read-only view of locally-detected engine accounts. Login flows land here later.`));
|
|
19084
19639
|
setProp(_el$40, "wrapMode", "word");
|
|
19085
|
-
|
|
19640
|
+
insertNode(_el$42, _el$43);
|
|
19641
|
+
setProp(_el$42, "flexDirection", "column");
|
|
19642
|
+
setProp(_el$42, "gap", 0);
|
|
19643
|
+
insertNode(_el$43, createTextNode(`claude-code`));
|
|
19644
|
+
insert(_el$42, createComponent2(Show, {
|
|
19645
|
+
get when() {
|
|
19646
|
+
return claudeStatus() === null;
|
|
19647
|
+
},
|
|
19648
|
+
get children() {
|
|
19649
|
+
var _el$45 = createElement("text");
|
|
19650
|
+
insertNode(_el$45, createTextNode(`Checking\u2026`));
|
|
19651
|
+
effect((_$p) => setProp(_el$45, "fg", theme.textMuted, _$p));
|
|
19652
|
+
return _el$45;
|
|
19653
|
+
}
|
|
19654
|
+
}), null);
|
|
19655
|
+
insert(_el$42, createComponent2(Show, {
|
|
19656
|
+
get when() {
|
|
19657
|
+
return claudeStatus();
|
|
19658
|
+
},
|
|
19659
|
+
children: (s2) => (() => {
|
|
19660
|
+
var _el$71 = createElement("box"), _el$72 = createElement("text");
|
|
19661
|
+
insertNode(_el$71, _el$72);
|
|
19662
|
+
setProp(_el$71, "flexDirection", "column");
|
|
19663
|
+
setProp(_el$71, "gap", 0);
|
|
19664
|
+
setProp(_el$72, "wrapMode", "word");
|
|
19665
|
+
insert(_el$72, (() => {
|
|
19666
|
+
var _c$ = memo2(() => !!s2().binary.found);
|
|
19667
|
+
return () => _c$() ? `Binary: ${s2().binary.path}` : `Binary: ${s2().binary.error}`;
|
|
19668
|
+
})());
|
|
19669
|
+
insert(_el$71, () => {
|
|
19670
|
+
const a2 = s2().account;
|
|
19671
|
+
if (a2.kind === "oauth") {
|
|
19672
|
+
const tail = [a2.organization, a2.billingType].filter((x2) => !!x2).join(" \xB7 ");
|
|
19673
|
+
return (() => {
|
|
19674
|
+
var _el$73 = createElement("text");
|
|
19675
|
+
setProp(_el$73, "wrapMode", "word");
|
|
19676
|
+
insert(_el$73, () => `\u25CF Logged in: ${a2.email}${tail ? ` (${tail})` : ""}`);
|
|
19677
|
+
effect((_$p) => setProp(_el$73, "fg", theme.success, _$p));
|
|
19678
|
+
return _el$73;
|
|
19679
|
+
})();
|
|
19680
|
+
}
|
|
19681
|
+
return (() => {
|
|
19682
|
+
var _el$74 = createElement("text");
|
|
19683
|
+
insertNode(_el$74, createTextNode(`\u25CB Not logged in`));
|
|
19684
|
+
effect((_$p) => setProp(_el$74, "fg", theme.textMuted, _$p));
|
|
19685
|
+
return _el$74;
|
|
19686
|
+
})();
|
|
19687
|
+
}, null);
|
|
19688
|
+
insert(_el$71, createComponent2(Show, {
|
|
19689
|
+
get when() {
|
|
19690
|
+
return s2().accountError;
|
|
19691
|
+
},
|
|
19692
|
+
children: (err) => (() => {
|
|
19693
|
+
var _el$76 = createElement("text");
|
|
19694
|
+
setProp(_el$76, "wrapMode", "word");
|
|
19695
|
+
insert(_el$76, () => `! ${err()}`);
|
|
19696
|
+
effect((_$p) => setProp(_el$76, "fg", theme.warning, _$p));
|
|
19697
|
+
return _el$76;
|
|
19698
|
+
})()
|
|
19699
|
+
}), null);
|
|
19700
|
+
effect((_$p) => setProp(_el$72, "fg", s2().binary.found ? theme.textMuted : theme.warning, _$p));
|
|
19701
|
+
return _el$71;
|
|
19702
|
+
})()
|
|
19703
|
+
}), null);
|
|
19704
|
+
insertNode(_el$47, _el$48);
|
|
19705
|
+
setProp(_el$47, "flexDirection", "column");
|
|
19706
|
+
setProp(_el$47, "gap", 0);
|
|
19707
|
+
insertNode(_el$48, createTextNode(`codex`));
|
|
19708
|
+
insert(_el$47, createComponent2(Show, {
|
|
19709
|
+
get when() {
|
|
19710
|
+
return codexStatus() === null;
|
|
19711
|
+
},
|
|
19712
|
+
get children() {
|
|
19713
|
+
var _el$50 = createElement("text");
|
|
19714
|
+
insertNode(_el$50, createTextNode(`Checking\u2026`));
|
|
19715
|
+
effect((_$p) => setProp(_el$50, "fg", theme.textMuted, _$p));
|
|
19716
|
+
return _el$50;
|
|
19717
|
+
}
|
|
19718
|
+
}), null);
|
|
19719
|
+
insert(_el$47, createComponent2(Show, {
|
|
19720
|
+
get when() {
|
|
19721
|
+
return codexStatus();
|
|
19722
|
+
},
|
|
19723
|
+
children: (s2) => (() => {
|
|
19724
|
+
var _el$77 = createElement("box"), _el$78 = createElement("text");
|
|
19725
|
+
insertNode(_el$77, _el$78);
|
|
19726
|
+
setProp(_el$77, "flexDirection", "column");
|
|
19727
|
+
setProp(_el$77, "gap", 0);
|
|
19728
|
+
setProp(_el$78, "wrapMode", "word");
|
|
19729
|
+
insert(_el$78, (() => {
|
|
19730
|
+
var _c$2 = memo2(() => !!s2().binary.found);
|
|
19731
|
+
return () => _c$2() ? `Binary: ${s2().binary.path}` : `Binary: ${s2().binary.error}`;
|
|
19732
|
+
})());
|
|
19733
|
+
insert(_el$77, () => {
|
|
19734
|
+
const a2 = s2().account;
|
|
19735
|
+
if (a2.kind === "chatgpt") {
|
|
19736
|
+
return (() => {
|
|
19737
|
+
var _el$79 = createElement("text");
|
|
19738
|
+
setProp(_el$79, "wrapMode", "word");
|
|
19739
|
+
insert(_el$79, () => `\u25CF ChatGPT login: ${a2.email}${a2.plan ? ` (${a2.plan})` : ""}`);
|
|
19740
|
+
effect((_$p) => setProp(_el$79, "fg", theme.success, _$p));
|
|
19741
|
+
return _el$79;
|
|
19742
|
+
})();
|
|
19743
|
+
}
|
|
19744
|
+
if (a2.kind === "apikey") {
|
|
19745
|
+
return (() => {
|
|
19746
|
+
var _el$80 = createElement("text");
|
|
19747
|
+
insertNode(_el$80, createTextNode(`\u25CF API key configured`));
|
|
19748
|
+
effect((_$p) => setProp(_el$80, "fg", theme.success, _$p));
|
|
19749
|
+
return _el$80;
|
|
19750
|
+
})();
|
|
19751
|
+
}
|
|
19752
|
+
return (() => {
|
|
19753
|
+
var _el$82 = createElement("text");
|
|
19754
|
+
insertNode(_el$82, createTextNode(`\u25CB Not logged in`));
|
|
19755
|
+
effect((_$p) => setProp(_el$82, "fg", theme.textMuted, _$p));
|
|
19756
|
+
return _el$82;
|
|
19757
|
+
})();
|
|
19758
|
+
}, null);
|
|
19759
|
+
insert(_el$77, createComponent2(Show, {
|
|
19760
|
+
get when() {
|
|
19761
|
+
return s2().accountError;
|
|
19762
|
+
},
|
|
19763
|
+
children: (err) => (() => {
|
|
19764
|
+
var _el$84 = createElement("text");
|
|
19765
|
+
setProp(_el$84, "wrapMode", "word");
|
|
19766
|
+
insert(_el$84, () => `! ${err()}`);
|
|
19767
|
+
effect((_$p) => setProp(_el$84, "fg", theme.warning, _$p));
|
|
19768
|
+
return _el$84;
|
|
19769
|
+
})()
|
|
19770
|
+
}), null);
|
|
19771
|
+
effect((_$p) => setProp(_el$78, "fg", s2().binary.found ? theme.textMuted : theme.warning, _$p));
|
|
19772
|
+
return _el$77;
|
|
19773
|
+
})()
|
|
19774
|
+
}), null);
|
|
19775
|
+
effect((_p$) => {
|
|
19776
|
+
var _v$20 = theme.text, _v$21 = TextAttributes7.BOLD, _v$22 = theme.textMuted, _v$23 = theme.text, _v$24 = TextAttributes7.BOLD, _v$25 = theme.text, _v$26 = TextAttributes7.BOLD;
|
|
19777
|
+
_v$20 !== _p$.e && (_p$.e = setProp(_el$38, "fg", _v$20, _p$.e));
|
|
19778
|
+
_v$21 !== _p$.t && (_p$.t = setProp(_el$38, "attributes", _v$21, _p$.t));
|
|
19779
|
+
_v$22 !== _p$.a && (_p$.a = setProp(_el$40, "fg", _v$22, _p$.a));
|
|
19780
|
+
_v$23 !== _p$.o && (_p$.o = setProp(_el$43, "fg", _v$23, _p$.o));
|
|
19781
|
+
_v$24 !== _p$.i && (_p$.i = setProp(_el$43, "attributes", _v$24, _p$.i));
|
|
19782
|
+
_v$25 !== _p$.n && (_p$.n = setProp(_el$48, "fg", _v$25, _p$.n));
|
|
19783
|
+
_v$26 !== _p$.s && (_p$.s = setProp(_el$48, "attributes", _v$26, _p$.s));
|
|
19784
|
+
return _p$;
|
|
19785
|
+
}, {
|
|
19786
|
+
e: undefined,
|
|
19787
|
+
t: undefined,
|
|
19788
|
+
a: undefined,
|
|
19789
|
+
o: undefined,
|
|
19790
|
+
i: undefined,
|
|
19791
|
+
n: undefined,
|
|
19792
|
+
s: undefined
|
|
19793
|
+
});
|
|
19794
|
+
return _el$37;
|
|
19795
|
+
}
|
|
19796
|
+
}), null);
|
|
19797
|
+
insert(_el$9, createComponent2(Show, {
|
|
19798
|
+
get when() {
|
|
19799
|
+
return section() === "dev";
|
|
19800
|
+
},
|
|
19801
|
+
get children() {
|
|
19802
|
+
var _el$52 = createElement("box"), _el$53 = createElement("text"), _el$55 = createElement("text");
|
|
19803
|
+
insertNode(_el$52, _el$53);
|
|
19804
|
+
insertNode(_el$52, _el$55);
|
|
19805
|
+
setProp(_el$52, "flexDirection", "column");
|
|
19806
|
+
setProp(_el$52, "gap", 1);
|
|
19807
|
+
insertNode(_el$53, createTextNode(`Reset UI state`));
|
|
19808
|
+
insertNode(_el$55, createTextNode(`Clears ~/.config/kobe/state.json and ~/.kobe/tasks.json, then quits kobe \u2014 relaunch to start fresh. Working session / Archive lists, pane sizes, theme, model picks all reset. Worktrees on disk and Claude Code session history are not touched.`));
|
|
19809
|
+
setProp(_el$55, "wrapMode", "word");
|
|
19810
|
+
insert(_el$52, () => {
|
|
19086
19811
|
const isCursor = () => level() === "body" && bodyRow() === 0;
|
|
19087
19812
|
return (() => {
|
|
19088
|
-
var _el$
|
|
19089
|
-
insertNode(_el$
|
|
19090
|
-
setProp(_el$
|
|
19091
|
-
setProp(_el$
|
|
19092
|
-
setProp(_el$
|
|
19093
|
-
setProp(_el$
|
|
19813
|
+
var _el$85 = createElement("box"), _el$86 = createElement("text");
|
|
19814
|
+
insertNode(_el$85, _el$86);
|
|
19815
|
+
setProp(_el$85, "flexDirection", "row");
|
|
19816
|
+
setProp(_el$85, "paddingLeft", 1);
|
|
19817
|
+
setProp(_el$85, "paddingRight", 1);
|
|
19818
|
+
setProp(_el$85, "onMouseUp", () => {
|
|
19094
19819
|
setLevel("body");
|
|
19095
19820
|
setBodyRow(0);
|
|
19096
19821
|
confirmReset();
|
|
19097
19822
|
});
|
|
19098
|
-
insertNode(_el$
|
|
19823
|
+
insertNode(_el$86, createTextNode(`[enter] Reset`));
|
|
19099
19824
|
effect((_p$) => {
|
|
19100
|
-
var _v$
|
|
19101
|
-
_v$
|
|
19102
|
-
_v$
|
|
19103
|
-
_v$
|
|
19825
|
+
var _v$46 = isCursor() ? theme.primary : theme.backgroundElement, _v$47 = isCursor() ? theme.selectedListItemText : theme.warning, _v$48 = TextAttributes7.BOLD;
|
|
19826
|
+
_v$46 !== _p$.e && (_p$.e = setProp(_el$85, "backgroundColor", _v$46, _p$.e));
|
|
19827
|
+
_v$47 !== _p$.t && (_p$.t = setProp(_el$86, "fg", _v$47, _p$.t));
|
|
19828
|
+
_v$48 !== _p$.a && (_p$.a = setProp(_el$86, "attributes", _v$48, _p$.a));
|
|
19104
19829
|
return _p$;
|
|
19105
19830
|
}, {
|
|
19106
19831
|
e: undefined,
|
|
19107
19832
|
t: undefined,
|
|
19108
19833
|
a: undefined
|
|
19109
19834
|
});
|
|
19110
|
-
return _el$
|
|
19835
|
+
return _el$85;
|
|
19111
19836
|
})();
|
|
19112
19837
|
}, null);
|
|
19113
|
-
insert(_el$
|
|
19838
|
+
insert(_el$52, createComponent2(Show, {
|
|
19114
19839
|
when: hasDaemon,
|
|
19115
19840
|
get children() {
|
|
19116
|
-
var _el$
|
|
19117
|
-
insertNode(_el$
|
|
19118
|
-
insertNode(_el$
|
|
19119
|
-
setProp(_el$
|
|
19120
|
-
setProp(_el$
|
|
19121
|
-
setProp(_el$
|
|
19122
|
-
insertNode(_el$
|
|
19123
|
-
insertNode(_el$
|
|
19124
|
-
setProp(_el$
|
|
19125
|
-
insert(_el$
|
|
19841
|
+
var _el$57 = createElement("box"), _el$58 = createElement("text"), _el$60 = createElement("text");
|
|
19842
|
+
insertNode(_el$57, _el$58);
|
|
19843
|
+
insertNode(_el$57, _el$60);
|
|
19844
|
+
setProp(_el$57, "flexDirection", "column");
|
|
19845
|
+
setProp(_el$57, "gap", 0);
|
|
19846
|
+
setProp(_el$57, "paddingTop", 1);
|
|
19847
|
+
insertNode(_el$58, createTextNode(`Restart backend`));
|
|
19848
|
+
insertNode(_el$60, createTextNode(`Stops the kobed daemon and quits this kobe window so the next launch spawns a fresh daemon \u2014 picks up daemon / orchestrator / engine edits without a process kill. Other attached kobe windows will lose their connection too.`));
|
|
19849
|
+
setProp(_el$60, "wrapMode", "word");
|
|
19850
|
+
insert(_el$57, () => {
|
|
19126
19851
|
const isCursor = () => level() === "body" && bodyRow() === 1;
|
|
19127
19852
|
return (() => {
|
|
19128
|
-
var _el$
|
|
19129
|
-
insertNode(_el$
|
|
19130
|
-
setProp(_el$
|
|
19131
|
-
setProp(_el$
|
|
19132
|
-
setProp(_el$
|
|
19133
|
-
setProp(_el$
|
|
19853
|
+
var _el$88 = createElement("box"), _el$89 = createElement("text");
|
|
19854
|
+
insertNode(_el$88, _el$89);
|
|
19855
|
+
setProp(_el$88, "flexDirection", "row");
|
|
19856
|
+
setProp(_el$88, "paddingLeft", 1);
|
|
19857
|
+
setProp(_el$88, "paddingRight", 1);
|
|
19858
|
+
setProp(_el$88, "onMouseUp", () => {
|
|
19134
19859
|
setLevel("body");
|
|
19135
19860
|
setBodyRow(1);
|
|
19136
19861
|
confirmRestartDaemon();
|
|
19137
19862
|
});
|
|
19138
|
-
insertNode(_el$
|
|
19863
|
+
insertNode(_el$89, createTextNode(`[enter] Restart`));
|
|
19139
19864
|
effect((_p$) => {
|
|
19140
|
-
var _v$
|
|
19141
|
-
_v$
|
|
19142
|
-
_v$
|
|
19143
|
-
_v$
|
|
19865
|
+
var _v$49 = isCursor() ? theme.primary : theme.backgroundElement, _v$50 = isCursor() ? theme.selectedListItemText : theme.accent, _v$51 = TextAttributes7.BOLD;
|
|
19866
|
+
_v$49 !== _p$.e && (_p$.e = setProp(_el$88, "backgroundColor", _v$49, _p$.e));
|
|
19867
|
+
_v$50 !== _p$.t && (_p$.t = setProp(_el$89, "fg", _v$50, _p$.t));
|
|
19868
|
+
_v$51 !== _p$.a && (_p$.a = setProp(_el$89, "attributes", _v$51, _p$.a));
|
|
19144
19869
|
return _p$;
|
|
19145
19870
|
}, {
|
|
19146
19871
|
e: undefined,
|
|
19147
19872
|
t: undefined,
|
|
19148
19873
|
a: undefined
|
|
19149
19874
|
});
|
|
19150
|
-
return _el$
|
|
19875
|
+
return _el$88;
|
|
19151
19876
|
})();
|
|
19152
19877
|
}, null);
|
|
19153
19878
|
effect((_p$) => {
|
|
19154
|
-
var _v$
|
|
19155
|
-
_v$
|
|
19156
|
-
_v$
|
|
19157
|
-
_v$
|
|
19879
|
+
var _v$27 = theme.text, _v$28 = TextAttributes7.BOLD, _v$29 = theme.textMuted;
|
|
19880
|
+
_v$27 !== _p$.e && (_p$.e = setProp(_el$58, "fg", _v$27, _p$.e));
|
|
19881
|
+
_v$28 !== _p$.t && (_p$.t = setProp(_el$58, "attributes", _v$28, _p$.t));
|
|
19882
|
+
_v$29 !== _p$.a && (_p$.a = setProp(_el$60, "fg", _v$29, _p$.a));
|
|
19158
19883
|
return _p$;
|
|
19159
19884
|
}, {
|
|
19160
19885
|
e: undefined,
|
|
19161
19886
|
t: undefined,
|
|
19162
19887
|
a: undefined
|
|
19163
19888
|
});
|
|
19164
|
-
return _el$
|
|
19889
|
+
return _el$57;
|
|
19165
19890
|
}
|
|
19166
19891
|
}), null);
|
|
19167
19892
|
effect((_p$) => {
|
|
19168
|
-
var _v$
|
|
19169
|
-
_v$
|
|
19170
|
-
_v$
|
|
19171
|
-
_v$
|
|
19893
|
+
var _v$30 = theme.text, _v$31 = TextAttributes7.BOLD, _v$32 = theme.textMuted;
|
|
19894
|
+
_v$30 !== _p$.e && (_p$.e = setProp(_el$53, "fg", _v$30, _p$.e));
|
|
19895
|
+
_v$31 !== _p$.t && (_p$.t = setProp(_el$53, "attributes", _v$31, _p$.t));
|
|
19896
|
+
_v$32 !== _p$.a && (_p$.a = setProp(_el$55, "fg", _v$32, _p$.a));
|
|
19172
19897
|
return _p$;
|
|
19173
19898
|
}, {
|
|
19174
19899
|
e: undefined,
|
|
19175
19900
|
t: undefined,
|
|
19176
19901
|
a: undefined
|
|
19177
19902
|
});
|
|
19178
|
-
return _el$
|
|
19903
|
+
return _el$52;
|
|
19179
19904
|
}
|
|
19180
19905
|
}), null);
|
|
19181
|
-
insertNode(_el$
|
|
19182
|
-
setProp(_el$
|
|
19183
|
-
insertNode(_el$
|
|
19906
|
+
insertNode(_el$62, _el$63);
|
|
19907
|
+
setProp(_el$62, "paddingTop", 0);
|
|
19908
|
+
insertNode(_el$63, createTextNode(`j/k pick \xB7 h/l switch level \xB7 enter activate \xB7 esc close`));
|
|
19184
19909
|
effect((_p$) => {
|
|
19185
|
-
var _v$
|
|
19186
|
-
_v$
|
|
19187
|
-
_v$
|
|
19188
|
-
_v$
|
|
19189
|
-
_v$
|
|
19910
|
+
var _v$33 = TextAttributes7.BOLD, _v$34 = theme.text, _v$35 = theme.textMuted, _v$36 = theme.textMuted;
|
|
19911
|
+
_v$33 !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$33, _p$.e));
|
|
19912
|
+
_v$34 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$34, _p$.t));
|
|
19913
|
+
_v$35 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$35, _p$.a));
|
|
19914
|
+
_v$36 !== _p$.o && (_p$.o = setProp(_el$63, "fg", _v$36, _p$.o));
|
|
19190
19915
|
return _p$;
|
|
19191
19916
|
}, {
|
|
19192
19917
|
e: undefined,
|
|
@@ -19210,6 +19935,7 @@ var init_settings_dialog = __esm(() => {
|
|
|
19210
19935
|
init_solid();
|
|
19211
19936
|
init_dev();
|
|
19212
19937
|
init_remote_orchestrator();
|
|
19938
|
+
init_account_detect();
|
|
19213
19939
|
init_env();
|
|
19214
19940
|
init_theme();
|
|
19215
19941
|
init_keymap();
|
|
@@ -19223,6 +19949,9 @@ var init_settings_dialog = __esm(() => {
|
|
|
19223
19949
|
SECTIONS = [{
|
|
19224
19950
|
id: "general",
|
|
19225
19951
|
label: "General"
|
|
19952
|
+
}, {
|
|
19953
|
+
id: "accounts",
|
|
19954
|
+
label: "Accounts"
|
|
19226
19955
|
}, {
|
|
19227
19956
|
id: "dev",
|
|
19228
19957
|
label: "Dev"
|
|
@@ -19326,9 +20055,9 @@ var pulse_default = "../pulse-n3cq1btw.wav";
|
|
|
19326
20055
|
var init_pulse = () => {};
|
|
19327
20056
|
|
|
19328
20057
|
// src/tui/lib/sound.ts
|
|
19329
|
-
import { existsSync as
|
|
20058
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
19330
20059
|
import { tmpdir as tmpdir2 } from "os";
|
|
19331
|
-
import { basename as basename2, isAbsolute, join as
|
|
20060
|
+
import { basename as basename2, isAbsolute, join as join11, resolve as resolve3 } from "path";
|
|
19332
20061
|
function args(player, file, volume) {
|
|
19333
20062
|
if (player === "ffplay")
|
|
19334
20063
|
return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
|
|
@@ -19349,15 +20078,15 @@ function args(player, file, volume) {
|
|
|
19349
20078
|
function pickPlayer() {
|
|
19350
20079
|
if (cachedPlayer !== undefined)
|
|
19351
20080
|
return cachedPlayer;
|
|
19352
|
-
const
|
|
19353
|
-
const segments =
|
|
19354
|
-
cachedPlayer = PLAYERS.find((p2) => segments.some((dir) =>
|
|
20081
|
+
const path9 = process.env.PATH ?? "";
|
|
20082
|
+
const segments = path9.split(":").filter(Boolean);
|
|
20083
|
+
cachedPlayer = PLAYERS.find((p2) => segments.some((dir) => existsSync6(join11(dir, p2)))) ?? null;
|
|
19355
20084
|
return cachedPlayer;
|
|
19356
20085
|
}
|
|
19357
20086
|
async function ensureAsset() {
|
|
19358
20087
|
cachedPath ??= (async () => {
|
|
19359
20088
|
mkdirSync3(DIR, { recursive: true });
|
|
19360
|
-
const dest =
|
|
20089
|
+
const dest = join11(DIR, basename2(pulseAsset));
|
|
19361
20090
|
const out = Bun.file(dest);
|
|
19362
20091
|
if (await out.exists())
|
|
19363
20092
|
return dest;
|
|
@@ -19370,9 +20099,9 @@ function pulse(volume = 0.4) {
|
|
|
19370
20099
|
const player = pickPlayer();
|
|
19371
20100
|
if (!player)
|
|
19372
20101
|
return;
|
|
19373
|
-
ensureAsset().then((
|
|
20102
|
+
ensureAsset().then((path9) => {
|
|
19374
20103
|
try {
|
|
19375
|
-
const proc = Bun.spawn(args(player,
|
|
20104
|
+
const proc = Bun.spawn(args(player, path9, volume), {
|
|
19376
20105
|
stdin: "ignore",
|
|
19377
20106
|
stdout: "ignore",
|
|
19378
20107
|
stderr: "ignore"
|
|
@@ -19387,7 +20116,7 @@ var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
|
|
|
19387
20116
|
var init_sound = __esm(() => {
|
|
19388
20117
|
init_pulse();
|
|
19389
20118
|
pulseAsset = isAbsolute(pulse_default) ? pulse_default : resolve3(import.meta.dir, pulse_default);
|
|
19390
|
-
DIR =
|
|
20119
|
+
DIR = join11(tmpdir2(), "kobe-sfx");
|
|
19391
20120
|
PLAYERS = [
|
|
19392
20121
|
"ffplay",
|
|
19393
20122
|
"mpv",
|
|
@@ -19405,12 +20134,12 @@ var init_sound = __esm(() => {
|
|
|
19405
20134
|
});
|
|
19406
20135
|
|
|
19407
20136
|
// src/tui/context/kv.tsx
|
|
19408
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
19409
|
-
import { homedir as
|
|
19410
|
-
import { dirname as dirname4, join as
|
|
20137
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
20138
|
+
import { homedir as homedir10 } from "os";
|
|
20139
|
+
import { dirname as dirname4, join as join12 } from "path";
|
|
19411
20140
|
function loadInitial() {
|
|
19412
20141
|
try {
|
|
19413
|
-
const text =
|
|
20142
|
+
const text = readFileSync7(STATE_PATH, "utf8");
|
|
19414
20143
|
const parsed = JSON.parse(text);
|
|
19415
20144
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19416
20145
|
return parsed;
|
|
@@ -19422,7 +20151,7 @@ var STATE_PATH, WRITE_DEBOUNCE_MS = 250, useKV, KVProvider;
|
|
|
19422
20151
|
var init_kv = __esm(() => {
|
|
19423
20152
|
init_dev2();
|
|
19424
20153
|
init_helper();
|
|
19425
|
-
STATE_PATH =
|
|
20154
|
+
STATE_PATH = join12(homedir10(), ".config", "kobe", "state.json");
|
|
19426
20155
|
({
|
|
19427
20156
|
use: useKV,
|
|
19428
20157
|
provider: KVProvider
|
|
@@ -20999,8 +21728,8 @@ var init_create_pr_button = __esm(() => {
|
|
|
20999
21728
|
|
|
21000
21729
|
// src/tui/lib/worktree-opener.ts
|
|
21001
21730
|
import { spawn as spawn5 } from "child_process";
|
|
21002
|
-
import { existsSync as
|
|
21003
|
-
import { basename as basename4, delimiter, isAbsolute as isAbsolute2, join as
|
|
21731
|
+
import { existsSync as existsSync7 } from "fs";
|
|
21732
|
+
import { basename as basename4, delimiter, isAbsolute as isAbsolute2, join as join13 } from "path";
|
|
21004
21733
|
function executableOnPath(command, env, exists) {
|
|
21005
21734
|
if (isAbsolute2(command))
|
|
21006
21735
|
return exists(command);
|
|
@@ -21008,7 +21737,7 @@ function executableOnPath(command, env, exists) {
|
|
|
21008
21737
|
for (const dir of pathEnv.split(delimiter)) {
|
|
21009
21738
|
if (!dir)
|
|
21010
21739
|
continue;
|
|
21011
|
-
if (exists(
|
|
21740
|
+
if (exists(join13(dir, command)))
|
|
21012
21741
|
return true;
|
|
21013
21742
|
}
|
|
21014
21743
|
return false;
|
|
@@ -21028,7 +21757,7 @@ function labelForOverride(command) {
|
|
|
21028
21757
|
function detectWorktreeOpener(deps = {}) {
|
|
21029
21758
|
const env = deps.env ?? process.env;
|
|
21030
21759
|
const platform = deps.platform ?? process.platform;
|
|
21031
|
-
const exists = deps.exists ??
|
|
21760
|
+
const exists = deps.exists ?? existsSync7;
|
|
21032
21761
|
const override = env.KOBE_OPEN_EDITOR?.trim();
|
|
21033
21762
|
if (override) {
|
|
21034
21763
|
return { id: "env", label: labelForOverride(override), command: override, args: [] };
|
|
@@ -21040,7 +21769,7 @@ function detectWorktreeOpener(deps = {}) {
|
|
|
21040
21769
|
}
|
|
21041
21770
|
if (platform === "darwin" && executableOnPath("open", env, exists)) {
|
|
21042
21771
|
for (const app of MAC_APP_CANDIDATES) {
|
|
21043
|
-
if (app.paths.some((
|
|
21772
|
+
if (app.paths.some((path9) => exists(path9))) {
|
|
21044
21773
|
return { id: app.id, label: app.label, command: "open", args: ["-a", app.appName] };
|
|
21045
21774
|
}
|
|
21046
21775
|
}
|
|
@@ -22102,7 +22831,7 @@ var init_update_dialog = __esm(() => {
|
|
|
22102
22831
|
});
|
|
22103
22832
|
|
|
22104
22833
|
// src/tui/component/top-bar.tsx
|
|
22105
|
-
import { spawnSync as
|
|
22834
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
22106
22835
|
import { TextAttributes as TextAttributes18 } from "@opentui/core";
|
|
22107
22836
|
function TopBar(props) {
|
|
22108
22837
|
const {
|
|
@@ -22133,7 +22862,7 @@ function TopBar(props) {
|
|
|
22133
22862
|
`);
|
|
22134
22863
|
process.stderr.write(`running: ${UPDATE_COMMAND}
|
|
22135
22864
|
`);
|
|
22136
|
-
const result =
|
|
22865
|
+
const result = spawnSync8("sh", ["-c", UPDATE_COMMAND], {
|
|
22137
22866
|
stdio: "inherit"
|
|
22138
22867
|
});
|
|
22139
22868
|
if (result.error) {
|
|
@@ -22387,34 +23116,160 @@ var init_sync = __esm(() => {
|
|
|
22387
23116
|
}));
|
|
22388
23117
|
});
|
|
22389
23118
|
|
|
22390
|
-
// src/
|
|
22391
|
-
|
|
22392
|
-
|
|
22393
|
-
import { homedir as homedir8 } from "os";
|
|
22394
|
-
import path7 from "path";
|
|
22395
|
-
function encodeCwd(cwd) {
|
|
22396
|
-
return cwd.replace(/[/.]/g, "-");
|
|
22397
|
-
}
|
|
22398
|
-
async function readHistory(sessionId, deps = defaultDeps2) {
|
|
22399
|
-
const root = deps.projectsDir();
|
|
22400
|
-
const projectDirs = await deps.readdir(root);
|
|
22401
|
-
for (const dir of projectDirs) {
|
|
22402
|
-
const candidate = path7.join(root, dir, `${sessionId}.jsonl`);
|
|
22403
|
-
let raw;
|
|
22404
|
-
try {
|
|
22405
|
-
raw = await deps.readFile(candidate);
|
|
22406
|
-
} catch {
|
|
22407
|
-
continue;
|
|
22408
|
-
}
|
|
22409
|
-
return sortByTimestamp(parseJsonl(raw, sessionId));
|
|
22410
|
-
}
|
|
22411
|
-
return [];
|
|
23119
|
+
// src/session/usage-metrics.ts
|
|
23120
|
+
function totalContextTokens(u2) {
|
|
23121
|
+
return u2.input_tokens + (u2.cache_read_input_tokens ?? 0) + (u2.cache_creation_input_tokens ?? 0);
|
|
22412
23122
|
}
|
|
22413
|
-
|
|
22414
|
-
const
|
|
23123
|
+
function parseTimestampMs(value) {
|
|
23124
|
+
const ms = new Date(value).getTime();
|
|
23125
|
+
return Number.isFinite(ms) ? ms : null;
|
|
23126
|
+
}
|
|
23127
|
+
function mergeIntervals(intervals) {
|
|
23128
|
+
if (intervals.length === 0)
|
|
23129
|
+
return [];
|
|
23130
|
+
const sorted = [...intervals].sort((a2, b2) => a2.startMs - b2.startMs);
|
|
23131
|
+
const first = sorted[0];
|
|
23132
|
+
if (!first)
|
|
23133
|
+
return [];
|
|
23134
|
+
const merged = [{ startMs: first.startMs, endMs: first.endMs }];
|
|
23135
|
+
for (let i2 = 1;i2 < sorted.length; i2++) {
|
|
23136
|
+
const current = sorted[i2];
|
|
23137
|
+
const last = merged[merged.length - 1];
|
|
23138
|
+
if (!current || !last)
|
|
23139
|
+
continue;
|
|
23140
|
+
if (current.startMs <= last.endMs) {
|
|
23141
|
+
last.endMs = Math.max(last.endMs, current.endMs);
|
|
23142
|
+
} else {
|
|
23143
|
+
merged.push({ startMs: current.startMs, endMs: current.endMs });
|
|
23144
|
+
}
|
|
23145
|
+
}
|
|
23146
|
+
return merged;
|
|
23147
|
+
}
|
|
23148
|
+
function durationMs(intervals) {
|
|
23149
|
+
return intervals.reduce((total, interval) => total + (interval.endMs - interval.startMs), 0);
|
|
23150
|
+
}
|
|
23151
|
+
function deriveSessionUsageMetrics(past) {
|
|
23152
|
+
let latestUsage;
|
|
23153
|
+
let latestUsageTimestampMs = null;
|
|
23154
|
+
let lastUserTimestampMs = null;
|
|
23155
|
+
let inputTokens = 0;
|
|
23156
|
+
let outputTokens = 0;
|
|
23157
|
+
const intervals = [];
|
|
23158
|
+
for (const message of past) {
|
|
23159
|
+
const timestampMs = parseTimestampMs(message.timestamp);
|
|
23160
|
+
if (message.role === "user" && timestampMs !== null) {
|
|
23161
|
+
lastUserTimestampMs = timestampMs;
|
|
23162
|
+
continue;
|
|
23163
|
+
}
|
|
23164
|
+
if (message.role !== "assistant" || !message.usage)
|
|
23165
|
+
continue;
|
|
23166
|
+
if (timestampMs !== null && (latestUsageTimestampMs === null || timestampMs > latestUsageTimestampMs)) {
|
|
23167
|
+
latestUsageTimestampMs = timestampMs;
|
|
23168
|
+
latestUsage = message.usage;
|
|
23169
|
+
} else if (latestUsage === undefined) {
|
|
23170
|
+
latestUsage = message.usage;
|
|
23171
|
+
}
|
|
23172
|
+
inputTokens += message.usage.input_tokens;
|
|
23173
|
+
outputTokens += message.usage.output_tokens;
|
|
23174
|
+
if (timestampMs !== null && lastUserTimestampMs !== null && timestampMs > lastUserTimestampMs) {
|
|
23175
|
+
intervals.push({ startMs: lastUserTimestampMs, endMs: timestampMs });
|
|
23176
|
+
}
|
|
23177
|
+
}
|
|
23178
|
+
if (!latestUsage)
|
|
23179
|
+
return;
|
|
23180
|
+
const totalDurationMs = durationMs(mergeIntervals(intervals));
|
|
23181
|
+
if (totalDurationMs <= 0)
|
|
23182
|
+
return latestUsage;
|
|
23183
|
+
return {
|
|
23184
|
+
...latestUsage,
|
|
23185
|
+
total_speed_tokens_per_second: (inputTokens + outputTokens) / (totalDurationMs / 1000)
|
|
23186
|
+
};
|
|
23187
|
+
}
|
|
23188
|
+
function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
|
|
23189
|
+
const startMs = startedAtIso ? parseTimestampMs(startedAtIso) : null;
|
|
23190
|
+
const endMs = parseTimestampMs(endedAtIso);
|
|
23191
|
+
if (startMs === null || endMs === null || endMs <= startMs)
|
|
23192
|
+
return usage;
|
|
23193
|
+
return {
|
|
23194
|
+
...usage,
|
|
23195
|
+
total_speed_tokens_per_second: (usage.input_tokens + usage.output_tokens) / ((endMs - startMs) / 1000)
|
|
23196
|
+
};
|
|
23197
|
+
}
|
|
23198
|
+
|
|
23199
|
+
// src/engine/claude-code-local/normalize.ts
|
|
23200
|
+
function normalizeClaudeContent(content) {
|
|
23201
|
+
if (typeof content === "string") {
|
|
23202
|
+
return content.length > 0 ? [{ type: "text", text: content }] : [];
|
|
23203
|
+
}
|
|
23204
|
+
if (!Array.isArray(content))
|
|
23205
|
+
return [];
|
|
23206
|
+
const out = [];
|
|
23207
|
+
for (const block of content) {
|
|
23208
|
+
if (typeof block === "string") {
|
|
23209
|
+
if (block.length > 0)
|
|
23210
|
+
out.push({ type: "text", text: block });
|
|
23211
|
+
continue;
|
|
23212
|
+
}
|
|
23213
|
+
if (!block || typeof block !== "object")
|
|
23214
|
+
continue;
|
|
23215
|
+
const b2 = block;
|
|
23216
|
+
if (b2.type === "text" && typeof b2.text === "string") {
|
|
23217
|
+
out.push({ type: "text", text: b2.text });
|
|
23218
|
+
continue;
|
|
23219
|
+
}
|
|
23220
|
+
if (b2.type === "tool_use") {
|
|
23221
|
+
out.push({
|
|
23222
|
+
type: "tool_call",
|
|
23223
|
+
callId: typeof b2.id === "string" ? b2.id : "",
|
|
23224
|
+
name: typeof b2.name === "string" ? b2.name : "",
|
|
23225
|
+
input: b2.input
|
|
23226
|
+
});
|
|
23227
|
+
continue;
|
|
23228
|
+
}
|
|
23229
|
+
if (b2.type === "tool_result") {
|
|
23230
|
+
out.push({
|
|
23231
|
+
type: "tool_result",
|
|
23232
|
+
callId: typeof b2.tool_use_id === "string" ? b2.tool_use_id : "",
|
|
23233
|
+
output: b2.content,
|
|
23234
|
+
isError: b2.is_error === true
|
|
23235
|
+
});
|
|
23236
|
+
continue;
|
|
23237
|
+
}
|
|
23238
|
+
if (b2.type === "thinking" && typeof b2.thinking === "string") {
|
|
23239
|
+
out.push({ type: "thinking", text: b2.thinking });
|
|
23240
|
+
}
|
|
23241
|
+
}
|
|
23242
|
+
return out;
|
|
23243
|
+
}
|
|
23244
|
+
|
|
23245
|
+
// src/engine/claude-code-local/history.ts
|
|
23246
|
+
import { randomUUID } from "crypto";
|
|
23247
|
+
import { appendFile, mkdir as mkdir2, readFile as readFile3, readdir, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
23248
|
+
import { homedir as homedir11 } from "os";
|
|
23249
|
+
import path9 from "path";
|
|
23250
|
+
function encodeCwd(cwd) {
|
|
23251
|
+
return cwd.replace(/[/.]/g, "-");
|
|
23252
|
+
}
|
|
23253
|
+
async function readHistory(sessionId, deps = defaultDeps4) {
|
|
23254
|
+
const root = deps.projectsDir();
|
|
23255
|
+
const projectDirs = await deps.readdir(root);
|
|
23256
|
+
for (const dir of projectDirs) {
|
|
23257
|
+
const candidate = path9.join(root, dir, `${sessionId}.jsonl`);
|
|
23258
|
+
let raw;
|
|
23259
|
+
try {
|
|
23260
|
+
raw = await deps.readFile(candidate);
|
|
23261
|
+
} catch {
|
|
23262
|
+
continue;
|
|
23263
|
+
}
|
|
23264
|
+
return sortByTimestamp(parseJsonl(raw, sessionId));
|
|
23265
|
+
}
|
|
23266
|
+
return [];
|
|
23267
|
+
}
|
|
23268
|
+
async function deleteHistory(sessionId, deps = defaultDeps4) {
|
|
23269
|
+
const root = deps.projectsDir();
|
|
22415
23270
|
const projectDirs = await deps.readdir(root);
|
|
22416
23271
|
for (const dir of projectDirs) {
|
|
22417
|
-
const candidate =
|
|
23272
|
+
const candidate = path9.join(root, dir, `${sessionId}.jsonl`);
|
|
22418
23273
|
try {
|
|
22419
23274
|
await unlink2(candidate);
|
|
22420
23275
|
} catch (err) {
|
|
@@ -22461,11 +23316,11 @@ function extractMessage(record, fallbackSessionId) {
|
|
|
22461
23316
|
return null;
|
|
22462
23317
|
if (!("content" in inner))
|
|
22463
23318
|
return null;
|
|
22464
|
-
const
|
|
23319
|
+
const blocks = normalizeClaudeContent(inner.content);
|
|
22465
23320
|
const ts = typeof record.timestamp === "string" ? record.timestamp : new Date().toISOString();
|
|
22466
23321
|
const sid = typeof record.sessionId === "string" ? record.sessionId : fallbackSessionId;
|
|
22467
23322
|
const usage = extractUsage(inner.usage);
|
|
22468
|
-
return usage ? { role,
|
|
23323
|
+
return usage ? { role, blocks, timestamp: ts, sessionId: sid, usage } : { role, blocks, timestamp: ts, sessionId: sid };
|
|
22469
23324
|
}
|
|
22470
23325
|
function extractUsage(v2) {
|
|
22471
23326
|
if (!isObject(v2))
|
|
@@ -22486,11 +23341,11 @@ function extractUsage(v2) {
|
|
|
22486
23341
|
function isObject(v2) {
|
|
22487
23342
|
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
22488
23343
|
}
|
|
22489
|
-
async function appendInterruptedUserPrompt(sessionId, cwd, prompt, deps =
|
|
23344
|
+
async function appendInterruptedUserPrompt(sessionId, cwd, prompt, deps = defaultDeps4) {
|
|
22490
23345
|
if (!prompt || prompt.trim().length === 0)
|
|
22491
23346
|
return;
|
|
22492
|
-
const projectDir =
|
|
22493
|
-
const filePath =
|
|
23347
|
+
const projectDir = path9.join(deps.projectsDir(), encodeCwd(cwd));
|
|
23348
|
+
const filePath = path9.join(projectDir, `${sessionId}.jsonl`);
|
|
22494
23349
|
let lines = [];
|
|
22495
23350
|
try {
|
|
22496
23351
|
const raw = await readFile3(filePath, "utf8");
|
|
@@ -22556,11 +23411,11 @@ ${prompt}` : prompt;
|
|
|
22556
23411
|
await appendFile(filePath, `${JSON.stringify(record)}
|
|
22557
23412
|
`);
|
|
22558
23413
|
}
|
|
22559
|
-
var
|
|
23414
|
+
var defaultDeps4;
|
|
22560
23415
|
var init_history = __esm(() => {
|
|
22561
|
-
|
|
23416
|
+
defaultDeps4 = {
|
|
22562
23417
|
projectsDir() {
|
|
22563
|
-
return
|
|
23418
|
+
return path9.join(homedir11(), ".claude", "projects");
|
|
22564
23419
|
},
|
|
22565
23420
|
async readdir(p2) {
|
|
22566
23421
|
try {
|
|
@@ -22644,16 +23499,16 @@ function delay(ms) {
|
|
|
22644
23499
|
|
|
22645
23500
|
// src/engine/claude-code-local/sessions.ts
|
|
22646
23501
|
import { readFile as readFile4, readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
22647
|
-
import { homedir as
|
|
22648
|
-
import
|
|
22649
|
-
async function listSessionsForCwd(cwd, deps =
|
|
22650
|
-
const projectDir =
|
|
23502
|
+
import { homedir as homedir12 } from "os";
|
|
23503
|
+
import path10 from "path";
|
|
23504
|
+
async function listSessionsForCwd(cwd, deps = defaultDeps5) {
|
|
23505
|
+
const projectDir = path10.join(deps.projectsDir(), encodeCwd(cwd));
|
|
22651
23506
|
const entries = await deps.readdir(projectDir);
|
|
22652
23507
|
const jsonlNames = entries.filter((n2) => n2.endsWith(".jsonl"));
|
|
22653
23508
|
const out = [];
|
|
22654
23509
|
for (const name of jsonlNames) {
|
|
22655
23510
|
const sessionId = name.slice(0, -".jsonl".length);
|
|
22656
|
-
const filePath =
|
|
23511
|
+
const filePath = path10.join(projectDir, name);
|
|
22657
23512
|
try {
|
|
22658
23513
|
const [meta, raw] = await Promise.all([deps.stat(filePath), deps.readFile(filePath)]);
|
|
22659
23514
|
const lines = raw.split(`
|
|
@@ -22696,29 +23551,23 @@ function extractFirstUserMessage(lines) {
|
|
|
22696
23551
|
return null;
|
|
22697
23552
|
}
|
|
22698
23553
|
function stringifyContent(content) {
|
|
22699
|
-
|
|
22700
|
-
return content.trim();
|
|
22701
|
-
if (!Array.isArray(content))
|
|
22702
|
-
return "";
|
|
23554
|
+
const blocks = normalizeClaudeContent(content);
|
|
22703
23555
|
const parts = [];
|
|
22704
|
-
for (const
|
|
22705
|
-
if (
|
|
22706
|
-
|
|
22707
|
-
if (block.type === "text" && typeof block.text === "string") {
|
|
22708
|
-
parts.push(block.text);
|
|
22709
|
-
}
|
|
23556
|
+
for (const b2 of blocks) {
|
|
23557
|
+
if (b2.type === "text")
|
|
23558
|
+
parts.push(b2.text);
|
|
22710
23559
|
}
|
|
22711
23560
|
return parts.join(" ").trim();
|
|
22712
23561
|
}
|
|
22713
23562
|
function isObject2(v2) {
|
|
22714
23563
|
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
22715
23564
|
}
|
|
22716
|
-
var
|
|
23565
|
+
var defaultDeps5, PREVIEW_MAX_CHARS = 200;
|
|
22717
23566
|
var init_sessions = __esm(() => {
|
|
22718
23567
|
init_history();
|
|
22719
|
-
|
|
23568
|
+
defaultDeps5 = {
|
|
22720
23569
|
projectsDir() {
|
|
22721
|
-
return
|
|
23570
|
+
return path10.join(homedir12(), ".claude", "projects");
|
|
22722
23571
|
},
|
|
22723
23572
|
async readdir(p2) {
|
|
22724
23573
|
try {
|
|
@@ -22765,22 +23614,804 @@ function buildArgs(opts) {
|
|
|
22765
23614
|
if (opts.permissionMode) {
|
|
22766
23615
|
args2.push("--permission-mode", opts.permissionMode);
|
|
22767
23616
|
}
|
|
22768
|
-
args2.push("--output-format", "stream-json", "--verbose");
|
|
22769
|
-
const mcpConfig = process.env.KOBE_MCP_CONFIG;
|
|
22770
|
-
if (mcpConfig && mcpConfig.length > 0) {
|
|
22771
|
-
args2.push("--mcp-config", mcpConfig);
|
|
23617
|
+
args2.push("--output-format", "stream-json", "--verbose");
|
|
23618
|
+
const mcpConfig = process.env.KOBE_MCP_CONFIG;
|
|
23619
|
+
if (mcpConfig && mcpConfig.length > 0) {
|
|
23620
|
+
args2.push("--mcp-config", mcpConfig);
|
|
23621
|
+
}
|
|
23622
|
+
if (opts.extraArgs && opts.extraArgs.length > 0) {
|
|
23623
|
+
args2.push(...opts.extraArgs);
|
|
23624
|
+
}
|
|
23625
|
+
return args2;
|
|
23626
|
+
}
|
|
23627
|
+
var init_spawn = () => {};
|
|
23628
|
+
|
|
23629
|
+
// src/engine/claude-code-local/stream.ts
|
|
23630
|
+
async function* parseStreamJson(lines, opts = {}) {
|
|
23631
|
+
let sessionIdEmitted = false;
|
|
23632
|
+
const toolNameById = new Map;
|
|
23633
|
+
for await (const rawLine of lines) {
|
|
23634
|
+
const line = rawLine.trim();
|
|
23635
|
+
if (!line)
|
|
23636
|
+
continue;
|
|
23637
|
+
let msg;
|
|
23638
|
+
try {
|
|
23639
|
+
msg = JSON.parse(line);
|
|
23640
|
+
} catch (err) {
|
|
23641
|
+
yield { type: "error", message: `stream-json parse failed: ${stringifyErr(err)}` };
|
|
23642
|
+
continue;
|
|
23643
|
+
}
|
|
23644
|
+
if (!isObject3(msg))
|
|
23645
|
+
continue;
|
|
23646
|
+
const type = typeof msg.type === "string" ? msg.type : undefined;
|
|
23647
|
+
if (!type)
|
|
23648
|
+
continue;
|
|
23649
|
+
if ("parent_tool_use_id" in msg && msg.parent_tool_use_id != null)
|
|
23650
|
+
continue;
|
|
23651
|
+
if (type === "system") {
|
|
23652
|
+
const subtype = typeof msg.subtype === "string" ? msg.subtype : undefined;
|
|
23653
|
+
if (subtype === "init" && !sessionIdEmitted) {
|
|
23654
|
+
const sid = typeof msg.session_id === "string" ? msg.session_id : undefined;
|
|
23655
|
+
if (sid) {
|
|
23656
|
+
sessionIdEmitted = true;
|
|
23657
|
+
opts.onSessionId?.(sid);
|
|
23658
|
+
}
|
|
23659
|
+
}
|
|
23660
|
+
continue;
|
|
23661
|
+
}
|
|
23662
|
+
if (type === "assistant") {
|
|
23663
|
+
const content = extractContentBlocks(msg);
|
|
23664
|
+
for (const block of content) {
|
|
23665
|
+
if (!isObject3(block))
|
|
23666
|
+
continue;
|
|
23667
|
+
const blockType = typeof block.type === "string" ? block.type : undefined;
|
|
23668
|
+
if (blockType === "text") {
|
|
23669
|
+
const text = typeof block.text === "string" ? block.text : "";
|
|
23670
|
+
if (text)
|
|
23671
|
+
yield { type: "assistant.delta", text };
|
|
23672
|
+
} else if (blockType === "tool_use") {
|
|
23673
|
+
const name = typeof block.name === "string" ? block.name : "tool";
|
|
23674
|
+
const id = typeof block.id === "string" ? block.id : undefined;
|
|
23675
|
+
if (id)
|
|
23676
|
+
toolNameById.set(id, name);
|
|
23677
|
+
const input = "input" in block ? block.input : undefined;
|
|
23678
|
+
yield { type: "tool.start", name, input };
|
|
23679
|
+
}
|
|
23680
|
+
}
|
|
23681
|
+
continue;
|
|
23682
|
+
}
|
|
23683
|
+
if (type === "user") {
|
|
23684
|
+
const content = extractContentBlocks(msg);
|
|
23685
|
+
for (const block of content) {
|
|
23686
|
+
if (!isObject3(block))
|
|
23687
|
+
continue;
|
|
23688
|
+
const blockType = typeof block.type === "string" ? block.type : undefined;
|
|
23689
|
+
if (blockType === "tool_result") {
|
|
23690
|
+
const id = typeof block.tool_use_id === "string" ? block.tool_use_id : undefined;
|
|
23691
|
+
const name = id && toolNameById.get(id) || "tool";
|
|
23692
|
+
const output = "content" in block ? block.content : undefined;
|
|
23693
|
+
yield { type: "tool.result", name, output };
|
|
23694
|
+
}
|
|
23695
|
+
}
|
|
23696
|
+
continue;
|
|
23697
|
+
}
|
|
23698
|
+
if (type === "result") {
|
|
23699
|
+
const usage = isObject3(msg.usage) ? msg.usage : undefined;
|
|
23700
|
+
if (usage) {
|
|
23701
|
+
const inTok = typeof usage.input_tokens === "number" ? usage.input_tokens : 0;
|
|
23702
|
+
const outTok = typeof usage.output_tokens === "number" ? usage.output_tokens : 0;
|
|
23703
|
+
const cacheRead = typeof usage.cache_read_input_tokens === "number" ? usage.cache_read_input_tokens : undefined;
|
|
23704
|
+
const cacheCreate = typeof usage.cache_creation_input_tokens === "number" ? usage.cache_creation_input_tokens : undefined;
|
|
23705
|
+
yield {
|
|
23706
|
+
type: "usage",
|
|
23707
|
+
input_tokens: inTok,
|
|
23708
|
+
output_tokens: outTok,
|
|
23709
|
+
...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {},
|
|
23710
|
+
...cacheCreate !== undefined ? { cache_creation_input_tokens: cacheCreate } : {}
|
|
23711
|
+
};
|
|
23712
|
+
}
|
|
23713
|
+
const subtype = typeof msg.subtype === "string" ? msg.subtype : "success";
|
|
23714
|
+
const isApiError = msg.is_error === true || typeof msg.api_error_status === "number";
|
|
23715
|
+
if (isApiError) {
|
|
23716
|
+
const status = typeof msg.api_error_status === "number" ? ` ${msg.api_error_status}` : "";
|
|
23717
|
+
const result = typeof msg.result === "string" ? msg.result.trim() : "";
|
|
23718
|
+
yield { type: "error", message: result ? `claude API error${status}: ${result}` : `claude API error${status}` };
|
|
23719
|
+
return;
|
|
23720
|
+
}
|
|
23721
|
+
if (subtype === "success") {
|
|
23722
|
+
yield { type: "done" };
|
|
23723
|
+
} else {
|
|
23724
|
+
yield { type: "error", message: `claude session ended: ${subtype}` };
|
|
23725
|
+
}
|
|
23726
|
+
return;
|
|
23727
|
+
}
|
|
23728
|
+
}
|
|
23729
|
+
}
|
|
23730
|
+
async function* readLines(stream) {
|
|
23731
|
+
let buf = "";
|
|
23732
|
+
for await (const chunk of stream) {
|
|
23733
|
+
const text = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
23734
|
+
buf += text;
|
|
23735
|
+
let nl = buf.indexOf(`
|
|
23736
|
+
`);
|
|
23737
|
+
while (nl !== -1) {
|
|
23738
|
+
yield buf.slice(0, nl);
|
|
23739
|
+
buf = buf.slice(nl + 1);
|
|
23740
|
+
nl = buf.indexOf(`
|
|
23741
|
+
`);
|
|
23742
|
+
}
|
|
23743
|
+
}
|
|
23744
|
+
if (buf.length > 0)
|
|
23745
|
+
yield buf;
|
|
23746
|
+
}
|
|
23747
|
+
function isObject3(v2) {
|
|
23748
|
+
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
23749
|
+
}
|
|
23750
|
+
function extractContentBlocks(msg) {
|
|
23751
|
+
if (Array.isArray(msg.content))
|
|
23752
|
+
return msg.content;
|
|
23753
|
+
const inner = msg.message;
|
|
23754
|
+
if (isObject3(inner) && Array.isArray(inner.content))
|
|
23755
|
+
return inner.content;
|
|
23756
|
+
return [];
|
|
23757
|
+
}
|
|
23758
|
+
function stringifyErr(err) {
|
|
23759
|
+
if (err instanceof Error)
|
|
23760
|
+
return err.message;
|
|
23761
|
+
try {
|
|
23762
|
+
return JSON.stringify(err);
|
|
23763
|
+
} catch {
|
|
23764
|
+
return String(err);
|
|
23765
|
+
}
|
|
23766
|
+
}
|
|
23767
|
+
|
|
23768
|
+
// src/engine/claude-code-local/index.ts
|
|
23769
|
+
class ClaudeCodeLocal {
|
|
23770
|
+
identity = claudeIdentity;
|
|
23771
|
+
capabilities = claudeCapabilities;
|
|
23772
|
+
registry = new SessionRegistry;
|
|
23773
|
+
running = new Map;
|
|
23774
|
+
binaryPathResolver;
|
|
23775
|
+
stopGraceMs;
|
|
23776
|
+
constructor(opts = {}) {
|
|
23777
|
+
this.binaryPathResolver = opts.binaryPathResolver ?? findClaudeBinary;
|
|
23778
|
+
this.stopGraceMs = opts.stopGraceMs ?? 5000;
|
|
23779
|
+
}
|
|
23780
|
+
async spawn(cwd, prompt, opts) {
|
|
23781
|
+
return this.start({ cwd, prompt, opts });
|
|
23782
|
+
}
|
|
23783
|
+
async resume(sessionId, prompt, opts) {
|
|
23784
|
+
const cwd = opts?.cwd ?? opts?.env?.KOBE_RESUME_CWD ?? process.cwd();
|
|
23785
|
+
return this.start({ cwd, prompt, opts, resumeSessionId: sessionId });
|
|
23786
|
+
}
|
|
23787
|
+
stream(handle) {
|
|
23788
|
+
const sid = handle.sessionId;
|
|
23789
|
+
const self = this;
|
|
23790
|
+
return {
|
|
23791
|
+
async* [Symbol.asyncIterator]() {
|
|
23792
|
+
const session = self.running.get(sid);
|
|
23793
|
+
if (!session)
|
|
23794
|
+
return;
|
|
23795
|
+
let idx = 0;
|
|
23796
|
+
while (true) {
|
|
23797
|
+
if (idx < session.queue.length) {
|
|
23798
|
+
const ev = session.queue[idx++];
|
|
23799
|
+
if (!ev)
|
|
23800
|
+
continue;
|
|
23801
|
+
yield ev;
|
|
23802
|
+
if (ev.type === "done" || ev.type === "error")
|
|
23803
|
+
return;
|
|
23804
|
+
continue;
|
|
23805
|
+
}
|
|
23806
|
+
if (session.closed)
|
|
23807
|
+
return;
|
|
23808
|
+
await new Promise((resolve4) => session.waiters.push(resolve4));
|
|
23809
|
+
}
|
|
23810
|
+
}
|
|
23811
|
+
};
|
|
23812
|
+
}
|
|
23813
|
+
async readHistory(sessionId) {
|
|
23814
|
+
const messages = await readHistory(sessionId);
|
|
23815
|
+
const usageMetrics = deriveSessionUsageMetrics(messages);
|
|
23816
|
+
return { messages, ...usageMetrics ? { usageMetrics } : {} };
|
|
23817
|
+
}
|
|
23818
|
+
async deleteHistory(sessionId) {
|
|
23819
|
+
return deleteHistory(sessionId);
|
|
23820
|
+
}
|
|
23821
|
+
async listSessions(cwd) {
|
|
23822
|
+
return listSessionsForCwd(cwd);
|
|
23823
|
+
}
|
|
23824
|
+
async stop(handle) {
|
|
23825
|
+
const sid = handle.sessionId;
|
|
23826
|
+
const session = this.running.get(sid);
|
|
23827
|
+
const shouldRescue = !!session && !session.completedNaturally && session.prompt.trim().length > 0;
|
|
23828
|
+
const rescuePrompt = session?.prompt ?? "";
|
|
23829
|
+
const rescueCwd = session?.cwd ?? handle.cwd;
|
|
23830
|
+
await this.registry.kill(sid, this.stopGraceMs);
|
|
23831
|
+
if (session) {
|
|
23832
|
+
session.closed = true;
|
|
23833
|
+
this.notify(session);
|
|
23834
|
+
this.running.delete(sid);
|
|
23835
|
+
}
|
|
23836
|
+
if (shouldRescue) {
|
|
23837
|
+
try {
|
|
23838
|
+
await appendInterruptedUserPrompt(sid, rescueCwd, rescuePrompt);
|
|
23839
|
+
} catch {}
|
|
23840
|
+
}
|
|
23841
|
+
}
|
|
23842
|
+
async start(args2) {
|
|
23843
|
+
const binaryPath = await this.binaryPathResolver();
|
|
23844
|
+
const cliPermissionMode = args2.opts?.permissionMode === "plan" ? "plan" : "bypassPermissions";
|
|
23845
|
+
const spawned = spawnClaudeProcess({
|
|
23846
|
+
binaryPath,
|
|
23847
|
+
cwd: args2.cwd,
|
|
23848
|
+
prompt: args2.prompt,
|
|
23849
|
+
model: args2.opts?.model,
|
|
23850
|
+
permissionMode: cliPermissionMode,
|
|
23851
|
+
env: args2.opts?.env,
|
|
23852
|
+
resumeSessionId: args2.resumeSessionId
|
|
23853
|
+
});
|
|
23854
|
+
let resolveHandle = () => {};
|
|
23855
|
+
let rejectHandle = () => {};
|
|
23856
|
+
const handlePromise = new Promise((res, rej) => {
|
|
23857
|
+
resolveHandle = res;
|
|
23858
|
+
rejectHandle = rej;
|
|
23859
|
+
});
|
|
23860
|
+
const queue = [];
|
|
23861
|
+
let session;
|
|
23862
|
+
let bound = false;
|
|
23863
|
+
const bind = (sessionId) => {
|
|
23864
|
+
if (bound)
|
|
23865
|
+
return;
|
|
23866
|
+
bound = true;
|
|
23867
|
+
session = {
|
|
23868
|
+
sessionId,
|
|
23869
|
+
cwd: args2.cwd,
|
|
23870
|
+
spawned,
|
|
23871
|
+
queue,
|
|
23872
|
+
waiters: [],
|
|
23873
|
+
closed: false,
|
|
23874
|
+
completedNaturally: false,
|
|
23875
|
+
prompt: args2.prompt,
|
|
23876
|
+
spawnedAtIso: new Date().toISOString()
|
|
23877
|
+
};
|
|
23878
|
+
this.running.set(sessionId, session);
|
|
23879
|
+
this.registry.register({
|
|
23880
|
+
sessionId,
|
|
23881
|
+
cwd: args2.cwd,
|
|
23882
|
+
proc: spawned.proc,
|
|
23883
|
+
startedAt: Date.now(),
|
|
23884
|
+
prompt: args2.prompt
|
|
23885
|
+
});
|
|
23886
|
+
resolveHandle({ sessionId, cwd: args2.cwd });
|
|
23887
|
+
};
|
|
23888
|
+
if (args2.resumeSessionId) {
|
|
23889
|
+
try {
|
|
23890
|
+
bind(args2.resumeSessionId);
|
|
23891
|
+
} catch (err) {
|
|
23892
|
+
try {
|
|
23893
|
+
spawned.proc.kill("SIGKILL");
|
|
23894
|
+
} catch {}
|
|
23895
|
+
rejectHandle(err);
|
|
23896
|
+
throw err;
|
|
23897
|
+
}
|
|
23898
|
+
}
|
|
23899
|
+
(async () => {
|
|
23900
|
+
const events = parseStreamJson(readLines(spawned.stdout), {
|
|
23901
|
+
onSessionId: (sid) => bind(sid)
|
|
23902
|
+
});
|
|
23903
|
+
try {
|
|
23904
|
+
for await (const ev of events) {
|
|
23905
|
+
const enriched = enrichUsageEvent(ev, session?.spawnedAtIso);
|
|
23906
|
+
queue.push(enriched);
|
|
23907
|
+
if (ev.type === "done" && session) {
|
|
23908
|
+
session.completedNaturally = true;
|
|
23909
|
+
}
|
|
23910
|
+
if (session)
|
|
23911
|
+
this.notify(session);
|
|
23912
|
+
}
|
|
23913
|
+
} catch (err) {
|
|
23914
|
+
const ev = {
|
|
23915
|
+
type: "error",
|
|
23916
|
+
message: `parser failure: ${err instanceof Error ? err.message : String(err)}`
|
|
23917
|
+
};
|
|
23918
|
+
queue.push(ev);
|
|
23919
|
+
if (session)
|
|
23920
|
+
this.notify(session);
|
|
23921
|
+
} finally {
|
|
23922
|
+
if (session) {
|
|
23923
|
+
session.closed = true;
|
|
23924
|
+
this.notify(session);
|
|
23925
|
+
this.registry.unregister(session.sessionId);
|
|
23926
|
+
}
|
|
23927
|
+
if (!bound) {
|
|
23928
|
+
rejectHandle(new Error("claude exited without emitting a session id"));
|
|
23929
|
+
}
|
|
23930
|
+
}
|
|
23931
|
+
})();
|
|
23932
|
+
drainStream(spawned.stderr);
|
|
23933
|
+
spawned.proc.once("error", (err) => {
|
|
23934
|
+
if (!bound)
|
|
23935
|
+
rejectHandle(err);
|
|
23936
|
+
});
|
|
23937
|
+
spawned.proc.once("exit", () => {
|
|
23938
|
+
if (!bound) {
|
|
23939
|
+
rejectHandle(new Error("claude exited before session id was captured"));
|
|
23940
|
+
}
|
|
23941
|
+
});
|
|
23942
|
+
return handlePromise;
|
|
23943
|
+
}
|
|
23944
|
+
notify(session) {
|
|
23945
|
+
const waiters = session.waiters;
|
|
23946
|
+
session.waiters = [];
|
|
23947
|
+
for (const w2 of waiters)
|
|
23948
|
+
w2();
|
|
23949
|
+
}
|
|
23950
|
+
}
|
|
23951
|
+
function drainStream(stream) {
|
|
23952
|
+
const s2 = stream;
|
|
23953
|
+
s2.on("data", () => {});
|
|
23954
|
+
s2.on("error", () => {});
|
|
23955
|
+
}
|
|
23956
|
+
function enrichUsageEvent(ev, startedAtIso) {
|
|
23957
|
+
if (ev.type !== "usage")
|
|
23958
|
+
return ev;
|
|
23959
|
+
return { type: "usage", ...withTotalSpeedForTurn(ev, startedAtIso, new Date().toISOString()) };
|
|
23960
|
+
}
|
|
23961
|
+
var init_claude_code_local = __esm(() => {
|
|
23962
|
+
init_binary();
|
|
23963
|
+
init_capabilities();
|
|
23964
|
+
init_history();
|
|
23965
|
+
init_sessions();
|
|
23966
|
+
init_spawn();
|
|
23967
|
+
});
|
|
23968
|
+
|
|
23969
|
+
// src/engine/codex-local/normalize.ts
|
|
23970
|
+
function normalizeCodexContent(raw) {
|
|
23971
|
+
if (typeof raw === "string") {
|
|
23972
|
+
return raw.length > 0 ? [{ type: "text", text: raw }] : [];
|
|
23973
|
+
}
|
|
23974
|
+
if (!Array.isArray(raw))
|
|
23975
|
+
return [];
|
|
23976
|
+
const blocks = [];
|
|
23977
|
+
for (const item of raw) {
|
|
23978
|
+
if (typeof item === "string") {
|
|
23979
|
+
if (item.length > 0)
|
|
23980
|
+
blocks.push({ type: "text", text: item });
|
|
23981
|
+
continue;
|
|
23982
|
+
}
|
|
23983
|
+
if (!isObject4(item))
|
|
23984
|
+
continue;
|
|
23985
|
+
const t2 = typeof item.type === "string" ? item.type : undefined;
|
|
23986
|
+
if (t2 === "input_text" || t2 === "output_text") {
|
|
23987
|
+
const text = typeof item.text === "string" ? item.text : "";
|
|
23988
|
+
if (text.length > 0)
|
|
23989
|
+
blocks.push({ type: "text", text });
|
|
23990
|
+
continue;
|
|
23991
|
+
}
|
|
23992
|
+
if (t2)
|
|
23993
|
+
blocks.push({ type: "text", text: `[codex: ${t2}]` });
|
|
23994
|
+
}
|
|
23995
|
+
return blocks;
|
|
23996
|
+
}
|
|
23997
|
+
function isObject4(v2) {
|
|
23998
|
+
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
23999
|
+
}
|
|
24000
|
+
|
|
24001
|
+
// src/engine/codex-local/history.ts
|
|
24002
|
+
import { readFile as readFile5, readdir as readdir3, unlink as unlink3 } from "fs/promises";
|
|
24003
|
+
import { homedir as homedir13 } from "os";
|
|
24004
|
+
import path11 from "path";
|
|
24005
|
+
async function listRolloutFiles(deps = defaultDeps6) {
|
|
24006
|
+
const root = deps.sessionsDir();
|
|
24007
|
+
const years = (await deps.readdir(root)).sort().reverse();
|
|
24008
|
+
const out = [];
|
|
24009
|
+
for (const y2 of years) {
|
|
24010
|
+
const yp = path11.join(root, y2);
|
|
24011
|
+
const months = (await deps.readdir(yp)).sort().reverse();
|
|
24012
|
+
for (const m2 of months) {
|
|
24013
|
+
const mp = path11.join(yp, m2);
|
|
24014
|
+
const days = (await deps.readdir(mp)).sort().reverse();
|
|
24015
|
+
for (const d2 of days) {
|
|
24016
|
+
const dp = path11.join(mp, d2);
|
|
24017
|
+
const files = (await deps.readdir(dp)).filter((f2) => f2.startsWith("rollout-") && f2.endsWith(".jsonl"));
|
|
24018
|
+
files.sort().reverse();
|
|
24019
|
+
for (const f2 of files)
|
|
24020
|
+
out.push(path11.join(dp, f2));
|
|
24021
|
+
}
|
|
24022
|
+
}
|
|
24023
|
+
}
|
|
24024
|
+
return out;
|
|
24025
|
+
}
|
|
24026
|
+
async function findRolloutFile(sessionId, deps = defaultDeps6) {
|
|
24027
|
+
const all = await listRolloutFiles(deps);
|
|
24028
|
+
for (const p2 of all) {
|
|
24029
|
+
if (path11.basename(p2).endsWith(`-${sessionId}.jsonl`))
|
|
24030
|
+
return p2;
|
|
24031
|
+
}
|
|
24032
|
+
return;
|
|
24033
|
+
}
|
|
24034
|
+
async function readHistoryWithMetrics(sessionId, deps = defaultDeps6) {
|
|
24035
|
+
const file = await findRolloutFile(sessionId, deps);
|
|
24036
|
+
if (!file)
|
|
24037
|
+
return { messages: [] };
|
|
24038
|
+
let raw;
|
|
24039
|
+
try {
|
|
24040
|
+
raw = await deps.readFile(file);
|
|
24041
|
+
} catch {
|
|
24042
|
+
return { messages: [] };
|
|
24043
|
+
}
|
|
24044
|
+
const messages = sortByTimestamp2(parseJsonl2(raw, sessionId));
|
|
24045
|
+
const usageMetrics = deriveCodexUsageMetrics(raw);
|
|
24046
|
+
return { messages, ...usageMetrics ? { usageMetrics } : {} };
|
|
24047
|
+
}
|
|
24048
|
+
async function deleteHistory2(sessionId, deps = defaultDeps6) {
|
|
24049
|
+
const file = await findRolloutFile(sessionId, deps);
|
|
24050
|
+
if (!file)
|
|
24051
|
+
return;
|
|
24052
|
+
try {
|
|
24053
|
+
await unlink3(file);
|
|
24054
|
+
} catch (err) {
|
|
24055
|
+
if (err.code === "ENOENT")
|
|
24056
|
+
return;
|
|
24057
|
+
throw err;
|
|
24058
|
+
}
|
|
24059
|
+
}
|
|
24060
|
+
function sortByTimestamp2(messages) {
|
|
24061
|
+
return messages.map((msg, idx) => ({ msg, idx })).sort((a2, b2) => {
|
|
24062
|
+
if (a2.msg.timestamp < b2.msg.timestamp)
|
|
24063
|
+
return -1;
|
|
24064
|
+
if (a2.msg.timestamp > b2.msg.timestamp)
|
|
24065
|
+
return 1;
|
|
24066
|
+
return a2.idx - b2.idx;
|
|
24067
|
+
}).map((entry) => entry.msg);
|
|
24068
|
+
}
|
|
24069
|
+
function parseJsonl2(raw, sessionId) {
|
|
24070
|
+
const out = [];
|
|
24071
|
+
for (const line of raw.split(`
|
|
24072
|
+
`)) {
|
|
24073
|
+
const trimmed = line.trim();
|
|
24074
|
+
if (!trimmed)
|
|
24075
|
+
continue;
|
|
24076
|
+
let parsed;
|
|
24077
|
+
try {
|
|
24078
|
+
parsed = JSON.parse(trimmed);
|
|
24079
|
+
} catch {
|
|
24080
|
+
continue;
|
|
24081
|
+
}
|
|
24082
|
+
if (!isObject5(parsed))
|
|
24083
|
+
continue;
|
|
24084
|
+
if (parsed.type !== "response_item")
|
|
24085
|
+
continue;
|
|
24086
|
+
const payload = isObject5(parsed.payload) ? parsed.payload : undefined;
|
|
24087
|
+
if (!payload)
|
|
24088
|
+
continue;
|
|
24089
|
+
if (payload.type !== "message")
|
|
24090
|
+
continue;
|
|
24091
|
+
const role = payload.role;
|
|
24092
|
+
if (role !== "user" && role !== "assistant" && role !== "system")
|
|
24093
|
+
continue;
|
|
24094
|
+
const blocks = normalizeCodexContent(payload.content);
|
|
24095
|
+
if (role === "user" && isEnvironmentContextEnvelope(blocks))
|
|
24096
|
+
continue;
|
|
24097
|
+
const ts = typeof parsed.timestamp === "string" ? parsed.timestamp : new Date().toISOString();
|
|
24098
|
+
out.push({ role, blocks, timestamp: ts, sessionId });
|
|
24099
|
+
}
|
|
24100
|
+
return out;
|
|
24101
|
+
}
|
|
24102
|
+
function deriveCodexUsageMetrics(raw) {
|
|
24103
|
+
let latestUsage;
|
|
24104
|
+
let latestUsageTimestampMs = null;
|
|
24105
|
+
let lastUserTimestampMs = null;
|
|
24106
|
+
let inputTokens = 0;
|
|
24107
|
+
let outputTokens = 0;
|
|
24108
|
+
const intervals = [];
|
|
24109
|
+
for (const line of raw.split(`
|
|
24110
|
+
`)) {
|
|
24111
|
+
const trimmed = line.trim();
|
|
24112
|
+
if (!trimmed)
|
|
24113
|
+
continue;
|
|
24114
|
+
let parsed;
|
|
24115
|
+
try {
|
|
24116
|
+
parsed = JSON.parse(trimmed);
|
|
24117
|
+
} catch {
|
|
24118
|
+
continue;
|
|
24119
|
+
}
|
|
24120
|
+
if (!isObject5(parsed))
|
|
24121
|
+
continue;
|
|
24122
|
+
const timestampMs = typeof parsed.timestamp === "string" ? parseTimestampMs2(parsed.timestamp) : null;
|
|
24123
|
+
if (parsed.type === "response_item") {
|
|
24124
|
+
const payload = isObject5(parsed.payload) ? parsed.payload : undefined;
|
|
24125
|
+
if (payload?.type === "message" && payload.role === "user" && timestampMs !== null) {
|
|
24126
|
+
const blocks = normalizeCodexContent(payload.content);
|
|
24127
|
+
if (!isEnvironmentContextEnvelope(blocks))
|
|
24128
|
+
lastUserTimestampMs = timestampMs;
|
|
24129
|
+
}
|
|
24130
|
+
continue;
|
|
24131
|
+
}
|
|
24132
|
+
if (parsed.type !== "turn.completed")
|
|
24133
|
+
continue;
|
|
24134
|
+
const usage = isObject5(parsed.usage) ? parsed.usage : undefined;
|
|
24135
|
+
if (!usage)
|
|
24136
|
+
continue;
|
|
24137
|
+
const snapshot = codexUsageToSnapshot(usage);
|
|
24138
|
+
if (!snapshot)
|
|
24139
|
+
continue;
|
|
24140
|
+
if (timestampMs !== null && (latestUsageTimestampMs === null || timestampMs > latestUsageTimestampMs)) {
|
|
24141
|
+
latestUsageTimestampMs = timestampMs;
|
|
24142
|
+
latestUsage = snapshot;
|
|
24143
|
+
} else if (latestUsage === undefined) {
|
|
24144
|
+
latestUsage = snapshot;
|
|
24145
|
+
}
|
|
24146
|
+
inputTokens += snapshot.input_tokens;
|
|
24147
|
+
outputTokens += snapshot.output_tokens;
|
|
24148
|
+
if (timestampMs !== null && lastUserTimestampMs !== null && timestampMs > lastUserTimestampMs) {
|
|
24149
|
+
intervals.push({ startMs: lastUserTimestampMs, endMs: timestampMs });
|
|
24150
|
+
}
|
|
24151
|
+
}
|
|
24152
|
+
if (!latestUsage)
|
|
24153
|
+
return;
|
|
24154
|
+
const durationMs2 = mergedDurationMs(intervals);
|
|
24155
|
+
if (durationMs2 <= 0)
|
|
24156
|
+
return latestUsage;
|
|
24157
|
+
return {
|
|
24158
|
+
...latestUsage,
|
|
24159
|
+
total_speed_tokens_per_second: (inputTokens + outputTokens) / (durationMs2 / 1000)
|
|
24160
|
+
};
|
|
24161
|
+
}
|
|
24162
|
+
function codexUsageToSnapshot(usage) {
|
|
24163
|
+
const input = numberOr(usage.input_tokens, 0);
|
|
24164
|
+
const output = numberOr(usage.output_tokens, 0) + numberOr(usage.reasoning_output_tokens, 0);
|
|
24165
|
+
const cacheRead = typeof usage.cached_input_tokens === "number" ? usage.cached_input_tokens : undefined;
|
|
24166
|
+
if (input <= 0 && output <= 0 && cacheRead === undefined)
|
|
24167
|
+
return;
|
|
24168
|
+
return {
|
|
24169
|
+
input_tokens: input,
|
|
24170
|
+
output_tokens: output,
|
|
24171
|
+
...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {}
|
|
24172
|
+
};
|
|
24173
|
+
}
|
|
24174
|
+
function parseTimestampMs2(value) {
|
|
24175
|
+
const ms = new Date(value).getTime();
|
|
24176
|
+
return Number.isFinite(ms) ? ms : null;
|
|
24177
|
+
}
|
|
24178
|
+
function mergedDurationMs(intervals) {
|
|
24179
|
+
if (intervals.length === 0)
|
|
24180
|
+
return 0;
|
|
24181
|
+
const sorted = [...intervals].sort((a2, b2) => a2.startMs - b2.startMs);
|
|
24182
|
+
let total = 0;
|
|
24183
|
+
let current = sorted[0];
|
|
24184
|
+
if (!current)
|
|
24185
|
+
return 0;
|
|
24186
|
+
for (let i2 = 1;i2 < sorted.length; i2++) {
|
|
24187
|
+
const next = sorted[i2];
|
|
24188
|
+
if (!next)
|
|
24189
|
+
continue;
|
|
24190
|
+
if (next.startMs <= current.endMs) {
|
|
24191
|
+
current = { startMs: current.startMs, endMs: Math.max(current.endMs, next.endMs) };
|
|
24192
|
+
} else {
|
|
24193
|
+
total += current.endMs - current.startMs;
|
|
24194
|
+
current = next;
|
|
24195
|
+
}
|
|
24196
|
+
}
|
|
24197
|
+
total += current.endMs - current.startMs;
|
|
24198
|
+
return total;
|
|
24199
|
+
}
|
|
24200
|
+
function numberOr(v2, fallback) {
|
|
24201
|
+
return typeof v2 === "number" && Number.isFinite(v2) ? v2 : fallback;
|
|
24202
|
+
}
|
|
24203
|
+
function isEnvironmentContextEnvelope(blocks) {
|
|
24204
|
+
if (blocks.length === 0)
|
|
24205
|
+
return false;
|
|
24206
|
+
for (const b2 of blocks) {
|
|
24207
|
+
if (b2.type !== "text")
|
|
24208
|
+
return false;
|
|
24209
|
+
const t2 = (b2.text ?? "").trim();
|
|
24210
|
+
if (!t2.startsWith("<environment_context>") || !t2.endsWith("</environment_context>"))
|
|
24211
|
+
return false;
|
|
24212
|
+
}
|
|
24213
|
+
return true;
|
|
24214
|
+
}
|
|
24215
|
+
function isObject5(v2) {
|
|
24216
|
+
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
24217
|
+
}
|
|
24218
|
+
var defaultDeps6;
|
|
24219
|
+
var init_history2 = __esm(() => {
|
|
24220
|
+
defaultDeps6 = {
|
|
24221
|
+
sessionsDir() {
|
|
24222
|
+
return path11.join(homedir13(), ".codex", "sessions");
|
|
24223
|
+
},
|
|
24224
|
+
async readdir(p2) {
|
|
24225
|
+
try {
|
|
24226
|
+
return await readdir3(p2);
|
|
24227
|
+
} catch {
|
|
24228
|
+
return [];
|
|
24229
|
+
}
|
|
24230
|
+
},
|
|
24231
|
+
async readFile(p2) {
|
|
24232
|
+
return await readFile5(p2, "utf8");
|
|
24233
|
+
}
|
|
24234
|
+
};
|
|
24235
|
+
});
|
|
24236
|
+
|
|
24237
|
+
// src/engine/codex-local/sessions.ts
|
|
24238
|
+
import { open as open3, stat as stat3 } from "fs/promises";
|
|
24239
|
+
async function listSessionsForCwd2(cwd, deps) {
|
|
24240
|
+
const files = await listRolloutFiles(deps);
|
|
24241
|
+
const out = [];
|
|
24242
|
+
for (const file of files) {
|
|
24243
|
+
const meta = await tryReadMeta(file);
|
|
24244
|
+
if (!meta)
|
|
24245
|
+
continue;
|
|
24246
|
+
if (meta.cwd !== cwd)
|
|
24247
|
+
continue;
|
|
24248
|
+
out.push({
|
|
24249
|
+
sessionId: meta.sessionId,
|
|
24250
|
+
mtimeMs: meta.mtimeMs,
|
|
24251
|
+
firstUserMessage: meta.firstUserMessage,
|
|
24252
|
+
messageCount: meta.messageCount
|
|
24253
|
+
});
|
|
24254
|
+
}
|
|
24255
|
+
out.sort((a2, b2) => b2.mtimeMs - a2.mtimeMs);
|
|
24256
|
+
return out;
|
|
24257
|
+
}
|
|
24258
|
+
async function tryReadMeta(file) {
|
|
24259
|
+
let st;
|
|
24260
|
+
try {
|
|
24261
|
+
st = await stat3(file);
|
|
24262
|
+
} catch {
|
|
24263
|
+
return null;
|
|
24264
|
+
}
|
|
24265
|
+
let sessionId;
|
|
24266
|
+
let cwd;
|
|
24267
|
+
let firstUser = null;
|
|
24268
|
+
let messageCount = 0;
|
|
24269
|
+
const handle = await open3(file, "r").catch(() => null);
|
|
24270
|
+
if (!handle)
|
|
24271
|
+
return null;
|
|
24272
|
+
try {
|
|
24273
|
+
let buf = "";
|
|
24274
|
+
let lineCount = 0;
|
|
24275
|
+
const processLine = (line) => {
|
|
24276
|
+
lineCount++;
|
|
24277
|
+
const parsed = safeParse(line);
|
|
24278
|
+
if (parsed) {
|
|
24279
|
+
if (parsed.type === "session_meta") {
|
|
24280
|
+
const payload = parsed.payload;
|
|
24281
|
+
if (isObject6(payload)) {
|
|
24282
|
+
if (typeof payload.id === "string")
|
|
24283
|
+
sessionId = payload.id;
|
|
24284
|
+
if (typeof payload.cwd === "string")
|
|
24285
|
+
cwd = payload.cwd;
|
|
24286
|
+
}
|
|
24287
|
+
} else if (parsed.type === "response_item" && isObject6(parsed.payload)) {
|
|
24288
|
+
const p2 = parsed.payload;
|
|
24289
|
+
if (p2.type === "message") {
|
|
24290
|
+
messageCount++;
|
|
24291
|
+
if (!firstUser && p2.role === "user") {
|
|
24292
|
+
firstUser = extractText(p2.content)?.slice(0, PREVIEW_CHAR_CAP) ?? null;
|
|
24293
|
+
}
|
|
24294
|
+
}
|
|
24295
|
+
}
|
|
24296
|
+
}
|
|
24297
|
+
return lineCount >= PREVIEW_HEAD_LINES;
|
|
24298
|
+
};
|
|
24299
|
+
const reader = handle.createReadStream({ encoding: "utf8" });
|
|
24300
|
+
outer:
|
|
24301
|
+
for await (const chunk of reader) {
|
|
24302
|
+
buf += chunk;
|
|
24303
|
+
let nl = buf.indexOf(`
|
|
24304
|
+
`);
|
|
24305
|
+
while (nl !== -1) {
|
|
24306
|
+
const line = buf.slice(0, nl);
|
|
24307
|
+
buf = buf.slice(nl + 1);
|
|
24308
|
+
nl = buf.indexOf(`
|
|
24309
|
+
`);
|
|
24310
|
+
if (processLine(line))
|
|
24311
|
+
break outer;
|
|
24312
|
+
}
|
|
24313
|
+
}
|
|
24314
|
+
if (lineCount < PREVIEW_HEAD_LINES && buf.trim())
|
|
24315
|
+
processLine(buf);
|
|
24316
|
+
} finally {
|
|
24317
|
+
await handle.close().catch(() => {});
|
|
24318
|
+
}
|
|
24319
|
+
if (!sessionId || !cwd)
|
|
24320
|
+
return null;
|
|
24321
|
+
return {
|
|
24322
|
+
sessionId,
|
|
24323
|
+
cwd,
|
|
24324
|
+
mtimeMs: st.mtimeMs,
|
|
24325
|
+
firstUserMessage: firstUser,
|
|
24326
|
+
messageCount
|
|
24327
|
+
};
|
|
24328
|
+
}
|
|
24329
|
+
function safeParse(line) {
|
|
24330
|
+
const t2 = line.trim();
|
|
24331
|
+
if (!t2)
|
|
24332
|
+
return null;
|
|
24333
|
+
try {
|
|
24334
|
+
const v2 = JSON.parse(t2);
|
|
24335
|
+
return isObject6(v2) ? v2 : null;
|
|
24336
|
+
} catch {
|
|
24337
|
+
return null;
|
|
24338
|
+
}
|
|
24339
|
+
}
|
|
24340
|
+
function extractText(content) {
|
|
24341
|
+
if (typeof content === "string")
|
|
24342
|
+
return content;
|
|
24343
|
+
if (!Array.isArray(content))
|
|
24344
|
+
return null;
|
|
24345
|
+
for (const item of content) {
|
|
24346
|
+
if (typeof item === "string" && item.length > 0)
|
|
24347
|
+
return item;
|
|
24348
|
+
if (isObject6(item)) {
|
|
24349
|
+
const t2 = item.type;
|
|
24350
|
+
if ((t2 === "input_text" || t2 === "output_text" || t2 === "text") && typeof item.text === "string") {
|
|
24351
|
+
return item.text;
|
|
24352
|
+
}
|
|
24353
|
+
}
|
|
24354
|
+
}
|
|
24355
|
+
return null;
|
|
24356
|
+
}
|
|
24357
|
+
function isObject6(v2) {
|
|
24358
|
+
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
24359
|
+
}
|
|
24360
|
+
var PREVIEW_HEAD_LINES = 40, PREVIEW_CHAR_CAP = 200;
|
|
24361
|
+
var init_sessions2 = __esm(() => {
|
|
24362
|
+
init_history2();
|
|
24363
|
+
});
|
|
24364
|
+
|
|
24365
|
+
// src/engine/codex-local/spawn.ts
|
|
24366
|
+
import { spawn as spawn7 } from "child_process";
|
|
24367
|
+
function spawnCodexProcess(opts) {
|
|
24368
|
+
const args2 = buildArgs2(opts);
|
|
24369
|
+
const proc = spawn7(opts.binaryPath, args2, {
|
|
24370
|
+
cwd: opts.cwd,
|
|
24371
|
+
env: { ...process.env, ...opts.env ?? {} },
|
|
24372
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
24373
|
+
});
|
|
24374
|
+
try {
|
|
24375
|
+
proc.stdin.end();
|
|
24376
|
+
} catch {}
|
|
24377
|
+
return {
|
|
24378
|
+
proc,
|
|
24379
|
+
stdout: proc.stdout,
|
|
24380
|
+
stderr: proc.stderr,
|
|
24381
|
+
args: args2
|
|
24382
|
+
};
|
|
24383
|
+
}
|
|
24384
|
+
function buildArgs2(opts) {
|
|
24385
|
+
const isResume = !!opts.resumeSessionId;
|
|
24386
|
+
const args2 = ["exec"];
|
|
24387
|
+
if (isResume) {
|
|
24388
|
+
args2.push("resume", opts.resumeSessionId);
|
|
24389
|
+
}
|
|
24390
|
+
args2.push("--json", "--skip-git-repo-check");
|
|
24391
|
+
if (opts.model) {
|
|
24392
|
+
args2.push("-m", opts.model);
|
|
24393
|
+
}
|
|
24394
|
+
if (!isResume) {
|
|
24395
|
+
args2.push("-C", opts.cwd);
|
|
24396
|
+
if (opts.permissionMode === "plan")
|
|
24397
|
+
args2.push("-s", "read-only");
|
|
24398
|
+
}
|
|
24399
|
+
if (opts.permissionMode !== "plan") {
|
|
24400
|
+
args2.push("--dangerously-bypass-approvals-and-sandbox");
|
|
22772
24401
|
}
|
|
22773
24402
|
if (opts.extraArgs && opts.extraArgs.length > 0) {
|
|
22774
24403
|
args2.push(...opts.extraArgs);
|
|
22775
24404
|
}
|
|
24405
|
+
args2.push(opts.prompt);
|
|
22776
24406
|
return args2;
|
|
22777
24407
|
}
|
|
22778
|
-
var
|
|
24408
|
+
var init_spawn2 = () => {};
|
|
22779
24409
|
|
|
22780
|
-
// src/engine/
|
|
22781
|
-
async function*
|
|
24410
|
+
// src/engine/codex-local/stream.ts
|
|
24411
|
+
async function* parseStreamJson2(lines, opts = {}) {
|
|
22782
24412
|
let sessionIdEmitted = false;
|
|
22783
24413
|
const toolNameById = new Map;
|
|
24414
|
+
const startedByItemId = new Set;
|
|
22784
24415
|
for await (const rawLine of lines) {
|
|
22785
24416
|
const line = rawLine.trim();
|
|
22786
24417
|
if (!line)
|
|
@@ -22789,89 +24420,82 @@ async function* parseStreamJson(lines, opts = {}) {
|
|
|
22789
24420
|
try {
|
|
22790
24421
|
msg = JSON.parse(line);
|
|
22791
24422
|
} catch (err) {
|
|
22792
|
-
yield { type: "error", message: `stream-json parse failed: ${
|
|
24423
|
+
yield { type: "error", message: `codex stream-json parse failed: ${stringifyErr2(err)}` };
|
|
22793
24424
|
continue;
|
|
22794
24425
|
}
|
|
22795
|
-
if (!
|
|
24426
|
+
if (!isObject7(msg))
|
|
22796
24427
|
continue;
|
|
22797
24428
|
const type = typeof msg.type === "string" ? msg.type : undefined;
|
|
22798
24429
|
if (!type)
|
|
22799
24430
|
continue;
|
|
22800
|
-
if (
|
|
22801
|
-
|
|
22802
|
-
|
|
22803
|
-
|
|
22804
|
-
|
|
22805
|
-
const sid = typeof msg.session_id === "string" ? msg.session_id : undefined;
|
|
22806
|
-
if (sid) {
|
|
22807
|
-
sessionIdEmitted = true;
|
|
22808
|
-
opts.onSessionId?.(sid);
|
|
22809
|
-
}
|
|
24431
|
+
if (type === "thread.started") {
|
|
24432
|
+
const sid = typeof msg.thread_id === "string" ? msg.thread_id : undefined;
|
|
24433
|
+
if (sid && !sessionIdEmitted) {
|
|
24434
|
+
sessionIdEmitted = true;
|
|
24435
|
+
opts.onSessionId?.(sid);
|
|
22810
24436
|
}
|
|
22811
24437
|
continue;
|
|
22812
24438
|
}
|
|
22813
|
-
if (type === "
|
|
22814
|
-
const content = extractContentBlocks(msg);
|
|
22815
|
-
for (const block of content) {
|
|
22816
|
-
if (!isObject3(block))
|
|
22817
|
-
continue;
|
|
22818
|
-
const blockType = typeof block.type === "string" ? block.type : undefined;
|
|
22819
|
-
if (blockType === "text") {
|
|
22820
|
-
const text = typeof block.text === "string" ? block.text : "";
|
|
22821
|
-
if (text)
|
|
22822
|
-
yield { type: "assistant.delta", text };
|
|
22823
|
-
} else if (blockType === "tool_use") {
|
|
22824
|
-
const name = typeof block.name === "string" ? block.name : "tool";
|
|
22825
|
-
const id = typeof block.id === "string" ? block.id : undefined;
|
|
22826
|
-
if (id)
|
|
22827
|
-
toolNameById.set(id, name);
|
|
22828
|
-
const input = "input" in block ? block.input : undefined;
|
|
22829
|
-
yield { type: "tool.start", name, input };
|
|
22830
|
-
}
|
|
22831
|
-
}
|
|
24439
|
+
if (type === "turn.started")
|
|
22832
24440
|
continue;
|
|
22833
|
-
|
|
22834
|
-
|
|
22835
|
-
|
|
22836
|
-
|
|
22837
|
-
|
|
24441
|
+
if (type === "item.started" || type === "item.completed") {
|
|
24442
|
+
const item = isObject7(msg.item) ? msg.item : undefined;
|
|
24443
|
+
if (!item)
|
|
24444
|
+
continue;
|
|
24445
|
+
const itemId = typeof item.id === "string" ? item.id : undefined;
|
|
24446
|
+
const itemType = typeof item.type === "string" ? item.type : "tool";
|
|
24447
|
+
if (itemType === "agent_message") {
|
|
24448
|
+
if (type !== "item.completed")
|
|
22838
24449
|
continue;
|
|
22839
|
-
const
|
|
22840
|
-
if (
|
|
22841
|
-
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22845
|
-
|
|
24450
|
+
const text = typeof item.text === "string" ? item.text : "";
|
|
24451
|
+
if (text)
|
|
24452
|
+
yield { type: "assistant.delta", text };
|
|
24453
|
+
continue;
|
|
24454
|
+
}
|
|
24455
|
+
if (itemId) {
|
|
24456
|
+
toolNameById.set(itemId, itemType);
|
|
24457
|
+
}
|
|
24458
|
+
if (type === "item.started") {
|
|
24459
|
+
if (itemId)
|
|
24460
|
+
startedByItemId.add(itemId);
|
|
24461
|
+
const input = stripIdAndType(item);
|
|
24462
|
+
yield { type: "tool.start", name: itemType, input };
|
|
24463
|
+
continue;
|
|
22846
24464
|
}
|
|
24465
|
+
if (itemId && !startedByItemId.has(itemId)) {
|
|
24466
|
+
const input = stripIdAndType(item);
|
|
24467
|
+
yield { type: "tool.start", name: itemType, input };
|
|
24468
|
+
}
|
|
24469
|
+
if (itemId)
|
|
24470
|
+
startedByItemId.delete(itemId);
|
|
24471
|
+
const output = stripIdAndType(item);
|
|
24472
|
+
yield { type: "tool.result", name: itemType, output };
|
|
22847
24473
|
continue;
|
|
22848
24474
|
}
|
|
22849
|
-
if (type === "
|
|
22850
|
-
const usage =
|
|
24475
|
+
if (type === "turn.completed") {
|
|
24476
|
+
const usage = isObject7(msg.usage) ? msg.usage : undefined;
|
|
22851
24477
|
if (usage) {
|
|
22852
|
-
const inTok =
|
|
22853
|
-
const outTok =
|
|
22854
|
-
const cacheRead = typeof usage.
|
|
22855
|
-
const cacheCreate = typeof usage.cache_creation_input_tokens === "number" ? usage.cache_creation_input_tokens : undefined;
|
|
24478
|
+
const inTok = numberOr2(usage.input_tokens, 0);
|
|
24479
|
+
const outTok = numberOr2(usage.output_tokens, 0) + numberOr2(usage.reasoning_output_tokens, 0);
|
|
24480
|
+
const cacheRead = typeof usage.cached_input_tokens === "number" ? usage.cached_input_tokens : undefined;
|
|
22856
24481
|
yield {
|
|
22857
24482
|
type: "usage",
|
|
22858
24483
|
input_tokens: inTok,
|
|
22859
24484
|
output_tokens: outTok,
|
|
22860
|
-
...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {}
|
|
22861
|
-
...cacheCreate !== undefined ? { cache_creation_input_tokens: cacheCreate } : {}
|
|
24485
|
+
...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {}
|
|
22862
24486
|
};
|
|
22863
24487
|
}
|
|
22864
|
-
|
|
22865
|
-
|
|
22866
|
-
|
|
22867
|
-
|
|
22868
|
-
|
|
22869
|
-
}
|
|
24488
|
+
yield { type: "done" };
|
|
24489
|
+
return;
|
|
24490
|
+
}
|
|
24491
|
+
if (type === "error") {
|
|
24492
|
+
const message = typeof msg.message === "string" ? msg.message : "codex emitted an error";
|
|
24493
|
+
yield { type: "error", message };
|
|
22870
24494
|
return;
|
|
22871
24495
|
}
|
|
22872
24496
|
}
|
|
22873
24497
|
}
|
|
22874
|
-
async function*
|
|
24498
|
+
async function* readLines2(stream) {
|
|
22875
24499
|
let buf = "";
|
|
22876
24500
|
for await (const chunk of stream) {
|
|
22877
24501
|
const text = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
@@ -22888,18 +24512,17 @@ async function* readLines(stream) {
|
|
|
22888
24512
|
if (buf.length > 0)
|
|
22889
24513
|
yield buf;
|
|
22890
24514
|
}
|
|
22891
|
-
function
|
|
24515
|
+
function isObject7(v2) {
|
|
22892
24516
|
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
22893
24517
|
}
|
|
22894
|
-
function
|
|
22895
|
-
|
|
22896
|
-
return msg.content;
|
|
22897
|
-
const inner = msg.message;
|
|
22898
|
-
if (isObject3(inner) && Array.isArray(inner.content))
|
|
22899
|
-
return inner.content;
|
|
22900
|
-
return [];
|
|
24518
|
+
function numberOr2(v2, fallback) {
|
|
24519
|
+
return typeof v2 === "number" && Number.isFinite(v2) ? v2 : fallback;
|
|
22901
24520
|
}
|
|
22902
|
-
function
|
|
24521
|
+
function stripIdAndType(item) {
|
|
24522
|
+
const { id: _id, type: _type, ...rest } = item;
|
|
24523
|
+
return rest;
|
|
24524
|
+
}
|
|
24525
|
+
function stringifyErr2(err) {
|
|
22903
24526
|
if (err instanceof Error)
|
|
22904
24527
|
return err.message;
|
|
22905
24528
|
try {
|
|
@@ -22909,14 +24532,16 @@ function stringifyErr(err) {
|
|
|
22909
24532
|
}
|
|
22910
24533
|
}
|
|
22911
24534
|
|
|
22912
|
-
// src/engine/
|
|
22913
|
-
class
|
|
24535
|
+
// src/engine/codex-local/index.ts
|
|
24536
|
+
class CodexLocal {
|
|
24537
|
+
identity = codexIdentity;
|
|
24538
|
+
capabilities = codexCapabilities;
|
|
22914
24539
|
registry = new SessionRegistry;
|
|
22915
24540
|
running = new Map;
|
|
22916
24541
|
binaryPathResolver;
|
|
22917
24542
|
stopGraceMs;
|
|
22918
24543
|
constructor(opts = {}) {
|
|
22919
|
-
this.binaryPathResolver = opts.binaryPathResolver ??
|
|
24544
|
+
this.binaryPathResolver = opts.binaryPathResolver ?? findCodexBinary;
|
|
22920
24545
|
this.stopGraceMs = opts.stopGraceMs ?? 5000;
|
|
22921
24546
|
}
|
|
22922
24547
|
async spawn(cwd, prompt, opts) {
|
|
@@ -22953,41 +24578,32 @@ class ClaudeCodeLocal {
|
|
|
22953
24578
|
};
|
|
22954
24579
|
}
|
|
22955
24580
|
async readHistory(sessionId) {
|
|
22956
|
-
return
|
|
24581
|
+
return readHistoryWithMetrics(sessionId);
|
|
22957
24582
|
}
|
|
22958
24583
|
async deleteHistory(sessionId) {
|
|
22959
|
-
return
|
|
24584
|
+
return deleteHistory2(sessionId);
|
|
22960
24585
|
}
|
|
22961
24586
|
async listSessions(cwd) {
|
|
22962
|
-
return
|
|
24587
|
+
return listSessionsForCwd2(cwd);
|
|
22963
24588
|
}
|
|
22964
24589
|
async stop(handle) {
|
|
22965
24590
|
const sid = handle.sessionId;
|
|
22966
|
-
const session = this.running.get(sid);
|
|
22967
|
-
const shouldRescue = !!session && !session.completedNaturally && session.prompt.trim().length > 0;
|
|
22968
|
-
const rescuePrompt = session?.prompt ?? "";
|
|
22969
|
-
const rescueCwd = session?.cwd ?? handle.cwd;
|
|
22970
24591
|
await this.registry.kill(sid, this.stopGraceMs);
|
|
24592
|
+
const session = this.running.get(sid);
|
|
22971
24593
|
if (session) {
|
|
22972
24594
|
session.closed = true;
|
|
22973
24595
|
this.notify(session);
|
|
22974
24596
|
this.running.delete(sid);
|
|
22975
24597
|
}
|
|
22976
|
-
if (shouldRescue) {
|
|
22977
|
-
try {
|
|
22978
|
-
await appendInterruptedUserPrompt(sid, rescueCwd, rescuePrompt);
|
|
22979
|
-
} catch {}
|
|
22980
|
-
}
|
|
22981
24598
|
}
|
|
22982
24599
|
async start(args2) {
|
|
22983
24600
|
const binaryPath = await this.binaryPathResolver();
|
|
22984
|
-
const
|
|
22985
|
-
const spawned = spawnClaudeProcess({
|
|
24601
|
+
const spawned = spawnCodexProcess({
|
|
22986
24602
|
binaryPath,
|
|
22987
24603
|
cwd: args2.cwd,
|
|
22988
24604
|
prompt: args2.prompt,
|
|
22989
24605
|
model: args2.opts?.model,
|
|
22990
|
-
permissionMode:
|
|
24606
|
+
permissionMode: args2.opts?.permissionMode,
|
|
22991
24607
|
env: args2.opts?.env,
|
|
22992
24608
|
resumeSessionId: args2.resumeSessionId
|
|
22993
24609
|
});
|
|
@@ -23011,8 +24627,7 @@ class ClaudeCodeLocal {
|
|
|
23011
24627
|
queue,
|
|
23012
24628
|
waiters: [],
|
|
23013
24629
|
closed: false,
|
|
23014
|
-
|
|
23015
|
-
prompt: args2.prompt
|
|
24630
|
+
spawnedAtIso: new Date().toISOString()
|
|
23016
24631
|
};
|
|
23017
24632
|
this.running.set(sessionId, session);
|
|
23018
24633
|
this.registry.register({
|
|
@@ -23036,22 +24651,20 @@ class ClaudeCodeLocal {
|
|
|
23036
24651
|
}
|
|
23037
24652
|
}
|
|
23038
24653
|
(async () => {
|
|
23039
|
-
const events =
|
|
24654
|
+
const events = parseStreamJson2(readLines2(spawned.stdout), {
|
|
23040
24655
|
onSessionId: (sid) => bind(sid)
|
|
23041
24656
|
});
|
|
23042
24657
|
try {
|
|
23043
24658
|
for await (const ev of events) {
|
|
23044
|
-
|
|
23045
|
-
|
|
23046
|
-
session.completedNaturally = true;
|
|
23047
|
-
}
|
|
24659
|
+
const enriched = enrichUsageEvent2(ev, session?.spawnedAtIso);
|
|
24660
|
+
queue.push(enriched);
|
|
23048
24661
|
if (session)
|
|
23049
24662
|
this.notify(session);
|
|
23050
24663
|
}
|
|
23051
24664
|
} catch (err) {
|
|
23052
24665
|
const ev = {
|
|
23053
24666
|
type: "error",
|
|
23054
|
-
message: `parser failure: ${err instanceof Error ? err.message : String(err)}`
|
|
24667
|
+
message: `codex parser failure: ${err instanceof Error ? err.message : String(err)}`
|
|
23055
24668
|
};
|
|
23056
24669
|
queue.push(ev);
|
|
23057
24670
|
if (session)
|
|
@@ -23061,20 +24674,21 @@ class ClaudeCodeLocal {
|
|
|
23061
24674
|
session.closed = true;
|
|
23062
24675
|
this.notify(session);
|
|
23063
24676
|
this.registry.unregister(session.sessionId);
|
|
24677
|
+
this.running.delete(session.sessionId);
|
|
23064
24678
|
}
|
|
23065
24679
|
if (!bound) {
|
|
23066
|
-
rejectHandle(new Error("
|
|
24680
|
+
rejectHandle(new Error("codex exited without emitting a session id"));
|
|
23067
24681
|
}
|
|
23068
24682
|
}
|
|
23069
24683
|
})();
|
|
23070
|
-
|
|
24684
|
+
drainStream2(spawned.stderr);
|
|
23071
24685
|
spawned.proc.once("error", (err) => {
|
|
23072
24686
|
if (!bound)
|
|
23073
24687
|
rejectHandle(err);
|
|
23074
24688
|
});
|
|
23075
24689
|
spawned.proc.once("exit", () => {
|
|
23076
24690
|
if (!bound) {
|
|
23077
|
-
rejectHandle(new Error("
|
|
24691
|
+
rejectHandle(new Error("codex exited before session id was captured"));
|
|
23078
24692
|
}
|
|
23079
24693
|
});
|
|
23080
24694
|
return handlePromise;
|
|
@@ -23086,16 +24700,22 @@ class ClaudeCodeLocal {
|
|
|
23086
24700
|
w2();
|
|
23087
24701
|
}
|
|
23088
24702
|
}
|
|
23089
|
-
function
|
|
24703
|
+
function drainStream2(stream) {
|
|
23090
24704
|
const s2 = stream;
|
|
23091
24705
|
s2.on("data", () => {});
|
|
23092
24706
|
s2.on("error", () => {});
|
|
23093
24707
|
}
|
|
23094
|
-
|
|
23095
|
-
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
24708
|
+
function enrichUsageEvent2(ev, startedAtIso) {
|
|
24709
|
+
if (ev.type !== "usage")
|
|
24710
|
+
return ev;
|
|
24711
|
+
return { type: "usage", ...withTotalSpeedForTurn(ev, startedAtIso, new Date().toISOString()) };
|
|
24712
|
+
}
|
|
24713
|
+
var init_codex_local = __esm(() => {
|
|
24714
|
+
init_binary2();
|
|
24715
|
+
init_capabilities2();
|
|
24716
|
+
init_history2();
|
|
24717
|
+
init_sessions2();
|
|
24718
|
+
init_spawn2();
|
|
23099
24719
|
});
|
|
23100
24720
|
|
|
23101
24721
|
// test/behavior/fake-engine.ts
|
|
@@ -23103,8 +24723,17 @@ var exports_fake_engine = {};
|
|
|
23103
24723
|
__export(exports_fake_engine, {
|
|
23104
24724
|
FakeAIEngine: () => FakeAIEngine
|
|
23105
24725
|
});
|
|
24726
|
+
function firstUserText(blocks) {
|
|
24727
|
+
for (const b2 of blocks) {
|
|
24728
|
+
if (b2.type === "text")
|
|
24729
|
+
return b2.text;
|
|
24730
|
+
}
|
|
24731
|
+
return null;
|
|
24732
|
+
}
|
|
23106
24733
|
|
|
23107
24734
|
class FakeAIEngine {
|
|
24735
|
+
identity = claudeIdentity;
|
|
24736
|
+
capabilities = claudeCapabilities;
|
|
23108
24737
|
nextId = 1;
|
|
23109
24738
|
queues = new Map;
|
|
23110
24739
|
historyBySession = new Map;
|
|
@@ -23162,7 +24791,9 @@ class FakeAIEngine {
|
|
|
23162
24791
|
};
|
|
23163
24792
|
}
|
|
23164
24793
|
async readHistory(sessionId) {
|
|
23165
|
-
|
|
24794
|
+
const messages = this.historyBySession.get(sessionId) ?? [];
|
|
24795
|
+
const usageMetrics = deriveSessionUsageMetrics(messages);
|
|
24796
|
+
return { messages, ...usageMetrics ? { usageMetrics } : {} };
|
|
23166
24797
|
}
|
|
23167
24798
|
async deleteHistory(sessionId) {
|
|
23168
24799
|
this.historyBySession.delete(sessionId);
|
|
@@ -23171,7 +24802,7 @@ class FakeAIEngine {
|
|
|
23171
24802
|
const out = [];
|
|
23172
24803
|
for (const [sessionId, msgs] of this.historyBySession) {
|
|
23173
24804
|
const firstUser = msgs.find((m2) => m2.role === "user");
|
|
23174
|
-
const preview = firstUser
|
|
24805
|
+
const preview = firstUser ? firstUserText(firstUser.blocks) : null;
|
|
23175
24806
|
out.push({
|
|
23176
24807
|
sessionId,
|
|
23177
24808
|
mtimeMs: 0,
|
|
@@ -23214,6 +24845,10 @@ class FakeAIEngine {
|
|
|
23214
24845
|
w2();
|
|
23215
24846
|
}
|
|
23216
24847
|
}
|
|
24848
|
+
var init_fake_engine = __esm(() => {
|
|
24849
|
+
init_capabilities();
|
|
24850
|
+
init_capabilities();
|
|
24851
|
+
});
|
|
23217
24852
|
|
|
23218
24853
|
// src/engine/dev-fake.ts
|
|
23219
24854
|
var exports_dev_fake = {};
|
|
@@ -23222,6 +24857,8 @@ __export(exports_dev_fake, {
|
|
|
23222
24857
|
});
|
|
23223
24858
|
|
|
23224
24859
|
class DevAIEngine {
|
|
24860
|
+
identity = claudeIdentity;
|
|
24861
|
+
capabilities = claudeCapabilities;
|
|
23225
24862
|
inner = new FakeAIEngine;
|
|
23226
24863
|
tickMs;
|
|
23227
24864
|
constructor(opts = {}) {
|
|
@@ -23354,12 +24991,15 @@ function buildPlanModeReply(prompt) {
|
|
|
23354
24991
|
{ type: "done" }
|
|
23355
24992
|
];
|
|
23356
24993
|
}
|
|
23357
|
-
var init_dev_fake = () => {
|
|
24994
|
+
var init_dev_fake = __esm(() => {
|
|
24995
|
+
init_fake_engine();
|
|
24996
|
+
init_capabilities();
|
|
24997
|
+
});
|
|
23358
24998
|
|
|
23359
24999
|
// src/tui/engine-bootstrap.ts
|
|
23360
25000
|
async function buildEngine() {
|
|
23361
25001
|
if (process.env.KOBE_TEST_ENGINE === "fake") {
|
|
23362
|
-
const { FakeAIEngine: FakeAIEngine2 } = await Promise.resolve().then(() => exports_fake_engine);
|
|
25002
|
+
const { FakeAIEngine: FakeAIEngine2 } = await Promise.resolve().then(() => (init_fake_engine(), exports_fake_engine));
|
|
23363
25003
|
const fake = new FakeAIEngine2;
|
|
23364
25004
|
await mountFakeEngineServer(fake);
|
|
23365
25005
|
return fake;
|
|
@@ -23370,6 +25010,16 @@ async function buildEngine() {
|
|
|
23370
25010
|
}
|
|
23371
25011
|
return new ClaudeCodeLocal;
|
|
23372
25012
|
}
|
|
25013
|
+
async function buildEngines() {
|
|
25014
|
+
if (process.env.KOBE_TEST_ENGINE) {
|
|
25015
|
+
const engine3 = await buildEngine();
|
|
25016
|
+
return { [engine3.capabilities.vendorId]: engine3 };
|
|
25017
|
+
}
|
|
25018
|
+
return {
|
|
25019
|
+
claude: new ClaudeCodeLocal,
|
|
25020
|
+
codex: new CodexLocal
|
|
25021
|
+
};
|
|
25022
|
+
}
|
|
23373
25023
|
async function mountFakeEngineServer(fake) {
|
|
23374
25024
|
const portStr = process.env.KOBE_TEST_FAKE_PORT;
|
|
23375
25025
|
if (!portStr)
|
|
@@ -23457,6 +25107,7 @@ async function mountFakeEngineServer(fake) {
|
|
|
23457
25107
|
}
|
|
23458
25108
|
var init_engine_bootstrap = __esm(() => {
|
|
23459
25109
|
init_claude_code_local();
|
|
25110
|
+
init_codex_local();
|
|
23460
25111
|
});
|
|
23461
25112
|
|
|
23462
25113
|
// src/tui/lib/format-plan-usage.ts
|
|
@@ -23682,17 +25333,17 @@ function validateRepoPath(repo) {
|
|
|
23682
25333
|
const trimmed = repo.trim();
|
|
23683
25334
|
if (!trimmed)
|
|
23684
25335
|
return "repo path is required";
|
|
23685
|
-
let
|
|
25336
|
+
let stat4;
|
|
23686
25337
|
try {
|
|
23687
|
-
|
|
25338
|
+
stat4 = fs5.statSync(trimmed);
|
|
23688
25339
|
} catch {
|
|
23689
25340
|
return `path does not exist: ${trimmed}`;
|
|
23690
25341
|
}
|
|
23691
|
-
if (!
|
|
25342
|
+
if (!stat4.isDirectory())
|
|
23692
25343
|
return `not a directory: ${trimmed}`;
|
|
23693
25344
|
try {
|
|
23694
|
-
const { spawnSync:
|
|
23695
|
-
const out =
|
|
25345
|
+
const { spawnSync: spawnSync9 } = __require("child_process");
|
|
25346
|
+
const out = spawnSync9("git", ["rev-parse", "--git-dir"], {
|
|
23696
25347
|
cwd: trimmed,
|
|
23697
25348
|
encoding: "utf-8",
|
|
23698
25349
|
timeout: 2000,
|
|
@@ -23709,8 +25360,8 @@ function getCurrentBranch(repo) {
|
|
|
23709
25360
|
if (!repo)
|
|
23710
25361
|
return null;
|
|
23711
25362
|
try {
|
|
23712
|
-
const { spawnSync:
|
|
23713
|
-
const out =
|
|
25363
|
+
const { spawnSync: spawnSync9 } = __require("child_process");
|
|
25364
|
+
const out = spawnSync9("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
23714
25365
|
cwd: repo,
|
|
23715
25366
|
encoding: "utf-8",
|
|
23716
25367
|
timeout: 2000,
|
|
@@ -23730,8 +25381,8 @@ function listLocalBranches(repo) {
|
|
|
23730
25381
|
if (!repo)
|
|
23731
25382
|
return [];
|
|
23732
25383
|
try {
|
|
23733
|
-
const { spawnSync:
|
|
23734
|
-
const out =
|
|
25384
|
+
const { spawnSync: spawnSync9 } = __require("child_process");
|
|
25385
|
+
const out = spawnSync9("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
|
|
23735
25386
|
cwd: repo,
|
|
23736
25387
|
encoding: "utf-8",
|
|
23737
25388
|
timeout: 2000
|
|
@@ -24850,8 +26501,8 @@ var init_resume_dialog = __esm(() => {
|
|
|
24850
26501
|
});
|
|
24851
26502
|
|
|
24852
26503
|
// src/tui/panes/chat/composer/clipboard-image.ts
|
|
24853
|
-
import { spawnSync as
|
|
24854
|
-
import { statSync as
|
|
26504
|
+
import { spawnSync as spawnSync9 } from "child_process";
|
|
26505
|
+
import { statSync as statSync6 } from "fs";
|
|
24855
26506
|
function clipboardImageSupported() {
|
|
24856
26507
|
return process.platform === "darwin";
|
|
24857
26508
|
}
|
|
@@ -24875,14 +26526,14 @@ function readClipboardImageMacOS(destPath) {
|
|
|
24875
26526
|
"end try"
|
|
24876
26527
|
].join(`
|
|
24877
26528
|
`);
|
|
24878
|
-
const result =
|
|
26529
|
+
const result = spawnSync9("osascript", ["-e", script], {
|
|
24879
26530
|
timeout: 5000,
|
|
24880
26531
|
stdio: ["ignore", "ignore", "ignore"]
|
|
24881
26532
|
});
|
|
24882
26533
|
if (result.status !== 0)
|
|
24883
26534
|
return null;
|
|
24884
26535
|
try {
|
|
24885
|
-
const st =
|
|
26536
|
+
const st = statSync6(destPath);
|
|
24886
26537
|
if (st.size === 0)
|
|
24887
26538
|
return null;
|
|
24888
26539
|
} catch {
|
|
@@ -24916,16 +26567,16 @@ function getHistory(key) {
|
|
|
24916
26567
|
return ring.slice();
|
|
24917
26568
|
}
|
|
24918
26569
|
var HISTORY_LIMIT = 200, STORE;
|
|
24919
|
-
var
|
|
26570
|
+
var init_history3 = __esm(() => {
|
|
24920
26571
|
STORE = new Map;
|
|
24921
26572
|
});
|
|
24922
26573
|
|
|
24923
26574
|
// src/tui/panes/chat/composer/image-paste.ts
|
|
24924
26575
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
24925
26576
|
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
24926
|
-
import { join as
|
|
26577
|
+
import { join as join14 } from "path";
|
|
24927
26578
|
function pastedImagesDir() {
|
|
24928
|
-
return
|
|
26579
|
+
return join14(kobeStateDir(), "pasted-images");
|
|
24929
26580
|
}
|
|
24930
26581
|
|
|
24931
26582
|
class ImagePasteRegistry {
|
|
@@ -25005,7 +26656,7 @@ function mimeTypeToExt(mimeType) {
|
|
|
25005
26656
|
return ".png";
|
|
25006
26657
|
}
|
|
25007
26658
|
function mintPath(ext) {
|
|
25008
|
-
return
|
|
26659
|
+
return join14(pastedImagesDir(), `${randomUUID2()}${ext}`);
|
|
25009
26660
|
}
|
|
25010
26661
|
var IMAGE_TOKEN_RE;
|
|
25011
26662
|
var init_image_paste = __esm(() => {
|
|
@@ -25026,10 +26677,10 @@ var init_keybindings2 = __esm(() => {
|
|
|
25026
26677
|
|
|
25027
26678
|
// src/tui/panes/chat/composer/mention.ts
|
|
25028
26679
|
async function getWorktreeFiles(worktreePath) {
|
|
25029
|
-
const
|
|
26680
|
+
const cached3 = fileListCache.get(worktreePath);
|
|
25030
26681
|
const now = Date.now();
|
|
25031
|
-
if (
|
|
25032
|
-
return
|
|
26682
|
+
if (cached3 && now - cached3.ts < CACHE_TTL_MS)
|
|
26683
|
+
return cached3.files;
|
|
25033
26684
|
try {
|
|
25034
26685
|
const files = await listFiles(worktreePath);
|
|
25035
26686
|
fileListCache.set(worktreePath, { files, ts: now });
|
|
@@ -25063,25 +26714,25 @@ function findMentionContext(text, cursor) {
|
|
|
25063
26714
|
}
|
|
25064
26715
|
return { atPos, query: text.slice(atPos + 1, cursor) };
|
|
25065
26716
|
}
|
|
25066
|
-
function formatDisplayPath(
|
|
25067
|
-
if (
|
|
25068
|
-
return
|
|
25069
|
-
return
|
|
26717
|
+
function formatDisplayPath(path12) {
|
|
26718
|
+
if (path12.startsWith("packages/"))
|
|
26719
|
+
return path12.slice("packages/".length);
|
|
26720
|
+
return path12;
|
|
25070
26721
|
}
|
|
25071
26722
|
function filterMentionMatches(files, query, limit) {
|
|
25072
26723
|
if (files.length === 0 || limit <= 0)
|
|
25073
26724
|
return [];
|
|
25074
26725
|
const q2 = query.toLowerCase();
|
|
25075
26726
|
if (q2.length === 0) {
|
|
25076
|
-
return files.slice(0, limit).map((
|
|
25077
|
-
path:
|
|
25078
|
-
displayPath: formatDisplayPath(
|
|
26727
|
+
return files.slice(0, limit).map((path12) => ({
|
|
26728
|
+
path: path12,
|
|
26729
|
+
displayPath: formatDisplayPath(path12),
|
|
25079
26730
|
score: 0
|
|
25080
26731
|
}));
|
|
25081
26732
|
}
|
|
25082
26733
|
const matches = [];
|
|
25083
|
-
for (const
|
|
25084
|
-
const lower =
|
|
26734
|
+
for (const path12 of files) {
|
|
26735
|
+
const lower = path12.toLowerCase();
|
|
25085
26736
|
const slash = lower.lastIndexOf("/");
|
|
25086
26737
|
const filename = slash >= 0 ? lower.slice(slash + 1) : lower;
|
|
25087
26738
|
let score = 0;
|
|
@@ -25095,8 +26746,8 @@ function filterMentionMatches(files, query, limit) {
|
|
|
25095
26746
|
score = 40;
|
|
25096
26747
|
else
|
|
25097
26748
|
continue;
|
|
25098
|
-
score -=
|
|
25099
|
-
matches.push({ path:
|
|
26749
|
+
score -= path12.length * 0.5;
|
|
26750
|
+
matches.push({ path: path12, displayPath: formatDisplayPath(path12), score });
|
|
25100
26751
|
}
|
|
25101
26752
|
matches.sort((a2, b2) => b2.score - a2.score);
|
|
25102
26753
|
return matches.slice(0, limit);
|
|
@@ -25114,7 +26765,7 @@ function resolvePlaceholder(opts) {
|
|
|
25114
26765
|
return opts.noTaskMessage ?? "(no task \u2014 press n to create)";
|
|
25115
26766
|
if (opts.isStreaming)
|
|
25116
26767
|
return "(streaming \u2014 enter to queue, ctrl+enter to steer)";
|
|
25117
|
-
return "Ask Claude\u2026";
|
|
26768
|
+
return opts.inputPlaceholder ?? "Ask Claude\u2026";
|
|
25118
26769
|
}
|
|
25119
26770
|
function Composer(props) {
|
|
25120
26771
|
const {
|
|
@@ -25243,7 +26894,7 @@ function Composer(props) {
|
|
|
25243
26894
|
total
|
|
25244
26895
|
};
|
|
25245
26896
|
});
|
|
25246
|
-
function insertMentionSelection(
|
|
26897
|
+
function insertMentionSelection(path12) {
|
|
25247
26898
|
const ref = textareaRef;
|
|
25248
26899
|
const ctx4 = mentionContext();
|
|
25249
26900
|
if (!ref || !ctx4)
|
|
@@ -25251,7 +26902,7 @@ function Composer(props) {
|
|
|
25251
26902
|
const cursor = ref.cursorOffset;
|
|
25252
26903
|
ref.setSelection(ctx4.atPos, cursor);
|
|
25253
26904
|
ref.deleteSelection();
|
|
25254
|
-
const inserted = `@${
|
|
26905
|
+
const inserted = `@${path12} `;
|
|
25255
26906
|
ref.insertText(inserted);
|
|
25256
26907
|
setMentionDismissedAt(null);
|
|
25257
26908
|
setMentionCursor(0);
|
|
@@ -25961,7 +27612,8 @@ function Composer(props) {
|
|
|
25961
27612
|
var _v$0 = resolvePlaceholder({
|
|
25962
27613
|
isStreaming: props.isStreaming,
|
|
25963
27614
|
hasTask: props.hasTask,
|
|
25964
|
-
noTaskMessage: props.noTaskMessage
|
|
27615
|
+
noTaskMessage: props.noTaskMessage,
|
|
27616
|
+
inputPlaceholder: props.inputPlaceholder?.()
|
|
25965
27617
|
}), _v$1 = theme.textMuted, _v$10 = theme.text, _v$11 = theme.backgroundElement, _v$12 = theme.backgroundElement;
|
|
25966
27618
|
_v$0 !== _p$.e && (_p$.e = setProp(_el$24, "placeholder", _v$0, _p$.e));
|
|
25967
27619
|
_v$1 !== _p$.t && (_p$.t = setProp(_el$24, "placeholderColor", _v$1, _p$.t));
|
|
@@ -26082,7 +27734,7 @@ var init_Composer = __esm(() => {
|
|
|
26082
27734
|
init_focus();
|
|
26083
27735
|
init_theme();
|
|
26084
27736
|
init_clipboard_image();
|
|
26085
|
-
|
|
27737
|
+
init_history3();
|
|
26086
27738
|
init_image_paste();
|
|
26087
27739
|
init_keybindings2();
|
|
26088
27740
|
init_mention();
|
|
@@ -26611,7 +28263,7 @@ function readWriteInput(input) {
|
|
|
26611
28263
|
};
|
|
26612
28264
|
}
|
|
26613
28265
|
function makeHeader(filePath, verb, adds, removes) {
|
|
26614
|
-
const
|
|
28266
|
+
const path12 = filePath || "(unknown file)";
|
|
26615
28267
|
const parts = [];
|
|
26616
28268
|
if (adds > 0) {
|
|
26617
28269
|
parts.push(`Added ${adds} ${adds === 1 ? "line" : "lines"}`);
|
|
@@ -26621,11 +28273,11 @@ function makeHeader(filePath, verb, adds, removes) {
|
|
|
26621
28273
|
parts.push(`${word} ${removes} ${removes === 1 ? "line" : "lines"}`);
|
|
26622
28274
|
}
|
|
26623
28275
|
if (parts.length === 0)
|
|
26624
|
-
return `${verb} ${
|
|
26625
|
-
return `${verb} ${
|
|
28276
|
+
return `${verb} ${path12}`;
|
|
28277
|
+
return `${verb} ${path12} \xB7 ${parts.join(", ")}`;
|
|
26626
28278
|
}
|
|
26627
28279
|
function makeMultiEditHeader(filePath, count, adds, removes) {
|
|
26628
|
-
const
|
|
28280
|
+
const path12 = filePath || "(unknown file)";
|
|
26629
28281
|
const segments = [];
|
|
26630
28282
|
if (count > 0) {
|
|
26631
28283
|
segments.push(`${count} ${count === 1 ? "edit" : "edits"}`);
|
|
@@ -26641,8 +28293,8 @@ function makeMultiEditHeader(filePath, count, adds, removes) {
|
|
|
26641
28293
|
if (lineParts.length > 0)
|
|
26642
28294
|
segments.push(lineParts.join(", "));
|
|
26643
28295
|
if (segments.length === 0)
|
|
26644
|
-
return `Edited ${
|
|
26645
|
-
return `Edited ${
|
|
28296
|
+
return `Edited ${path12}`;
|
|
28297
|
+
return `Edited ${path12} \xB7 ${segments.join(" \xB7 ")}`;
|
|
26646
28298
|
}
|
|
26647
28299
|
var COLLAPSED_LINE_CAP = 10;
|
|
26648
28300
|
|
|
@@ -26705,6 +28357,39 @@ function summarizeGlob(input, output, done) {
|
|
|
26705
28357
|
return `${head} \xB7 ${lines.length} ${lines.length === 1 ? "file" : "files"}`;
|
|
26706
28358
|
}
|
|
26707
28359
|
|
|
28360
|
+
// src/tui/panes/chat/tool-registry.ts
|
|
28361
|
+
function lookupToolMeta(name, vendor = "claude") {
|
|
28362
|
+
return registries[vendor][name] ?? DEFAULT_META;
|
|
28363
|
+
}
|
|
28364
|
+
function classifyTool(name, vendor = "claude") {
|
|
28365
|
+
return lookupToolMeta(name, vendor).bucket;
|
|
28366
|
+
}
|
|
28367
|
+
var DEFAULT_META, claudeRegistry, registries;
|
|
28368
|
+
var init_tool_registry = __esm(() => {
|
|
28369
|
+
DEFAULT_META = {
|
|
28370
|
+
bucket: "other",
|
|
28371
|
+
banner: "default",
|
|
28372
|
+
body: "default"
|
|
28373
|
+
};
|
|
28374
|
+
claudeRegistry = {
|
|
28375
|
+
Edit: { bucket: "other", banner: "default", body: "edit-diff" },
|
|
28376
|
+
Write: { bucket: "other", banner: "default", body: "edit-diff" },
|
|
28377
|
+
MultiEdit: { bucket: "other", banner: "default", body: "multi-edit-diff" },
|
|
28378
|
+
Bash: { bucket: "bash", banner: "bash", body: "bash-output" },
|
|
28379
|
+
BashOutput: { bucket: "bash", banner: "default", body: "default" },
|
|
28380
|
+
KillShell: { bucket: "bash", banner: "default", body: "default" },
|
|
28381
|
+
Read: { bucket: "read", banner: "read-grep-glob", body: "read-grep-glob" },
|
|
28382
|
+
NotebookRead: { bucket: "read", banner: "default", body: "default" },
|
|
28383
|
+
Grep: { bucket: "search", banner: "read-grep-glob", body: "read-grep-glob" },
|
|
28384
|
+
Glob: { bucket: "list", banner: "read-grep-glob", body: "read-grep-glob" },
|
|
28385
|
+
LS: { bucket: "list", banner: "default", body: "default" }
|
|
28386
|
+
};
|
|
28387
|
+
registries = {
|
|
28388
|
+
claude: claudeRegistry,
|
|
28389
|
+
codex: {}
|
|
28390
|
+
};
|
|
28391
|
+
});
|
|
28392
|
+
|
|
26708
28393
|
// src/tui/panes/chat/MessageList.tsx
|
|
26709
28394
|
import { TextAttributes as TextAttributes23 } from "@opentui/core";
|
|
26710
28395
|
function previewToolInput(input) {
|
|
@@ -26936,12 +28621,13 @@ function ToolRow(props) {
|
|
|
26936
28621
|
const r2 = () => props.row;
|
|
26937
28622
|
const prefixGlyph = () => r2().done ? BLACK_CIRCLE : "\u273B";
|
|
26938
28623
|
const prefixColor = () => r2().done ? theme.success : theme.warning;
|
|
26939
|
-
const
|
|
26940
|
-
const
|
|
26941
|
-
const
|
|
26942
|
-
const
|
|
26943
|
-
const
|
|
26944
|
-
const
|
|
28624
|
+
const meta = () => lookupToolMeta(r2().name);
|
|
28625
|
+
const isDiffTool = () => meta().body === "edit-diff";
|
|
28626
|
+
const isMultiEdit = () => meta().body === "multi-edit-diff";
|
|
28627
|
+
const isBash = () => meta().banner === "bash";
|
|
28628
|
+
const isReadGrepGlob = () => meta().banner === "read-grep-glob";
|
|
28629
|
+
const usesCustomBanner = () => meta().banner !== "default" || meta().body !== "default";
|
|
28630
|
+
const usesCustomBody = () => meta().body === "edit-diff" || meta().body === "multi-edit-diff" || meta().body === "bash-output";
|
|
26945
28631
|
const diff = () => {
|
|
26946
28632
|
if (r2().name === "Edit")
|
|
26947
28633
|
return formatEditDiff(r2().input);
|
|
@@ -28241,34 +29927,6 @@ function MessageList(props) {
|
|
|
28241
29927
|
});
|
|
28242
29928
|
}
|
|
28243
29929
|
}), null);
|
|
28244
|
-
insert(_el$153, createComponent2(Show, {
|
|
28245
|
-
get when() {
|
|
28246
|
-
return props.error;
|
|
28247
|
-
},
|
|
28248
|
-
get children() {
|
|
28249
|
-
var _el$157 = createElement("box"), _el$158 = createElement("text"), _el$160 = createElement("text"), _el$161 = createTextNode(`error: `);
|
|
28250
|
-
insertNode(_el$157, _el$158);
|
|
28251
|
-
insertNode(_el$157, _el$160);
|
|
28252
|
-
setProp(_el$157, "paddingTop", 1);
|
|
28253
|
-
setProp(_el$157, "flexDirection", "row");
|
|
28254
|
-
setProp(_el$157, "gap", 1);
|
|
28255
|
-
insertNode(_el$158, createTextNode(`\u203B`));
|
|
28256
|
-
insertNode(_el$160, _el$161);
|
|
28257
|
-
insert(_el$160, () => props.error, null);
|
|
28258
|
-
effect((_p$) => {
|
|
28259
|
-
var _v$71 = theme.error, _v$72 = TextAttributes23.BOLD, _v$73 = theme.error;
|
|
28260
|
-
_v$71 !== _p$.e && (_p$.e = setProp(_el$158, "fg", _v$71, _p$.e));
|
|
28261
|
-
_v$72 !== _p$.t && (_p$.t = setProp(_el$158, "attributes", _v$72, _p$.t));
|
|
28262
|
-
_v$73 !== _p$.a && (_p$.a = setProp(_el$160, "fg", _v$73, _p$.a));
|
|
28263
|
-
return _p$;
|
|
28264
|
-
}, {
|
|
28265
|
-
e: undefined,
|
|
28266
|
-
t: undefined,
|
|
28267
|
-
a: undefined
|
|
28268
|
-
});
|
|
28269
|
-
return _el$157;
|
|
28270
|
-
}
|
|
28271
|
-
}), null);
|
|
28272
29930
|
return _el$153;
|
|
28273
29931
|
})();
|
|
28274
29932
|
}
|
|
@@ -28339,17 +29997,6 @@ function groupRenderItems(messages, expandedFoldStartIndex = null) {
|
|
|
28339
29997
|
}
|
|
28340
29998
|
return items;
|
|
28341
29999
|
}
|
|
28342
|
-
function classifyTool(name) {
|
|
28343
|
-
if (name === "Grep")
|
|
28344
|
-
return "search";
|
|
28345
|
-
if (name === "Read" || name === "NotebookRead")
|
|
28346
|
-
return "read";
|
|
28347
|
-
if (name === "Glob" || name === "LS")
|
|
28348
|
-
return "list";
|
|
28349
|
-
if (name === "Bash" || name === "BashOutput" || name === "KillShell")
|
|
28350
|
-
return "bash";
|
|
28351
|
-
return "other";
|
|
28352
|
-
}
|
|
28353
30000
|
function summarizeToolRun(c2, inFlight = false) {
|
|
28354
30001
|
const verbs = inFlight ? {
|
|
28355
30002
|
search: ["Searching", "searching"],
|
|
@@ -28400,29 +30047,29 @@ function ToolFoldRow(props) {
|
|
|
28400
30047
|
const glyph = () => props.inFlight ? "\u273B" : props.expanded ? "\u25BC" : "\u25B6";
|
|
28401
30048
|
const fg = () => props.inFlight ? theme.warning : theme.textMuted;
|
|
28402
30049
|
return (() => {
|
|
28403
|
-
var _el$
|
|
28404
|
-
insertNode(_el$
|
|
28405
|
-
insertNode(_el$
|
|
28406
|
-
setProp(_el$
|
|
28407
|
-
setProp(_el$
|
|
28408
|
-
setProp(_el$
|
|
28409
|
-
setProp(_el$
|
|
28410
|
-
insert(_el$
|
|
28411
|
-
insertNode(_el$
|
|
28412
|
-
setProp(_el$
|
|
28413
|
-
insert(_el$
|
|
30050
|
+
var _el$157 = createElement("box"), _el$158 = createElement("text"), _el$159 = createElement("box"), _el$160 = createElement("text");
|
|
30051
|
+
insertNode(_el$157, _el$158);
|
|
30052
|
+
insertNode(_el$157, _el$159);
|
|
30053
|
+
setProp(_el$157, "paddingTop", 1);
|
|
30054
|
+
setProp(_el$157, "flexDirection", "row");
|
|
30055
|
+
setProp(_el$157, "gap", 1);
|
|
30056
|
+
setProp(_el$157, "onMouseUp", () => props.onToggle());
|
|
30057
|
+
insert(_el$158, glyph);
|
|
30058
|
+
insertNode(_el$159, _el$160);
|
|
30059
|
+
setProp(_el$159, "flexGrow", 1);
|
|
30060
|
+
insert(_el$160, () => props.summary);
|
|
28414
30061
|
effect((_p$) => {
|
|
28415
|
-
var _v$
|
|
28416
|
-
_v$
|
|
28417
|
-
_v$
|
|
28418
|
-
_v$
|
|
30062
|
+
var _v$71 = fg(), _v$72 = TextAttributes23.DIM, _v$73 = theme.textMuted;
|
|
30063
|
+
_v$71 !== _p$.e && (_p$.e = setProp(_el$158, "fg", _v$71, _p$.e));
|
|
30064
|
+
_v$72 !== _p$.t && (_p$.t = setProp(_el$158, "attributes", _v$72, _p$.t));
|
|
30065
|
+
_v$73 !== _p$.a && (_p$.a = setProp(_el$160, "fg", _v$73, _p$.a));
|
|
28419
30066
|
return _p$;
|
|
28420
30067
|
}, {
|
|
28421
30068
|
e: undefined,
|
|
28422
30069
|
t: undefined,
|
|
28423
30070
|
a: undefined
|
|
28424
30071
|
});
|
|
28425
|
-
return _el$
|
|
30072
|
+
return _el$157;
|
|
28426
30073
|
})();
|
|
28427
30074
|
}
|
|
28428
30075
|
var BLACK_CIRCLE, OTHER_SENTINEL = "__kobe_other__", TOOL_FOLD_THRESHOLD = 3;
|
|
@@ -28441,41 +30088,10 @@ var init_MessageList = __esm(() => {
|
|
|
28441
30088
|
init_keymap();
|
|
28442
30089
|
init_Markdown();
|
|
28443
30090
|
init_image_paste();
|
|
30091
|
+
init_tool_registry();
|
|
28444
30092
|
BLACK_CIRCLE = process.platform === "darwin" ? "\u23FA" : "\u25CF";
|
|
28445
30093
|
});
|
|
28446
30094
|
|
|
28447
|
-
// src/tui/panes/chat/composer/claude-settings.ts
|
|
28448
|
-
var init_claude_settings2 = __esm(() => {
|
|
28449
|
-
init_claude_settings();
|
|
28450
|
-
});
|
|
28451
|
-
|
|
28452
|
-
// src/tui/panes/chat/composer/models.ts
|
|
28453
|
-
function modelLabelFor(id) {
|
|
28454
|
-
const resolved = id ?? resolveDefaultModelId();
|
|
28455
|
-
const canonical = ALIAS_TO_ID[resolved] ?? resolved;
|
|
28456
|
-
const match = MODEL_CHOICES.find((m2) => m2.id === canonical);
|
|
28457
|
-
return match?.label ?? resolved;
|
|
28458
|
-
}
|
|
28459
|
-
var DEFAULT_MODEL_ID = "claude-opus-4-7[1m]", MODEL_CHOICES, ALIAS_TO_ID;
|
|
28460
|
-
var init_models = __esm(() => {
|
|
28461
|
-
init_claude_settings2();
|
|
28462
|
-
init_claude_settings2();
|
|
28463
|
-
MODEL_CHOICES = [
|
|
28464
|
-
{ id: "claude-opus-4-7[1m]", label: "Opus 4.7 1M", hint: "long context, default" },
|
|
28465
|
-
{ id: "claude-opus-4-7", label: "Opus 4.7", hint: "most capable, slowest" },
|
|
28466
|
-
{ id: "claude-sonnet-4-6[1m]", label: "sonnet 4.6 (1M)", hint: "long context" },
|
|
28467
|
-
{ id: "claude-sonnet-4-6", label: "sonnet 4.6" },
|
|
28468
|
-
{ id: "claude-haiku-4-5-20251001", label: "haiku 4.5", hint: "fastest, cheapest" }
|
|
28469
|
-
];
|
|
28470
|
-
ALIAS_TO_ID = {
|
|
28471
|
-
opus: "claude-opus-4-7",
|
|
28472
|
-
"opus[1m]": "claude-opus-4-7[1m]",
|
|
28473
|
-
sonnet: "claude-sonnet-4-6",
|
|
28474
|
-
"sonnet[1m]": "claude-sonnet-4-6[1m]",
|
|
28475
|
-
haiku: "claude-haiku-4-5-20251001"
|
|
28476
|
-
};
|
|
28477
|
-
});
|
|
28478
|
-
|
|
28479
30095
|
// src/tui/panes/chat/composer/ModelPicker.tsx
|
|
28480
30096
|
import { TextAttributes as TextAttributes24 } from "@opentui/core";
|
|
28481
30097
|
function ModelPicker(props) {
|
|
@@ -28483,11 +30099,12 @@ function ModelPicker(props) {
|
|
|
28483
30099
|
const {
|
|
28484
30100
|
theme
|
|
28485
30101
|
} = useTheme();
|
|
28486
|
-
const
|
|
28487
|
-
const
|
|
30102
|
+
const choices = createMemo(() => allModels());
|
|
30103
|
+
const seed = props.current ?? defaultCapabilities.defaultModelId();
|
|
30104
|
+
const initial = choices().findIndex((m2) => m2.id === seed);
|
|
28488
30105
|
const [cursor, setCursor] = createSignal(initial >= 0 ? initial : 0);
|
|
28489
30106
|
function commit() {
|
|
28490
|
-
const choice =
|
|
30107
|
+
const choice = choices()[cursor()];
|
|
28491
30108
|
if (!choice)
|
|
28492
30109
|
return;
|
|
28493
30110
|
props.onPick(choice.id);
|
|
@@ -28496,16 +30113,36 @@ function ModelPicker(props) {
|
|
|
28496
30113
|
useBindings(() => ({
|
|
28497
30114
|
bindings: [{
|
|
28498
30115
|
key: "up",
|
|
28499
|
-
cmd: () =>
|
|
30116
|
+
cmd: () => {
|
|
30117
|
+
const n2 = choices().length;
|
|
30118
|
+
if (n2 === 0)
|
|
30119
|
+
return;
|
|
30120
|
+
setCursor((c2) => (c2 - 1 + n2) % n2);
|
|
30121
|
+
}
|
|
28500
30122
|
}, {
|
|
28501
30123
|
key: "down",
|
|
28502
|
-
cmd: () =>
|
|
30124
|
+
cmd: () => {
|
|
30125
|
+
const n2 = choices().length;
|
|
30126
|
+
if (n2 === 0)
|
|
30127
|
+
return;
|
|
30128
|
+
setCursor((c2) => (c2 + 1) % n2);
|
|
30129
|
+
}
|
|
28503
30130
|
}, {
|
|
28504
30131
|
key: "k",
|
|
28505
|
-
cmd: () =>
|
|
30132
|
+
cmd: () => {
|
|
30133
|
+
const n2 = choices().length;
|
|
30134
|
+
if (n2 === 0)
|
|
30135
|
+
return;
|
|
30136
|
+
setCursor((c2) => (c2 - 1 + n2) % n2);
|
|
30137
|
+
}
|
|
28506
30138
|
}, {
|
|
28507
30139
|
key: "j",
|
|
28508
|
-
cmd: () =>
|
|
30140
|
+
cmd: () => {
|
|
30141
|
+
const n2 = choices().length;
|
|
30142
|
+
if (n2 === 0)
|
|
30143
|
+
return;
|
|
30144
|
+
setCursor((c2) => (c2 + 1) % n2);
|
|
30145
|
+
}
|
|
28509
30146
|
}, {
|
|
28510
30147
|
key: "return",
|
|
28511
30148
|
cmd: commit
|
|
@@ -28529,7 +30166,9 @@ function ModelPicker(props) {
|
|
|
28529
30166
|
setProp(_el$7, "flexDirection", "column");
|
|
28530
30167
|
setProp(_el$7, "paddingBottom", 1);
|
|
28531
30168
|
insert(_el$7, createComponent2(For, {
|
|
28532
|
-
each
|
|
30169
|
+
get each() {
|
|
30170
|
+
return choices();
|
|
30171
|
+
},
|
|
28533
30172
|
children: (choice, i2) => {
|
|
28534
30173
|
const active = () => i2() === cursor();
|
|
28535
30174
|
return (() => {
|
|
@@ -28599,11 +30238,11 @@ var init_ModelPicker = __esm(() => {
|
|
|
28599
30238
|
init_solid();
|
|
28600
30239
|
init_solid();
|
|
28601
30240
|
init_solid();
|
|
30241
|
+
init_registry2();
|
|
28602
30242
|
init_dev();
|
|
28603
30243
|
init_theme();
|
|
28604
30244
|
init_keymap();
|
|
28605
30245
|
init_dialog();
|
|
28606
|
-
init_models();
|
|
28607
30246
|
ModelPicker.show = (dialog, current) => {
|
|
28608
30247
|
return new Promise((resolve4) => {
|
|
28609
30248
|
dialog.replace(() => createComponent2(ModelPicker, {
|
|
@@ -28616,30 +30255,30 @@ var init_ModelPicker = __esm(() => {
|
|
|
28616
30255
|
});
|
|
28617
30256
|
|
|
28618
30257
|
// src/tui/panes/chat/composer/user-slashes.ts
|
|
28619
|
-
import { readFile as
|
|
28620
|
-
import { homedir as
|
|
28621
|
-
import { join as
|
|
30258
|
+
import { readFile as readFile6, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
30259
|
+
import { homedir as homedir15 } from "os";
|
|
30260
|
+
import { join as join15 } from "path";
|
|
28622
30261
|
function resolveHome() {
|
|
28623
|
-
return process.env.HOME ??
|
|
30262
|
+
return process.env.HOME ?? homedir15();
|
|
28624
30263
|
}
|
|
28625
|
-
async function safeReaddir(
|
|
30264
|
+
async function safeReaddir(path12) {
|
|
28626
30265
|
try {
|
|
28627
|
-
return await
|
|
30266
|
+
return await readdir4(path12);
|
|
28628
30267
|
} catch {
|
|
28629
30268
|
return [];
|
|
28630
30269
|
}
|
|
28631
30270
|
}
|
|
28632
|
-
async function isFile(
|
|
30271
|
+
async function isFile(path12) {
|
|
28633
30272
|
try {
|
|
28634
|
-
const s2 = await
|
|
30273
|
+
const s2 = await stat4(path12);
|
|
28635
30274
|
return s2.isFile();
|
|
28636
30275
|
} catch {
|
|
28637
30276
|
return false;
|
|
28638
30277
|
}
|
|
28639
30278
|
}
|
|
28640
|
-
async function isDir(
|
|
30279
|
+
async function isDir(path12) {
|
|
28641
30280
|
try {
|
|
28642
|
-
const s2 = await
|
|
30281
|
+
const s2 = await stat4(path12);
|
|
28643
30282
|
return s2.isDirectory();
|
|
28644
30283
|
} catch {
|
|
28645
30284
|
return false;
|
|
@@ -28710,9 +30349,9 @@ function extractDescription(content) {
|
|
|
28710
30349
|
}
|
|
28711
30350
|
return null;
|
|
28712
30351
|
}
|
|
28713
|
-
async function tryReadDescription(
|
|
30352
|
+
async function tryReadDescription(path12) {
|
|
28714
30353
|
try {
|
|
28715
|
-
const content = await
|
|
30354
|
+
const content = await readFile6(path12, "utf-8");
|
|
28716
30355
|
return extractDescription(content);
|
|
28717
30356
|
} catch {
|
|
28718
30357
|
return null;
|
|
@@ -28724,7 +30363,7 @@ async function scanCommandsDir(dir) {
|
|
|
28724
30363
|
for (const entry of entries) {
|
|
28725
30364
|
if (!entry.endsWith(".md"))
|
|
28726
30365
|
continue;
|
|
28727
|
-
const full =
|
|
30366
|
+
const full = join15(dir, entry);
|
|
28728
30367
|
if (!await isFile(full))
|
|
28729
30368
|
continue;
|
|
28730
30369
|
const name = entry.slice(0, -3);
|
|
@@ -28737,10 +30376,10 @@ async function scanSkillsDir(dir) {
|
|
|
28737
30376
|
const entries = await safeReaddir(dir);
|
|
28738
30377
|
const out = [];
|
|
28739
30378
|
for (const entry of entries) {
|
|
28740
|
-
const sub =
|
|
30379
|
+
const sub = join15(dir, entry);
|
|
28741
30380
|
if (!await isDir(sub))
|
|
28742
30381
|
continue;
|
|
28743
|
-
const skillMd =
|
|
30382
|
+
const skillMd = join15(sub, "SKILL.md");
|
|
28744
30383
|
if (!await isFile(skillMd))
|
|
28745
30384
|
continue;
|
|
28746
30385
|
const description = await tryReadDescription(skillMd) ?? "";
|
|
@@ -28750,8 +30389,8 @@ async function scanSkillsDir(dir) {
|
|
|
28750
30389
|
}
|
|
28751
30390
|
async function scanBasePath(claudeDir) {
|
|
28752
30391
|
const [skills, commands] = await Promise.all([
|
|
28753
|
-
scanSkillsDir(
|
|
28754
|
-
scanCommandsDir(
|
|
30392
|
+
scanSkillsDir(join15(claudeDir, "skills")),
|
|
30393
|
+
scanCommandsDir(join15(claudeDir, "commands"))
|
|
28755
30394
|
]);
|
|
28756
30395
|
const map = new Map;
|
|
28757
30396
|
for (const e2 of skills)
|
|
@@ -28761,8 +30400,8 @@ async function scanBasePath(claudeDir) {
|
|
|
28761
30400
|
return [...map.values()];
|
|
28762
30401
|
}
|
|
28763
30402
|
async function loadUserSlashes(worktreePath) {
|
|
28764
|
-
const projectScan = worktreePath ? scanBasePath(
|
|
28765
|
-
const globalScan = scanBasePath(
|
|
30403
|
+
const projectScan = worktreePath ? scanBasePath(join15(worktreePath, ".claude")) : Promise.resolve([]);
|
|
30404
|
+
const globalScan = scanBasePath(join15(resolveHome(), ".claude"));
|
|
28766
30405
|
const [project, global] = await Promise.all([projectScan, globalScan]);
|
|
28767
30406
|
const map = new Map;
|
|
28768
30407
|
for (const e2 of global)
|
|
@@ -28774,32 +30413,6 @@ async function loadUserSlashes(worktreePath) {
|
|
|
28774
30413
|
var init_user_slashes = () => {};
|
|
28775
30414
|
|
|
28776
30415
|
// src/tui/panes/chat/context-meter.ts
|
|
28777
|
-
function parseContextWindowSize(modelIdentifier) {
|
|
28778
|
-
const delimitedMatch = /(?:\(|\[)\s*(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])\s*(?:\)|\])/i.exec(modelIdentifier);
|
|
28779
|
-
if (delimitedMatch?.[1] && delimitedMatch[2]) {
|
|
28780
|
-
const parsed2 = Number.parseFloat(delimitedMatch[1].replace(/[,_]/g, ""));
|
|
28781
|
-
if (Number.isFinite(parsed2) && parsed2 > 0) {
|
|
28782
|
-
return Math.round(parsed2 * (delimitedMatch[2].toLowerCase() === "m" ? 1e6 : 1000));
|
|
28783
|
-
}
|
|
28784
|
-
}
|
|
28785
|
-
const contextMatch = /\b(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])(?:\s*(?:token\s*)?context)?\b/i.exec(modelIdentifier);
|
|
28786
|
-
if (!contextMatch?.[1] || !contextMatch[2])
|
|
28787
|
-
return null;
|
|
28788
|
-
const parsed = Number.parseFloat(contextMatch[1].replace(/[,_]/g, ""));
|
|
28789
|
-
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
28790
|
-
return null;
|
|
28791
|
-
return Math.round(parsed * (contextMatch[2].toLowerCase() === "m" ? 1e6 : 1000));
|
|
28792
|
-
}
|
|
28793
|
-
function contextWindowTokensForModel(modelId) {
|
|
28794
|
-
const id = modelId ?? resolveDefaultModelId();
|
|
28795
|
-
const parsedWindow = parseContextWindowSize(id);
|
|
28796
|
-
if (parsedWindow !== null)
|
|
28797
|
-
return parsedWindow;
|
|
28798
|
-
const inPicker = MODEL_CHOICES.some((m2) => m2.id === id);
|
|
28799
|
-
if (inPicker)
|
|
28800
|
-
return STD_CTX;
|
|
28801
|
-
return STD_CTX;
|
|
28802
|
-
}
|
|
28803
30416
|
function formatTokShort(n2) {
|
|
28804
30417
|
if (n2 >= 1e6) {
|
|
28805
30418
|
const m2 = n2 / 1e6;
|
|
@@ -28818,8 +30431,10 @@ function formatTotalSpeed(tokensPerSecond) {
|
|
|
28818
30431
|
return `${(tokensPerSecond / 1000).toFixed(1)}k t/s`;
|
|
28819
30432
|
return `${tokensPerSecond.toFixed(1)} t/s`;
|
|
28820
30433
|
}
|
|
28821
|
-
function formatContextUsageCompact(u2, modelId) {
|
|
28822
|
-
const
|
|
30434
|
+
function formatContextUsageCompact(u2, modelId, vendor) {
|
|
30435
|
+
const caps = vendor ? getCapabilities(vendor) : capabilitiesForModelId(modelId);
|
|
30436
|
+
const id = modelId ?? caps.defaultModelId();
|
|
30437
|
+
const window2 = caps.contextWindowFor(id);
|
|
28823
30438
|
const total = totalContextTokens(u2);
|
|
28824
30439
|
if (total <= 0 || window2 <= 0)
|
|
28825
30440
|
return null;
|
|
@@ -28827,10 +30442,8 @@ function formatContextUsageCompact(u2, modelId) {
|
|
|
28827
30442
|
const speed = formatTotalSpeed(u2.total_speed_tokens_per_second);
|
|
28828
30443
|
return [`${pct2}% \xB7 ${formatTokShort(total)}/${formatTokShort(window2)}`, speed].filter(Boolean).join(" \xB7 ");
|
|
28829
30444
|
}
|
|
28830
|
-
var STD_CTX = 200000;
|
|
28831
30445
|
var init_context_meter = __esm(() => {
|
|
28832
|
-
|
|
28833
|
-
init_models();
|
|
30446
|
+
init_registry2();
|
|
28834
30447
|
});
|
|
28835
30448
|
|
|
28836
30449
|
// src/tui/panes/chat/store.ts
|
|
@@ -28877,7 +30490,7 @@ function setMessagesFromHistory(state, past, usageMetrics) {
|
|
|
28877
30490
|
for (const m2 of past) {
|
|
28878
30491
|
appendRowsFromMessage(rows, toolIndexById, m2);
|
|
28879
30492
|
}
|
|
28880
|
-
const latestUsage = usageMetrics
|
|
30493
|
+
const latestUsage = usageMetrics;
|
|
28881
30494
|
return {
|
|
28882
30495
|
...state,
|
|
28883
30496
|
messages: capMessages(rows, new Date().toISOString()),
|
|
@@ -28955,12 +30568,13 @@ function applyEvent(state, ev, nowIso = new Date().toISOString()) {
|
|
|
28955
30568
|
case "usage":
|
|
28956
30569
|
return {
|
|
28957
30570
|
...state,
|
|
28958
|
-
lastUsage:
|
|
30571
|
+
lastUsage: {
|
|
28959
30572
|
input_tokens: ev.input_tokens,
|
|
28960
30573
|
output_tokens: ev.output_tokens,
|
|
28961
30574
|
cache_read_input_tokens: ev.cache_read_input_tokens,
|
|
28962
|
-
cache_creation_input_tokens: ev.cache_creation_input_tokens
|
|
28963
|
-
|
|
30575
|
+
cache_creation_input_tokens: ev.cache_creation_input_tokens,
|
|
30576
|
+
total_speed_tokens_per_second: ev.total_speed_tokens_per_second
|
|
30577
|
+
}
|
|
28964
30578
|
};
|
|
28965
30579
|
case "done":
|
|
28966
30580
|
return { ...state, isStreaming: false, activeTurnStartedAt: undefined };
|
|
@@ -29062,16 +30676,6 @@ function pushSystemError(state, message, nowIso = new Date().toISOString()) {
|
|
|
29062
30676
|
}
|
|
29063
30677
|
function appendRowsFromMessage(rows, toolIndexById, m2) {
|
|
29064
30678
|
const ts = m2.timestamp;
|
|
29065
|
-
if (typeof m2.content === "string") {
|
|
29066
|
-
if (m2.content.length === 0)
|
|
29067
|
-
return;
|
|
29068
|
-
const row = textRow(m2.role, m2.content, ts);
|
|
29069
|
-
if (row)
|
|
29070
|
-
rows.push(row);
|
|
29071
|
-
return;
|
|
29072
|
-
}
|
|
29073
|
-
if (!Array.isArray(m2.content))
|
|
29074
|
-
return;
|
|
29075
30679
|
let textBuf = "";
|
|
29076
30680
|
const flushText = () => {
|
|
29077
30681
|
if (textBuf.length === 0)
|
|
@@ -29081,25 +30685,18 @@ function appendRowsFromMessage(rows, toolIndexById, m2) {
|
|
|
29081
30685
|
rows.push(row);
|
|
29082
30686
|
textBuf = "";
|
|
29083
30687
|
};
|
|
29084
|
-
for (const block of m2.
|
|
29085
|
-
if (
|
|
29086
|
-
textBuf += block;
|
|
29087
|
-
continue;
|
|
29088
|
-
}
|
|
29089
|
-
if (!block || typeof block !== "object")
|
|
29090
|
-
continue;
|
|
29091
|
-
const b2 = block;
|
|
29092
|
-
if (b2.type === "text" && typeof b2.text === "string") {
|
|
29093
|
-
textBuf += b2.text;
|
|
30688
|
+
for (const block of m2.blocks) {
|
|
30689
|
+
if (block.type === "text") {
|
|
30690
|
+
textBuf += block.text;
|
|
29094
30691
|
continue;
|
|
29095
30692
|
}
|
|
29096
|
-
if (
|
|
30693
|
+
if (block.type === "tool_call") {
|
|
29097
30694
|
flushText();
|
|
29098
|
-
const id =
|
|
30695
|
+
const id = block.callId.length > 0 ? block.callId : undefined;
|
|
29099
30696
|
const row = {
|
|
29100
30697
|
kind: "tool",
|
|
29101
|
-
name:
|
|
29102
|
-
input:
|
|
30698
|
+
name: block.name,
|
|
30699
|
+
input: block.input,
|
|
29103
30700
|
done: false,
|
|
29104
30701
|
ts,
|
|
29105
30702
|
toolUseId: id
|
|
@@ -29110,11 +30707,11 @@ function appendRowsFromMessage(rows, toolIndexById, m2) {
|
|
|
29110
30707
|
toolIndexById.set(id, idx);
|
|
29111
30708
|
continue;
|
|
29112
30709
|
}
|
|
29113
|
-
if (
|
|
30710
|
+
if (block.type === "tool_result") {
|
|
29114
30711
|
flushText();
|
|
29115
|
-
const id =
|
|
30712
|
+
const id = block.callId.length > 0 ? block.callId : undefined;
|
|
29116
30713
|
const idx = id !== undefined ? toolIndexById.get(id) : undefined;
|
|
29117
|
-
const output =
|
|
30714
|
+
const output = block.output;
|
|
29118
30715
|
if (idx !== undefined) {
|
|
29119
30716
|
const target = rows[idx];
|
|
29120
30717
|
if (target && target.kind === "tool") {
|
|
@@ -29347,6 +30944,7 @@ var init_use_chat_session = __esm(() => {
|
|
|
29347
30944
|
});
|
|
29348
30945
|
|
|
29349
30946
|
// src/tui/panes/chat/Chat.tsx
|
|
30947
|
+
import { TextAttributes as TextAttributes25 } from "@opentui/core";
|
|
29350
30948
|
function Chat(props) {
|
|
29351
30949
|
const {
|
|
29352
30950
|
theme
|
|
@@ -29422,8 +31020,10 @@ function Chat(props) {
|
|
|
29422
31020
|
if (!u2)
|
|
29423
31021
|
return null;
|
|
29424
31022
|
const task = props.orchestrator.getTask(tid);
|
|
29425
|
-
const
|
|
29426
|
-
|
|
31023
|
+
const tab = task?.tabs.find((t2) => t2.id === tabId);
|
|
31024
|
+
const vendor = tab?.vendor ?? task?.vendor ?? "claude";
|
|
31025
|
+
const modelId2 = tab?.model ?? task?.model ?? getCapabilities(vendor).defaultModelId();
|
|
31026
|
+
return formatContextUsageCompact(u2, modelId2, vendor);
|
|
29427
31027
|
});
|
|
29428
31028
|
createEffect(on(contextMeterLabel, (label) => {
|
|
29429
31029
|
props.onContextMeter?.(label ?? null);
|
|
@@ -29482,7 +31082,9 @@ function Chat(props) {
|
|
|
29482
31082
|
const id = props.taskId();
|
|
29483
31083
|
if (!id)
|
|
29484
31084
|
return;
|
|
29485
|
-
|
|
31085
|
+
const task = tasksAcc().find((t2) => t2.id === id);
|
|
31086
|
+
const tab = task?.tabs.find((t2) => t2.id === activeTabId());
|
|
31087
|
+
return tab?.model ?? task?.model;
|
|
29486
31088
|
});
|
|
29487
31089
|
const worktreePath = createMemo(() => {
|
|
29488
31090
|
const id = props.taskId();
|
|
@@ -29491,14 +31093,22 @@ function Chat(props) {
|
|
|
29491
31093
|
return tasksAcc().find((t2) => t2.id === id)?.worktreePath ?? undefined;
|
|
29492
31094
|
});
|
|
29493
31095
|
const modelLabel = createMemo(() => modelLabelFor(modelId()));
|
|
31096
|
+
const inputPlaceholder = createMemo(() => {
|
|
31097
|
+
const id = props.taskId();
|
|
31098
|
+
const task = id ? tasksAcc().find((t2) => t2.id === id) : undefined;
|
|
31099
|
+
const tab = task?.tabs.find((t2) => t2.id === activeTabId());
|
|
31100
|
+
const vendor = tab?.vendor ?? task?.vendor ?? "claude";
|
|
31101
|
+
return getIdentity(vendor).inputPlaceholder;
|
|
31102
|
+
});
|
|
29494
31103
|
async function chooseModel() {
|
|
29495
31104
|
const id = props.taskId();
|
|
29496
31105
|
if (!id)
|
|
29497
31106
|
return;
|
|
31107
|
+
const tabId = activeTabId() ?? undefined;
|
|
29498
31108
|
const result = await ModelPicker.show(dialog, modelId());
|
|
29499
31109
|
if (result === undefined)
|
|
29500
31110
|
return;
|
|
29501
|
-
await props.orchestrator.setModel(id, result).catch((err) => {
|
|
31111
|
+
await props.orchestrator.setModel(id, result, tabId).catch((err) => {
|
|
29502
31112
|
console.error("[kobe] setModel failed:", err);
|
|
29503
31113
|
});
|
|
29504
31114
|
}
|
|
@@ -29586,7 +31196,7 @@ function Chat(props) {
|
|
|
29586
31196
|
try {
|
|
29587
31197
|
await props.orchestrator.runTask(taskId, dispatched.text, tabId);
|
|
29588
31198
|
} catch (err) {
|
|
29589
|
-
patchActiveState((s2) => pushSystemError(s2, `queued runTask failed: ${
|
|
31199
|
+
patchActiveState((s2) => pushSystemError(s2, `queued runTask failed: ${stringifyErr3(err)}`));
|
|
29590
31200
|
}
|
|
29591
31201
|
} finally {
|
|
29592
31202
|
dispatching = false;
|
|
@@ -29608,7 +31218,7 @@ function Chat(props) {
|
|
|
29608
31218
|
try {
|
|
29609
31219
|
await props.orchestrator.clearTab(taskId, tabId);
|
|
29610
31220
|
} catch (err) {
|
|
29611
|
-
patchActiveState((s2) => pushSystemError(s2, `/clear failed: ${
|
|
31221
|
+
patchActiveState((s2) => pushSystemError(s2, `/clear failed: ${stringifyErr3(err)}`));
|
|
29612
31222
|
}
|
|
29613
31223
|
return;
|
|
29614
31224
|
}
|
|
@@ -29622,7 +31232,7 @@ function Chat(props) {
|
|
|
29622
31232
|
try {
|
|
29623
31233
|
await props.orchestrator.steerTask(taskId, text, tabId);
|
|
29624
31234
|
} catch (err) {
|
|
29625
|
-
patchActiveState((s2) => pushSystemError(s2, `steer failed: ${
|
|
31235
|
+
patchActiveState((s2) => pushSystemError(s2, `steer failed: ${stringifyErr3(err)}`));
|
|
29626
31236
|
}
|
|
29627
31237
|
} finally {
|
|
29628
31238
|
dispatching = false;
|
|
@@ -29642,7 +31252,7 @@ function Chat(props) {
|
|
|
29642
31252
|
try {
|
|
29643
31253
|
await props.orchestrator.runTask(taskId, text, tabId);
|
|
29644
31254
|
} catch (err) {
|
|
29645
|
-
patchActiveState((s2) => pushSystemError(s2, `runTask failed: ${
|
|
31255
|
+
patchActiveState((s2) => pushSystemError(s2, `runTask failed: ${stringifyErr3(err)}`));
|
|
29646
31256
|
}
|
|
29647
31257
|
}
|
|
29648
31258
|
function cancelQueued(id) {
|
|
@@ -29666,7 +31276,7 @@ function Chat(props) {
|
|
|
29666
31276
|
setExpandedFoldStartIndex(null);
|
|
29667
31277
|
props.orchestrator.setActiveTab(taskId, tab.id);
|
|
29668
31278
|
} catch (err) {
|
|
29669
|
-
patchActiveState((s2) => pushSystemError(s2, `createTab failed: ${
|
|
31279
|
+
patchActiveState((s2) => pushSystemError(s2, `createTab failed: ${stringifyErr3(err)}`));
|
|
29670
31280
|
}
|
|
29671
31281
|
}
|
|
29672
31282
|
async function closeActiveTab() {
|
|
@@ -29684,7 +31294,7 @@ function Chat(props) {
|
|
|
29684
31294
|
setExpandedFoldStartIndex(null);
|
|
29685
31295
|
}
|
|
29686
31296
|
} catch (err) {
|
|
29687
|
-
patchActiveState((s2) => pushSystemError(s2, `closeTab failed: ${
|
|
31297
|
+
patchActiveState((s2) => pushSystemError(s2, `closeTab failed: ${stringifyErr3(err)}`));
|
|
29688
31298
|
}
|
|
29689
31299
|
}
|
|
29690
31300
|
function selectTabByIndex(idx) {
|
|
@@ -29737,7 +31347,7 @@ function Chat(props) {
|
|
|
29737
31347
|
try {
|
|
29738
31348
|
await props.orchestrator.interruptTask(taskId, tabId);
|
|
29739
31349
|
} catch (err) {
|
|
29740
|
-
patchActiveState((s2) => pushSystemError(s2, `interrupt failed: ${
|
|
31350
|
+
patchActiveState((s2) => pushSystemError(s2, `interrupt failed: ${stringifyErr3(err)}`));
|
|
29741
31351
|
}
|
|
29742
31352
|
}
|
|
29743
31353
|
useBindings(() => ({
|
|
@@ -29813,7 +31423,7 @@ function Chat(props) {
|
|
|
29813
31423
|
kind: "ask_question",
|
|
29814
31424
|
answers
|
|
29815
31425
|
}).catch((err) => {
|
|
29816
|
-
patchActiveState((s2) => pushSystemError(s2, `respondToInput failed: ${
|
|
31426
|
+
patchActiveState((s2) => pushSystemError(s2, `respondToInput failed: ${stringifyErr3(err)}`));
|
|
29817
31427
|
});
|
|
29818
31428
|
setDraft("");
|
|
29819
31429
|
return;
|
|
@@ -29871,9 +31481,6 @@ function Chat(props) {
|
|
|
29871
31481
|
get showEmptyPlaceholder() {
|
|
29872
31482
|
return !showThinking();
|
|
29873
31483
|
},
|
|
29874
|
-
get error() {
|
|
29875
|
-
return activeState().error;
|
|
29876
|
-
},
|
|
29877
31484
|
onApprove: (requestId, approve) => {
|
|
29878
31485
|
const taskId = props.taskId();
|
|
29879
31486
|
if (!taskId)
|
|
@@ -29882,7 +31489,7 @@ function Chat(props) {
|
|
|
29882
31489
|
kind: "approve_plan",
|
|
29883
31490
|
approve
|
|
29884
31491
|
}).catch((err) => {
|
|
29885
|
-
patchActiveState((s2) => pushSystemError(s2, `respondToInput failed: ${
|
|
31492
|
+
patchActiveState((s2) => pushSystemError(s2, `respondToInput failed: ${stringifyErr3(err)}`));
|
|
29886
31493
|
});
|
|
29887
31494
|
},
|
|
29888
31495
|
onAnswer: (requestId, answers) => {
|
|
@@ -29893,7 +31500,7 @@ function Chat(props) {
|
|
|
29893
31500
|
kind: "ask_question",
|
|
29894
31501
|
answers
|
|
29895
31502
|
}).catch((err) => {
|
|
29896
|
-
patchActiveState((s2) => pushSystemError(s2, `respondToInput failed: ${
|
|
31503
|
+
patchActiveState((s2) => pushSystemError(s2, `respondToInput failed: ${stringifyErr3(err)}`));
|
|
29897
31504
|
});
|
|
29898
31505
|
},
|
|
29899
31506
|
onClaimComposerFocus: setQuestionInlineFocus,
|
|
@@ -29923,6 +31530,44 @@ function Chat(props) {
|
|
|
29923
31530
|
});
|
|
29924
31531
|
}
|
|
29925
31532
|
}), null);
|
|
31533
|
+
insert(_el$, createComponent2(Show, {
|
|
31534
|
+
get when() {
|
|
31535
|
+
return activeState().error;
|
|
31536
|
+
},
|
|
31537
|
+
children: (err) => (() => {
|
|
31538
|
+
var _el$7 = createElement("box"), _el$8 = createElement("text"), _el$0 = createElement("text"), _el$10 = createElement("text");
|
|
31539
|
+
insertNode(_el$7, _el$8);
|
|
31540
|
+
insertNode(_el$7, _el$0);
|
|
31541
|
+
insertNode(_el$7, _el$10);
|
|
31542
|
+
setProp(_el$7, "flexDirection", "row");
|
|
31543
|
+
setProp(_el$7, "gap", 1);
|
|
31544
|
+
setProp(_el$7, "paddingLeft", 1);
|
|
31545
|
+
setProp(_el$7, "paddingRight", 1);
|
|
31546
|
+
setProp(_el$7, "flexShrink", 0);
|
|
31547
|
+
insertNode(_el$8, createTextNode(`!`));
|
|
31548
|
+
setProp(_el$8, "wrapMode", "none");
|
|
31549
|
+
insertNode(_el$0, createTextNode(`error`));
|
|
31550
|
+
setProp(_el$0, "wrapMode", "none");
|
|
31551
|
+
setProp(_el$10, "wrapMode", "none");
|
|
31552
|
+
insert(_el$10, err);
|
|
31553
|
+
effect((_p$) => {
|
|
31554
|
+
var { backgroundElement: _v$, warning: _v$2 } = theme, _v$3 = TextAttributes25.BOLD, _v$4 = theme.warning, _v$5 = theme.textMuted;
|
|
31555
|
+
_v$ !== _p$.e && (_p$.e = setProp(_el$7, "backgroundColor", _v$, _p$.e));
|
|
31556
|
+
_v$2 !== _p$.t && (_p$.t = setProp(_el$8, "fg", _v$2, _p$.t));
|
|
31557
|
+
_v$3 !== _p$.a && (_p$.a = setProp(_el$8, "attributes", _v$3, _p$.a));
|
|
31558
|
+
_v$4 !== _p$.o && (_p$.o = setProp(_el$0, "fg", _v$4, _p$.o));
|
|
31559
|
+
_v$5 !== _p$.i && (_p$.i = setProp(_el$10, "fg", _v$5, _p$.i));
|
|
31560
|
+
return _p$;
|
|
31561
|
+
}, {
|
|
31562
|
+
e: undefined,
|
|
31563
|
+
t: undefined,
|
|
31564
|
+
a: undefined,
|
|
31565
|
+
o: undefined,
|
|
31566
|
+
i: undefined
|
|
31567
|
+
});
|
|
31568
|
+
return _el$7;
|
|
31569
|
+
})()
|
|
31570
|
+
}), null);
|
|
29926
31571
|
insert(_el$, createComponent2(Show, {
|
|
29927
31572
|
get when() {
|
|
29928
31573
|
return !pendingQuestion();
|
|
@@ -29951,6 +31596,7 @@ function Chat(props) {
|
|
|
29951
31596
|
permissionMode,
|
|
29952
31597
|
onCyclePermissionMode: cyclePermissionMode,
|
|
29953
31598
|
modelLabel,
|
|
31599
|
+
inputPlaceholder,
|
|
29954
31600
|
onChooseModel: () => void chooseModel(),
|
|
29955
31601
|
worktreePath,
|
|
29956
31602
|
queue: () => activeState().queue,
|
|
@@ -29962,7 +31608,7 @@ function Chat(props) {
|
|
|
29962
31608
|
return _el$;
|
|
29963
31609
|
})();
|
|
29964
31610
|
}
|
|
29965
|
-
function
|
|
31611
|
+
function stringifyErr3(err) {
|
|
29966
31612
|
if (err instanceof Error)
|
|
29967
31613
|
return err.message;
|
|
29968
31614
|
try {
|
|
@@ -29981,6 +31627,7 @@ var init_Chat = __esm(() => {
|
|
|
29981
31627
|
init_solid();
|
|
29982
31628
|
init_solid();
|
|
29983
31629
|
init_solid();
|
|
31630
|
+
init_registry2();
|
|
29984
31631
|
init_dev();
|
|
29985
31632
|
init_resume_dialog();
|
|
29986
31633
|
init_keybindings();
|
|
@@ -29992,7 +31639,6 @@ var init_Chat = __esm(() => {
|
|
|
29992
31639
|
init_MessageList();
|
|
29993
31640
|
init_ModelPicker();
|
|
29994
31641
|
init_builtin_slashes();
|
|
29995
|
-
init_models();
|
|
29996
31642
|
init_user_slashes();
|
|
29997
31643
|
init_context_meter();
|
|
29998
31644
|
init_store2();
|
|
@@ -30000,7 +31646,7 @@ var init_Chat = __esm(() => {
|
|
|
30000
31646
|
});
|
|
30001
31647
|
|
|
30002
31648
|
// src/tui/component/sidebar.tsx
|
|
30003
|
-
import { TextAttributes as
|
|
31649
|
+
import { TextAttributes as TextAttributes26 } from "@opentui/core";
|
|
30004
31650
|
function Sidebar(props) {
|
|
30005
31651
|
const {
|
|
30006
31652
|
theme
|
|
@@ -30026,7 +31672,7 @@ function Sidebar(props) {
|
|
|
30026
31672
|
setProp(_el$2, "paddingBottom", 1);
|
|
30027
31673
|
insert(_el$3, () => props.title);
|
|
30028
31674
|
effect((_p$) => {
|
|
30029
|
-
var _v$ = theme.text, _v$2 =
|
|
31675
|
+
var _v$ = theme.text, _v$2 = TextAttributes26.BOLD;
|
|
30030
31676
|
_v$ !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$, _p$.e));
|
|
30031
31677
|
_v$2 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$2, _p$.t));
|
|
30032
31678
|
return _p$;
|
|
@@ -30145,12 +31791,12 @@ var init_sidebar = __esm(() => {
|
|
|
30145
31791
|
});
|
|
30146
31792
|
|
|
30147
31793
|
// src/tui/panes/sidebar/git-head.ts
|
|
30148
|
-
import { spawnSync as
|
|
31794
|
+
import { spawnSync as spawnSync10 } from "child_process";
|
|
30149
31795
|
function readCurrentBranch(repo) {
|
|
30150
31796
|
if (!repo)
|
|
30151
31797
|
return "";
|
|
30152
31798
|
try {
|
|
30153
|
-
const out =
|
|
31799
|
+
const out = spawnSync10("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
30154
31800
|
cwd: repo,
|
|
30155
31801
|
encoding: "utf8",
|
|
30156
31802
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -30160,7 +31806,7 @@ function readCurrentBranch(repo) {
|
|
|
30160
31806
|
if (name && name !== "HEAD")
|
|
30161
31807
|
return name;
|
|
30162
31808
|
}
|
|
30163
|
-
const head =
|
|
31809
|
+
const head = spawnSync10("git", ["rev-parse", "--verify", "HEAD"], {
|
|
30164
31810
|
cwd: repo,
|
|
30165
31811
|
encoding: "utf8",
|
|
30166
31812
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -30285,7 +31931,7 @@ var init_keys4 = __esm(() => {
|
|
|
30285
31931
|
});
|
|
30286
31932
|
|
|
30287
31933
|
// src/tui/panes/sidebar/Sidebar.tsx
|
|
30288
|
-
import { TextAttributes as
|
|
31934
|
+
import { TextAttributes as TextAttributes27 } from "@opentui/core";
|
|
30289
31935
|
function Sidebar2(props) {
|
|
30290
31936
|
const {
|
|
30291
31937
|
theme
|
|
@@ -30377,7 +32023,7 @@ function Sidebar2(props) {
|
|
|
30377
32023
|
return () => _c$() ? `[ ${tab.label} ]` : tab.label;
|
|
30378
32024
|
})());
|
|
30379
32025
|
effect((_p$) => {
|
|
30380
|
-
var _v$7 = active() ? theme.primary : theme.textMuted, _v$8 = active() ?
|
|
32026
|
+
var _v$7 = active() ? theme.primary : theme.textMuted, _v$8 = active() ? TextAttributes27.BOLD : undefined;
|
|
30381
32027
|
_v$7 !== _p$.e && (_p$.e = setProp(_el$13, "fg", _v$7, _p$.e));
|
|
30382
32028
|
_v$8 !== _p$.t && (_p$.t = setProp(_el$13, "attributes", _v$8, _p$.t));
|
|
30383
32029
|
return _p$;
|
|
@@ -30472,7 +32118,7 @@ function Sidebar2(props) {
|
|
|
30472
32118
|
}
|
|
30473
32119
|
}), null);
|
|
30474
32120
|
effect((_p$) => {
|
|
30475
|
-
var _v$9 = isCursor() ? theme.primary : isSelected() ? theme.backgroundElement : undefined, _v$0 = isCursor() ? theme.selectedListItemText : badgeColor(), _v$1 = isCursor() ? theme.selectedListItemText : theme.text, _v$10 = (isMain || isSelected() && !isCursor()) && !isCursor() ?
|
|
32121
|
+
var _v$9 = isCursor() ? theme.primary : isSelected() ? theme.backgroundElement : undefined, _v$0 = isCursor() ? theme.selectedListItemText : badgeColor(), _v$1 = isCursor() ? theme.selectedListItemText : theme.text, _v$10 = (isMain || isSelected() && !isCursor()) && !isCursor() ? TextAttributes27.BOLD : undefined;
|
|
30476
32122
|
_v$9 !== _p$.e && (_p$.e = setProp(_el$14, "backgroundColor", _v$9, _p$.e));
|
|
30477
32123
|
_v$0 !== _p$.t && (_p$.t = setProp(_el$15, "fg", _v$0, _p$.t));
|
|
30478
32124
|
_v$1 !== _p$.a && (_p$.a = setProp(_el$16, "fg", _v$1, _p$.a));
|
|
@@ -30510,7 +32156,7 @@ function Sidebar2(props) {
|
|
|
30510
32156
|
setProp(_el$11, "wrapMode", "none");
|
|
30511
32157
|
setProp(_el$11, "onMouseUp", () => props.onAddTask?.());
|
|
30512
32158
|
effect((_p$) => {
|
|
30513
|
-
var _v$ = props.width ? props.width() : SIDEBAR_WIDTH, _v$2 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$3 =
|
|
32159
|
+
var _v$ = props.width ? props.width() : SIDEBAR_WIDTH, _v$2 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$3 = TextAttributes27.BOLD, _v$4 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$5 = TextAttributes27.BOLD, _v$6 = theme.textMuted;
|
|
30514
32160
|
_v$ !== _p$.e && (_p$.e = setProp(_el$, "width", _v$, _p$.e));
|
|
30515
32161
|
_v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
|
|
30516
32162
|
_v$3 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$3, _p$.a));
|
|
@@ -30580,12 +32226,12 @@ var init_Sidebar = __esm(() => {
|
|
|
30580
32226
|
});
|
|
30581
32227
|
|
|
30582
32228
|
// src/orchestrator/bridge/server.ts
|
|
30583
|
-
import { mkdir as mkdir3, unlink as
|
|
32229
|
+
import { mkdir as mkdir3, unlink as unlink4 } from "fs/promises";
|
|
30584
32230
|
import { createServer } from "net";
|
|
30585
32231
|
import { dirname as dirname5 } from "path";
|
|
30586
32232
|
async function startBridgeServer(orch, socketPath) {
|
|
30587
32233
|
await mkdir3(dirname5(socketPath), { recursive: true });
|
|
30588
|
-
await
|
|
32234
|
+
await unlink4(socketPath).catch(() => {});
|
|
30589
32235
|
const conns = new Set;
|
|
30590
32236
|
const server = createServer((conn) => {
|
|
30591
32237
|
conns.add(conn);
|
|
@@ -30626,7 +32272,7 @@ async function startBridgeServer(orch, socketPath) {
|
|
|
30626
32272
|
conn.destroy();
|
|
30627
32273
|
conns.clear();
|
|
30628
32274
|
await new Promise((resolve4) => server.close(() => resolve4()));
|
|
30629
|
-
await
|
|
32275
|
+
await unlink4(socketPath).catch(() => {});
|
|
30630
32276
|
}
|
|
30631
32277
|
};
|
|
30632
32278
|
}
|
|
@@ -30719,23 +32365,19 @@ __export(exports_bridge, {
|
|
|
30719
32365
|
bridgeSocketPathForHome: () => bridgeSocketPathForHome
|
|
30720
32366
|
});
|
|
30721
32367
|
import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
|
|
30722
|
-
import { homedir as
|
|
30723
|
-
import { join as
|
|
32368
|
+
import { homedir as homedir16 } from "os";
|
|
32369
|
+
import { join as join16 } from "path";
|
|
30724
32370
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
30725
32371
|
function bridgeSocketPathForHome(home, pid = process.pid) {
|
|
30726
|
-
const runDir =
|
|
30727
|
-
|
|
30728
|
-
const macTempSocket = process.platform === "darwin" && preferred.startsWith(tmpdir3());
|
|
30729
|
-
if (preferred.length <= UNIX_SOCKET_PATH_LIMIT && !macTempSocket)
|
|
30730
|
-
return preferred;
|
|
30731
|
-
const shortTmp = process.platform === "darwin" ? "/tmp" : tmpdir3();
|
|
30732
|
-
return join15(shortTmp, `kobe-bridge-${pid}.sock`);
|
|
32372
|
+
const runDir = join16(home, ".kobe", "run");
|
|
32373
|
+
return fitSocketPath(join16(runDir, `bridge-${pid}.sock`), home, "bridge", pid);
|
|
30733
32374
|
}
|
|
30734
32375
|
async function startBridge(orch, opts = {}) {
|
|
30735
|
-
const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ??
|
|
30736
|
-
const runDir =
|
|
32376
|
+
const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir16();
|
|
32377
|
+
const runDir = join16(home, ".kobe", "run");
|
|
30737
32378
|
const socketPath = bridgeSocketPathForHome(home);
|
|
30738
|
-
const mcpConfigPath =
|
|
32379
|
+
const mcpConfigPath = join16(runDir, `mcp-${process.pid}.json`);
|
|
32380
|
+
await mkdir4(runDir, { recursive: true });
|
|
30739
32381
|
const server = await startBridgeServer(orch, socketPath);
|
|
30740
32382
|
await mkdir4(runDir, { recursive: true });
|
|
30741
32383
|
const moduleExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
|
|
@@ -30758,14 +32400,14 @@ async function startBridge(orch, opts = {}) {
|
|
|
30758
32400
|
}
|
|
30759
32401
|
};
|
|
30760
32402
|
}
|
|
30761
|
-
var UNIX_SOCKET_PATH_LIMIT = 103;
|
|
30762
32403
|
var init_bridge = __esm(() => {
|
|
32404
|
+
init_paths2();
|
|
30763
32405
|
init_server();
|
|
30764
32406
|
});
|
|
30765
32407
|
|
|
30766
32408
|
// src/tui/app.tsx
|
|
30767
|
-
import { homedir as
|
|
30768
|
-
import { join as
|
|
32409
|
+
import { homedir as homedir17 } from "os";
|
|
32410
|
+
import { join as join17 } from "path";
|
|
30769
32411
|
function Shell(props) {
|
|
30770
32412
|
const themeCtx = useTheme();
|
|
30771
32413
|
const {
|
|
@@ -30821,8 +32463,8 @@ function Shell(props) {
|
|
|
30821
32463
|
return pp.prompt;
|
|
30822
32464
|
});
|
|
30823
32465
|
const worktreePathAcc = createMemo(() => {
|
|
30824
|
-
const
|
|
30825
|
-
return
|
|
32466
|
+
const path12 = activeTask()?.worktreePath;
|
|
32467
|
+
return path12 ? path12 : null;
|
|
30826
32468
|
});
|
|
30827
32469
|
const taskIdNullAcc = createMemo(() => selectedId());
|
|
30828
32470
|
const diffBaseAcc = createMemo(() => worktreePathAcc() ? "HEAD" : null);
|
|
@@ -31292,17 +32934,17 @@ async function startApp() {
|
|
|
31292
32934
|
} of loadUserThemes()) {
|
|
31293
32935
|
addTheme(name, theme);
|
|
31294
32936
|
}
|
|
31295
|
-
const homeDir2 = process.env.KOBE_HOME_DIR ??
|
|
32937
|
+
const homeDir2 = process.env.KOBE_HOME_DIR ?? homedir17();
|
|
31296
32938
|
let orchestrator;
|
|
31297
32939
|
if (process.env.KOBE_TEST_ENGINE || process.env.KOBE_NO_DAEMON === "1") {
|
|
31298
|
-
const
|
|
32940
|
+
const engines = await buildEngines();
|
|
31299
32941
|
const store2 = new TaskIndexStore({
|
|
31300
32942
|
homeDir: homeDir2
|
|
31301
32943
|
});
|
|
31302
32944
|
await store2.load();
|
|
31303
32945
|
const worktrees = new GitWorktreeManager;
|
|
31304
32946
|
orchestrator = new Orchestrator({
|
|
31305
|
-
|
|
32947
|
+
engines,
|
|
31306
32948
|
store: store2,
|
|
31307
32949
|
worktrees
|
|
31308
32950
|
});
|
|
@@ -31393,7 +33035,7 @@ var exports_tui = {};
|
|
|
31393
33035
|
__export(exports_tui, {
|
|
31394
33036
|
startTui: () => startTui
|
|
31395
33037
|
});
|
|
31396
|
-
import { TextAttributes as
|
|
33038
|
+
import { TextAttributes as TextAttributes28 } from "@opentui/core";
|
|
31397
33039
|
function HelpHint() {
|
|
31398
33040
|
const {
|
|
31399
33041
|
theme
|
|
@@ -31467,7 +33109,7 @@ function Banner() {
|
|
|
31467
33109
|
insert(_el$20, selected);
|
|
31468
33110
|
insert(_el$12, createComponent2(HelpHint, {}), null);
|
|
31469
33111
|
effect((_p$) => {
|
|
31470
|
-
var _v$7 = theme.primary, _v$8 =
|
|
33112
|
+
var _v$7 = theme.primary, _v$8 = TextAttributes28.BOLD, _v$9 = theme.borderActive, _v$0 = theme.text, _v$1 = theme.textMuted, _v$10 = {
|
|
31471
33113
|
fg: theme.accent
|
|
31472
33114
|
};
|
|
31473
33115
|
_v$7 !== _p$.e && (_p$.e = setProp(_el$13, "fg", _v$7, _p$.e));
|