cli-remote-agent 1.0.8 → 1.0.11
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.js +86 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.js +83 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -215,7 +215,7 @@ var init_types = __esm({
|
|
|
215
215
|
});
|
|
216
216
|
|
|
217
217
|
// ../shared/src/protocol.ts
|
|
218
|
-
var AGENT_HEARTBEAT, TERMINAL_OUTPUT, TERMINAL_EXIT, TERMINAL_OPEN, TERMINAL_INPUT, TERMINAL_RESIZE, TERMINAL_CLOSE, SESSION_ATTACH, SESSION_DETACH, SESSION_BUFFER, SESSION_SYNC, SESSION_SYNC_RESULT, VPN_LIST, VPN_CONNECT, VPN_DISCONNECT, VPN_UPDATE, FILES_LIST, FILES_DOWNLOAD, FILES_LIST_RESULT, FILES_DOWNLOAD_READY, FILES_DOWNLOAD_ERROR, AGENT_EXEC, AGENT_EXEC_RESULT, CLAUDE_CONV_READ, CLAUDE_CONV_DATA, CLAUDE_SESSIONS_LIST, CLAUDE_SESSIONS_RESULT, CLAUDE_HOOK, TMUX_LIST, TMUX_LIST_RESULT, TMUX_KILL, TMUX_KILL_RESULT;
|
|
218
|
+
var AGENT_HEARTBEAT, TERMINAL_OUTPUT, TERMINAL_EXIT, TERMINAL_OPEN, TERMINAL_INPUT, TERMINAL_RESIZE, TERMINAL_CLOSE, SESSION_ATTACH, SESSION_DETACH, SESSION_BUFFER, SESSION_SYNC, SESSION_SYNC_RESULT, VPN_LIST, VPN_CONNECT, VPN_DISCONNECT, VPN_UPDATE, FILES_LIST, FILES_DOWNLOAD, FILES_LIST_RESULT, FILES_DOWNLOAD_READY, FILES_DOWNLOAD_ERROR, AGENT_EXEC, AGENT_EXEC_RESULT, CLAUDE_CONV_READ, CLAUDE_CONV_DATA, CLAUDE_SESSIONS_LIST, CLAUDE_SESSIONS_RESULT, CLAUDE_HOOK, TMUX_LIST, TMUX_LIST_RESULT, TMUX_KILL, TMUX_KILL_RESULT, TMUX_SCROLL;
|
|
219
219
|
var init_protocol = __esm({
|
|
220
220
|
"../shared/src/protocol.ts"() {
|
|
221
221
|
"use strict";
|
|
@@ -251,6 +251,7 @@ var init_protocol = __esm({
|
|
|
251
251
|
TMUX_LIST_RESULT = "tmux:list:result";
|
|
252
252
|
TMUX_KILL = "tmux:kill";
|
|
253
253
|
TMUX_KILL_RESULT = "tmux:kill:result";
|
|
254
|
+
TMUX_SCROLL = "tmux:scroll";
|
|
254
255
|
}
|
|
255
256
|
});
|
|
256
257
|
|
|
@@ -550,6 +551,7 @@ function getLiveClaudeSessions() {
|
|
|
550
551
|
out.push({
|
|
551
552
|
pid: obj.pid,
|
|
552
553
|
cwd: typeof obj.cwd === "string" ? obj.cwd : "",
|
|
554
|
+
sessionId: typeof obj.sessionId === "string" ? obj.sessionId : void 0,
|
|
553
555
|
name: typeof obj.name === "string" ? obj.name : void 0,
|
|
554
556
|
status: typeof obj.status === "string" ? obj.status : void 0,
|
|
555
557
|
updatedAt: typeof obj.updatedAt === "number" ? obj.updatedAt : void 0
|
|
@@ -580,6 +582,15 @@ function isDescendantOf(pid, ancestor, parents) {
|
|
|
580
582
|
}
|
|
581
583
|
return false;
|
|
582
584
|
}
|
|
585
|
+
async function findClaudeSessionForPtyPid(ptyPid) {
|
|
586
|
+
const live = getLiveClaudeSessions().filter((s) => s.sessionId);
|
|
587
|
+
if (live.length === 0) return null;
|
|
588
|
+
const parents = await getPidParents();
|
|
589
|
+
for (let i = live.length - 1; i >= 0; i--) {
|
|
590
|
+
if (isDescendantOf(live[i].pid, ptyPid, parents)) return live[i].sessionId;
|
|
591
|
+
}
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
583
594
|
var import_fs6, import_path5, import_os2, import_child_process2, import_util, execFileAsync, LIVE_SESSIONS_DIR;
|
|
584
595
|
var init_claude_live = __esm({
|
|
585
596
|
"src/claude-live.ts"() {
|
|
@@ -647,6 +658,41 @@ async function killTmuxSession(name) {
|
|
|
647
658
|
return { ok: false, error: msg };
|
|
648
659
|
}
|
|
649
660
|
}
|
|
661
|
+
async function scrollTmux(name, direction) {
|
|
662
|
+
const bin = resolveTmuxPath();
|
|
663
|
+
if (!bin) return { inCopyMode: false };
|
|
664
|
+
const target = `=${name}`;
|
|
665
|
+
const run = (args) => execFileAsync2(bin, args, { timeout: 4e3 }).catch(() => {
|
|
666
|
+
});
|
|
667
|
+
const inMode = async () => {
|
|
668
|
+
try {
|
|
669
|
+
const { stdout } = await execFileAsync2(bin, ["display-message", "-t", target, "-p", "#{pane_in_mode}"], { timeout: 3e3 });
|
|
670
|
+
return stdout.trim() === "1";
|
|
671
|
+
} catch {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
if (direction === "exit") {
|
|
676
|
+
if (await inMode()) await run(["send-keys", "-t", target, "-X", "cancel"]);
|
|
677
|
+
return { inCopyMode: false };
|
|
678
|
+
}
|
|
679
|
+
if (direction === "up") {
|
|
680
|
+
await run(["copy-mode", "-t", target]);
|
|
681
|
+
await run(["send-keys", "-t", target, "-X", "halfpage-up"]);
|
|
682
|
+
return { inCopyMode: true };
|
|
683
|
+
}
|
|
684
|
+
if (!await inMode()) return { inCopyMode: false };
|
|
685
|
+
await run(["send-keys", "-t", target, "-X", "halfpage-down"]);
|
|
686
|
+
try {
|
|
687
|
+
const { stdout } = await execFileAsync2(bin, ["display-message", "-t", target, "-p", "#{scroll_position}"], { timeout: 3e3 });
|
|
688
|
+
if ((parseInt(stdout.trim(), 10) || 0) === 0) {
|
|
689
|
+
await run(["send-keys", "-t", target, "-X", "cancel"]);
|
|
690
|
+
return { inCopyMode: false };
|
|
691
|
+
}
|
|
692
|
+
} catch {
|
|
693
|
+
}
|
|
694
|
+
return { inCopyMode: true };
|
|
695
|
+
}
|
|
650
696
|
async function enrichWithPanesAndClaude(sessions2) {
|
|
651
697
|
if (IS_WIN || sessions2.length === 0) return;
|
|
652
698
|
try {
|
|
@@ -845,6 +891,9 @@ var init_pty_session = __esm({
|
|
|
845
891
|
});
|
|
846
892
|
}
|
|
847
893
|
}
|
|
894
|
+
get pid() {
|
|
895
|
+
return this.pty.pid;
|
|
896
|
+
}
|
|
848
897
|
write(data) {
|
|
849
898
|
this.pty.write(data);
|
|
850
899
|
}
|
|
@@ -911,6 +960,9 @@ function createTerminalSession(sessionId, cols, rows, shellPreference, cwd, onDa
|
|
|
911
960
|
sessions.set(sessionId, session);
|
|
912
961
|
logger.info({ sessionId, cols, rows }, "PTY session created");
|
|
913
962
|
}
|
|
963
|
+
function getSessionPid(sessionId) {
|
|
964
|
+
return sessions.get(sessionId)?.pid;
|
|
965
|
+
}
|
|
914
966
|
function writeToSession(sessionId, data) {
|
|
915
967
|
sessions.get(sessionId)?.write(data);
|
|
916
968
|
}
|
|
@@ -1805,7 +1857,7 @@ var init_claude_conversation = __esm({
|
|
|
1805
1857
|
|
|
1806
1858
|
// src/index.ts
|
|
1807
1859
|
var index_exports = {};
|
|
1808
|
-
var import_socket, config, lock, socket, SESSION_MAX_IDLE_MS, reaperInterval, vpnProfiles;
|
|
1860
|
+
var import_socket, config, lock, socket, SESSION_MAX_IDLE_MS, reaperInterval, sessionTmuxName, sessionInCopyMode, vpnProfiles;
|
|
1809
1861
|
var init_index = __esm({
|
|
1810
1862
|
"src/index.ts"() {
|
|
1811
1863
|
"use strict";
|
|
@@ -1824,6 +1876,7 @@ var init_index = __esm({
|
|
|
1824
1876
|
init_claude_sessions();
|
|
1825
1877
|
init_claude_conversation();
|
|
1826
1878
|
init_terminal_manager();
|
|
1879
|
+
init_claude_live();
|
|
1827
1880
|
if (!isConfigured()) {
|
|
1828
1881
|
console.error("No agent configured. Run: crc-agent setup");
|
|
1829
1882
|
process.exit(1);
|
|
@@ -1894,9 +1947,12 @@ var init_index = __esm({
|
|
|
1894
1947
|
socket.emit(CLAUDE_HOOK, payload);
|
|
1895
1948
|
}
|
|
1896
1949
|
});
|
|
1950
|
+
sessionTmuxName = /* @__PURE__ */ new Map();
|
|
1951
|
+
sessionInCopyMode = /* @__PURE__ */ new Map();
|
|
1897
1952
|
socket.on(TERMINAL_OPEN, (payload) => {
|
|
1898
1953
|
const { sessionId, cols, rows, tmux, launch } = payload;
|
|
1899
1954
|
if (!sessionId) return;
|
|
1955
|
+
if (tmux) sessionTmuxName.set(sessionId, tmux);
|
|
1900
1956
|
let launchSpec;
|
|
1901
1957
|
if (tmux) {
|
|
1902
1958
|
const spec = buildTmuxLaunch(tmux, launch);
|
|
@@ -1920,6 +1976,8 @@ var init_index = __esm({
|
|
|
1920
1976
|
},
|
|
1921
1977
|
(sid, exitCode) => {
|
|
1922
1978
|
socket.emit(TERMINAL_EXIT, { sessionId: sid, exitCode });
|
|
1979
|
+
sessionTmuxName.delete(sid);
|
|
1980
|
+
sessionInCopyMode.delete(sid);
|
|
1923
1981
|
},
|
|
1924
1982
|
launchSpec
|
|
1925
1983
|
);
|
|
@@ -1941,7 +1999,20 @@ var init_index = __esm({
|
|
|
1941
1999
|
logger.info({ name, ok: result.ok, error: result.error }, "tmux kill requested");
|
|
1942
2000
|
socket.emit(TMUX_KILL_RESULT, { requestId, name, ok: result.ok, error: result.error });
|
|
1943
2001
|
});
|
|
2002
|
+
socket.on(TMUX_SCROLL, async (payload) => {
|
|
2003
|
+
const { sessionId, direction } = payload;
|
|
2004
|
+
const name = sessionId && sessionTmuxName.get(sessionId);
|
|
2005
|
+
if (!name) return;
|
|
2006
|
+
const { inCopyMode } = await scrollTmux(name, direction);
|
|
2007
|
+
sessionInCopyMode.set(sessionId, inCopyMode);
|
|
2008
|
+
});
|
|
1944
2009
|
socket.on(TERMINAL_INPUT, (payload) => {
|
|
2010
|
+
const name = sessionTmuxName.get(payload.sessionId);
|
|
2011
|
+
if (name && sessionInCopyMode.get(payload.sessionId)) {
|
|
2012
|
+
sessionInCopyMode.set(payload.sessionId, false);
|
|
2013
|
+
scrollTmux(name, "exit").catch(() => {
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
1945
2016
|
writeToSession(payload.sessionId, payload.data);
|
|
1946
2017
|
});
|
|
1947
2018
|
socket.on(TERMINAL_RESIZE, (payload) => {
|
|
@@ -1949,6 +2020,8 @@ var init_index = __esm({
|
|
|
1949
2020
|
});
|
|
1950
2021
|
socket.on(TERMINAL_CLOSE, (payload) => {
|
|
1951
2022
|
closeSession(payload.sessionId);
|
|
2023
|
+
sessionTmuxName.delete(payload.sessionId);
|
|
2024
|
+
sessionInCopyMode.delete(payload.sessionId);
|
|
1952
2025
|
});
|
|
1953
2026
|
socket.on(SESSION_SYNC, () => {
|
|
1954
2027
|
const aliveIds = getAliveSessionIds();
|
|
@@ -2025,7 +2098,17 @@ var init_index = __esm({
|
|
|
2025
2098
|
});
|
|
2026
2099
|
socket.on(CLAUDE_CONV_READ, async (payload) => {
|
|
2027
2100
|
try {
|
|
2028
|
-
|
|
2101
|
+
let sessionId = payload.sessionId;
|
|
2102
|
+
if (!sessionId && payload.terminalSessionId) {
|
|
2103
|
+
const ptyPid = getSessionPid(payload.terminalSessionId);
|
|
2104
|
+
const resolved = ptyPid ? await findClaudeSessionForPtyPid(ptyPid) : null;
|
|
2105
|
+
if (!resolved) {
|
|
2106
|
+
socket.emit(CLAUDE_CONV_DATA, { sessionId: "", messages: [], totalLines: 0 });
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
sessionId = resolved;
|
|
2110
|
+
}
|
|
2111
|
+
const result = await readConversation(payload.projectPath, payload.afterLine || 0, sessionId);
|
|
2029
2112
|
if (result) {
|
|
2030
2113
|
socket.emit(CLAUDE_CONV_DATA, {
|
|
2031
2114
|
sessionId: result.sessionId,
|