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.
- package/dist/commands/attach.js +107 -48
- package/package.json +1 -1
package/dist/commands/attach.js
CHANGED
|
@@ -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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
122
|
-
|
|
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
|
-
|
|
213
|
-
return true; // Can't detect on other platforms, assume yes
|
|
214
|
-
}
|
|
245
|
+
const platform = os.platform();
|
|
215
246
|
try {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
|
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=
|
|
258
|
-
success: 'bg=
|
|
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=
|
|
318
|
+
warning: 'bg=colour214,fg=black,bold', // Orange for warning
|
|
261
319
|
};
|
|
262
|
-
const duration = style === 'uploading' ? 30000 :
|
|
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.,
|
|
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, '
|
|
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
|
|
594
|
-
showTmuxMessage(ipAddress, keyPath, sessionName, '
|
|
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
|
-
|
|
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, '
|
|
666
|
+
showTmuxMessage(ipAddress, keyPath, sessionName, 'UPLOAD FAILED: ' + (result.error || 'unknown'), 'error');
|
|
608
667
|
}
|
|
609
668
|
}
|
|
610
669
|
catch {
|