claude-remote 0.4.2 → 0.4.3

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 +49 -31
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-remote",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
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
@@ -5,7 +5,7 @@ const os = require('os');
5
5
  const pty = require('node-pty');
6
6
  const { WebSocketServer, WebSocket } = require('ws');
7
7
  const crypto = require('crypto');
8
- const { execSync } = require('child_process');
8
+ const { execSync, spawn } = require('child_process');
9
9
 
10
10
  // --- CLI argument parsing ---
11
11
  // Separate bridge args (CWD positional) from claude passthrough flags.
@@ -657,10 +657,6 @@ function createTempImageFile(buffer, mediaType, uploadId) {
657
657
  return tmpFile;
658
658
  }
659
659
 
660
- function shellQuote(value) {
661
- return `'${String(value || '').replace(/'/g, `'\\''`)}'`;
662
- }
663
-
664
660
  function getLinuxClipboardTool() {
665
661
  if (process.platform === 'win32' || process.platform === 'darwin') return null;
666
662
  try {
@@ -687,6 +683,38 @@ function assertLinuxClipboardAvailable() {
687
683
  if (tool) return tool;
688
684
  throw new Error('Linux image paste requires xclip or wl-copy on the server. Install one and try again.');
689
685
  }
686
+
687
+ function startLinuxClipboardImage(tmpFile, mediaType) {
688
+ const type = String(mediaType || 'image/png').toLowerCase();
689
+ const tool = assertLinuxClipboardAvailable();
690
+ const imageBuffer = fs.readFileSync(tmpFile);
691
+ const args = tool === 'xclip'
692
+ ? ['-selection', 'clipboard', '-t', type, '-i', '-loops', '1']
693
+ : ['--type', type, '--paste-once'];
694
+ const child = spawn(tool, args, {
695
+ detached: true,
696
+ stdio: ['pipe', 'ignore', 'pipe'],
697
+ });
698
+ let stderr = '';
699
+ child.on('error', (err) => {
700
+ log(`Linux clipboard process error (${tool}): ${err.message}`);
701
+ });
702
+ child.stderr.on('data', (chunk) => {
703
+ stderr += chunk.toString('utf8');
704
+ if (stderr.length > 2000) stderr = stderr.slice(-2000);
705
+ });
706
+ child.on('exit', (code, signal) => {
707
+ const extra = stderr.trim() ? ` stderr=${JSON.stringify(stderr.trim())}` : '';
708
+ log(`Linux clipboard process exited (${tool}) code=${code ?? 'null'} signal=${signal ?? 'null'}${extra}`);
709
+ });
710
+ child.stdin.on('error', (err) => {
711
+ if (err.code !== 'EPIPE') log(`Linux clipboard stdin error (${tool}): ${err.message}`);
712
+ });
713
+ child.stdin.end(imageBuffer);
714
+ child.unref();
715
+ log(`Linux clipboard process started (${tool}) pid=${child.pid ?? 'null'} type=${type} bytes=${imageBuffer.length}`);
716
+ return tool;
717
+ }
690
718
 
691
719
  setInterval(() => {
692
720
  const now = Date.now();
@@ -1689,33 +1717,23 @@ function handlePreparedImageUpload({ tmpFile, mediaType, text, logLabel = '', on
1689
1717
  } else if (isMac) {
1690
1718
  execSync(`osascript -e 'set the clipboard to (read POSIX file "${tmpFile}" as 芦class PNGf禄)'`, { timeout: 10000 });
1691
1719
  } else {
1692
- const type = String(mediaType || 'image/png').toLowerCase();
1693
- const tool = assertLinuxClipboardAvailable();
1694
- const quotedPath = shellQuote(tmpFile);
1695
- const quotedType = shellQuote(type);
1696
- if (tool === 'xclip') {
1697
- execSync(`xclip -selection clipboard -t ${quotedType} -i < ${quotedPath}`, {
1698
- timeout: 10000,
1699
- shell: '/bin/sh',
1700
- });
1701
- } else {
1702
- execSync(`wl-copy --type ${quotedType} < ${quotedPath}`, {
1703
- timeout: 10000,
1704
- shell: '/bin/sh',
1705
- });
1706
- }
1707
- log(`Linux clipboard set with ${tool} (${type})`);
1720
+ const tool = startLinuxClipboardImage(tmpFile, mediaType);
1721
+ log(`Linux clipboard armed with ${tool}`);
1708
1722
  }
1709
1723
  log('Clipboard set with image');
1710
1724
 
1711
- if (isWin) claudeProc.write('\x1bv');
1712
- else claudeProc.write('\x16');
1713
- log('Sent image paste keypress to PTY');
1714
-
1715
- setTimeout(() => {
1716
- if (!claudeProc) return;
1717
- const trimmedText = (text || '').trim();
1718
- if (trimmedText) claudeProc.write(trimmedText);
1725
+ const pasteDelayMs = isWin || isMac ? 0 : 150;
1726
+ setTimeout(() => {
1727
+ if (!claudeProc) return;
1728
+ if (isWin) claudeProc.write('\x1bv');
1729
+ else claudeProc.write('\x16');
1730
+ log('Sent image paste keypress to PTY');
1731
+ }, pasteDelayMs);
1732
+
1733
+ setTimeout(() => {
1734
+ if (!claudeProc) return;
1735
+ const trimmedText = (text || '').trim();
1736
+ if (trimmedText) claudeProc.write(trimmedText);
1719
1737
 
1720
1738
  setTimeout(() => {
1721
1739
  if (claudeProc) claudeProc.write('\r');
@@ -1727,8 +1745,8 @@ function handlePreparedImageUpload({ tmpFile, mediaType, text, logLabel = '', on
1727
1745
  try { fs.unlinkSync(tmpFile); } catch {}
1728
1746
  }
1729
1747
  }, 5000);
1730
- }, 150);
1731
- }, 1000);
1748
+ }, 150);
1749
+ }, 1000 + pasteDelayMs);
1732
1750
  } catch (err) {
1733
1751
  log(`Image upload error: ${err.message}`);
1734
1752
  if (onCleanup) onCleanup();