genbox 1.0.168 → 1.0.170

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.
@@ -50,8 +50,9 @@ const confirm_1 = __importDefault(require("@inquirer/confirm"));
50
50
  // Image extensions we handle for auto-upload
51
51
  const IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg', '.ico', '.heic', '.tiff'];
52
52
  function getClipboard() {
53
+ const platform = os.platform();
53
54
  try {
54
- if (os.platform() === 'darwin') {
55
+ if (platform === 'darwin') {
55
56
  // First, try to get file path if a file was copied (Cmd+C on a file in Finder)
56
57
  try {
57
58
  const filePath = (0, child_process_1.execSync)(`osascript -e 'POSIX path of (the clipboard as «class furl»)' 2>/dev/null`, { encoding: 'utf-8' }).trim();
@@ -65,7 +66,8 @@ function getClipboard() {
65
66
  // Fall back to text clipboard
66
67
  return (0, child_process_1.execSync)('pbpaste 2>/dev/null', { encoding: 'utf-8' }).trim();
67
68
  }
68
- else if (os.platform() === 'linux') {
69
+ else if (platform === 'linux') {
70
+ // Linux: Try xclip first, then xsel
69
71
  try {
70
72
  return (0, child_process_1.execSync)('xclip -selection clipboard -o 2>/dev/null', { encoding: 'utf-8' }).trim();
71
73
  }
@@ -73,6 +75,20 @@ function getClipboard() {
73
75
  return (0, child_process_1.execSync)('xsel --clipboard --output 2>/dev/null', { encoding: 'utf-8' }).trim();
74
76
  }
75
77
  }
78
+ else if (platform === 'win32') {
79
+ // Windows: Use PowerShell to get clipboard
80
+ try {
81
+ // Try to get file path first
82
+ const filePath = (0, child_process_1.execSync)('powershell -command "Get-Clipboard -Format FileDropList | Select-Object -First 1 -ExpandProperty FullName"', { encoding: 'utf-8', timeout: 2000 }).trim();
83
+ if (filePath && filePath.length > 0) {
84
+ return filePath;
85
+ }
86
+ }
87
+ catch {
88
+ // No file, try text
89
+ }
90
+ return (0, child_process_1.execSync)('powershell -command "Get-Clipboard"', { encoding: 'utf-8', timeout: 2000 }).trim();
91
+ }
76
92
  }
77
93
  catch {
78
94
  return '';
@@ -80,14 +96,16 @@ function getClipboard() {
80
96
  return '';
81
97
  }
82
98
  function setClipboard(text) {
99
+ const platform = os.platform();
83
100
  try {
84
- if (os.platform() === 'darwin') {
101
+ if (platform === 'darwin') {
85
102
  // Use osascript to set clipboard - this properly overwrites file references
86
103
  const escaped = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
87
104
  (0, child_process_1.execSync)(`osascript -e 'set the clipboard to "${escaped}"'`, { encoding: 'utf-8' });
88
105
  return true;
89
106
  }
90
- else if (os.platform() === 'linux') {
107
+ else if (platform === 'linux') {
108
+ // Linux: Try xclip first, then xsel
91
109
  try {
92
110
  (0, child_process_1.execSync)(`printf '%s' "${text.replace(/"/g, '\\"')}" | xclip -selection clipboard`, { encoding: 'utf-8' });
93
111
  return true;
@@ -97,6 +115,12 @@ function setClipboard(text) {
97
115
  return true;
98
116
  }
99
117
  }
118
+ else if (platform === 'win32') {
119
+ // Windows: Use PowerShell to set clipboard
120
+ const escaped = text.replace(/"/g, '`"').replace(/\$/g, '`$');
121
+ (0, child_process_1.execSync)(`powershell -command "Set-Clipboard -Value '${escaped}'"`, { encoding: 'utf-8', timeout: 2000 });
122
+ return true;
123
+ }
100
124
  }
101
125
  catch {
102
126
  return false;
@@ -118,13 +142,22 @@ function stripAnsi(text) {
118
142
  function extractLocalImagePath(text) {
119
143
  // Strip ANSI codes and clean path
120
144
  const cleaned = cleanPath(stripAnsi(text));
121
- // Patterns for local paths
122
- const patterns = [
145
+ const platform = os.platform();
146
+ // Patterns for local paths (Unix-style)
147
+ const unixPatterns = [
123
148
  /\/var\/folders\/[^\n\r\t]+/,
124
149
  /\/private\/var\/folders\/[^\n\r\t]+/,
125
150
  /\/tmp\/[^\n\r\t]+/,
126
151
  /\/Users\/[^\n\r\t]+/,
152
+ /\/home\/(?!dev)[^\n\r\t]+/, // /home but not /home/dev (which is remote)
127
153
  ];
154
+ // Patterns for Windows paths
155
+ const windowsPatterns = [
156
+ /[A-Z]:\\Users\\[^\n\r\t]+/i,
157
+ /[A-Z]:\\Temp\\[^\n\r\t]+/i,
158
+ /[A-Z]:\\Windows\\Temp\\[^\n\r\t]+/i,
159
+ ];
160
+ const patterns = platform === 'win32' ? windowsPatterns : unixPatterns;
128
161
  for (const pattern of patterns) {
129
162
  const match = cleaned.match(pattern);
130
163
  if (match) {
@@ -209,41 +242,66 @@ function uploadFileSync(localPath, ipAddress, keyPath) {
209
242
  }
210
243
  // Check if our terminal window is currently in foreground
211
244
  function isTerminalInForeground(genboxName) {
212
- if (os.platform() !== 'darwin') {
213
- return true; // Can't detect on other platforms, assume yes
214
- }
245
+ const platform = os.platform();
215
246
  try {
216
- // Get frontmost app and window title
217
- const script = `
218
- tell application "System Events"
219
- set frontApp to first application process whose frontmost is true
220
- set frontAppName to name of frontApp
221
- try
222
- tell frontApp
223
- set windowTitle to name of window 1
224
- end tell
225
- on error
226
- set windowTitle to ""
227
- end try
228
- return frontAppName & "|" & windowTitle
229
- end tell
230
- `;
231
- const result = (0, child_process_1.execSync)(`osascript -e '${script}'`, { encoding: 'utf-8', timeout: 1000 }).trim();
232
- const [appName, windowTitle] = result.split('|');
233
- // Check if it's a terminal app
234
- const terminalApps = ['iTerm2', 'Terminal', 'Hyper', 'Alacritty', 'kitty', 'Warp'];
235
- if (!terminalApps.some(t => appName.includes(t))) {
236
- return false; // Not a terminal in foreground
247
+ if (platform === 'darwin') {
248
+ // macOS: Use AppleScript to get frontmost app and window title
249
+ const script = `
250
+ tell application "System Events"
251
+ set frontApp to first application process whose frontmost is true
252
+ set frontAppName to name of frontApp
253
+ try
254
+ tell frontApp
255
+ set windowTitle to name of window 1
256
+ end tell
257
+ on error
258
+ set windowTitle to ""
259
+ end try
260
+ return frontAppName & "|" & windowTitle
261
+ end tell
262
+ `;
263
+ const result = (0, child_process_1.execSync)(`osascript -e '${script}'`, { encoding: 'utf-8', timeout: 1000 }).trim();
264
+ const [appName, windowTitle] = result.split('|');
265
+ // Check if it's a terminal app
266
+ const terminalApps = ['iTerm2', 'Terminal', 'Hyper', 'Alacritty', 'kitty', 'Warp'];
267
+ if (!terminalApps.some(t => appName.includes(t))) {
268
+ return false;
269
+ }
270
+ // Check if window title contains our genbox name
271
+ return windowTitle?.toLowerCase().includes(genboxName.toLowerCase()) ||
272
+ windowTitle?.includes(`@${genboxName}`) ||
273
+ windowTitle?.includes('dev@');
237
274
  }
238
- // Check if window title contains our genbox name
239
- if (windowTitle && windowTitle.toLowerCase().includes(genboxName.toLowerCase())) {
240
- return true;
275
+ else if (platform === 'linux') {
276
+ // Linux: Use xdotool to get active window title
277
+ try {
278
+ const windowTitle = (0, child_process_1.execSync)('xdotool getactivewindow getwindowname 2>/dev/null', {
279
+ encoding: 'utf-8',
280
+ timeout: 1000
281
+ }).trim();
282
+ return windowTitle.toLowerCase().includes(genboxName.toLowerCase()) ||
283
+ windowTitle.includes(`@${genboxName}`) ||
284
+ windowTitle.includes('dev@');
285
+ }
286
+ catch {
287
+ // xdotool not installed, allow anyway
288
+ return true;
289
+ }
241
290
  }
242
- // Also check for common patterns like "dev@genbox-name" or "genbox-name"
243
- if (windowTitle && (windowTitle.includes(`@${genboxName}`) || windowTitle.includes(`dev@`))) {
244
- return true;
291
+ else if (platform === 'win32') {
292
+ // Windows: Use PowerShell to get active window title
293
+ try {
294
+ const windowTitle = (0, child_process_1.execSync)('powershell -command "(Get-Process | Where-Object {$_.MainWindowHandle -eq (Add-Type -MemberDefinition \'[DllImport(\\"user32.dll\\")]public static extern IntPtr GetForegroundWindow();\' -Name Win32 -Namespace Native -PassThru)::GetForegroundWindow()}).MainWindowTitle"', { encoding: 'utf-8', timeout: 2000 }).trim();
295
+ return windowTitle.toLowerCase().includes(genboxName.toLowerCase()) ||
296
+ windowTitle.includes(`@${genboxName}`) ||
297
+ windowTitle.includes('dev@');
298
+ }
299
+ catch {
300
+ // PowerShell failed, allow anyway
301
+ return true;
302
+ }
245
303
  }
246
- return false;
304
+ return true; // Unknown platform, allow
247
305
  }
248
306
  catch {
249
307
  return true; // On error, assume yes to not break functionality
@@ -252,14 +310,14 @@ function isTerminalInForeground(genboxName) {
252
310
  // Show message in tmux status bar with styling
253
311
  function showTmuxMessage(ipAddress, keyPath, sessionName, message, style = 'success') {
254
312
  const controlPath = getControlPath(ipAddress);
255
- // Style configurations
313
+ // Style configurations (tmux colors work on any platform since it runs on remote Linux)
256
314
  const styles = {
257
- uploading: 'bg=colour214,fg=black,bold', // Orange/yellow for uploading
258
- success: 'bg=colour34,fg=white,bold', // Nice green for success
315
+ uploading: 'bg=colour220,fg=black,bold', // Yellow for uploading
316
+ success: 'bg=colour40,fg=black,bold', // Bright green for success
259
317
  error: 'bg=colour196,fg=white,bold', // Red for error
260
- warning: 'bg=colour208,fg=black,bold', // Orange for warning
318
+ warning: 'bg=colour214,fg=black,bold', // Orange for warning
261
319
  };
262
- const duration = style === 'uploading' ? 30000 : 4000; // Long duration for uploading (will be replaced)
320
+ const duration = style === 'uploading' ? 30000 : 5000; // Long duration for uploading (will be replaced)
263
321
  const msgStyle = styles[style];
264
322
  try {
265
323
  // Set message style and display message
@@ -576,10 +634,10 @@ async function attachToSession(ipAddress, keyPath, sessionName, genboxName) {
576
634
  }
577
635
  // Try to extract a local image path
578
636
  const extractResult = extractLocalImagePath(currentClipboard);
579
- // If file path was detected but file doesn't exist (e.g., macOS temp file was cleaned up)
637
+ // If file path was detected but file doesn't exist (e.g., temp file was cleaned up)
580
638
  if (extractResult.notFound) {
581
639
  const fileName = path.basename(extractResult.notFound);
582
- showTmuxMessage(ipAddress, keyPath, sessionName, ' File not found: ' + fileName.substring(0, 30), 'warning');
640
+ showTmuxMessage(ipAddress, keyPath, sessionName, 'FILE NOT FOUND: ' + fileName.substring(0, 40), 'warning');
583
641
  return;
584
642
  }
585
643
  if (!extractResult.path) {
@@ -590,8 +648,8 @@ async function attachToSession(ipAddress, keyPath, sessionName, genboxName) {
590
648
  if (uploadedPaths.has(localPath)) {
591
649
  return;
592
650
  }
593
- // Show uploading message in tmux (orange background, stays until replaced)
594
- showTmuxMessage(ipAddress, keyPath, sessionName, '📤 Uploading image...', 'uploading');
651
+ // Show processing message in tmux (yellow background, stays until replaced)
652
+ showTmuxMessage(ipAddress, keyPath, sessionName, 'PROCESSING IMAGE - please wait...', 'uploading');
595
653
  // Upload the file (this happens in background, might cause brief pause)
596
654
  const result = uploadFileSync(localPath, ipAddress, keyPath);
597
655
  if (result.success) {
@@ -600,11 +658,12 @@ async function attachToSession(ipAddress, keyPath, sessionName, genboxName) {
600
658
  // Replace clipboard with remote path
601
659
  setClipboard(result.remotePath);
602
660
  // Show success in tmux (green background, replaces uploading message immediately)
603
- showTmuxMessage(ipAddress, keyPath, sessionName, ' Image ready! Cmd+V to paste', 'success');
661
+ const pasteKey = os.platform() === 'darwin' ? 'Cmd+V' : 'Ctrl+V';
662
+ showTmuxMessage(ipAddress, keyPath, sessionName, `READY - Press ${pasteKey} to paste image path`, 'success');
604
663
  }
605
664
  else {
606
665
  // Show failure in tmux (red background)
607
- showTmuxMessage(ipAddress, keyPath, sessionName, ' Upload failed: ' + (result.error || 'unknown'), 'error');
666
+ showTmuxMessage(ipAddress, keyPath, sessionName, 'UPLOAD FAILED: ' + (result.error || 'unknown'), 'error');
608
667
  }
609
668
  }
610
669
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.168",
3
+ "version": "1.0.170",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {