@cliphijack/santaclaude 0.6.0 → 0.8.0
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/package.json +1 -1
- package/santaclaude.js +15 -1
package/package.json
CHANGED
package/santaclaude.js
CHANGED
|
@@ -64,6 +64,14 @@ function listWindows(session) {
|
|
|
64
64
|
try { return execFileSync('tmux', ['list-windows', '-t', session, '-F', '#{window_name}'], { encoding: 'utf8' }).trim().split('\n').filter(Boolean); }
|
|
65
65
|
catch (e) { return []; }
|
|
66
66
|
}
|
|
67
|
+
// 각 루돌프(탭) 화면 캡처 — 웹 미러용 (마지막 ~30줄)
|
|
68
|
+
function captureWindow(session, name) {
|
|
69
|
+
try { return execFileSync('tmux', ['capture-pane', '-t', session + ':' + name, '-p', '-S', '-30'], { encoding: 'utf8' }).replace(/\n+$/, '').slice(-4000); }
|
|
70
|
+
catch (e) { return ''; }
|
|
71
|
+
}
|
|
72
|
+
function captureAll(session) {
|
|
73
|
+
const o = {}; for (const w of listWindows(session)) o[w] = captureWindow(session, w); return o;
|
|
74
|
+
}
|
|
67
75
|
|
|
68
76
|
async function post(api, p, body) {
|
|
69
77
|
const r = await fetch(api + p, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
|
@@ -108,13 +116,19 @@ async function run(conf) {
|
|
|
108
116
|
} else if (cmd.action === 'kill') {
|
|
109
117
|
execFileSync('tmux', ['kill-window', '-t', session + ':' + cmd.name]);
|
|
110
118
|
console.log(` 💤 루돌프 "${session}:${cmd.name}" 탭 닫음`);
|
|
119
|
+
} else if (cmd.action === 'input') {
|
|
120
|
+
// 웹에서 친 입력을 해당 탭(루돌프)에 주입 — 양방향 웹 터미널
|
|
121
|
+
const w = (cmd.name && windowExists(session, cmd.name)) ? (session + ':' + cmd.name) : pane;
|
|
122
|
+
execFileSync('tmux', ['send-keys', '-t', w, '-l', String(cmd.text || '')]);
|
|
123
|
+
setTimeout(() => { try { execFileSync('tmux', ['send-keys', '-t', w, 'Enter']); } catch (e) {} }, 300);
|
|
124
|
+
console.log(` ⌨️ 입력 → ${w}: ${String(cmd.text || '').slice(0, 50)}`);
|
|
111
125
|
}
|
|
112
126
|
} catch (e) { console.warn(` ⚠️ 제어 실패(${cmd.action} ${cmd.name}): ${e.message}`); }
|
|
113
127
|
}
|
|
114
128
|
|
|
115
129
|
async function tick() {
|
|
116
130
|
try {
|
|
117
|
-
post(api, '/api/heartbeat', { token, pane, sessions: listWindows(session) }).catch(() => {}); // 보고 =
|
|
131
|
+
post(api, '/api/heartbeat', { token, pane, sessions: listWindows(session), screens: captureAll(session) }).catch(() => {}); // 보고 = 윈도우 목록 + 각 탭 화면 미러
|
|
118
132
|
post(api, '/api/control/claim', { token }).then((c) => { for (const cmd of (c && c.commands) || []) runControl(cmd); }).catch(() => {});
|
|
119
133
|
const d = await post(api, '/api/jobs/claim', { token });
|
|
120
134
|
for (const j of (d.jobs || [])) {
|