claude-remote 0.2.4 → 0.3.1

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/server.js +44 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-remote",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "Remote control bridge for Claude Code REPL - drive from phone/WebUI",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -129,6 +129,7 @@ let tailCatchingUp = false; // true while reading historical transcript content
129
129
  const isTTY = process.stdin.isTTY && process.stdout.isTTY;
130
130
  const LEGACY_REPLAY_DELAY_MS = 1500;
131
131
  const IMAGE_UPLOAD_TTL_MS = 15 * 60 * 1000;
132
+ const IMAGE_UPLOAD_DIR = path.join(CLAUDE_HOME, 'remote-uploads');
132
133
  let turnStateVersion = 0;
133
134
  let turnState = {
134
135
  phase: 'idle',
@@ -566,14 +567,18 @@ function cleanupImageUpload(uploadId) {
566
567
 
567
568
  function cleanupClientUploads(ws) {
568
569
  for (const [uploadId, upload] of pendingImageUploads) {
569
- if (upload.owner === ws) cleanupImageUpload(uploadId);
570
+ if (upload.owner === ws && !upload.submitted) cleanupImageUpload(uploadId);
570
571
  }
571
572
  }
572
573
 
573
574
  function createTempImageFile(buffer, mediaType, uploadId) {
574
- const tmpDir = process.env.CLAUDE_CODE_TMPDIR || os.tmpdir();
575
+ const isLinux = process.platform !== 'win32' && process.platform !== 'darwin';
576
+ const tmpDir = isLinux
577
+ ? IMAGE_UPLOAD_DIR
578
+ : (process.env.CLAUDE_CODE_TMPDIR || os.tmpdir());
575
579
  const type = String(mediaType || 'image/png').toLowerCase();
576
580
  const ext = type.includes('jpeg') || type.includes('jpg') ? '.jpg' : '.png';
581
+ fs.mkdirSync(tmpDir, { recursive: true });
577
582
  const tmpFile = path.join(tmpDir, `bridge_upload_${uploadId}_${Date.now()}${ext}`);
578
583
  fs.writeFileSync(tmpFile, buffer);
579
584
  return tmpFile;
@@ -668,6 +673,18 @@ wss.on('connection', (ws, req) => {
668
673
  sendReplay(ws, canResume ? msg.lastSeq : null);
669
674
  break;
670
675
  }
676
+ case 'foreground_probe': {
677
+ const probeId = typeof msg.probeId === 'string' ? msg.probeId : '';
678
+ sendWs(ws, {
679
+ type: 'foreground_probe_ack',
680
+ probeId,
681
+ sessionId: currentSessionId,
682
+ lastSeq: latestEventSeq(),
683
+ cwd: CWD,
684
+ }, 'foreground_probe');
685
+ log(`Foreground probe ack -> ${wsLabel(ws)} probeId=${probeId || 'none'} session=${currentSessionId ?? 'null'} lastSeq=${latestEventSeq()}`);
686
+ break;
687
+ }
671
688
  case 'input':
672
689
  // Raw terminal keystrokes from xterm.js in WebUI
673
690
  if (claudeProc) claudeProc.write(msg.data);
@@ -848,13 +865,16 @@ wss.on('connection', (ws, req) => {
848
865
  break;
849
866
  }
850
867
  try {
868
+ const shouldCleanupAfterSubmit = process.platform === 'win32' || process.platform === 'darwin';
851
869
  handlePreparedImageUpload({
852
870
  tmpFile: upload.tmpFile,
853
871
  mediaType: upload.mediaType,
854
872
  text: msg.text || '',
855
873
  logLabel: upload.name || uploadId,
856
- onCleanup: () => cleanupImageUpload(uploadId),
874
+ onCleanup: shouldCleanupAfterSubmit ? () => cleanupImageUpload(uploadId) : null,
857
875
  });
876
+ upload.submitted = true;
877
+ upload.updatedAt = Date.now();
858
878
  setTurnState('running', { reason: 'image_submit' });
859
879
  sendUploadStatus(ws, uploadId, 'submitted');
860
880
  } catch (err) {
@@ -1481,11 +1501,27 @@ function handlePreparedImageUpload({ tmpFile, mediaType, text, logLabel = '', on
1481
1501
 
1482
1502
  const isWin = process.platform === 'win32';
1483
1503
  const isMac = process.platform === 'darwin';
1504
+ const isLinux = !isWin && !isMac;
1484
1505
 
1485
1506
  try {
1486
1507
  const stat = fs.statSync(tmpFile);
1487
1508
  log(`Image ready: ${logLabel || path.basename(tmpFile)} (${stat.size} bytes)`);
1488
1509
 
1510
+ if (isLinux) {
1511
+ const quotedPath = JSON.stringify(tmpFile);
1512
+ const trimmedText = (text || '').trim();
1513
+ const prompt = trimmedText
1514
+ ? `Please analyze the image at this path: ${quotedPath}\n\nUser note:\n${trimmedText}`
1515
+ : `Analyze this image: ${quotedPath}`;
1516
+
1517
+ claudeProc.write(prompt);
1518
+ setTimeout(() => {
1519
+ if (claudeProc) claudeProc.write('\r');
1520
+ log(`Sent Linux image path prompt: ${tmpFile}`);
1521
+ }, 150);
1522
+ return;
1523
+ }
1524
+
1489
1525
  if (isWin) {
1490
1526
  const psCmd = `Add-Type -AssemblyName System.Drawing; Add-Type -AssemblyName System.Windows.Forms; $img = [System.Drawing.Image]::FromFile('${tmpFile.replace(/'/g, "''")}'); [System.Windows.Forms.Clipboard]::SetImage($img); $img.Dispose()`;
1491
1527
  execSync(`powershell -NoProfile -STA -Command "${psCmd}"`, { timeout: 10000 });
@@ -1551,6 +1587,11 @@ function handleImageUpload(msg) {
1551
1587
  mediaType: msg.mediaType,
1552
1588
  text: msg.text || '',
1553
1589
  });
1590
+ if (process.platform !== 'win32' && process.platform !== 'darwin') {
1591
+ setTimeout(() => {
1592
+ try { fs.unlinkSync(tmpFile); } catch {}
1593
+ }, IMAGE_UPLOAD_TTL_MS).unref();
1594
+ }
1554
1595
  setTurnState('running', { reason: 'legacy_image_upload' });
1555
1596
  } catch (err) {
1556
1597
  log(`Image upload error: ${err.message}`);