@cliphijack/santaclaude 1.0.5 → 1.0.7

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/santaclaude.js +14 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cliphijack/santaclaude",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "publishConfig": { "access": "public" },
5
5
  "description": "SantaClaude 커넥터 — 클라우드 예약을 내 로컬 Claude(tmux)에 발사",
6
6
  "bin": { "santaclaude": "./santaclaude.js" },
package/santaclaude.js CHANGED
@@ -277,6 +277,15 @@ async function run(conf) {
277
277
  const w = (cmd.name && windowExists(session, cmd.name)) ? (session + ':' + cmd.name) : pane;
278
278
  execFileSync('tmux', ['send-keys', '-t', w, String(cmd.key || '')]);
279
279
  console.log(` ⌨️ 키 → ${w}: ${cmd.key}`);
280
+ } else if (cmd.action === 'resize') {
281
+ // 웹 뷰어 폭에 맞춰 tmux 윈도우 리사이즈 — claude TUI가 그 폭으로 reflow(모바일 가로스크롤 해소)
282
+ // 보통 cols만 옴 = 폭만 맞추고 높이는 그대로(스크롤백으로 세로 내역 풍부하게). rows 오면 같이 적용.
283
+ const w = (cmd.name && windowExists(session, cmd.name)) ? (session + ':' + cmd.name) : pane;
284
+ const c = parseInt(cmd.cols) || 0, rr = parseInt(cmd.rows) || 0;
285
+ const args = ['resize-window', '-t', w];
286
+ if (c > 0) args.push('-x', String(Math.max(20, Math.min(400, c))));
287
+ if (rr > 0) args.push('-y', String(Math.max(10, Math.min(200, rr))));
288
+ if (args.length > 3) { try { execFileSync('tmux', args); console.log(` 📐 리사이즈 ${w} → 폭${c || '그대로'}${rr ? '×높이' + rr : ''}`); } catch (e) {} }
280
289
  }
281
290
  } catch (e) { console.warn(` ⚠️ 제어 실패(${cmd.action} ${cmd.name}): ${e.message}`); }
282
291
  }
@@ -307,10 +316,8 @@ async function run(conf) {
307
316
  const r = await fetch(api + '/api/skill', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, name, publisher, files, public: true }) });
308
317
  const j = await r.json();
309
318
  if (j.ok) {
310
- console.log(` 🏪 스킬 "${name}" 마켓 클린등록 완료`);
319
+ console.log(` 🏪 스킬 "${name}" 마켓 클린등록 완료`); // 확인은 웹 마켓에 — claude 입력칸 주입 금지(사용자 메시지로 오인됨)
311
320
  try { fs.rmSync(d, { recursive: true, force: true }); } catch (e) {}
312
- execFileSync('tmux', ['send-keys', '-t', pane, '-l', '🏪 "' + name + '" 마켓에 클린 등록됐어!']);
313
- setTimeout(() => { try { execFileSync('tmux', ['send-keys', '-t', pane, 'Enter']); } catch (e) {} }, 300);
314
321
  } else { console.warn(` 스킬 등록 실패(${name}): ${j.error}`); try { fs.unlinkSync(path.join(d, 'READY')); } catch (e) {} }
315
322
  } catch (e) { console.error('[scan fetch err]', e.message); }
316
323
  }
@@ -330,10 +337,8 @@ async function run(conf) {
330
337
  try {
331
338
  const j = await post(api, '/api/schedule', Object.assign({}, req, { token }));
332
339
  if (j.ok) {
333
- console.log(` 🎄 세션 예약 등록 — ${req.kind || 'once'} · ${String(req.message).slice(0, 40)}`);
340
+ console.log(` 🎄 세션 예약 등록 — ${req.kind || 'once'} · ${String(req.message).slice(0, 40)}`); // 확인은 웹 예약내역에 — claude 주입 금지
334
341
  try { fs.unlinkSync(fp); } catch (e) {}
335
- execFileSync('tmux', ['send-keys', '-t', pane, '-l', '🎄 예약 걸렸어 — ' + String(req.message).slice(0, 40)]);
336
- setTimeout(() => { try { execFileSync('tmux', ['send-keys', '-t', pane, 'Enter']); } catch (e) {} }, 300);
337
342
  } else { console.warn(` 예약 실패: ${j.error}`); try { fs.renameSync(fp, fp + '.err'); } catch (e) {} }
338
343
  } catch (e) { console.error('[sched fetch err]', e.message); }
339
344
  }
@@ -343,7 +348,7 @@ async function run(conf) {
343
348
  // 루돌프가 사용자에게 보내는 파일 — santa-show 스킬이 ~/.santaclaude/outbox/ 에 복사하면 릴레이
344
349
  const OUTBOXDIR = path.join(os.homedir(), '.santaclaude', 'outbox');
345
350
  const MAXOUT = 18 * 1024 * 1024; // 18MB (KV 릴레이 한계 — 초과는 R2 예정)
346
- function tmuxNote(msg) { try { execFileSync('tmux', ['send-keys', '-t', pane, '-l', msg]); setTimeout(() => { try { execFileSync('tmux', ['send-keys', '-t', pane, 'Enter']); } catch (e) {} }, 300); } catch (e) {} }
351
+ // 주의: 상태 알림을 claude pane send-keys 하면 사용자 메시지로 오인돼 claude가 되받아침 절대 금지. 확인은 UI(드로어)에서.
347
352
  async function scanOutbox() {
348
353
  try {
349
354
  if (!fs.existsSync(OUTBOXDIR)) return;
@@ -352,7 +357,7 @@ async function run(conf) {
352
357
  const fp = path.join(OUTBOXDIR, fn);
353
358
  let st; try { st = fs.statSync(fp); } catch (e) { continue; }
354
359
  if (!st.isFile()) continue;
355
- if (st.size > MAXOUT) { console.warn(` ⚠️ "${fn}" ${(st.size / 1048576).toFixed(1)}MB > 18MB — 못 보냄`); try { fs.renameSync(fp, fp + '.toobig'); } catch (e) {} tmuxNote('⚠️ ' + fn + ' 는 18MB 초과라 아직 못 보내 (곧 큰 파일도 지원). 잘라 보내거나 링크로.'); continue; }
360
+ if (st.size > MAXOUT) { console.warn(` ⚠️ "${fn}" ${(st.size / 1048576).toFixed(1)}MB > 18MB — 못 보냄(R2 대기)`); try { fs.renameSync(fp, fp + '.toobig'); } catch (e) {} continue; }
356
361
  const ext = (fn.split('.').pop() || '').toLowerCase();
357
362
  const mime = EXT_MIME[ext] || 'application/octet-stream';
358
363
  let data; try { data = fs.readFileSync(fp).toString('base64'); } catch (e) { continue; }
@@ -360,9 +365,8 @@ async function run(conf) {
360
365
  const j = await post(api, '/api/upload', { token, data, mime, name: fn });
361
366
  if (j && j.id) {
362
367
  await post(api, '/api/outfile', { token, id: j.id, name: fn, mime, size: st.size }).catch(() => {});
363
- console.log(` 📤 "${fn}" (${(st.size / 1024).toFixed(0)}KB) → 사용자 화면으로 보냄`);
368
+ console.log(` 📤 "${fn}" (${(st.size / 1024).toFixed(0)}KB) → 사용자 화면으로 보냄`); // 확인은 웹 드로어 '받은파일'에 — claude 주입 금지
364
369
  try { fs.unlinkSync(fp); } catch (e) {}
365
- tmuxNote('📤 보냈어 — ' + fn);
366
370
  } else { console.warn(` 파일 보내기 실패(${fn}): ${j && j.error}`); try { fs.renameSync(fp, fp + '.err'); } catch (e) {} }
367
371
  } catch (e) { console.error('[outbox fetch err]', e.message); }
368
372
  }