genbox 1.0.155 → 1.0.157
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 +140 -2
- package/package.json +1 -1
package/dist/commands/attach.js
CHANGED
|
@@ -47,6 +47,107 @@ const path = __importStar(require("path"));
|
|
|
47
47
|
const fs = __importStar(require("fs"));
|
|
48
48
|
const select_1 = __importDefault(require("@inquirer/select"));
|
|
49
49
|
const confirm_1 = __importDefault(require("@inquirer/confirm"));
|
|
50
|
+
// Image extensions we handle for auto-upload
|
|
51
|
+
const IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg', '.ico', '.heic', '.tiff'];
|
|
52
|
+
function getClipboard() {
|
|
53
|
+
try {
|
|
54
|
+
if (os.platform() === 'darwin') {
|
|
55
|
+
return (0, child_process_1.execSync)('pbpaste 2>/dev/null', { encoding: 'utf-8' }).trim();
|
|
56
|
+
}
|
|
57
|
+
else if (os.platform() === 'linux') {
|
|
58
|
+
try {
|
|
59
|
+
return (0, child_process_1.execSync)('xclip -selection clipboard -o 2>/dev/null', { encoding: 'utf-8' }).trim();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return (0, child_process_1.execSync)('xsel --clipboard --output 2>/dev/null', { encoding: 'utf-8' }).trim();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
return '';
|
|
70
|
+
}
|
|
71
|
+
function setClipboard(text) {
|
|
72
|
+
try {
|
|
73
|
+
if (os.platform() === 'darwin') {
|
|
74
|
+
(0, child_process_1.execSync)(`printf '%s' "${text.replace(/"/g, '\\"')}" | pbcopy`, { encoding: 'utf-8' });
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
else if (os.platform() === 'linux') {
|
|
78
|
+
try {
|
|
79
|
+
(0, child_process_1.execSync)(`printf '%s' "${text.replace(/"/g, '\\"')}" | xclip -selection clipboard`, { encoding: 'utf-8' });
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
(0, child_process_1.execSync)(`printf '%s' "${text.replace(/"/g, '\\"')}" | xsel --clipboard --input`, { encoding: 'utf-8' });
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
function isImageFile(filePath) {
|
|
94
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
95
|
+
return IMAGE_EXTENSIONS.includes(ext);
|
|
96
|
+
}
|
|
97
|
+
function cleanPath(text) {
|
|
98
|
+
// Remove escape characters from terminal drag-drop (backslash before spaces)
|
|
99
|
+
return text.replace(/\\ /g, ' ').trim();
|
|
100
|
+
}
|
|
101
|
+
function extractLocalImagePath(text) {
|
|
102
|
+
const cleaned = cleanPath(text);
|
|
103
|
+
// Patterns for local paths
|
|
104
|
+
const patterns = [
|
|
105
|
+
/\/var\/folders\/[^\n\r]+/,
|
|
106
|
+
/\/private\/var\/folders\/[^\n\r]+/,
|
|
107
|
+
/\/tmp\/[^\n\r]+/,
|
|
108
|
+
/\/Users\/[^\n\r]+/,
|
|
109
|
+
];
|
|
110
|
+
for (const pattern of patterns) {
|
|
111
|
+
const match = cleaned.match(pattern);
|
|
112
|
+
if (match) {
|
|
113
|
+
let potentialPath = match[0].trim();
|
|
114
|
+
// Remove trailing quotes or special chars
|
|
115
|
+
potentialPath = potentialPath.replace(/['"]+$/, '');
|
|
116
|
+
if (fs.existsSync(potentialPath) && isImageFile(potentialPath)) {
|
|
117
|
+
return potentialPath;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Check if the whole cleaned text is a valid image path
|
|
122
|
+
if (fs.existsSync(cleaned) && isImageFile(cleaned)) {
|
|
123
|
+
return cleaned;
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
function uploadFileSync(localPath, ipAddress, keyPath) {
|
|
128
|
+
const fileName = path.basename(localPath);
|
|
129
|
+
const timestamp = Date.now();
|
|
130
|
+
const remotePath = `/home/dev/uploads/${timestamp}-${fileName}`;
|
|
131
|
+
try {
|
|
132
|
+
// Ensure uploads directory exists
|
|
133
|
+
(0, child_process_1.execSync)(`ssh -i "${keyPath}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dev@${ipAddress} "mkdir -p /home/dev/uploads" 2>/dev/null`, { encoding: 'utf-8', timeout: 10000 });
|
|
134
|
+
// Upload file
|
|
135
|
+
const result = (0, child_process_1.spawnSync)('scp', [
|
|
136
|
+
'-i', keyPath,
|
|
137
|
+
'-o', 'StrictHostKeyChecking=no',
|
|
138
|
+
'-o', 'UserKnownHostsFile=/dev/null',
|
|
139
|
+
localPath,
|
|
140
|
+
`dev@${ipAddress}:${remotePath}`
|
|
141
|
+
], { timeout: 60000 });
|
|
142
|
+
if (result.status !== 0) {
|
|
143
|
+
return { success: false, remotePath: '', error: 'SCP failed' };
|
|
144
|
+
}
|
|
145
|
+
return { success: true, remotePath };
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
return { success: false, remotePath: '', error: error.message };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
50
151
|
function getPrivateSshKey() {
|
|
51
152
|
const home = os.homedir();
|
|
52
153
|
const potentialKeys = [
|
|
@@ -318,8 +419,9 @@ exports.attachCommand = new commander_1.Command('attach')
|
|
|
318
419
|
});
|
|
319
420
|
async function attachToSession(ipAddress, keyPath, sessionName, genboxName) {
|
|
320
421
|
console.log(chalk_1.default.dim(`\nAttaching to ${chalk_1.default.bold(sessionName)} on ${chalk_1.default.bold(genboxName)}...`));
|
|
321
|
-
console.log(chalk_1.default.dim('
|
|
322
|
-
console.log(
|
|
422
|
+
console.log(chalk_1.default.dim('Detach: Ctrl+b d'));
|
|
423
|
+
console.log('');
|
|
424
|
+
console.log(chalk_1.default.yellow('📋 Image Upload:') + chalk_1.default.dim(' Drag image → select path → Cmd+C → auto-uploads → Cmd+V\n'));
|
|
323
425
|
const sshArgs = [
|
|
324
426
|
'-t',
|
|
325
427
|
'-i', keyPath,
|
|
@@ -328,9 +430,45 @@ async function attachToSession(ipAddress, keyPath, sessionName, genboxName) {
|
|
|
328
430
|
`dev@${ipAddress}`,
|
|
329
431
|
`tmux attach -t ${sessionName}`
|
|
330
432
|
];
|
|
433
|
+
// Start clipboard watcher
|
|
434
|
+
let lastClipboard = getClipboard();
|
|
435
|
+
const uploadedPaths = new Set();
|
|
436
|
+
const clipboardInterval = setInterval(() => {
|
|
437
|
+
try {
|
|
438
|
+
const currentClipboard = getClipboard();
|
|
439
|
+
// Check if clipboard changed
|
|
440
|
+
if (currentClipboard === lastClipboard) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
lastClipboard = currentClipboard;
|
|
444
|
+
// Try to extract a local image path
|
|
445
|
+
const localPath = extractLocalImagePath(currentClipboard);
|
|
446
|
+
if (!localPath) {
|
|
447
|
+
return; // Not a local image path
|
|
448
|
+
}
|
|
449
|
+
// Check if we already uploaded this
|
|
450
|
+
if (uploadedPaths.has(localPath)) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
// Upload the file (this happens in background, might cause brief pause)
|
|
454
|
+
const result = uploadFileSync(localPath, ipAddress, keyPath);
|
|
455
|
+
if (result.success) {
|
|
456
|
+
uploadedPaths.add(localPath);
|
|
457
|
+
// Replace clipboard with remote path
|
|
458
|
+
setClipboard(result.remotePath);
|
|
459
|
+
// Use stderr to not interfere with terminal
|
|
460
|
+
process.stderr.write(`\n${chalk_1.default.green('✓')} Uploaded ${chalk_1.default.dim(path.basename(localPath))} → clipboard has remote path. Just Cmd+V!\n`);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
// Ignore clipboard errors during SSH session
|
|
465
|
+
}
|
|
466
|
+
}, 500);
|
|
331
467
|
const ssh = (0, child_process_1.spawn)('ssh', sshArgs, { stdio: 'inherit' });
|
|
332
468
|
return new Promise((resolve) => {
|
|
333
469
|
ssh.on('close', (code) => {
|
|
470
|
+
// Stop clipboard watcher
|
|
471
|
+
clearInterval(clipboardInterval);
|
|
334
472
|
if (code === 0) {
|
|
335
473
|
console.log(chalk_1.default.dim('\nDetached from session.'));
|
|
336
474
|
}
|