@cliphijack/santaclaude 0.1.0 → 0.3.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 +34 -4
package/package.json
CHANGED
package/santaclaude.js
CHANGED
|
@@ -35,6 +35,14 @@ function inject(pane, message) {
|
|
|
35
35
|
execFileSync('tmux', ['send-keys', '-t', pane, '-l', oneLine]);
|
|
36
36
|
setTimeout(() => { try { execFileSync('tmux', ['send-keys', '-t', pane, 'Enter']); } catch (e) {} }, 350);
|
|
37
37
|
}
|
|
38
|
+
// 세션이 없을 때만 — tmux 세션 새로 만들고 그 안에 claude 자동 실행
|
|
39
|
+
function spawnClaude(session, cmd) {
|
|
40
|
+
execFileSync('tmux', ['new-session', '-d', '-s', session]);
|
|
41
|
+
try { execFileSync('sh', ['-c', 'sleep 1']); } catch (e) {} // 셸 초기화 대기
|
|
42
|
+
execFileSync('tmux', ['send-keys', '-t', session, '-l', cmd]);
|
|
43
|
+
try { execFileSync('sh', ['-c', 'sleep 0.3']); } catch (e) {}
|
|
44
|
+
execFileSync('tmux', ['send-keys', '-t', session, 'Enter']);
|
|
45
|
+
}
|
|
38
46
|
|
|
39
47
|
async function post(api, p, body) {
|
|
40
48
|
const r = await fetch(api + p, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
|
@@ -45,20 +53,40 @@ async function run(conf) {
|
|
|
45
53
|
const { token, pane, api = DEFAULT_API } = conf;
|
|
46
54
|
const every = Math.max(5, Number(conf.every) || 20) * 1000;
|
|
47
55
|
if (!token || !pane) { console.error('설정 없음. 먼저: santaclaude connect --token <T> --pane <pane>'); process.exit(1); }
|
|
56
|
+
const session = String(pane).split(':')[0];
|
|
57
|
+
const claudeCmd = conf.claudeCmd || 'claude --dangerously-skip-permissions';
|
|
48
58
|
let warned = false; // 중복방지는 서버가 책임 (claim 시 next_fire 원자적 전진) — 클라 영구셋은 반복예약을 영구차단하므로 안 둠
|
|
49
59
|
|
|
50
60
|
console.log(`🛷 SantaClaude 커넥터 가동 — pane=${pane} · ${every / 1000}s 폴링 · ${api}`);
|
|
51
|
-
if (
|
|
61
|
+
if (paneExists(pane)) {
|
|
62
|
+
console.log(` ✅ tmux "${session}" 세션에 붙음 (기존 claude에 주입).`);
|
|
63
|
+
} else if (conf.spawn !== false) {
|
|
64
|
+
console.log(` 🦌 "${session}" 세션이 없어서 claude를 새로 띄울게…`);
|
|
65
|
+
try {
|
|
66
|
+
spawnClaude(session, claudeCmd);
|
|
67
|
+
console.log(` ✅ "${session}"에 claude 띄웠어. 처음이면 새 창에서 tmux attach -t ${session} 로 로그인 1회 해줘 (이후엔 자동).`);
|
|
68
|
+
} catch (e) { console.warn(` ⚠️ claude 자동실행 실패(${e.message}). tmux new -s ${session} 로 직접 띄워줘.`); }
|
|
69
|
+
} else {
|
|
70
|
+
console.warn(` ⚠️ tmux "${session}" 세션 없음 (--no-spawn) — 직접 띄워줘.`);
|
|
71
|
+
}
|
|
52
72
|
|
|
53
73
|
async function tick() {
|
|
54
74
|
try {
|
|
55
75
|
post(api, '/api/heartbeat', { token, pane }).catch(() => {});
|
|
56
76
|
const d = await post(api, '/api/jobs/claim', { token });
|
|
57
77
|
for (const j of (d.jobs || [])) {
|
|
58
|
-
|
|
78
|
+
// 잡의 대상 세션 우선, 없으면 기본 pane. 대상 세션이 없으면 기본 pane으로 폴백
|
|
79
|
+
let tpane = (j.target && String(j.target).trim()) || pane;
|
|
80
|
+
if (!paneExists(tpane)) {
|
|
81
|
+
if (conf.spawn !== false && tpane !== pane) {
|
|
82
|
+
try { spawnClaude(String(tpane).split(':')[0], claudeCmd); console.log(` 🦌 대상 "${tpane}" 세션이 없어서 새로 띄웠어 (tmux attach 로 로그인 1회).`); }
|
|
83
|
+
catch (e) { tpane = pane; }
|
|
84
|
+
} else if (!paneExists(tpane)) { tpane = pane; }
|
|
85
|
+
}
|
|
86
|
+
if (!paneExists(tpane)) { if (!warned) { console.warn(` ⚠️ 주입할 세션 "${tpane}" 없음 — 보류`); warned = true; } continue; }
|
|
59
87
|
warned = false;
|
|
60
|
-
console.log(`[발사] ${new Date().toISOString()} → ${
|
|
61
|
-
inject(
|
|
88
|
+
console.log(`[발사] ${new Date().toISOString()} → ${tpane}: ${String(j.message).slice(0, 60)}`);
|
|
89
|
+
inject(tpane, '[SantaClaude] ' + j.message);
|
|
62
90
|
}
|
|
63
91
|
} catch (e) { console.error('[폴링 오류]', e.message); }
|
|
64
92
|
}
|
|
@@ -75,6 +103,8 @@ async function main() {
|
|
|
75
103
|
const token = a.token, pane = a.pane;
|
|
76
104
|
if (!token || !pane) { console.error('사용: santaclaude connect --token <TOKEN> --pane <tmux:pane>'); process.exit(1); }
|
|
77
105
|
const conf = { token, pane, api: a.api || DEFAULT_API, every: a.every ? Number(a.every) : 20 };
|
|
106
|
+
if (a['no-spawn']) conf.spawn = false;
|
|
107
|
+
if (a['claude-cmd']) conf.claudeCmd = a['claude-cmd'];
|
|
78
108
|
saveConf(conf);
|
|
79
109
|
console.log(`✅ 페어링 저장 → ${CONF}`);
|
|
80
110
|
return run(conf);
|