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.
@@ -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('Tip: Scroll with mouse wheel, detach with Ctrl+b d'));
322
- console.log(chalk_1.default.dim(' Use `gb cp <file>` to copy local files to this genbox.\n'));
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.155",
3
+ "version": "1.0.157",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {