@sma1lboy/kobe 0.7.21 → 0.7.23
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/cli/index.js +731 -273
- package/dist/web-ui/assets/AppShell-C0hhV6qB.js +16 -0
- package/dist/web-ui/assets/AppShell-NZ5KYJKh.css +1 -0
- package/dist/web-ui/assets/{routes-CObX3u9V.js → ChatTerminal-j3zmSA6m.js} +3 -3
- package/dist/web-ui/assets/ChatTerminal-kHJ-D0s7.css +1 -0
- package/dist/web-ui/assets/index-BPEy1cTR.css +2 -0
- package/dist/web-ui/assets/index-CMnpXQhx.js +10 -0
- package/dist/web-ui/assets/jsx-runtime-bzQ4Vb5N.js +1 -0
- package/dist/web-ui/assets/overview-Gj7Z3GGU.js +1 -0
- package/dist/web-ui/assets/routes-BQZWyGJi.js +1 -0
- package/dist/web-ui/assets/task._taskId-CF3ph7W5.js +1 -0
- package/dist/web-ui/assets/triage-DT7sWoTl.js +1 -0
- package/dist/web-ui/index.html +3 -2
- package/dist/web-ui/pty-server.mjs +24 -6
- package/package.json +1 -1
- package/dist/web-ui/assets/index-Bz2DJrhb.css +0 -2
- package/dist/web-ui/assets/index-DHNbtmKS.js +0 -10
- package/dist/web-ui/assets/routes-O3B6rKf2.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -90,7 +90,7 @@ var init_package = __esm(() => {
|
|
|
90
90
|
package_default = {
|
|
91
91
|
$schema: "https://json.schemastore.org/package.json",
|
|
92
92
|
name: "@sma1lboy/kobe",
|
|
93
|
-
version: "0.7.
|
|
93
|
+
version: "0.7.23",
|
|
94
94
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
95
95
|
type: "module",
|
|
96
96
|
packageManager: "bun@1.3.13",
|
|
@@ -3891,6 +3891,18 @@ function isDaemonVersionStale(daemonVersion, clientVersion) {
|
|
|
3891
3891
|
return false;
|
|
3892
3892
|
return daemonVersion !== clientVersion;
|
|
3893
3893
|
}
|
|
3894
|
+
function isChannelName(value) {
|
|
3895
|
+
return typeof value === "string" && CHANNEL_NAME_SET.has(value);
|
|
3896
|
+
}
|
|
3897
|
+
function normalizeChannelFilter(value) {
|
|
3898
|
+
if (!Array.isArray(value))
|
|
3899
|
+
return null;
|
|
3900
|
+
const set = new Set;
|
|
3901
|
+
for (const name of value)
|
|
3902
|
+
if (isChannelName(name))
|
|
3903
|
+
set.add(name);
|
|
3904
|
+
return set.size > 0 ? set : null;
|
|
3905
|
+
}
|
|
3894
3906
|
function serializeTask(task) {
|
|
3895
3907
|
return {
|
|
3896
3908
|
id: task.id,
|
|
@@ -3912,7 +3924,7 @@ function frameToLine(frame) {
|
|
|
3912
3924
|
return `${JSON.stringify(frame)}
|
|
3913
3925
|
`;
|
|
3914
3926
|
}
|
|
3915
|
-
var DAEMON_PROTOCOL_VERSION = 3, MIN_COMPATIBLE_PROTOCOL_VERSION = 2, CHANNEL_NAMES;
|
|
3927
|
+
var DAEMON_PROTOCOL_VERSION = 3, MIN_COMPATIBLE_PROTOCOL_VERSION = 2, CHANNEL_NAMES, CHANNEL_NAME_SET;
|
|
3916
3928
|
var init_protocol = __esm(() => {
|
|
3917
3929
|
CHANNEL_NAMES = [
|
|
3918
3930
|
"task.snapshot",
|
|
@@ -3924,6 +3936,7 @@ var init_protocol = __esm(() => {
|
|
|
3924
3936
|
"task.jobs",
|
|
3925
3937
|
"worktree.changes"
|
|
3926
3938
|
];
|
|
3939
|
+
CHANNEL_NAME_SET = new Set(CHANNEL_NAMES);
|
|
3927
3940
|
});
|
|
3928
3941
|
|
|
3929
3942
|
// ../kobe-daemon/src/daemon/paths.ts
|
|
@@ -4685,7 +4698,7 @@ async function probeBinary(probe2) {
|
|
|
4685
4698
|
return { found: false, error: err instanceof Error ? err.message : String(err) };
|
|
4686
4699
|
}
|
|
4687
4700
|
}
|
|
4688
|
-
async function
|
|
4701
|
+
async function probeAvailableVendors(deps) {
|
|
4689
4702
|
const probes = [
|
|
4690
4703
|
["claude", () => deps.findClaudeBinary()],
|
|
4691
4704
|
["codex", () => deps.findCodexBinary()],
|
|
@@ -4694,6 +4707,18 @@ async function detectAvailableVendors(deps = defaultDeps5) {
|
|
|
4694
4707
|
const detected = await Promise.all(probes.map(async ([vendor, probe2]) => (await probeBinary(probe2)).found ? vendor : null));
|
|
4695
4708
|
return detected.filter((v) => v !== null);
|
|
4696
4709
|
}
|
|
4710
|
+
function detectAvailableVendors(deps = defaultDeps5) {
|
|
4711
|
+
if (deps !== defaultDeps5)
|
|
4712
|
+
return probeAvailableVendors(deps);
|
|
4713
|
+
if (cachedDefaultVendors)
|
|
4714
|
+
return cachedDefaultVendors;
|
|
4715
|
+
const pending = probeAvailableVendors(deps).catch((err) => {
|
|
4716
|
+
cachedDefaultVendors = null;
|
|
4717
|
+
throw err;
|
|
4718
|
+
});
|
|
4719
|
+
cachedDefaultVendors = pending;
|
|
4720
|
+
return pending;
|
|
4721
|
+
}
|
|
4697
4722
|
async function availableEngineIds(deps = defaultDeps5) {
|
|
4698
4723
|
const builtins = await detectAvailableVendors(deps);
|
|
4699
4724
|
return [...builtins, ...getCustomEngineIds()];
|
|
@@ -4850,7 +4875,7 @@ function hasStringDeep(value, interestingKeys, depth = 0) {
|
|
|
4850
4875
|
}
|
|
4851
4876
|
return false;
|
|
4852
4877
|
}
|
|
4853
|
-
var defaultDeps5;
|
|
4878
|
+
var defaultDeps5, cachedDefaultVendors = null;
|
|
4854
4879
|
var init_account_detect = __esm(() => {
|
|
4855
4880
|
init_repos();
|
|
4856
4881
|
init_binary();
|
|
@@ -6291,6 +6316,28 @@ var init_auto_title = __esm(() => {
|
|
|
6291
6316
|
init_registry();
|
|
6292
6317
|
});
|
|
6293
6318
|
|
|
6319
|
+
// src/tui/panes/terminal/launch.ts
|
|
6320
|
+
function wrapEngineLaunch(engineCmd, remoteKey, remoteCwd) {
|
|
6321
|
+
if (!remoteKey)
|
|
6322
|
+
return engineCmd;
|
|
6323
|
+
const host = execHostForRepo(remoteKey);
|
|
6324
|
+
host.ensureReady();
|
|
6325
|
+
return host.wrapCommand(engineCmd, { tty: true, cwd: remoteCwd });
|
|
6326
|
+
}
|
|
6327
|
+
function inheritedEnvPrefix() {
|
|
6328
|
+
const parts = [];
|
|
6329
|
+
for (const key of ["KOBE_HOME_DIR", "KOBE_DAEMON_SOCKET_PATH", "KOBE_TMUX_SOCKET"]) {
|
|
6330
|
+
const value = process.env[key];
|
|
6331
|
+
if (value && value.length > 0)
|
|
6332
|
+
parts.push(`${key}=${shellQuote(value)}`);
|
|
6333
|
+
}
|
|
6334
|
+
return parts.length > 0 ? `${parts.join(" ")} ` : "";
|
|
6335
|
+
}
|
|
6336
|
+
var REMOTE_KEY_OPTION = "@kobe_remote";
|
|
6337
|
+
var init_launch = __esm(() => {
|
|
6338
|
+
init_resolve();
|
|
6339
|
+
});
|
|
6340
|
+
|
|
6294
6341
|
// src/tmux/client.ts
|
|
6295
6342
|
var exports_client = {};
|
|
6296
6343
|
__export(exports_client, {
|
|
@@ -6590,7 +6637,7 @@ async function ensureFallbackSession() {
|
|
|
6590
6637
|
"-P",
|
|
6591
6638
|
"-F",
|
|
6592
6639
|
"#{pane_id}",
|
|
6593
|
-
keepAlive(tasksPaneCommand(kobeCliInvocation()))
|
|
6640
|
+
keepAlive(inheritedEnvPrefix() + tasksPaneCommand(kobeCliInvocation()))
|
|
6594
6641
|
]);
|
|
6595
6642
|
const tasksPane = r1.stdout.trim();
|
|
6596
6643
|
if (tasksPane) {
|
|
@@ -6617,6 +6664,7 @@ async function switchClientBeforeKill(killedName, nextSessionName) {
|
|
|
6617
6664
|
var KOBE_TMUX_SOCKET, SAFE_SPAWN_CWD, PANE_ROLE_OPTION = "@kobe_role", CLAUDE_ROLE_OPTION, CLAUDE_ROLE_VALUE = "claude", CHAT_TAB_SESSION_ID_OPTION = "@kobe_session_id", KOBE_HOME_SESSION = "kobe-home", HOME_KIND_OPTION = "@kobe_home";
|
|
6618
6665
|
var init_client2 = __esm(() => {
|
|
6619
6666
|
init_invocation();
|
|
6667
|
+
init_launch();
|
|
6620
6668
|
KOBE_TMUX_SOCKET = process.env.KOBE_TMUX_SOCKET?.trim() || "kobe";
|
|
6621
6669
|
SAFE_SPAWN_CWD = homedir11() || "/";
|
|
6622
6670
|
CLAUDE_ROLE_OPTION = PANE_ROLE_OPTION;
|
|
@@ -6770,11 +6818,13 @@ async function runAutoTitlePass(orch, derive = deriveTitleFromSession) {
|
|
|
6770
6818
|
}
|
|
6771
6819
|
return renamed;
|
|
6772
6820
|
}
|
|
6773
|
-
function startAutoTitlePoller(orch, intervalMs = DEFAULT_AUTO_TITLE_POLL_MS) {
|
|
6821
|
+
function startAutoTitlePoller(orch, intervalMs = DEFAULT_AUTO_TITLE_POLL_MS, hasSubscribers) {
|
|
6774
6822
|
if (intervalMs <= 0)
|
|
6775
6823
|
return () => {};
|
|
6776
6824
|
let running = false;
|
|
6777
6825
|
const tick = () => {
|
|
6826
|
+
if (hasSubscribers && !hasSubscribers())
|
|
6827
|
+
return;
|
|
6778
6828
|
if (running)
|
|
6779
6829
|
return;
|
|
6780
6830
|
running = true;
|
|
@@ -7517,6 +7567,8 @@ class WorktreeChangesCollector {
|
|
|
7517
7567
|
tick() {
|
|
7518
7568
|
if (this.stopped)
|
|
7519
7569
|
return;
|
|
7570
|
+
if (this.options.hasSubscribers && !this.options.hasSubscribers())
|
|
7571
|
+
return;
|
|
7520
7572
|
try {
|
|
7521
7573
|
const tracked = trackedWorktreePaths(this.orch.listTasks());
|
|
7522
7574
|
let pruned = false;
|
|
@@ -7571,10 +7623,10 @@ class WorktreeChangesCollector {
|
|
|
7571
7623
|
this.bus.publish("worktree.changes", { changes });
|
|
7572
7624
|
}
|
|
7573
7625
|
}
|
|
7574
|
-
function startWorktreeChangesCollector(orch, bus, tickMs = DEFAULT_WORKTREE_CHANGES_TICK_MS) {
|
|
7626
|
+
function startWorktreeChangesCollector(orch, bus, tickMs = DEFAULT_WORKTREE_CHANGES_TICK_MS, hasSubscribers) {
|
|
7575
7627
|
if (tickMs <= 0)
|
|
7576
7628
|
return () => {};
|
|
7577
|
-
const collector = new WorktreeChangesCollector(orch, bus);
|
|
7629
|
+
const collector = new WorktreeChangesCollector(orch, bus, { hasSubscribers });
|
|
7578
7630
|
collector.tick();
|
|
7579
7631
|
const timer = setInterval(() => collector.tick(), tickMs);
|
|
7580
7632
|
timer.unref?.();
|
|
@@ -7617,6 +7669,12 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7617
7669
|
n++;
|
|
7618
7670
|
return n;
|
|
7619
7671
|
}
|
|
7672
|
+
function hasSubscribers() {
|
|
7673
|
+
for (const c of clients)
|
|
7674
|
+
if (c.subscribed)
|
|
7675
|
+
return true;
|
|
7676
|
+
return false;
|
|
7677
|
+
}
|
|
7620
7678
|
function cancelIdleTimer() {
|
|
7621
7679
|
if (idleTimer) {
|
|
7622
7680
|
clearTimeout(idleTimer);
|
|
@@ -7652,7 +7710,8 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7652
7710
|
socket,
|
|
7653
7711
|
buffer: "",
|
|
7654
7712
|
subscribed: false,
|
|
7655
|
-
holdsLifetime: false
|
|
7713
|
+
holdsLifetime: false,
|
|
7714
|
+
channels: null
|
|
7656
7715
|
};
|
|
7657
7716
|
clients.add(client);
|
|
7658
7717
|
socket.on("data", (chunk) => {
|
|
@@ -7684,7 +7743,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7684
7743
|
updateTimer.unref?.();
|
|
7685
7744
|
}
|
|
7686
7745
|
const autoTitlePollMs = options.autoTitlePollMs ?? DEFAULT_AUTO_TITLE_POLL_MS;
|
|
7687
|
-
const stopAutoTitlePoller = startAutoTitlePoller(orch, autoTitlePollMs);
|
|
7746
|
+
const stopAutoTitlePoller = startAutoTitlePoller(orch, autoTitlePollMs, hasSubscribers);
|
|
7688
7747
|
const stopUiPrefsWatcher = startUiPrefsWatcher(bus, {
|
|
7689
7748
|
statePath: defaultUiPrefsStatePath(options.homeDir),
|
|
7690
7749
|
debounceMs: options.uiPrefsDebounceMs ?? DEFAULT_UI_PREFS_DEBOUNCE_MS
|
|
@@ -7693,7 +7752,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7693
7752
|
path: defaultKeybindingsPath(options.homeDir),
|
|
7694
7753
|
debounceMs: options.keybindingsDebounceMs ?? DEFAULT_KEYBINDINGS_DEBOUNCE_MS
|
|
7695
7754
|
});
|
|
7696
|
-
const stopWorktreeChangesCollector = startWorktreeChangesCollector(orch, bus, options.worktreeChangesTickMs ?? DEFAULT_WORKTREE_CHANGES_TICK_MS);
|
|
7755
|
+
const stopWorktreeChangesCollector = startWorktreeChangesCollector(orch, bus, options.worktreeChangesTickMs ?? DEFAULT_WORKTREE_CHANGES_TICK_MS, hasSubscribers);
|
|
7697
7756
|
const serverApi = {
|
|
7698
7757
|
socketPath,
|
|
7699
7758
|
pidPath,
|
|
@@ -7743,21 +7802,28 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7743
7802
|
async function dispatch(req, client) {
|
|
7744
7803
|
if (req.name === "subscribe") {
|
|
7745
7804
|
const payload = objectPayload(req.payload);
|
|
7805
|
+
const wasSubscribed = client.subscribed;
|
|
7746
7806
|
client.subscribed = true;
|
|
7747
7807
|
const role = payload.role === "gui" ? "gui" : "pane";
|
|
7748
7808
|
client.holdsLifetime = role === "gui";
|
|
7809
|
+
client.channels = normalizeChannelFilter(payload.channels);
|
|
7810
|
+
const firstSubscriber = !wasSubscribed;
|
|
7749
7811
|
if (client.holdsLifetime)
|
|
7750
7812
|
cancelIdleTimer();
|
|
7751
|
-
logDaemonInfo("conn", `client #${client.id} subscribed as ${role} \u2014 ${clients.size} client(s), ${guiCount()} gui`);
|
|
7813
|
+
logDaemonInfo("conn", `client #${client.id} subscribed as ${role}${client.channels ? ` [${[...client.channels].join(",")}]` : ""} \u2014 ${clients.size} client(s), ${guiCount()} gui${firstSubscriber ? " (collectors resume)" : ""}`);
|
|
7752
7814
|
for (const event of bus.snapshot()) {
|
|
7815
|
+
if (client.channels && !client.channels.has(event.channel))
|
|
7816
|
+
continue;
|
|
7753
7817
|
writeFrame(client, { type: "event", name: event.channel, payload: event.payload });
|
|
7754
7818
|
}
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7819
|
+
if (!client.channels || client.channels.has("engine-state")) {
|
|
7820
|
+
for (const payload2 of activity.currentNonIdle()) {
|
|
7821
|
+
writeFrame(client, {
|
|
7822
|
+
type: "event",
|
|
7823
|
+
name: "engine-state",
|
|
7824
|
+
payload: payload2
|
|
7825
|
+
});
|
|
7826
|
+
}
|
|
7761
7827
|
}
|
|
7762
7828
|
return {};
|
|
7763
7829
|
}
|
|
@@ -7816,10 +7882,13 @@ function writeFrame(client, frame) {
|
|
|
7816
7882
|
client.socket.write(frameToLine(frame));
|
|
7817
7883
|
}
|
|
7818
7884
|
function broadcast(clients, frame) {
|
|
7885
|
+
const channel = frame.type === "event" && frame.name !== "daemon.stopping" ? frame.name : null;
|
|
7819
7886
|
let line = null;
|
|
7820
7887
|
for (const client of clients) {
|
|
7821
7888
|
if (!client.subscribed && frame.type === "event")
|
|
7822
7889
|
continue;
|
|
7890
|
+
if (channel && client.channels && !client.channels.has(channel))
|
|
7891
|
+
continue;
|
|
7823
7892
|
line ??= frameToLine(frame);
|
|
7824
7893
|
client.socket.write(line);
|
|
7825
7894
|
}
|
|
@@ -10436,28 +10505,6 @@ var init_tmux_border_theme = __esm(() => {
|
|
|
10436
10505
|
FOCUS_ACCENT_SLOT_NAMES2 = ["primary", "success", "info"];
|
|
10437
10506
|
});
|
|
10438
10507
|
|
|
10439
|
-
// src/tui/panes/terminal/launch.ts
|
|
10440
|
-
function wrapEngineLaunch(engineCmd, remoteKey, remoteCwd) {
|
|
10441
|
-
if (!remoteKey)
|
|
10442
|
-
return engineCmd;
|
|
10443
|
-
const host = execHostForRepo(remoteKey);
|
|
10444
|
-
host.ensureReady();
|
|
10445
|
-
return host.wrapCommand(engineCmd, { tty: true, cwd: remoteCwd });
|
|
10446
|
-
}
|
|
10447
|
-
function inheritedEnvPrefix() {
|
|
10448
|
-
const parts = [];
|
|
10449
|
-
for (const key of ["KOBE_HOME_DIR", "KOBE_DAEMON_SOCKET_PATH", "KOBE_TMUX_SOCKET"]) {
|
|
10450
|
-
const value = process.env[key];
|
|
10451
|
-
if (value && value.length > 0)
|
|
10452
|
-
parts.push(`${key}=${shellQuote(value)}`);
|
|
10453
|
-
}
|
|
10454
|
-
return parts.length > 0 ? `${parts.join(" ")} ` : "";
|
|
10455
|
-
}
|
|
10456
|
-
var REMOTE_KEY_OPTION = "@kobe_remote";
|
|
10457
|
-
var init_launch = __esm(() => {
|
|
10458
|
-
init_resolve();
|
|
10459
|
-
});
|
|
10460
|
-
|
|
10461
10508
|
// src/tui/panes/terminal/pane-heal.ts
|
|
10462
10509
|
function parseKobePaneRows(stdout) {
|
|
10463
10510
|
const rows = [];
|
|
@@ -10480,9 +10527,6 @@ function parseKobePaneRows(stdout) {
|
|
|
10480
10527
|
}
|
|
10481
10528
|
return rows;
|
|
10482
10529
|
}
|
|
10483
|
-
function paneIdsByRole(rows, role) {
|
|
10484
|
-
return rows.filter((row) => row.role === role).map((row) => row.paneId);
|
|
10485
|
-
}
|
|
10486
10530
|
function planPaneHeals(rows, opts) {
|
|
10487
10531
|
const byWindow = new Map;
|
|
10488
10532
|
for (const row of rows) {
|
|
@@ -10498,7 +10542,8 @@ function planPaneHeals(rows, opts) {
|
|
|
10498
10542
|
if (tasksPane && (opts.force || tasksPane.version !== opts.currentVersion)) {
|
|
10499
10543
|
targets.push({ role: "tasks", paneId: tasksPane.paneId });
|
|
10500
10544
|
}
|
|
10501
|
-
|
|
10545
|
+
const opsNeedsRespawn = opts.force ? true : claudePane && (opts.vendorChanged || opsPane?.version !== opts.currentVersion);
|
|
10546
|
+
if (opsPane && opsNeedsRespawn) {
|
|
10502
10547
|
targets.push({ role: "ops", paneId: opsPane.paneId, claudePaneId: claudePane ?? null });
|
|
10503
10548
|
}
|
|
10504
10549
|
}
|
|
@@ -10533,15 +10578,22 @@ function respawnCommandsFor(targets, args) {
|
|
|
10533
10578
|
}
|
|
10534
10579
|
return commands;
|
|
10535
10580
|
}
|
|
10536
|
-
async function relaunchEngineInAllWindows(session, cwd, command, remoteKey) {
|
|
10581
|
+
async function relaunchEngineInAllWindows(session, cwd, command, remoteKey, vendor) {
|
|
10537
10582
|
const rows = await listKobePanes(session);
|
|
10538
10583
|
if (!rows)
|
|
10539
10584
|
return false;
|
|
10540
|
-
const enginePanes =
|
|
10585
|
+
const enginePanes = rows.filter((r) => r.role === "claude");
|
|
10541
10586
|
if (enginePanes.length === 0)
|
|
10542
10587
|
return false;
|
|
10543
|
-
const
|
|
10544
|
-
|
|
10588
|
+
const localCwd = localSpawnCwd(cwd);
|
|
10589
|
+
const commands = [];
|
|
10590
|
+
for (const pane of enginePanes) {
|
|
10591
|
+
const launch = withClaudeSessionId(command, vendor);
|
|
10592
|
+
const cmd = keepAlive(wrapEngineLaunch(shellQuoteArgv(launch.argv), remoteKey, cwd));
|
|
10593
|
+
commands.push(["respawn-pane", "-k", "-c", localCwd, "-t", pane.paneId, cmd]);
|
|
10594
|
+
commands.push(launch.sessionId ? ["set-window-option", "-t", pane.paneId, CHAT_TAB_SESSION_ID_OPTION, launch.sessionId] : ["set-window-option", "-u", "-t", pane.paneId, CHAT_TAB_SESSION_ID_OPTION]);
|
|
10595
|
+
}
|
|
10596
|
+
await runTmuxSequence(commands);
|
|
10545
10597
|
return true;
|
|
10546
10598
|
}
|
|
10547
10599
|
async function globalLayoutPrefs() {
|
|
@@ -10583,7 +10635,11 @@ async function healWorkspaceLayout(session, versions) {
|
|
|
10583
10635
|
}
|
|
10584
10636
|
}
|
|
10585
10637
|
if (versions) {
|
|
10586
|
-
commands.push(...respawnCommandsFor(planPaneHeals(rows, {
|
|
10638
|
+
commands.push(...respawnCommandsFor(planPaneHeals(rows, {
|
|
10639
|
+
currentVersion: CURRENT_VERSION,
|
|
10640
|
+
force: false,
|
|
10641
|
+
vendorChanged: versions.vendorChanged
|
|
10642
|
+
}), versions));
|
|
10587
10643
|
}
|
|
10588
10644
|
if (commands.length > 0)
|
|
10589
10645
|
await runTmuxSequence(commands);
|
|
@@ -10650,6 +10706,7 @@ async function refreshKobeWorkspacePanes(session) {
|
|
|
10650
10706
|
var PANE_VERSION_OPTION = "@kobe_pane_version", KOBE_PANE_LIST_FORMAT;
|
|
10651
10707
|
var init_pane_heal = __esm(() => {
|
|
10652
10708
|
init_invocation();
|
|
10709
|
+
init_interactive_command();
|
|
10653
10710
|
init_resolve();
|
|
10654
10711
|
init_client2();
|
|
10655
10712
|
init_tmux_border_theme();
|
|
@@ -10988,10 +11045,15 @@ async function ensureSessionImpl(opts) {
|
|
|
10988
11045
|
return true;
|
|
10989
11046
|
}
|
|
10990
11047
|
if (action.kind === "respawn-engine") {
|
|
10991
|
-
if (await relaunchEngineInAllWindows(opts.name, opts.cwd, opts.command, remoteKey)) {
|
|
11048
|
+
if (await relaunchEngineInAllWindows(opts.name, opts.cwd, opts.command, remoteKey, opts.vendor)) {
|
|
10992
11049
|
if (opts.vendor)
|
|
10993
11050
|
await setSessionOption(opts.name, "@kobe_vendor", opts.vendor);
|
|
10994
|
-
await healWorkspaceLayout(opts.name, {
|
|
11051
|
+
await healWorkspaceLayout(opts.name, {
|
|
11052
|
+
cwd: opts.cwd,
|
|
11053
|
+
taskId: opts.taskId,
|
|
11054
|
+
vendor: opts.vendor,
|
|
11055
|
+
vendorChanged: true
|
|
11056
|
+
});
|
|
10995
11057
|
return true;
|
|
10996
11058
|
}
|
|
10997
11059
|
}
|
|
@@ -11643,6 +11705,23 @@ async function setActive(ctx) {
|
|
|
11643
11705
|
await daemon.request("task.setActive", { taskId });
|
|
11644
11706
|
return { ok: true, activeTaskId: taskId };
|
|
11645
11707
|
}
|
|
11708
|
+
async function archive(ctx) {
|
|
11709
|
+
const daemon = daemonOf(ctx);
|
|
11710
|
+
const taskId = ctx.args.require("task-id");
|
|
11711
|
+
const archived = ctx.args.bool("archived") ?? true;
|
|
11712
|
+
const res = await daemon.request("task.archive", { taskId, archived });
|
|
11713
|
+
if (archived)
|
|
11714
|
+
await ctx.runtime.tearDownSession(taskId);
|
|
11715
|
+
return res;
|
|
11716
|
+
}
|
|
11717
|
+
async function deleteTask(ctx) {
|
|
11718
|
+
const daemon = daemonOf(ctx);
|
|
11719
|
+
const taskId = ctx.args.require("task-id");
|
|
11720
|
+
const force = ctx.args.bool("force") ?? false;
|
|
11721
|
+
const res = await daemon.request("task.delete", { taskId, force });
|
|
11722
|
+
await ctx.runtime.tearDownSession(taskId);
|
|
11723
|
+
return res;
|
|
11724
|
+
}
|
|
11646
11725
|
async function adopt(ctx) {
|
|
11647
11726
|
const daemon = daemonOf(ctx);
|
|
11648
11727
|
const { args } = ctx;
|
|
@@ -12006,10 +12085,7 @@ var init_api_cmd = __esm(() => {
|
|
|
12006
12085
|
F.taskId(),
|
|
12007
12086
|
{ name: "archived", type: "bool", default: "true", description: "true to archive, false to unarchive." }
|
|
12008
12087
|
],
|
|
12009
|
-
handler:
|
|
12010
|
-
taskId: ctx.args.require("task-id"),
|
|
12011
|
-
archived: ctx.args.bool("archived") ?? true
|
|
12012
|
-
})
|
|
12088
|
+
handler: archive
|
|
12013
12089
|
},
|
|
12014
12090
|
{
|
|
12015
12091
|
name: "pin",
|
|
@@ -12039,10 +12115,7 @@ var init_api_cmd = __esm(() => {
|
|
|
12039
12115
|
name: "delete",
|
|
12040
12116
|
summary: "Permanently remove a task (and its worktree). DESTRUCTIVE \u2014 prefer `archive`. Needs --force on a dirty worktree.",
|
|
12041
12117
|
flags: [F.taskId(), { name: "force", type: "bool", description: "Delete even with uncommitted changes." }],
|
|
12042
|
-
handler:
|
|
12043
|
-
taskId: ctx.args.require("task-id"),
|
|
12044
|
-
force: ctx.args.bool("force") ?? false
|
|
12045
|
-
})
|
|
12118
|
+
handler: deleteTask
|
|
12046
12119
|
},
|
|
12047
12120
|
{
|
|
12048
12121
|
name: "discover-adoptable",
|
|
@@ -12086,7 +12159,12 @@ var init_api_cmd = __esm(() => {
|
|
|
12086
12159
|
isTaskRunning: (taskId) => sessionExists(tmuxSessionName(taskId)),
|
|
12087
12160
|
deliverPrompt: (client, target, prompt) => deliverPrompt(client, target, prompt),
|
|
12088
12161
|
resolveRepoRoot: async (absPath) => (await Promise.resolve().then(() => (init_repos(), exports_repos))).resolveRepoRoot(absPath),
|
|
12089
|
-
readWorktreeChanges: async (worktreePath) => (await Promise.resolve().then(() => (init_worktree_changes(), exports_worktree_changes))).readWorktreeChanges(worktreePath)
|
|
12162
|
+
readWorktreeChanges: async (worktreePath) => (await Promise.resolve().then(() => (init_worktree_changes(), exports_worktree_changes))).readWorktreeChanges(worktreePath),
|
|
12163
|
+
tearDownSession: async (taskId) => {
|
|
12164
|
+
const session = tmuxSessionName(taskId);
|
|
12165
|
+
await switchClientBeforeKill(session).catch(() => {});
|
|
12166
|
+
await killSession(session).catch(() => {});
|
|
12167
|
+
}
|
|
12090
12168
|
};
|
|
12091
12169
|
});
|
|
12092
12170
|
|
|
@@ -13071,6 +13149,19 @@ function unquoteGitPath(p) {
|
|
|
13071
13149
|
return inner.replace(/\\([\\"nt])/g, (_m, c) => c === "n" ? `
|
|
13072
13150
|
` : c === "t" ? "\t" : c);
|
|
13073
13151
|
}
|
|
13152
|
+
async function mapPool(items, limit, fn) {
|
|
13153
|
+
const results = new Array(items.length);
|
|
13154
|
+
const width = Math.max(1, Math.min(limit, items.length));
|
|
13155
|
+
let next = 0;
|
|
13156
|
+
async function worker() {
|
|
13157
|
+
while (next < items.length) {
|
|
13158
|
+
const index = next++;
|
|
13159
|
+
results[index] = await fn(items[index], index);
|
|
13160
|
+
}
|
|
13161
|
+
}
|
|
13162
|
+
await Promise.all(Array.from({ length: width }, () => worker()));
|
|
13163
|
+
return results;
|
|
13164
|
+
}
|
|
13074
13165
|
async function untrackedPatch(cwd, relPath) {
|
|
13075
13166
|
const res = await runGit(cwd, ["diff", "--no-color", "--no-index", "--", "/dev/null", relPath]);
|
|
13076
13167
|
if (res.stdout.trim())
|
|
@@ -13120,6 +13211,7 @@ async function handleDiffRequest(req, url) {
|
|
|
13120
13211
|
const stagedByFile = splitDiffByFile(staged.stdout);
|
|
13121
13212
|
const records = statusRes.stdout.split("\x00");
|
|
13122
13213
|
const files = [];
|
|
13214
|
+
const untrackedJobs = [];
|
|
13123
13215
|
for (let i = 0;i < records.length; i++) {
|
|
13124
13216
|
const rec = records[i];
|
|
13125
13217
|
if (!rec)
|
|
@@ -13136,7 +13228,7 @@ async function handleDiffRequest(req, url) {
|
|
|
13136
13228
|
const staged2 = x !== " " && x !== "?";
|
|
13137
13229
|
let patch = "";
|
|
13138
13230
|
if (untracked) {
|
|
13139
|
-
|
|
13231
|
+
untrackedJobs.push({ fileIndex: files.length, path: path11 });
|
|
13140
13232
|
} else {
|
|
13141
13233
|
patch = stagedByFile.get(path11) ?? unstagedByFile.get(path11) ?? "";
|
|
13142
13234
|
if (stagedByFile.has(path11) && unstagedByFile.has(path11)) {
|
|
@@ -13150,13 +13242,75 @@ async function handleDiffRequest(req, url) {
|
|
|
13150
13242
|
patch
|
|
13151
13243
|
});
|
|
13152
13244
|
}
|
|
13245
|
+
if (untrackedJobs.length > 0) {
|
|
13246
|
+
const patches = await mapPool(untrackedJobs, UNTRACKED_DIFF_CONCURRENCY, (job) => untrackedPatch(worktreePath, job.path));
|
|
13247
|
+
for (let j = 0;j < untrackedJobs.length; j++) {
|
|
13248
|
+
files[untrackedJobs[j].fileIndex].patch = patches[j];
|
|
13249
|
+
}
|
|
13250
|
+
}
|
|
13153
13251
|
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
13154
13252
|
const raw = [staged.stdout, unstaged.stdout].filter(Boolean).join(`
|
|
13155
13253
|
`);
|
|
13156
13254
|
const result = { files, raw };
|
|
13157
13255
|
return Response.json(result);
|
|
13158
13256
|
}
|
|
13159
|
-
var GIT_TIMEOUT_MS = 15000;
|
|
13257
|
+
var GIT_TIMEOUT_MS = 15000, UNTRACKED_DIFF_CONCURRENCY = 8;
|
|
13258
|
+
|
|
13259
|
+
// src/web/history.ts
|
|
13260
|
+
import { isAbsolute } from "path";
|
|
13261
|
+
function isSafeVendor(value) {
|
|
13262
|
+
return typeof value === "string" && value.length > 0 && /^[A-Za-z0-9_-]+$/.test(value);
|
|
13263
|
+
}
|
|
13264
|
+
function isSafeSessionId(value) {
|
|
13265
|
+
return typeof value === "string" && value.length > 0 && /^[A-Za-z0-9._-]+$/.test(value) && !value.includes("..");
|
|
13266
|
+
}
|
|
13267
|
+
async function handleSessions(url) {
|
|
13268
|
+
const worktreePath = url.searchParams.get("worktreePath");
|
|
13269
|
+
const vendor = url.searchParams.get("vendor") ?? "claude";
|
|
13270
|
+
if (!worktreePath || !isAbsolute(worktreePath)) {
|
|
13271
|
+
return Response.json({ error: "worktreePath must be an absolute path" }, { status: 400 });
|
|
13272
|
+
}
|
|
13273
|
+
if (!isSafeVendor(vendor)) {
|
|
13274
|
+
return Response.json({ error: "invalid vendor" }, { status: 400 });
|
|
13275
|
+
}
|
|
13276
|
+
try {
|
|
13277
|
+
const reader = engineEntry(vendor).history;
|
|
13278
|
+
const [sessions, latestMtime] = await Promise.all([
|
|
13279
|
+
reader.listSessionIdsForWorktree(worktreePath),
|
|
13280
|
+
reader.latestTranscriptMtimeForWorktree(worktreePath)
|
|
13281
|
+
]);
|
|
13282
|
+
return Response.json({ sessions, latestMtime });
|
|
13283
|
+
} catch (err) {
|
|
13284
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
13285
|
+
}
|
|
13286
|
+
}
|
|
13287
|
+
async function handleMessages(url) {
|
|
13288
|
+
const vendor = url.searchParams.get("vendor") ?? "claude";
|
|
13289
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
13290
|
+
if (!isSafeVendor(vendor)) {
|
|
13291
|
+
return Response.json({ error: "invalid vendor" }, { status: 400 });
|
|
13292
|
+
}
|
|
13293
|
+
if (!isSafeSessionId(sessionId)) {
|
|
13294
|
+
return Response.json({ error: "invalid sessionId" }, { status: 400 });
|
|
13295
|
+
}
|
|
13296
|
+
try {
|
|
13297
|
+
const messages = await engineEntry(vendor).history.readHistory(sessionId);
|
|
13298
|
+
return Response.json({ messages });
|
|
13299
|
+
} catch (err) {
|
|
13300
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
13301
|
+
}
|
|
13302
|
+
}
|
|
13303
|
+
async function handleHistoryRequest(req, url) {
|
|
13304
|
+
if (url.pathname !== SESSIONS_ROUTE && url.pathname !== MESSAGES_ROUTE)
|
|
13305
|
+
return null;
|
|
13306
|
+
if (req.method !== "GET")
|
|
13307
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
13308
|
+
return url.pathname === SESSIONS_ROUTE ? handleSessions(url) : handleMessages(url);
|
|
13309
|
+
}
|
|
13310
|
+
var SESSIONS_ROUTE = "/api/history/sessions", MESSAGES_ROUTE = "/api/history/messages";
|
|
13311
|
+
var init_history4 = __esm(() => {
|
|
13312
|
+
init_registry();
|
|
13313
|
+
});
|
|
13160
13314
|
|
|
13161
13315
|
// src/web/notes.ts
|
|
13162
13316
|
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
@@ -13223,6 +13377,104 @@ var init_notes = __esm(() => {
|
|
|
13223
13377
|
init_env();
|
|
13224
13378
|
});
|
|
13225
13379
|
|
|
13380
|
+
// src/web/themes.ts
|
|
13381
|
+
function resolveHex(themeJson, value, chain = []) {
|
|
13382
|
+
if (value === undefined)
|
|
13383
|
+
return null;
|
|
13384
|
+
if (typeof value !== "string")
|
|
13385
|
+
return resolveHex(themeJson, value.dark, chain);
|
|
13386
|
+
if (value === "transparent" || value === "none")
|
|
13387
|
+
return null;
|
|
13388
|
+
if (value.startsWith("#"))
|
|
13389
|
+
return value;
|
|
13390
|
+
if (chain.includes(value))
|
|
13391
|
+
return null;
|
|
13392
|
+
const next = themeJson.defs?.[value] ?? themeJson.theme[value];
|
|
13393
|
+
return resolveHex(themeJson, next, [...chain, value]);
|
|
13394
|
+
}
|
|
13395
|
+
function clampByte(n) {
|
|
13396
|
+
return Math.max(0, Math.min(255, Math.round(n)));
|
|
13397
|
+
}
|
|
13398
|
+
function mix(a, b, t) {
|
|
13399
|
+
const pa = Number.parseInt(a.slice(1), 16);
|
|
13400
|
+
const pb = Number.parseInt(b.slice(1), 16);
|
|
13401
|
+
const ch = (p, shift) => p >> shift & 255;
|
|
13402
|
+
const out = clampByte(ch(pa, 16) + (ch(pb, 16) - ch(pa, 16)) * t) << 16 | clampByte(ch(pa, 8) + (ch(pb, 8) - ch(pa, 8)) * t) << 8 | clampByte(ch(pa, 0) + (ch(pb, 0) - ch(pa, 0)) * t);
|
|
13403
|
+
return `#${out.toString(16).padStart(6, "0")}`;
|
|
13404
|
+
}
|
|
13405
|
+
function toWebPalette(themeJson) {
|
|
13406
|
+
const slot = (name) => resolveHex(themeJson, themeJson.theme[name]);
|
|
13407
|
+
const bg = slot("background");
|
|
13408
|
+
const fg = slot("text");
|
|
13409
|
+
if (!bg || !fg)
|
|
13410
|
+
return null;
|
|
13411
|
+
const muted = slot("textMuted") ?? mix(fg, bg, 0.35);
|
|
13412
|
+
const primary = slot("primary") ?? fg;
|
|
13413
|
+
const line = slot("border") ?? mix(fg, bg, 0.8);
|
|
13414
|
+
return {
|
|
13415
|
+
bg,
|
|
13416
|
+
surface: slot("backgroundPanel") ?? bg,
|
|
13417
|
+
inset: slot("backgroundElement") ?? bg,
|
|
13418
|
+
menu: slot("backgroundMenu") ?? slot("backgroundElement") ?? bg,
|
|
13419
|
+
line,
|
|
13420
|
+
"line-subtle": slot("borderSubtle") ?? line,
|
|
13421
|
+
"line-active": slot("borderActive") ?? mix(line, fg, 0.3),
|
|
13422
|
+
fg,
|
|
13423
|
+
muted,
|
|
13424
|
+
subtle: mix(muted, bg, 0.35),
|
|
13425
|
+
primary,
|
|
13426
|
+
"primary-hover": mix(primary, fg, 0.35),
|
|
13427
|
+
"kobe-orange": primary,
|
|
13428
|
+
"kobe-green": slot("success") ?? fg,
|
|
13429
|
+
"kobe-blue": slot("info") ?? fg,
|
|
13430
|
+
"kobe-red": slot("error") ?? fg,
|
|
13431
|
+
"kobe-yellow": slot("warning") ?? fg,
|
|
13432
|
+
"kobe-violet": slot("secondary") ?? primary
|
|
13433
|
+
};
|
|
13434
|
+
}
|
|
13435
|
+
function handleThemesRequest(req, url) {
|
|
13436
|
+
if (url.pathname !== THEMES_ROUTE)
|
|
13437
|
+
return null;
|
|
13438
|
+
if (req.method !== "GET")
|
|
13439
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
13440
|
+
return Response.json({ themes: WEB_THEMES });
|
|
13441
|
+
}
|
|
13442
|
+
var THEME_JSONS, WEB_THEMES, THEMES_ROUTE = "/api/themes";
|
|
13443
|
+
var init_themes = __esm(() => {
|
|
13444
|
+
init_claude();
|
|
13445
|
+
init_conductor();
|
|
13446
|
+
init_dracula();
|
|
13447
|
+
init_nord();
|
|
13448
|
+
init_opencode();
|
|
13449
|
+
init_osaka_jade();
|
|
13450
|
+
init_tokyonight();
|
|
13451
|
+
THEME_JSONS = {
|
|
13452
|
+
claude: claude_default,
|
|
13453
|
+
conductor: conductor_default,
|
|
13454
|
+
dracula: dracula_default,
|
|
13455
|
+
nord: nord_default,
|
|
13456
|
+
opencode: opencode_default,
|
|
13457
|
+
"osaka-jade": osaka_jade_default,
|
|
13458
|
+
tokyonight: tokyonight_default
|
|
13459
|
+
};
|
|
13460
|
+
WEB_THEMES = Object.fromEntries(Object.entries(THEME_JSONS).map(([name, json]) => [name, toWebPalette(json)]).filter((entry) => entry[1] !== null));
|
|
13461
|
+
});
|
|
13462
|
+
|
|
13463
|
+
// ../kobe-web/server/spa-channels.ts
|
|
13464
|
+
var SPA_CHANNELS, SPA_CHANNEL_SET;
|
|
13465
|
+
var init_spa_channels = __esm(() => {
|
|
13466
|
+
SPA_CHANNELS = [
|
|
13467
|
+
"task.snapshot",
|
|
13468
|
+
"active-task",
|
|
13469
|
+
"engine-state",
|
|
13470
|
+
"update",
|
|
13471
|
+
"task.jobs",
|
|
13472
|
+
"worktree.changes",
|
|
13473
|
+
"ui-prefs"
|
|
13474
|
+
];
|
|
13475
|
+
SPA_CHANNEL_SET = new Set(SPA_CHANNELS);
|
|
13476
|
+
});
|
|
13477
|
+
|
|
13226
13478
|
// ../kobe-web/server/daemon-link.ts
|
|
13227
13479
|
function sleep2(ms) {
|
|
13228
13480
|
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
@@ -13239,6 +13491,9 @@ class DaemonLink {
|
|
|
13239
13491
|
activeTaskId = null;
|
|
13240
13492
|
engineStates = {};
|
|
13241
13493
|
update = null;
|
|
13494
|
+
jobs = {};
|
|
13495
|
+
worktreeChanges = {};
|
|
13496
|
+
uiPrefs = null;
|
|
13242
13497
|
async start() {
|
|
13243
13498
|
await this.connectOnce(true);
|
|
13244
13499
|
}
|
|
@@ -13248,6 +13503,9 @@ class DaemonLink {
|
|
|
13248
13503
|
activeTaskId: this.activeTaskId,
|
|
13249
13504
|
engineStates: this.engineStates,
|
|
13250
13505
|
update: this.update,
|
|
13506
|
+
jobs: this.jobs,
|
|
13507
|
+
worktreeChanges: this.worktreeChanges,
|
|
13508
|
+
uiPrefs: this.uiPrefs,
|
|
13251
13509
|
connected: this.connected
|
|
13252
13510
|
};
|
|
13253
13511
|
}
|
|
@@ -13289,9 +13547,10 @@ class DaemonLink {
|
|
|
13289
13547
|
if (hello.tasks)
|
|
13290
13548
|
this.tasks = hello.tasks;
|
|
13291
13549
|
this.engineStates = {};
|
|
13550
|
+
this.jobs = {};
|
|
13292
13551
|
client.on("*", (frame) => this.onFrame(frame.name, frame.payload));
|
|
13293
13552
|
client.onLifecycle("close", () => this.onDrop(client));
|
|
13294
|
-
await client.subscribe({ role: "gui" });
|
|
13553
|
+
await client.subscribe({ role: "gui", channels: SPA_CHANNELS });
|
|
13295
13554
|
} catch (err) {
|
|
13296
13555
|
client.close();
|
|
13297
13556
|
throw err;
|
|
@@ -13322,9 +13581,16 @@ class DaemonLink {
|
|
|
13322
13581
|
}
|
|
13323
13582
|
onFrame(name, payload) {
|
|
13324
13583
|
switch (name) {
|
|
13325
|
-
case "task.snapshot":
|
|
13326
|
-
|
|
13584
|
+
case "task.snapshot": {
|
|
13585
|
+
const tasks = payload.tasks;
|
|
13586
|
+
this.tasks = tasks;
|
|
13587
|
+
const live = new Set(tasks.map((t) => t.id));
|
|
13588
|
+
const kept = Object.entries(this.engineStates).filter(([id]) => live.has(id));
|
|
13589
|
+
if (kept.length !== Object.keys(this.engineStates).length) {
|
|
13590
|
+
this.engineStates = Object.fromEntries(kept);
|
|
13591
|
+
}
|
|
13327
13592
|
break;
|
|
13593
|
+
}
|
|
13328
13594
|
case "active-task":
|
|
13329
13595
|
this.activeTaskId = payload.taskId;
|
|
13330
13596
|
break;
|
|
@@ -13336,11 +13602,29 @@ class DaemonLink {
|
|
|
13336
13602
|
case "update":
|
|
13337
13603
|
this.update = payload.info;
|
|
13338
13604
|
break;
|
|
13605
|
+
case "task.jobs": {
|
|
13606
|
+
const job = payload;
|
|
13607
|
+
if (job.phase === "running") {
|
|
13608
|
+
this.jobs = { ...this.jobs, [job.taskId]: job };
|
|
13609
|
+
} else {
|
|
13610
|
+
const { [job.taskId]: _done, ...rest } = this.jobs;
|
|
13611
|
+
this.jobs = rest;
|
|
13612
|
+
}
|
|
13613
|
+
break;
|
|
13614
|
+
}
|
|
13615
|
+
case "worktree.changes":
|
|
13616
|
+
this.worktreeChanges = payload.changes;
|
|
13617
|
+
break;
|
|
13618
|
+
case "ui-prefs":
|
|
13619
|
+
this.uiPrefs = payload;
|
|
13620
|
+
break;
|
|
13339
13621
|
case "daemon.stopping":
|
|
13340
13622
|
return;
|
|
13341
13623
|
default:
|
|
13342
13624
|
break;
|
|
13343
13625
|
}
|
|
13626
|
+
if (!SPA_CHANNEL_SET.has(name))
|
|
13627
|
+
return;
|
|
13344
13628
|
const event = { channel: name, payload };
|
|
13345
13629
|
for (const sink of this.eventSinks)
|
|
13346
13630
|
sink(event);
|
|
@@ -13359,6 +13643,32 @@ var init_daemon_link = __esm(() => {
|
|
|
13359
13643
|
init_daemon_process();
|
|
13360
13644
|
init_paths2();
|
|
13361
13645
|
init_protocol();
|
|
13646
|
+
init_spa_channels();
|
|
13647
|
+
});
|
|
13648
|
+
|
|
13649
|
+
// ../kobe-web/server/rpc-allowlist.ts
|
|
13650
|
+
var WEB_RPC_ALLOWLIST, WEB_RPC_ALLOWSET;
|
|
13651
|
+
var init_rpc_allowlist = __esm(() => {
|
|
13652
|
+
WEB_RPC_ALLOWLIST = [
|
|
13653
|
+
"daemon.status",
|
|
13654
|
+
"task.list",
|
|
13655
|
+
"task.get",
|
|
13656
|
+
"task.create",
|
|
13657
|
+
"task.archive",
|
|
13658
|
+
"task.rename",
|
|
13659
|
+
"task.setBranch",
|
|
13660
|
+
"task.setVendor",
|
|
13661
|
+
"task.delete",
|
|
13662
|
+
"task.pin",
|
|
13663
|
+
"task.move",
|
|
13664
|
+
"task.status",
|
|
13665
|
+
"task.ensureMain",
|
|
13666
|
+
"task.ensureWorktree",
|
|
13667
|
+
"task.setActive",
|
|
13668
|
+
"worktree.discoverAdoptable",
|
|
13669
|
+
"worktree.adopt"
|
|
13670
|
+
];
|
|
13671
|
+
WEB_RPC_ALLOWSET = new Set(WEB_RPC_ALLOWLIST);
|
|
13362
13672
|
});
|
|
13363
13673
|
|
|
13364
13674
|
// ../kobe-web/server/session.ts
|
|
@@ -13411,9 +13721,15 @@ async function terminalSpec(link, taskId) {
|
|
|
13411
13721
|
const shell = process.env.SHELL?.trim() || "/bin/zsh";
|
|
13412
13722
|
return { cwd: worktreePath, command: [shell, "-il"] };
|
|
13413
13723
|
}
|
|
13724
|
+
async function tearDownTaskSession(taskId) {
|
|
13725
|
+
const session = tmuxSessionName(taskId);
|
|
13726
|
+
await switchClientBeforeKill(session).catch(() => {});
|
|
13727
|
+
await killSession(session).catch(() => {});
|
|
13728
|
+
}
|
|
13414
13729
|
var init_session = __esm(() => {
|
|
13415
13730
|
init_interactive_command();
|
|
13416
13731
|
init_repo_init();
|
|
13732
|
+
init_client2();
|
|
13417
13733
|
init_tmux();
|
|
13418
13734
|
});
|
|
13419
13735
|
|
|
@@ -13457,20 +13773,35 @@ data: ${JSON.stringify(data)}
|
|
|
13457
13773
|
}
|
|
13458
13774
|
});
|
|
13459
13775
|
}
|
|
13460
|
-
async function rpcResponse(req, link) {
|
|
13776
|
+
async function rpcResponse(req, link, tearDown) {
|
|
13461
13777
|
try {
|
|
13462
13778
|
const { name, payload } = await req.json();
|
|
13463
13779
|
if (!name)
|
|
13464
13780
|
return Response.json({ error: "missing rpc name" }, { status: 400 });
|
|
13465
|
-
if (name
|
|
13466
|
-
|
|
13781
|
+
if (!WEB_RPC_ALLOWSET.has(name)) {
|
|
13782
|
+
return Response.json({ error: `rpc ${name} is not exposed to the web UI` }, { status: 403 });
|
|
13467
13783
|
}
|
|
13468
13784
|
const result = await link.request(name, payload);
|
|
13785
|
+
const taskId = payload?.taskId;
|
|
13786
|
+
if (typeof taskId === "string") {
|
|
13787
|
+
const archiving = name === "task.archive" && payload.archived !== false;
|
|
13788
|
+
if (name === "task.delete" || archiving)
|
|
13789
|
+
tearDown(taskId);
|
|
13790
|
+
}
|
|
13469
13791
|
return Response.json({ result });
|
|
13470
13792
|
} catch (err) {
|
|
13471
13793
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
13472
13794
|
}
|
|
13473
13795
|
}
|
|
13796
|
+
async function enginesResponse() {
|
|
13797
|
+
try {
|
|
13798
|
+
const ids = await availableEngineIds();
|
|
13799
|
+
const engines = ids.map((id) => ({ id, label: engineDisplayName(id) }));
|
|
13800
|
+
return Response.json({ engines: engines.length > 0 ? engines : [{ id: "claude", label: "Claude" }] });
|
|
13801
|
+
} catch (err) {
|
|
13802
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
13803
|
+
}
|
|
13804
|
+
}
|
|
13474
13805
|
async function sessionResponse(req, link) {
|
|
13475
13806
|
try {
|
|
13476
13807
|
const { taskId } = await req.json();
|
|
@@ -13491,6 +13822,49 @@ async function specResponse(url, link, spec) {
|
|
|
13491
13822
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
13492
13823
|
}
|
|
13493
13824
|
}
|
|
13825
|
+
function createRequestHandler(deps) {
|
|
13826
|
+
const { link, sseSends, staticDir } = deps;
|
|
13827
|
+
const tearDown = deps.tearDownSession ?? ((taskId) => void tearDownTaskSession(taskId));
|
|
13828
|
+
return async function handle(req) {
|
|
13829
|
+
const url = new URL(req.url);
|
|
13830
|
+
if (url.pathname === WEB_HEALTH_PATH)
|
|
13831
|
+
return new Response(WEB_HEALTH_MARKER);
|
|
13832
|
+
if (url.pathname === "/events") {
|
|
13833
|
+
return sseResponse((send2) => {
|
|
13834
|
+
send2("snapshot", link.snapshot());
|
|
13835
|
+
sseSends.add(send2);
|
|
13836
|
+
return () => {
|
|
13837
|
+
sseSends.delete(send2);
|
|
13838
|
+
};
|
|
13839
|
+
});
|
|
13840
|
+
}
|
|
13841
|
+
if (url.pathname === "/api/rpc" && req.method === "POST")
|
|
13842
|
+
return rpcResponse(req, link, tearDown);
|
|
13843
|
+
if (url.pathname === "/api/session" && req.method === "POST")
|
|
13844
|
+
return sessionResponse(req, link);
|
|
13845
|
+
if (url.pathname === "/api/engine-spec" && req.method === "GET")
|
|
13846
|
+
return specResponse(url, link, engineSpec);
|
|
13847
|
+
if (url.pathname === "/api/terminal-spec" && req.method === "GET")
|
|
13848
|
+
return specResponse(url, link, terminalSpec);
|
|
13849
|
+
if (url.pathname === "/api/engines" && req.method === "GET")
|
|
13850
|
+
return enginesResponse();
|
|
13851
|
+
const notes = await handleNotesRequest(req, url);
|
|
13852
|
+
if (notes)
|
|
13853
|
+
return notes;
|
|
13854
|
+
const diff = await handleDiffRequest(req, url);
|
|
13855
|
+
if (diff)
|
|
13856
|
+
return diff;
|
|
13857
|
+
const history = await handleHistoryRequest(req, url);
|
|
13858
|
+
if (history)
|
|
13859
|
+
return history;
|
|
13860
|
+
const themes = handleThemesRequest(req, url);
|
|
13861
|
+
if (themes)
|
|
13862
|
+
return themes;
|
|
13863
|
+
if (staticDir)
|
|
13864
|
+
return staticResponse(url.pathname, staticDir);
|
|
13865
|
+
return new Response("not found", { status: 404 });
|
|
13866
|
+
};
|
|
13867
|
+
}
|
|
13494
13868
|
async function staticResponse(pathname, staticDir) {
|
|
13495
13869
|
const rel = pathname === "/" ? "/index.html" : pathname;
|
|
13496
13870
|
const resolved = normalize2(join12(staticDir, rel));
|
|
@@ -13557,41 +13931,9 @@ async function createBridgeServer(opts = {}) {
|
|
|
13557
13931
|
for (const send2 of sseSends)
|
|
13558
13932
|
send2("snapshot", link.snapshot());
|
|
13559
13933
|
});
|
|
13560
|
-
const
|
|
13561
|
-
|
|
13562
|
-
|
|
13563
|
-
async fetch(req) {
|
|
13564
|
-
const url = new URL(req.url);
|
|
13565
|
-
if (url.pathname === WEB_HEALTH_PATH)
|
|
13566
|
-
return new Response(WEB_HEALTH_MARKER);
|
|
13567
|
-
if (url.pathname === "/events") {
|
|
13568
|
-
return sseResponse((send2) => {
|
|
13569
|
-
send2("snapshot", link.snapshot());
|
|
13570
|
-
sseSends.add(send2);
|
|
13571
|
-
return () => {
|
|
13572
|
-
sseSends.delete(send2);
|
|
13573
|
-
};
|
|
13574
|
-
});
|
|
13575
|
-
}
|
|
13576
|
-
if (url.pathname === "/api/rpc" && req.method === "POST")
|
|
13577
|
-
return rpcResponse(req, link);
|
|
13578
|
-
if (url.pathname === "/api/session" && req.method === "POST")
|
|
13579
|
-
return sessionResponse(req, link);
|
|
13580
|
-
if (url.pathname === "/api/engine-spec" && req.method === "GET")
|
|
13581
|
-
return specResponse(url, link, engineSpec);
|
|
13582
|
-
if (url.pathname === "/api/terminal-spec" && req.method === "GET")
|
|
13583
|
-
return specResponse(url, link, terminalSpec);
|
|
13584
|
-
const notes = await handleNotesRequest(req, url);
|
|
13585
|
-
if (notes)
|
|
13586
|
-
return notes;
|
|
13587
|
-
const diff = await handleDiffRequest(req, url);
|
|
13588
|
-
if (diff)
|
|
13589
|
-
return diff;
|
|
13590
|
-
if (staticDir)
|
|
13591
|
-
return staticResponse(url.pathname, staticDir);
|
|
13592
|
-
return new Response("not found", { status: 404 });
|
|
13593
|
-
}
|
|
13594
|
-
});
|
|
13934
|
+
const handle = createRequestHandler({ link, sseSends, staticDir });
|
|
13935
|
+
const hostname = process.env.KOBE_WEB_HOST?.trim() || "127.0.0.1";
|
|
13936
|
+
const server = Bun.serve({ port, hostname, idleTimeout: 0, fetch: handle });
|
|
13595
13937
|
return {
|
|
13596
13938
|
port: server.port ?? port,
|
|
13597
13939
|
close() {
|
|
@@ -13602,8 +13944,13 @@ async function createBridgeServer(opts = {}) {
|
|
|
13602
13944
|
}
|
|
13603
13945
|
var WEB_HEALTH_MARKER = "kobe-web", WEB_HEALTH_PATH = "/__kobe_web";
|
|
13604
13946
|
var init_bridge = __esm(() => {
|
|
13947
|
+
init_account_detect();
|
|
13948
|
+
init_interactive_command();
|
|
13949
|
+
init_history4();
|
|
13605
13950
|
init_notes();
|
|
13951
|
+
init_themes();
|
|
13606
13952
|
init_daemon_link();
|
|
13953
|
+
init_rpc_allowlist();
|
|
13607
13954
|
init_session();
|
|
13608
13955
|
});
|
|
13609
13956
|
|
|
@@ -13618,8 +13965,13 @@ __export(exports_web_cmd, {
|
|
|
13618
13965
|
runWebSubcommand: () => runWebSubcommand
|
|
13619
13966
|
});
|
|
13620
13967
|
import { existsSync as existsSync12 } from "fs";
|
|
13968
|
+
import { homedir as homedir17 } from "os";
|
|
13621
13969
|
import { resolve as resolve6 } from "path";
|
|
13622
13970
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13971
|
+
function homeLabel() {
|
|
13972
|
+
const explicit = process.env.KOBE_HOME_DIR?.trim();
|
|
13973
|
+
return explicit ? `sandbox: ${explicit}` : `${homedir17()}/.kobe (production)`;
|
|
13974
|
+
}
|
|
13623
13975
|
function resolveStaticDir() {
|
|
13624
13976
|
const here = fileURLToPath3(import.meta.url);
|
|
13625
13977
|
const candidates = [
|
|
@@ -13737,10 +14089,14 @@ async function runWebSubcommand(args) {
|
|
|
13737
14089
|
};
|
|
13738
14090
|
if (bridgeOnly) {
|
|
13739
14091
|
process.stdout.write(`kobe web bridge listening on http://localhost:${bridge.port} (routes only)
|
|
14092
|
+
`);
|
|
14093
|
+
process.stdout.write(` home: ${homeLabel()}
|
|
13740
14094
|
`);
|
|
13741
14095
|
} else {
|
|
13742
14096
|
pty = await startPtyServer({ webPort: bridge.port, takeover });
|
|
13743
14097
|
process.stdout.write(`kobe web \u2192 http://localhost:${bridge.port}
|
|
14098
|
+
`);
|
|
14099
|
+
process.stdout.write(` home: ${homeLabel()}
|
|
13744
14100
|
`);
|
|
13745
14101
|
if (!pty) {
|
|
13746
14102
|
process.stderr.write(`kobe web: PTY server not found; terminal tabs will be unavailable
|
|
@@ -13890,17 +14246,29 @@ var init_skill_cmd = __esm(() => {
|
|
|
13890
14246
|
var exports_hook_cmd = {};
|
|
13891
14247
|
__export(exports_hook_cmd, {
|
|
13892
14248
|
runHookSubcommand: () => runHookSubcommand,
|
|
14249
|
+
readTextWithTimeout: () => readTextWithTimeout,
|
|
13893
14250
|
parseWorktreeAddPath: () => parseWorktreeAddPath,
|
|
13894
14251
|
ensureGlobalKobeHooks: () => ensureGlobalKobeHooks
|
|
13895
14252
|
});
|
|
13896
|
-
import { homedir as
|
|
14253
|
+
import { homedir as homedir18 } from "os";
|
|
13897
14254
|
import { join as join13, resolve as resolve7 } from "path";
|
|
13898
|
-
async function
|
|
14255
|
+
async function readTextWithTimeout(read, timeoutMs = STDIN_READ_TIMEOUT_MS) {
|
|
14256
|
+
let raceTimer;
|
|
13899
14257
|
try {
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
new Promise((resolve8) =>
|
|
14258
|
+
return await Promise.race([
|
|
14259
|
+
read(),
|
|
14260
|
+
new Promise((resolve8) => {
|
|
14261
|
+
raceTimer = setTimeout(() => resolve8(""), timeoutMs);
|
|
14262
|
+
})
|
|
13903
14263
|
]);
|
|
14264
|
+
} finally {
|
|
14265
|
+
if (raceTimer !== undefined)
|
|
14266
|
+
clearTimeout(raceTimer);
|
|
14267
|
+
}
|
|
14268
|
+
}
|
|
14269
|
+
async function readStdinPayload() {
|
|
14270
|
+
try {
|
|
14271
|
+
const text = await readTextWithTimeout(() => Bun.stdin.text());
|
|
13904
14272
|
if (!text.trim())
|
|
13905
14273
|
return {};
|
|
13906
14274
|
const parsed = JSON.parse(text);
|
|
@@ -14014,7 +14382,7 @@ function activityHookAdapters() {
|
|
|
14014
14382
|
return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsHooks());
|
|
14015
14383
|
}
|
|
14016
14384
|
function globalSettingsPath() {
|
|
14017
|
-
return join13(
|
|
14385
|
+
return join13(homedir18(), ".claude", "settings.json");
|
|
14018
14386
|
}
|
|
14019
14387
|
function persistedSyncPath(stored) {
|
|
14020
14388
|
if (!stored || stored === "off")
|
|
@@ -14066,7 +14434,7 @@ async function runHookSetup(_argv) {
|
|
|
14066
14434
|
].join(`
|
|
14067
14435
|
`));
|
|
14068
14436
|
}
|
|
14069
|
-
var WORKTREE_CREATED_VERB = "worktree-created", SYNC_SETTING_KEY = "externalWorktreeSync";
|
|
14437
|
+
var STDIN_READ_TIMEOUT_MS = 500, WORKTREE_CREATED_VERB = "worktree-created", SYNC_SETTING_KEY = "externalWorktreeSync";
|
|
14070
14438
|
var init_hook_cmd = __esm(() => {
|
|
14071
14439
|
init_daemon_process();
|
|
14072
14440
|
init_hook_adapter2();
|
|
@@ -15400,6 +15768,8 @@ class RemoteOrchestrator {
|
|
|
15400
15768
|
setConnectionState;
|
|
15401
15769
|
ensureReachable;
|
|
15402
15770
|
role;
|
|
15771
|
+
channels;
|
|
15772
|
+
subscribesTasks;
|
|
15403
15773
|
reconnecting = false;
|
|
15404
15774
|
constructor(client, options = {}) {
|
|
15405
15775
|
this.client = client;
|
|
@@ -15435,6 +15805,8 @@ class RemoteOrchestrator {
|
|
|
15435
15805
|
this.setConnectionState = (next) => setConnectionState(() => next);
|
|
15436
15806
|
this.ensureReachable = options.ensureReachable ?? ensureDaemonReachable;
|
|
15437
15807
|
this.role = options.role ?? "pane";
|
|
15808
|
+
this.channels = options.channels;
|
|
15809
|
+
this.subscribesTasks = !options.channels || options.channels.includes("task.snapshot");
|
|
15438
15810
|
this.client.on("*", (frame) => this.handleEvent(frame.name, frame.payload));
|
|
15439
15811
|
this.client.onLifecycle("close", () => {
|
|
15440
15812
|
this.setConnectionState("disconnected");
|
|
@@ -15484,9 +15856,9 @@ class RemoteOrchestrator {
|
|
|
15484
15856
|
throw new Error(`kobe daemon is protocol v${daemonVersion} (min v${daemonMin}); this client is v${DAEMON_PROTOCOL_VERSION} (min v${MIN_COMPATIBLE_PROTOCOL_VERSION}). Restart the daemon (\`kobe daemon restart\`) or upgrade kobe.`);
|
|
15485
15857
|
}
|
|
15486
15858
|
this.setDaemonVersionSig(typeof hello.kobeVersion === "string" ? hello.kobeVersion : null);
|
|
15487
|
-
if (hello.tasks)
|
|
15859
|
+
if (hello.tasks && this.subscribesTasks)
|
|
15488
15860
|
this.setTasks(hello.tasks.map(deserializeTask));
|
|
15489
|
-
await this.client.subscribe({ role: this.role });
|
|
15861
|
+
await this.client.subscribe({ role: this.role, channels: this.channels });
|
|
15490
15862
|
if (hello.capabilities?.includes("worktree.changes")) {
|
|
15491
15863
|
if (this.worktreeChangesAcc() === null)
|
|
15492
15864
|
this.setWorktreeChangesSig(new Map);
|
|
@@ -17190,97 +17562,104 @@ function NewTaskDialogView(props) {
|
|
|
17190
17562
|
setCloneParent(joinDrill(cloneParent(), split.base, picked));
|
|
17191
17563
|
setCloneParentCursor(absoluteIndex);
|
|
17192
17564
|
}
|
|
17565
|
+
const adoptSelectAll = () => {
|
|
17566
|
+
const list2 = adoptList();
|
|
17567
|
+
if (list2.length === 0)
|
|
17568
|
+
return;
|
|
17569
|
+
const allSelected = list2.every((w) => adoptSelected().has(w.path));
|
|
17570
|
+
setAdoptSelected(allSelected ? new Set : new Set(list2.map((w) => w.path)));
|
|
17571
|
+
};
|
|
17193
17572
|
useBindings(() => ({
|
|
17194
|
-
bindings: [
|
|
17195
|
-
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
|
|
17199
|
-
|
|
17200
|
-
|
|
17201
|
-
|
|
17202
|
-
|
|
17203
|
-
|
|
17204
|
-
|
|
17205
|
-
|
|
17206
|
-
|
|
17207
|
-
|
|
17208
|
-
|
|
17209
|
-
|
|
17210
|
-
|
|
17211
|
-
|
|
17212
|
-
|
|
17213
|
-
|
|
17573
|
+
bindings: [
|
|
17574
|
+
{
|
|
17575
|
+
key: "tab",
|
|
17576
|
+
cmd: () => setField((f) => nextField(f, tab()))
|
|
17577
|
+
},
|
|
17578
|
+
{
|
|
17579
|
+
key: "ctrl+]",
|
|
17580
|
+
cmd: () => switchToTab(nextDialogTab(tab()))
|
|
17581
|
+
},
|
|
17582
|
+
{
|
|
17583
|
+
key: "ctrl+[",
|
|
17584
|
+
cmd: () => switchToTab(nextDialogTab(tab()))
|
|
17585
|
+
},
|
|
17586
|
+
{
|
|
17587
|
+
key: "ctrl+e",
|
|
17588
|
+
cmd: () => setVendor((v) => nextVendorWithin(availableVendors(), v))
|
|
17589
|
+
},
|
|
17590
|
+
{
|
|
17591
|
+
key: "up",
|
|
17592
|
+
cmd: () => {
|
|
17593
|
+
if (cloneInFlight())
|
|
17214
17594
|
return;
|
|
17215
|
-
|
|
17216
|
-
|
|
17217
|
-
|
|
17218
|
-
|
|
17219
|
-
|
|
17220
|
-
if (list2.length === 0)
|
|
17595
|
+
if (tab() === "existing" && field() === "repo") {
|
|
17596
|
+
const list2 = activeList();
|
|
17597
|
+
if (list2.length === 0)
|
|
17598
|
+
return;
|
|
17599
|
+
setRepoCursor(clampCursor(repoCursor() - 1, list2.length));
|
|
17221
17600
|
return;
|
|
17222
|
-
|
|
17223
|
-
|
|
17224
|
-
|
|
17225
|
-
|
|
17226
|
-
|
|
17227
|
-
|
|
17601
|
+
}
|
|
17602
|
+
if (tab() === "existing" && field() === "baseRef") {
|
|
17603
|
+
const list2 = branchFiltered();
|
|
17604
|
+
if (list2.length === 0)
|
|
17605
|
+
return;
|
|
17606
|
+
setBranchCursor(clampCursor(branchCursor() - 1, list2.length));
|
|
17228
17607
|
return;
|
|
17229
|
-
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
|
|
17233
|
-
|
|
17234
|
-
|
|
17608
|
+
}
|
|
17609
|
+
if (tab() === "clone" && field() === "cloneParent") {
|
|
17610
|
+
const list2 = cloneParentFiltered();
|
|
17611
|
+
if (list2.length === 0)
|
|
17612
|
+
return;
|
|
17613
|
+
setCloneParentCursor(clampCursor(cloneParentCursor() - 1, list2.length));
|
|
17235
17614
|
return;
|
|
17236
|
-
|
|
17615
|
+
}
|
|
17616
|
+
if (tab() === "adopt") {
|
|
17617
|
+
const list2 = adoptList();
|
|
17618
|
+
if (list2.length === 0)
|
|
17619
|
+
return;
|
|
17620
|
+
setAdoptCursor(clampCursor(adoptCursor() - 1, list2.length));
|
|
17621
|
+
}
|
|
17237
17622
|
}
|
|
17238
|
-
}
|
|
17239
|
-
|
|
17240
|
-
|
|
17241
|
-
|
|
17242
|
-
|
|
17243
|
-
return;
|
|
17244
|
-
if (tab() === "existing" && field() === "repo") {
|
|
17245
|
-
const list2 = activeList();
|
|
17246
|
-
if (list2.length === 0)
|
|
17623
|
+
},
|
|
17624
|
+
{
|
|
17625
|
+
key: "down",
|
|
17626
|
+
cmd: () => {
|
|
17627
|
+
if (cloneInFlight())
|
|
17247
17628
|
return;
|
|
17248
|
-
|
|
17249
|
-
|
|
17250
|
-
|
|
17251
|
-
|
|
17252
|
-
|
|
17253
|
-
if (list2.length === 0)
|
|
17629
|
+
if (tab() === "existing" && field() === "repo") {
|
|
17630
|
+
const list2 = activeList();
|
|
17631
|
+
if (list2.length === 0)
|
|
17632
|
+
return;
|
|
17633
|
+
setRepoCursor(clampCursor(repoCursor() + 1, list2.length));
|
|
17254
17634
|
return;
|
|
17255
|
-
|
|
17256
|
-
|
|
17257
|
-
|
|
17258
|
-
|
|
17259
|
-
|
|
17260
|
-
|
|
17635
|
+
}
|
|
17636
|
+
if (tab() === "existing" && field() === "baseRef") {
|
|
17637
|
+
const list2 = branchFiltered();
|
|
17638
|
+
if (list2.length === 0)
|
|
17639
|
+
return;
|
|
17640
|
+
setBranchCursor(clampCursor(branchCursor() + 1, list2.length));
|
|
17261
17641
|
return;
|
|
17262
|
-
|
|
17263
|
-
|
|
17264
|
-
|
|
17265
|
-
|
|
17266
|
-
|
|
17267
|
-
|
|
17642
|
+
}
|
|
17643
|
+
if (tab() === "clone" && field() === "cloneParent") {
|
|
17644
|
+
const list2 = cloneParentFiltered();
|
|
17645
|
+
if (list2.length === 0)
|
|
17646
|
+
return;
|
|
17647
|
+
setCloneParentCursor(clampCursor(cloneParentCursor() + 1, list2.length));
|
|
17268
17648
|
return;
|
|
17269
|
-
|
|
17649
|
+
}
|
|
17650
|
+
if (tab() === "adopt") {
|
|
17651
|
+
const list2 = adoptList();
|
|
17652
|
+
if (list2.length === 0)
|
|
17653
|
+
return;
|
|
17654
|
+
setAdoptCursor(clampCursor(adoptCursor() + 1, list2.length));
|
|
17655
|
+
}
|
|
17270
17656
|
}
|
|
17271
|
-
}
|
|
17272
|
-
|
|
17273
|
-
|
|
17274
|
-
|
|
17275
|
-
|
|
17276
|
-
|
|
17277
|
-
const list2 = adoptList();
|
|
17278
|
-
if (list2.length === 0)
|
|
17279
|
-
return;
|
|
17280
|
-
const allSelected = list2.every((w) => adoptSelected().has(w.path));
|
|
17281
|
-
setAdoptSelected(allSelected ? new Set : new Set(list2.map((w) => w.path)));
|
|
17282
|
-
}
|
|
17283
|
-
}]
|
|
17657
|
+
},
|
|
17658
|
+
...tab() === "adopt" ? [{
|
|
17659
|
+
key: "ctrl+a",
|
|
17660
|
+
cmd: adoptSelectAll
|
|
17661
|
+
}] : []
|
|
17662
|
+
]
|
|
17284
17663
|
}));
|
|
17285
17664
|
useBindings(() => ({
|
|
17286
17665
|
enabled: field() === "confirm" && !cloneInFlight(),
|
|
@@ -18193,6 +18572,35 @@ var init_quick_task_composer = __esm(() => {
|
|
|
18193
18572
|
};
|
|
18194
18573
|
});
|
|
18195
18574
|
|
|
18575
|
+
// src/client/connect-pane-orchestrator.ts
|
|
18576
|
+
async function connectPaneOrchestrator(options = {}) {
|
|
18577
|
+
const tag = options.logTag ?? "orch-connect";
|
|
18578
|
+
const connect2 = options.connect ?? connectIfRunning;
|
|
18579
|
+
let remote = null;
|
|
18580
|
+
try {
|
|
18581
|
+
const client = await connect2();
|
|
18582
|
+
if (!client) {
|
|
18583
|
+
logClient(tag, "no daemon running \u2014 caller degrades");
|
|
18584
|
+
return null;
|
|
18585
|
+
}
|
|
18586
|
+
remote = new RemoteOrchestrator(client, {
|
|
18587
|
+
...options.orchestratorOptions,
|
|
18588
|
+
channels: options.channels
|
|
18589
|
+
});
|
|
18590
|
+
await remote.init();
|
|
18591
|
+
return remote;
|
|
18592
|
+
} catch (err) {
|
|
18593
|
+
logClientError(tag, err);
|
|
18594
|
+
remote?.dispose();
|
|
18595
|
+
return null;
|
|
18596
|
+
}
|
|
18597
|
+
}
|
|
18598
|
+
var init_connect_pane_orchestrator = __esm(() => {
|
|
18599
|
+
init_client_log();
|
|
18600
|
+
init_daemon_process();
|
|
18601
|
+
init_remote_orchestrator();
|
|
18602
|
+
});
|
|
18603
|
+
|
|
18196
18604
|
// src/tui/context/focus.tsx
|
|
18197
18605
|
function FocusProvider(props) {
|
|
18198
18606
|
const [focused, setFocusedSignal] = createSignal(props.initial ?? "sidebar");
|
|
@@ -18909,7 +19317,7 @@ var init_pulse = () => {};
|
|
|
18909
19317
|
// src/tui/lib/sound.ts
|
|
18910
19318
|
import { existsSync as existsSync14, mkdirSync as mkdirSync6 } from "fs";
|
|
18911
19319
|
import { tmpdir as tmpdir2 } from "os";
|
|
18912
|
-
import { basename as basename6, isAbsolute, join as join15, resolve as resolve8 } from "path";
|
|
19320
|
+
import { basename as basename6, isAbsolute as isAbsolute2, join as join15, resolve as resolve8 } from "path";
|
|
18913
19321
|
function args(player, file, volume) {
|
|
18914
19322
|
if (player === "ffplay")
|
|
18915
19323
|
return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
|
|
@@ -18967,7 +19375,7 @@ function pulse(volume = 0.4) {
|
|
|
18967
19375
|
var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
|
|
18968
19376
|
var init_sound = __esm(() => {
|
|
18969
19377
|
init_pulse();
|
|
18970
|
-
pulseAsset =
|
|
19378
|
+
pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve8(import.meta.dir, pulse_default);
|
|
18971
19379
|
DIR = join15(tmpdir2(), "kobe-sfx");
|
|
18972
19380
|
PLAYERS = [
|
|
18973
19381
|
"ffplay",
|
|
@@ -19195,20 +19603,12 @@ function UiPrefsSync(props) {
|
|
|
19195
19603
|
let disposed = false;
|
|
19196
19604
|
onMount(() => {
|
|
19197
19605
|
(async () => {
|
|
19198
|
-
|
|
19199
|
-
|
|
19200
|
-
|
|
19201
|
-
|
|
19202
|
-
|
|
19203
|
-
return;
|
|
19204
|
-
}
|
|
19205
|
-
remote = new RemoteOrchestrator(client);
|
|
19206
|
-
await remote.init();
|
|
19207
|
-
} catch (err) {
|
|
19208
|
-
logClientError("ui-prefs", err);
|
|
19209
|
-
remote?.dispose();
|
|
19606
|
+
const remote = await connectPaneOrchestrator({
|
|
19607
|
+
logTag: "ui-prefs",
|
|
19608
|
+
channels: ["ui-prefs", "keybindings"]
|
|
19609
|
+
});
|
|
19610
|
+
if (!remote)
|
|
19210
19611
|
return;
|
|
19211
|
-
}
|
|
19212
19612
|
if (disposed) {
|
|
19213
19613
|
remote.dispose();
|
|
19214
19614
|
return;
|
|
@@ -19266,9 +19666,8 @@ var init_host_boot = __esm(() => {
|
|
|
19266
19666
|
init_solid();
|
|
19267
19667
|
init_solid();
|
|
19268
19668
|
init_client_log();
|
|
19269
|
-
init_daemon_process();
|
|
19270
19669
|
init_dev();
|
|
19271
|
-
|
|
19670
|
+
init_connect_pane_orchestrator();
|
|
19272
19671
|
init_focus();
|
|
19273
19672
|
init_keybindings_user();
|
|
19274
19673
|
init_kv();
|
|
@@ -22430,9 +22829,9 @@ var init_task_actions = __esm(() => {
|
|
|
22430
22829
|
// src/tui/lib/worktree-opener.ts
|
|
22431
22830
|
import { spawn as spawn5 } from "child_process";
|
|
22432
22831
|
import { existsSync as existsSync15 } from "fs";
|
|
22433
|
-
import { basename as basename7, delimiter, isAbsolute as
|
|
22832
|
+
import { basename as basename7, delimiter, isAbsolute as isAbsolute3, join as join17 } from "path";
|
|
22434
22833
|
function executableOnPath(command, env, exists) {
|
|
22435
|
-
if (
|
|
22834
|
+
if (isAbsolute3(command))
|
|
22436
22835
|
return exists(command);
|
|
22437
22836
|
const pathEnv = env.PATH ?? "";
|
|
22438
22837
|
for (const dir of pathEnv.split(delimiter)) {
|
|
@@ -23860,7 +24259,9 @@ var init_Sidebar = __esm(() => {
|
|
|
23860
24259
|
// src/tui/tasks-pane/host.tsx
|
|
23861
24260
|
var exports_host3 = {};
|
|
23862
24261
|
__export(exports_host3, {
|
|
23863
|
-
startTasksPane: () => startTasksPane
|
|
24262
|
+
startTasksPane: () => startTasksPane,
|
|
24263
|
+
legendRowCap: () => legendRowCap,
|
|
24264
|
+
legendCap: () => legendCap
|
|
23864
24265
|
});
|
|
23865
24266
|
import { existsSync as existsSync16 } from "fs";
|
|
23866
24267
|
import { stat as stat5 } from "fs/promises";
|
|
@@ -24002,7 +24403,7 @@ function TasksShell(props) {
|
|
|
24002
24403
|
async function archiveTask(id) {
|
|
24003
24404
|
await archiveTaskFlow(taskActions, id);
|
|
24004
24405
|
}
|
|
24005
|
-
async function
|
|
24406
|
+
async function deleteTask2(id) {
|
|
24006
24407
|
await deleteTaskFlow(taskActions, id);
|
|
24007
24408
|
}
|
|
24008
24409
|
async function renameTask(id) {
|
|
@@ -24117,13 +24518,16 @@ function TasksShell(props) {
|
|
|
24117
24518
|
if (exists) {
|
|
24118
24519
|
const cwd2 = await getSessionOption(name, "@kobe_worktree") || task?.worktreePath || "";
|
|
24119
24520
|
if (worktreeCwdUsable(cwd2)) {
|
|
24521
|
+
const init3 = task?.repo ? resolveRepoInit(task.repo, cwd2) : {};
|
|
24120
24522
|
await ensureSession({
|
|
24121
24523
|
name,
|
|
24122
24524
|
cwd: cwd2,
|
|
24123
24525
|
command: interactiveEngineCommand(task?.vendor),
|
|
24124
24526
|
taskId: id,
|
|
24125
24527
|
vendor: task?.vendor,
|
|
24126
|
-
repo: task?.repo
|
|
24528
|
+
repo: task?.repo,
|
|
24529
|
+
initScript: init3.initScript,
|
|
24530
|
+
initPrompt: init3.initPrompt
|
|
24127
24531
|
});
|
|
24128
24532
|
}
|
|
24129
24533
|
await runTmux(["switch-client", "-t", `=${name}`]);
|
|
@@ -24222,7 +24626,7 @@ function TasksShell(props) {
|
|
|
24222
24626
|
} : undefined;
|
|
24223
24627
|
},
|
|
24224
24628
|
onRenameRequest: (id) => void renameTask(id),
|
|
24225
|
-
onDeleteRequest: (id) => void
|
|
24629
|
+
onDeleteRequest: (id) => void deleteTask2(id),
|
|
24226
24630
|
onArchiveRequest: (id) => void archiveTask(id),
|
|
24227
24631
|
onLocalMergeRequest: (id) => {
|
|
24228
24632
|
const task = props.tasks().find((t) => t.id === id);
|
|
@@ -24253,6 +24657,17 @@ function TasksShell(props) {
|
|
|
24253
24657
|
return _el$;
|
|
24254
24658
|
})();
|
|
24255
24659
|
}
|
|
24660
|
+
function legendCap(id) {
|
|
24661
|
+
const row = findBinding(id);
|
|
24662
|
+
if (!row)
|
|
24663
|
+
return null;
|
|
24664
|
+
const cap = row.hint?.keys ?? row.keys[0];
|
|
24665
|
+
return cap && cap.length > 0 ? cap : null;
|
|
24666
|
+
}
|
|
24667
|
+
function legendRowCap(ids) {
|
|
24668
|
+
const caps = ids.map(legendCap).filter((c) => c !== null);
|
|
24669
|
+
return caps.length > 0 ? caps.join("/") : null;
|
|
24670
|
+
}
|
|
24256
24671
|
function ShortcutHints(props) {
|
|
24257
24672
|
const {
|
|
24258
24673
|
theme
|
|
@@ -24340,55 +24755,70 @@ function ShortcutHints(props) {
|
|
|
24340
24755
|
});
|
|
24341
24756
|
return out;
|
|
24342
24757
|
};
|
|
24343
|
-
const defaultHints = () =>
|
|
24344
|
-
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
|
|
24349
|
-
|
|
24350
|
-
|
|
24351
|
-
|
|
24352
|
-
|
|
24353
|
-
|
|
24354
|
-
|
|
24355
|
-
|
|
24356
|
-
|
|
24357
|
-
|
|
24358
|
-
|
|
24359
|
-
|
|
24360
|
-
|
|
24361
|
-
|
|
24362
|
-
|
|
24363
|
-
|
|
24364
|
-
|
|
24365
|
-
|
|
24366
|
-
|
|
24367
|
-
|
|
24368
|
-
|
|
24369
|
-
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
|
|
24379
|
-
|
|
24380
|
-
|
|
24381
|
-
|
|
24382
|
-
|
|
24383
|
-
|
|
24384
|
-
|
|
24385
|
-
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
|
|
24390
|
-
|
|
24391
|
-
|
|
24758
|
+
const defaultHints = () => {
|
|
24759
|
+
keymapVersion();
|
|
24760
|
+
const rows = [
|
|
24761
|
+
{
|
|
24762
|
+
ids: ["sidebar.select"],
|
|
24763
|
+
label: "open"
|
|
24764
|
+
},
|
|
24765
|
+
{
|
|
24766
|
+
ids: ["tasks.focusEngine"],
|
|
24767
|
+
label: "focus engine"
|
|
24768
|
+
},
|
|
24769
|
+
{
|
|
24770
|
+
ids: ["task.new"],
|
|
24771
|
+
label: "new task"
|
|
24772
|
+
},
|
|
24773
|
+
{
|
|
24774
|
+
ids: ["settings.open.sidebar"],
|
|
24775
|
+
label: "settings"
|
|
24776
|
+
},
|
|
24777
|
+
{
|
|
24778
|
+
ids: ["tasks.openWorktree"],
|
|
24779
|
+
label: "open wt"
|
|
24780
|
+
},
|
|
24781
|
+
{
|
|
24782
|
+
ids: ["sidebar.view"],
|
|
24783
|
+
label: "views"
|
|
24784
|
+
},
|
|
24785
|
+
{
|
|
24786
|
+
ids: ["sidebar.sort"],
|
|
24787
|
+
label: "sort"
|
|
24788
|
+
},
|
|
24789
|
+
{
|
|
24790
|
+
ids: ["sidebar.localMerge"],
|
|
24791
|
+
label: "move task",
|
|
24792
|
+
dimWhenMain: true
|
|
24793
|
+
},
|
|
24794
|
+
{
|
|
24795
|
+
ids: ["sidebar.archive", "sidebar.delete"],
|
|
24796
|
+
label: "un/archive\xB7delete"
|
|
24797
|
+
},
|
|
24798
|
+
{
|
|
24799
|
+
ids: ["sidebar.rename", "tasks.renameBranch", "tasks.cycleEngine"],
|
|
24800
|
+
label: "name/branch/engine",
|
|
24801
|
+
dimWhenMain: true
|
|
24802
|
+
},
|
|
24803
|
+
{
|
|
24804
|
+
ids: ["help.open"],
|
|
24805
|
+
label: "help"
|
|
24806
|
+
}
|
|
24807
|
+
];
|
|
24808
|
+
const out = [];
|
|
24809
|
+
for (const row of rows) {
|
|
24810
|
+
const k = legendRowCap(row.ids);
|
|
24811
|
+
if (k === null)
|
|
24812
|
+
continue;
|
|
24813
|
+
out.push({
|
|
24814
|
+
k,
|
|
24815
|
+
label: row.label,
|
|
24816
|
+
dimWhenMain: row.dimWhenMain
|
|
24817
|
+
});
|
|
24818
|
+
}
|
|
24819
|
+
out.push(...tmuxHints());
|
|
24820
|
+
return out;
|
|
24821
|
+
};
|
|
24392
24822
|
const MOVE_HINTS = [{
|
|
24393
24823
|
k: "j/k",
|
|
24394
24824
|
label: "reorder"
|
|
@@ -24658,10 +25088,14 @@ async function startSettingsHost() {
|
|
|
24658
25088
|
setup: async () => {
|
|
24659
25089
|
let orch = null;
|
|
24660
25090
|
try {
|
|
24661
|
-
const client = await
|
|
24662
|
-
|
|
24663
|
-
|
|
24664
|
-
|
|
25091
|
+
const client = await connectIfRunning();
|
|
25092
|
+
if (client) {
|
|
25093
|
+
const remote = new RemoteOrchestrator(client);
|
|
25094
|
+
await remote.init();
|
|
25095
|
+
orch = remote;
|
|
25096
|
+
} else {
|
|
25097
|
+
console.error("[kobe settings] no daemon running; Restart backend disabled");
|
|
25098
|
+
}
|
|
24665
25099
|
} catch (err) {
|
|
24666
25100
|
console.error("[kobe settings] daemon unavailable; Restart backend disabled:", err);
|
|
24667
25101
|
}
|
|
@@ -26476,10 +26910,16 @@ var init_pr_prompt = __esm(() => {
|
|
|
26476
26910
|
var exports_host7 = {};
|
|
26477
26911
|
__export(exports_host7, {
|
|
26478
26912
|
startOpsPreview: () => startOpsPreview,
|
|
26479
|
-
startOpsHost: () => startOpsHost
|
|
26913
|
+
startOpsHost: () => startOpsHost,
|
|
26914
|
+
nextActivityPollDelay: () => nextActivityPollDelay
|
|
26480
26915
|
});
|
|
26481
26916
|
import { createHash as createHash5 } from "crypto";
|
|
26482
26917
|
import { SyntaxStyle } from "@opentui/core";
|
|
26918
|
+
function nextActivityPollDelay(currentMs, idleStreak) {
|
|
26919
|
+
if (idleStreak < ACTIVITY_IDLE_RAMP_POLLS)
|
|
26920
|
+
return ACTIVITY_POLL_MIN_MS;
|
|
26921
|
+
return Math.min(currentMs * 2, ACTIVITY_POLL_MAX_MS);
|
|
26922
|
+
}
|
|
26483
26923
|
function OpsShell(props) {
|
|
26484
26924
|
const {
|
|
26485
26925
|
theme
|
|
@@ -26489,6 +26929,10 @@ function OpsShell(props) {
|
|
|
26489
26929
|
let primed = false;
|
|
26490
26930
|
onMount(() => {
|
|
26491
26931
|
let disposed = false;
|
|
26932
|
+
let timer;
|
|
26933
|
+
let delayMs = ACTIVITY_POLL_MIN_MS;
|
|
26934
|
+
let idleStreak = 0;
|
|
26935
|
+
let lastSeenMtime = 0;
|
|
26492
26936
|
async function poll() {
|
|
26493
26937
|
try {
|
|
26494
26938
|
const mtime = await latestTranscriptMtime(props.vendor, props.worktree);
|
|
@@ -26497,15 +26941,29 @@ function OpsShell(props) {
|
|
|
26497
26941
|
if (!primed) {
|
|
26498
26942
|
primed = true;
|
|
26499
26943
|
setBaseline(mtime);
|
|
26944
|
+
lastSeenMtime = mtime;
|
|
26945
|
+
}
|
|
26946
|
+
if (mtime > lastSeenMtime) {
|
|
26947
|
+
lastSeenMtime = mtime;
|
|
26948
|
+
idleStreak = 0;
|
|
26949
|
+
} else {
|
|
26950
|
+
idleStreak++;
|
|
26500
26951
|
}
|
|
26501
26952
|
setLatest(mtime);
|
|
26502
|
-
} catch {
|
|
26953
|
+
} catch {
|
|
26954
|
+
idleStreak++;
|
|
26955
|
+
} finally {
|
|
26956
|
+
if (!disposed) {
|
|
26957
|
+
delayMs = nextActivityPollDelay(delayMs, idleStreak);
|
|
26958
|
+
timer = setTimeout(() => void poll(), delayMs);
|
|
26959
|
+
}
|
|
26960
|
+
}
|
|
26503
26961
|
}
|
|
26504
26962
|
poll();
|
|
26505
|
-
const timer = setInterval(() => void poll(), ACTIVITY_POLL_MS);
|
|
26506
26963
|
onCleanup(() => {
|
|
26507
26964
|
disposed = true;
|
|
26508
|
-
|
|
26965
|
+
if (timer)
|
|
26966
|
+
clearTimeout(timer);
|
|
26509
26967
|
});
|
|
26510
26968
|
});
|
|
26511
26969
|
const hasNewActivity = () => primed && latest() > baseline();
|
|
@@ -26867,7 +27325,7 @@ async function startOpsPreview(args2) {
|
|
|
26867
27325
|
})
|
|
26868
27326
|
});
|
|
26869
27327
|
}
|
|
26870
|
-
var
|
|
27328
|
+
var ACTIVITY_POLL_MIN_MS = 2500, ACTIVITY_POLL_MAX_MS = 20000, ACTIVITY_IDLE_RAMP_POLLS = 3, TURN_STATUS_POLL_MS = 1500, STABLE_POLLS_FOR_DONE = 2, CHAT_TAB_STATE_OPTION2 = "@kobe_tab_state";
|
|
26871
27329
|
var init_host7 = __esm(() => {
|
|
26872
27330
|
init_solid();
|
|
26873
27331
|
init_solid();
|