@cliphijack/santaclaude 1.0.16 → 1.0.18

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 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cliphijack/santaclaude",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "publishConfig": { "access": "public" },
5
5
  "description": "SantaClaude 커넥터 — 클라우드 예약을 내 로컬 Claude(tmux)에 발사",
6
6
  "bin": { "santaclaude": "./santaclaude.js" },
package/santaclaude.js CHANGED
@@ -308,7 +308,7 @@ description: 산타 Gbrain — 내 노트·문서·지식 폴더에서 답을
308
308
  }
309
309
  };
310
310
  // 확장자 → MIME (보낼 파일 타입 판별)
311
- const EXT_MIME = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp', svg: 'image/svg+xml', bmp: 'image/bmp', pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv', json: 'application/json', log: 'text/plain', html: 'text/html', xml: 'text/xml', mp3: 'audio/mpeg', wav: 'audio/wav', ogg: 'audio/ogg', m4a: 'audio/mp4', flac: 'audio/flac', mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime', mkv: 'video/x-matroska', zip: 'application/zip', gz: 'application/gzip', tar: 'application/x-tar' };
311
+ const EXT_MIME = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp', svg: 'image/svg+xml', bmp: 'image/bmp', pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv', json: 'application/json', log: 'text/plain', html: 'text/html', xml: 'text/xml', mp3: 'audio/mpeg', wav: 'audio/wav', ogg: 'audio/ogg', m4a: 'audio/mp4', flac: 'audio/flac', mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime', mkv: 'video/x-matroska', zip: 'application/zip', gz: 'application/gzip', tar: 'application/x-tar', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', xls: 'application/vnd.ms-excel', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', doc: 'application/msword', pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ppt: 'application/vnd.ms-powerpoint', rtf: 'application/rtf', yaml: 'text/yaml', yml: 'text/yaml' };
312
312
  function installDefaultSkills(cwd) {
313
313
  const base = cwd && fs.existsSync(cwd) ? cwd : os.homedir();
314
314
  for (const [name, files] of Object.entries(DEFAULT_SKILLS)) {
@@ -392,27 +392,34 @@ async function run(conf) {
392
392
  return;
393
393
  }
394
394
  if (cmd.action === 'image') {
395
- // 웹이 올린 이미지를 받아 로컬에 저장하고 claude에 경로 주입
395
+ // 웹이 올린 파일을 받아 로컬에 저장하고 claude에 경로 주입 (이미지·txt·mp3·mp4 등 전부)
396
396
  const dir = path.join(os.homedir(), '.santaclaude', 'img');
397
397
  try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}
398
- const paths = [];
398
+ const MIME_EXT = {}; for (const k in EXT_MIME) if (!(EXT_MIME[k] in MIME_EXT)) MIME_EXT[EXT_MIME[k]] = k; // mime→ext 역매핑
399
+ const paths = []; let allImg = true;
399
400
  for (const im of (cmd.imgs || [])) {
400
401
  try {
401
402
  const r = await fetch(api + '/api/img?token=' + encodeURIComponent(token) + '&id=' + encodeURIComponent(im.id));
402
403
  if (!r.ok) continue;
403
404
  const j = await r.json();
404
- const ext = String(j.mime || '').includes('png') ? 'png' : String(j.mime || '').includes('webp') ? 'webp' : 'jpg';
405
- const fp = path.join(dir, Date.now() + '_' + paths.length + '.' + ext);
405
+ const mime = String(j.mime || '');
406
+ if (!mime.startsWith('image/')) allImg = false;
407
+ const safe = String(im.name || j.name || '').replace(/[\/\\\x00-\x1f]/g, '').replace(/\s+/g, '_').slice(0, 80); // 한글 등 유니코드 보존, 경로·제어문자만 제거(execFileSync 무쉘이라 안전)
408
+ let fname;
409
+ if (safe && /\.[A-Za-z0-9]+$/.test(safe)) fname = Date.now() + '_' + paths.length + '_' + safe; // 원본 파일명·확장자 보존
410
+ else { const ext = MIME_EXT[mime] || (mime.includes('png') ? 'png' : mime.includes('webp') ? 'webp' : mime.startsWith('image/') ? 'jpg' : 'bin'); fname = Date.now() + '_' + paths.length + '.' + ext; }
411
+ const fp = path.join(dir, fname);
406
412
  fs.writeFileSync(fp, Buffer.from(j.data, 'base64'));
407
413
  paths.push(fp);
408
414
  } catch (e) {}
409
415
  }
410
416
  if (paths.length) {
411
417
  const w = (cmd.name && windowExists(session, cmd.name)) ? (session + ':' + cmd.name) : pane;
412
- const msg = (cmd.note ? cmd.note + ' ' : '') + '첨부 이미지 ' + paths.length + '장 봐줘: ' + paths.join(' ');
418
+ const label = allImg ? ('첨부 이미지 ' + paths.length + '') : ('첨부 파일 ' + paths.length + '');
419
+ const msg = (cmd.note ? cmd.note + ' ' : '') + label + ' 봐줘: ' + paths.join(' ');
413
420
  execFileSync('tmux', ['send-keys', '-t', w, '-l', msg]);
414
421
  setTimeout(() => { try { execFileSync('tmux', ['send-keys', '-t', w, 'Enter']); } catch (e) {} }, 300);
415
- console.log(` 🖼️ 이미지 ${paths.length} → ${w}`);
422
+ console.log(` 📎 ${label} → ${w}`);
416
423
  }
417
424
  return;
418
425
  }