genbox 1.0.208 → 1.0.209

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.
@@ -62,6 +62,7 @@ const ssh_proxy_1 = require("../utils/ssh-proxy");
62
62
  const unified_session_1 = require("../lib/unified-session");
63
63
  const genbox_progress_1 = require("../lib/genbox-progress");
64
64
  const genbox_wizard_1 = require("../lib/genbox-wizard");
65
+ const interactive_prompt_1 = require("../utils/interactive-prompt");
65
66
  const child_process_1 = require("child_process");
66
67
  const os = __importStar(require("os"));
67
68
  const path = __importStar(require("path"));
@@ -442,7 +443,8 @@ async function startLocalMultipass(genbox) {
442
443
  /**
443
444
  * Create and attach to a session on a genbox
444
445
  */
445
- async function startSessionOnGenbox(provider, genbox) {
446
+ async function startSessionOnGenbox(provider, genbox, options = {}) {
447
+ const { initialPrompt } = options;
446
448
  let targetGenbox = genbox;
447
449
  let sshTarget;
448
450
  let sshPortArgs = [];
@@ -482,6 +484,8 @@ async function startSessionOnGenbox(provider, genbox) {
482
484
  }
483
485
  const keyPath = requireSshKey();
484
486
  const sessionName = `${provider}-${Date.now().toString(36)}`;
487
+ // Always start CLI without prompt - we'll inject it after attaching
488
+ // This keeps the session interactive instead of one-shot mode
485
489
  const cliCommand = provider === 'claude'
486
490
  ? 'claude --dangerously-skip-permissions'
487
491
  : provider === 'gemini'
@@ -602,7 +606,8 @@ exec ${cliCommand}
602
606
  socketPath,
603
607
  user: 'dev',
604
608
  port: sshPort,
605
- verbose: true, // Debug mode
609
+ verbose: true,
610
+ initialPrompt, // Inject prompt after connection
606
611
  });
607
612
  await new Promise(resolve => proc.on('close', () => resolve()));
608
613
  console.log(chalk_1.default.dim('\nDetached from session.'));
@@ -677,7 +682,8 @@ async function killSession(session) {
677
682
  /**
678
683
  * Start a direct (no isolation) session
679
684
  */
680
- async function startDirectSession(provider) {
685
+ async function startDirectSession(provider, options = {}) {
686
+ const { initialPrompt } = options;
681
687
  // Check for dtach
682
688
  const dtachStatus = await ensureLocalDtach();
683
689
  if (dtachStatus === 'cancel') {
@@ -695,6 +701,7 @@ async function startDirectSession(provider) {
695
701
  syncEnabled: false, // Local-first: no API sync by default
696
702
  });
697
703
  const sessionName = session.name;
704
+ // Always start CLI without prompt - inject after connection
698
705
  const cliCommand = provider === 'claude'
699
706
  ? 'claude --dangerously-skip-permissions'
700
707
  : provider === 'gemini'
@@ -724,7 +731,39 @@ async function startDirectSession(provider) {
724
731
  }
725
732
  // Mark session as running
726
733
  await sessionManager.markRunning(session.id);
727
- const proc = (0, child_process_1.spawn)('bash', ['-c', cmd], { stdio: 'inherit' });
734
+ // For direct mode, we need to pipe stdin to inject the prompt
735
+ const proc = (0, child_process_1.spawn)('bash', ['-c', cmd], {
736
+ stdio: initialPrompt ? ['pipe', 'inherit', 'inherit'] : 'inherit',
737
+ });
738
+ // Inject initial prompt after CLI is ready
739
+ if (initialPrompt && proc.stdin) {
740
+ setTimeout(() => {
741
+ if (proc.stdin && !proc.killed) {
742
+ // Send prompt character by character with slower typing
743
+ const chars = initialPrompt.split('');
744
+ let i = 0;
745
+ const typeChar = () => {
746
+ if (i < chars.length && proc.stdin && !proc.killed) {
747
+ proc.stdin.write(chars[i]);
748
+ i++;
749
+ setTimeout(typeChar, 15); // 15ms between chars (slower, more reliable)
750
+ }
751
+ else if (proc.stdin && !proc.killed) {
752
+ // All chars sent, press Enter, then pipe user input
753
+ setTimeout(() => {
754
+ proc.stdin?.write('\r');
755
+ // Continue piping user input after prompt is sent
756
+ setTimeout(() => {
757
+ process.stdin.pipe(proc.stdin);
758
+ process.stdin.resume();
759
+ }, 200);
760
+ }, 100);
761
+ }
762
+ };
763
+ typeChar();
764
+ }
765
+ }, 3000); // 3 second delay for local sessions
766
+ }
728
767
  await new Promise((resolve) => {
729
768
  proc.on('close', () => {
730
769
  if (useDtach && fs.existsSync(socketPath)) {
@@ -894,6 +933,179 @@ async function runInteractiveFlow(provider) {
894
933
  break;
895
934
  }
896
935
  }
936
+ /**
937
+ * Interactive prompt mode flow (-p flag)
938
+ *
939
+ * This flow:
940
+ * 1. Collects multiline prompt (inline or via $EDITOR with -e)
941
+ * 2. Collects image paths (optional)
942
+ * 3. Shows target selection (genbox vs direct)
943
+ * 4. Uploads images to genbox if needed (waits for genbox to be ready)
944
+ * 5. Starts session with the composed prompt
945
+ */
946
+ async function runPromptModeFlow(provider, options = {}) {
947
+ const { useEditor = false } = options;
948
+ console.log(chalk_1.default.bold(`\n${provider.charAt(0).toUpperCase() + provider.slice(1)} - Interactive Prompt Mode`));
949
+ if (useEditor) {
950
+ console.log(chalk_1.default.dim('Compose your prompt in $EDITOR with optional images\n'));
951
+ }
952
+ else {
953
+ console.log(chalk_1.default.dim('Compose your prompt with optional images\n'));
954
+ }
955
+ // Step 1: Collect prompt and images
956
+ const collected = await (0, interactive_prompt_1.collectPromptWithImages)({ useEditor });
957
+ if (!collected) {
958
+ console.log(chalk_1.default.dim('\nCancelled.'));
959
+ return;
960
+ }
961
+ const { text, images } = collected;
962
+ // Show what was collected
963
+ console.log(chalk_1.default.dim(`\nPrompt: ${text.substring(0, 50)}${text.length > 50 ? '...' : ''}`));
964
+ if (images.length > 0) {
965
+ console.log(chalk_1.default.dim(`Images: ${images.length} file(s)`));
966
+ }
967
+ // Step 2: Select target (simplified menu since we have a specific prompt)
968
+ console.log(chalk_1.default.dim('\nFetching genboxes...'));
969
+ const genboxes = await getGenboxes(true);
970
+ const targetChoices = [];
971
+ // Running genboxes first (can upload immediately)
972
+ const runningGenboxes = genboxes.filter(g => g.status === 'running' && g.ipAddress);
973
+ const stoppedGenboxes = genboxes.filter(g => g.status === 'stopped');
974
+ if (runningGenboxes.length > 0) {
975
+ for (const g of runningGenboxes) {
976
+ const localLabel = g._isLocal ? chalk_1.default.yellow(' (local)') : '';
977
+ targetChoices.push({
978
+ name: `${chalk_1.default.green('●')} ${g.name}${localLabel} ${chalk_1.default.dim('- ready, can upload immediately')}`,
979
+ value: { type: 'genbox', genbox: g },
980
+ });
981
+ }
982
+ }
983
+ if (stoppedGenboxes.length > 0) {
984
+ for (const g of stoppedGenboxes) {
985
+ const localLabel = g._isLocal ? chalk_1.default.yellow(' (local)') : '';
986
+ targetChoices.push({
987
+ name: `${chalk_1.default.yellow('○')} ${g.name}${localLabel} ${chalk_1.default.dim('- will start, then upload')}`,
988
+ value: { type: 'genbox', genbox: g },
989
+ });
990
+ }
991
+ }
992
+ // Create new option
993
+ targetChoices.push({
994
+ name: chalk_1.default.dim('─────────────────────────────────────'),
995
+ value: { type: 'cancel' },
996
+ disabled: true,
997
+ });
998
+ targetChoices.push({
999
+ name: chalk_1.default.green('+') + ' Create new Genbox ' + chalk_1.default.dim('(will upload after creation)'),
1000
+ value: { type: 'create-genbox' },
1001
+ });
1002
+ // Direct option (no upload needed)
1003
+ if (images.length > 0) {
1004
+ targetChoices.push({
1005
+ name: chalk_1.default.dim(' Run directly (images stay local)'),
1006
+ value: { type: 'direct' },
1007
+ });
1008
+ }
1009
+ else {
1010
+ targetChoices.push({
1011
+ name: chalk_1.default.dim(' Run directly on this machine'),
1012
+ value: { type: 'direct' },
1013
+ });
1014
+ }
1015
+ targetChoices.push({
1016
+ name: chalk_1.default.dim(' Cancel'),
1017
+ value: { type: 'cancel' },
1018
+ });
1019
+ let targetAction;
1020
+ try {
1021
+ targetAction = await (0, select_1.default)({
1022
+ message: 'Where should this run?',
1023
+ choices: targetChoices.filter(c => !c.disabled),
1024
+ });
1025
+ }
1026
+ catch {
1027
+ console.log(chalk_1.default.dim('\nCancelled.'));
1028
+ return;
1029
+ }
1030
+ // Step 3: Handle based on target
1031
+ switch (targetAction.type) {
1032
+ case 'genbox': {
1033
+ const genbox = targetAction.genbox;
1034
+ let finalPrompt;
1035
+ if (images.length > 0) {
1036
+ // Upload images to genbox (waits for ready if needed)
1037
+ const uploadResult = await (0, interactive_prompt_1.uploadImagesToGenbox)(images, genbox, {
1038
+ showProgress: true,
1039
+ waitForReady: true,
1040
+ });
1041
+ if (uploadResult.errors.length > 0 && uploadResult.remotePaths.length === 0) {
1042
+ console.log(chalk_1.default.yellow('\nWarning: Could not upload images. Proceeding with prompt only.'));
1043
+ finalPrompt = text;
1044
+ }
1045
+ else {
1046
+ if (uploadResult.errors.length > 0) {
1047
+ console.log(chalk_1.default.yellow(`\nWarning: ${uploadResult.errors.length} image(s) failed to upload.`));
1048
+ }
1049
+ finalPrompt = (0, interactive_prompt_1.buildPromptWithImages)(text, uploadResult.remotePaths, { isRemote: true });
1050
+ }
1051
+ }
1052
+ else {
1053
+ finalPrompt = text;
1054
+ }
1055
+ // Start session with prompt
1056
+ await startSessionOnGenbox(provider, genbox, { initialPrompt: finalPrompt });
1057
+ break;
1058
+ }
1059
+ case 'create-genbox': {
1060
+ // Use wizard to create genbox
1061
+ const result = await (0, genbox_wizard_1.runCreateWizard)({
1062
+ provider,
1063
+ includeDirect: false,
1064
+ });
1065
+ if (!result.success || !result.genbox) {
1066
+ console.log(chalk_1.default.dim('\nGenbox creation cancelled or failed.'));
1067
+ return;
1068
+ }
1069
+ const genbox = result.genbox;
1070
+ let finalPrompt;
1071
+ if (images.length > 0) {
1072
+ // Upload images to newly created genbox
1073
+ const uploadResult = await (0, interactive_prompt_1.uploadImagesToGenbox)(images, genbox, {
1074
+ showProgress: true,
1075
+ waitForReady: true, // Wizard already waits, but just in case
1076
+ });
1077
+ if (uploadResult.remotePaths.length > 0) {
1078
+ finalPrompt = (0, interactive_prompt_1.buildPromptWithImages)(text, uploadResult.remotePaths, { isRemote: true });
1079
+ }
1080
+ else {
1081
+ console.log(chalk_1.default.yellow('\nWarning: Could not upload images. Proceeding with prompt only.'));
1082
+ finalPrompt = text;
1083
+ }
1084
+ }
1085
+ else {
1086
+ finalPrompt = text;
1087
+ }
1088
+ await startSessionOnGenbox(provider, genbox, { initialPrompt: finalPrompt });
1089
+ break;
1090
+ }
1091
+ case 'direct': {
1092
+ const proceed = await showDirectModeWarning();
1093
+ if (!proceed) {
1094
+ console.log(chalk_1.default.dim('\nCancelled.'));
1095
+ return;
1096
+ }
1097
+ // Direct mode: use local paths
1098
+ const finalPrompt = images.length > 0
1099
+ ? (0, interactive_prompt_1.buildPromptWithImages)(text, images, { isRemote: false })
1100
+ : text;
1101
+ await startDirectSession(provider, { initialPrompt: finalPrompt });
1102
+ break;
1103
+ }
1104
+ case 'cancel':
1105
+ console.log(chalk_1.default.dim('\nCancelled.'));
1106
+ break;
1107
+ }
1108
+ }
897
1109
  /**
898
1110
  * Create list subcommand for a provider
899
1111
  */
@@ -1196,9 +1408,13 @@ function createProviderCommand(provider) {
1196
1408
  .option('--on <genbox>', 'Use specific genbox')
1197
1409
  .option('--direct', 'Run directly on this machine (no isolation)')
1198
1410
  .option('-y, --yes', 'Skip confirmations')
1411
+ .option('-p, --prompt-mode', 'Interactive prompt mode: compose prompt with optional images')
1412
+ .option('-e, --editor', 'Use $EDITOR for prompt input (with -p)')
1199
1413
  .addHelpText('after', `
1200
1414
  Examples:
1201
1415
  gb ${provider} Interactive mode
1416
+ gb ${provider} -p Prompt mode: inline multiline input
1417
+ gb ${provider} -p -e Prompt mode: open $EDITOR for input
1202
1418
  gb ${provider} list List all ${provider} sessions
1203
1419
  gb ${provider} attach [session] Attach to a session
1204
1420
  gb ${provider} stop [session] Stop a session
@@ -1209,6 +1425,11 @@ Examples:
1209
1425
  `)
1210
1426
  .action(async (options) => {
1211
1427
  try {
1428
+ // -p / --prompt-mode: Interactive prompt with optional images
1429
+ if (options.promptMode) {
1430
+ await runPromptModeFlow(provider, { useEditor: options.editor });
1431
+ return;
1432
+ }
1212
1433
  // --on: Use specific genbox
1213
1434
  if (options.on) {
1214
1435
  const genboxes = await getGenboxes();
@@ -58,7 +58,6 @@ const local_orchestrator_1 = require("../../lib/local-orchestrator");
58
58
  const local_genbox_provisioner_1 = require("../../lib/local-genbox-provisioner");
59
59
  const hooks_configurator_1 = require("../../lib/hooks-configurator");
60
60
  const api_1 = require("../../api");
61
- const genbox_selector_1 = require("../../genbox-selector");
62
61
  const prompts_1 = require("@inquirer/prompts");
63
62
  const list_1 = require("./list");
64
63
  const start_1 = require("./start");
@@ -70,7 +69,7 @@ const child_process_1 = require("child_process");
70
69
  const os = __importStar(require("os"));
71
70
  const path = __importStar(require("path"));
72
71
  const fs = __importStar(require("fs"));
73
- const image_upload_1 = require("../../utils/image-upload");
72
+ const ssh_proxy_1 = require("../../utils/ssh-proxy");
74
73
  /**
75
74
  * Check if a command is available
76
75
  */
@@ -706,16 +705,9 @@ async function getAllGenboxesWithSessions(options) {
706
705
  }
707
706
  // Get cloud genboxes
708
707
  try {
709
- // Fetch all genboxes and filter by project name (same approach as gb list)
708
+ // Fetch all genboxes (same approach as gb claude/gemini - no project filtering)
710
709
  const response = await (0, api_1.fetchApi)('/genboxes');
711
- let genboxes = response || [];
712
- // Filter by project name unless --all is specified
713
- if (!options.all) {
714
- const projectName = (0, genbox_selector_1.getProjectContext)();
715
- if (projectName) {
716
- genboxes = genboxes.filter(g => g.project === projectName || g.workspace === projectName);
717
- }
718
- }
710
+ const genboxes = response || [];
719
711
  for (const genbox of genboxes) {
720
712
  if (genbox.status !== 'running' || !genbox.ipAddress) {
721
713
  continue;
@@ -884,19 +876,9 @@ async function attachToCloudSession(session) {
884
876
  const genboxName = session.genboxName || session.name;
885
877
  console.log(chalk_1.default.dim(`\nAttaching to ${session.name} on ${genboxName}...`));
886
878
  console.log(chalk_1.default.dim('Tip: Detach with Ctrl+\\'));
887
- console.log('');
888
- console.log(chalk_1.default.yellow('Image Upload Active') + chalk_1.default.dim(' - Cmd+C image file, auto-uploads, Cmd+V'));
889
- console.log(chalk_1.default.dim('Setting up connection...\n'));
879
+ console.log(chalk_1.default.dim(`Image auto-upload: ${chalk_1.default.green('enabled')} - Paste local image paths, they'll be uploaded automatically\n`));
890
880
  // Ensure dtach is installed on remote
891
881
  const hasDtach = await ensureDtachInstalled(session.ipAddress, keyPath);
892
- // Set up SSH multiplexing for faster uploads
893
- (0, image_upload_1.setupSshMultiplexing)(session.ipAddress, keyPath);
894
- // Start clipboard watcher
895
- const clipboardInterval = (0, image_upload_1.startClipboardWatcher)({
896
- ipAddress: session.ipAddress,
897
- keyPath,
898
- genboxName,
899
- });
900
882
  // Build the remote command
901
883
  const socketDir = getRemoteDtachSocketDir();
902
884
  const socketPath = `${socketDir}/${session.name}.sock`;
@@ -914,19 +896,15 @@ async function attachToCloudSession(session) {
914
896
  // Fallback to direct execution (no persistence)
915
897
  remoteCmd = `bash -c '${cdToProject} source ~/.nvm/nvm.sh 2>/dev/null; ${cliCommand}'`;
916
898
  }
917
- const sshArgs = [
918
- '-t',
919
- '-i', keyPath,
920
- '-o', 'StrictHostKeyChecking=no',
921
- '-o', 'UserKnownHostsFile=/dev/null',
922
- `dev@${session.ipAddress}`,
923
- remoteCmd
924
- ];
925
- const proc = (0, child_process_1.spawn)('ssh', sshArgs, { stdio: 'inherit' });
899
+ // Use SSH proxy with image interception
900
+ const proc = (0, ssh_proxy_1.createSshProxy)({
901
+ ipAddress: session.ipAddress,
902
+ keyPath,
903
+ genboxName,
904
+ command: remoteCmd,
905
+ });
926
906
  await new Promise((resolve) => {
927
907
  proc.on('close', (code) => {
928
- // Stop clipboard watcher
929
- clearInterval(clipboardInterval);
930
908
  if (code === 0) {
931
909
  console.log(chalk_1.default.dim('\nDetached from session.'));
932
910
  }
@@ -965,23 +943,13 @@ async function attachToLocalGenboxSession(session) {
965
943
  }
966
944
  const keyPath = getPrivateSshKey();
967
945
  const genboxName = session.name;
968
- console.log('');
969
- console.log(chalk_1.default.yellow('Image Upload Active') + chalk_1.default.dim(' - Cmd+C image file, auto-uploads, Cmd+V'));
970
- console.log(chalk_1.default.dim('Setting up connection...\n'));
946
+ console.log(chalk_1.default.dim(`Image auto-upload: ${chalk_1.default.green('enabled')} - Paste local image paths, they'll be uploaded automatically\n`));
971
947
  // Ensure dtach is installed on remote
972
948
  const hasDtach = await ensureDtachInstalled(vmIp, keyPath);
973
- // Set up SSH multiplexing for faster uploads
974
- (0, image_upload_1.setupSshMultiplexing)(vmIp, keyPath);
975
949
  // Detect provider from session name
976
950
  const isGemini = session.name.toLowerCase().includes('gemini');
977
951
  const provider = isGemini ? 'gemini' : 'claude';
978
952
  const cliCommand = isGemini ? 'gemini --yolo' : 'claude --dangerously-skip-permissions';
979
- // Start clipboard watcher
980
- const clipboardInterval = (0, image_upload_1.startClipboardWatcher)({
981
- ipAddress: vmIp,
982
- keyPath,
983
- genboxName,
984
- });
985
953
  // Build the remote command
986
954
  const socketDir = getRemoteDtachSocketDir();
987
955
  const socketPath = `${socketDir}/${provider}.sock`;
@@ -994,21 +962,15 @@ async function attachToLocalGenboxSession(session) {
994
962
  else {
995
963
  remoteCmd = `bash -c '${cdToProject} source ~/.nvm/nvm.sh 2>/dev/null; ${cliCommand}'`;
996
964
  }
997
- // SSH into VM and attach/create dtach session
998
- const sshArgs = [
999
- '-t',
1000
- '-i', keyPath,
1001
- '-o', 'StrictHostKeyChecking=no',
1002
- '-o', 'UserKnownHostsFile=/dev/null',
1003
- '-o', 'LogLevel=ERROR',
1004
- `dev@${vmIp}`,
1005
- remoteCmd
1006
- ];
1007
- const proc = (0, child_process_1.spawn)('ssh', sshArgs, { stdio: 'inherit' });
965
+ // Use SSH proxy with image interception
966
+ const proc = (0, ssh_proxy_1.createSshProxy)({
967
+ ipAddress: vmIp,
968
+ keyPath,
969
+ genboxName,
970
+ command: remoteCmd,
971
+ });
1008
972
  await new Promise((resolve) => {
1009
973
  proc.on('close', () => {
1010
- // Stop clipboard watcher
1011
- clearInterval(clipboardInterval);
1012
974
  console.log(chalk_1.default.dim('\nDetached from session.'));
1013
975
  resolve();
1014
976
  });
@@ -1220,68 +1182,37 @@ async function attachToGenboxSession(genbox, sessionName) {
1220
1182
  return;
1221
1183
  }
1222
1184
  const keyPath = getPrivateSshKey();
1223
- console.log('');
1224
- console.log(chalk_1.default.yellow('Image Upload Active') + chalk_1.default.dim(' - Cmd+C image file, auto-uploads, Cmd+V'));
1225
- console.log(chalk_1.default.dim('Setting up connection...\n'));
1185
+ console.log(chalk_1.default.dim(`Image auto-upload: ${chalk_1.default.green('enabled')} - Paste local image paths, they'll be uploaded automatically\n`));
1226
1186
  // Ensure dtach is installed
1227
1187
  await ensureDtachInstalled(vmIp, keyPath);
1228
- // Set up SSH multiplexing for faster uploads
1229
- (0, image_upload_1.setupSshMultiplexing)(vmIp, keyPath);
1230
- // Start clipboard watcher
1231
- const clipboardInterval = (0, image_upload_1.startClipboardWatcher)({
1188
+ const socketDir = getRemoteDtachSocketDir();
1189
+ const socketPath = `${socketDir}/${sessionName}.sock`;
1190
+ // Use SSH proxy with image interception for dtach attach
1191
+ const proc = (0, ssh_proxy_1.attachWithProxy)({
1232
1192
  ipAddress: vmIp,
1233
1193
  keyPath,
1234
1194
  genboxName: genbox.name,
1195
+ socketPath,
1235
1196
  });
1236
- const socketDir = getRemoteDtachSocketDir();
1237
- const socketPath = `${socketDir}/${sessionName}.sock`;
1238
- const sshArgs = [
1239
- '-t',
1240
- '-i', keyPath,
1241
- '-o', 'StrictHostKeyChecking=no',
1242
- '-o', 'UserKnownHostsFile=/dev/null',
1243
- '-o', 'LogLevel=ERROR',
1244
- `dev@${vmIp}`,
1245
- `dtach -a ${socketPath}`
1246
- ];
1247
- const proc = (0, child_process_1.spawn)('ssh', sshArgs, { stdio: 'inherit' });
1248
- await new Promise(resolve => proc.on('close', () => {
1249
- clearInterval(clipboardInterval);
1250
- resolve();
1251
- }));
1197
+ await new Promise(resolve => proc.on('close', () => resolve()));
1252
1198
  console.log(chalk_1.default.dim('\nDetached from session.'));
1253
1199
  }
1254
1200
  }
1255
1201
  else if (genbox.type === 'cloud' && genbox.ipAddress) {
1256
1202
  const keyPath = getPrivateSshKey();
1257
- console.log('');
1258
- console.log(chalk_1.default.yellow('Image Upload Active') + chalk_1.default.dim(' - Cmd+C image file, auto-uploads, Cmd+V'));
1259
- console.log(chalk_1.default.dim('Setting up connection...\n'));
1203
+ console.log(chalk_1.default.dim(`Image auto-upload: ${chalk_1.default.green('enabled')} - Paste local image paths, they'll be uploaded automatically\n`));
1260
1204
  // Ensure dtach is installed
1261
1205
  await ensureDtachInstalled(genbox.ipAddress, keyPath);
1262
- // Set up SSH multiplexing for faster uploads
1263
- (0, image_upload_1.setupSshMultiplexing)(genbox.ipAddress, keyPath);
1264
- // Start clipboard watcher
1265
- const clipboardInterval = (0, image_upload_1.startClipboardWatcher)({
1206
+ const socketDir = getRemoteDtachSocketDir();
1207
+ const socketPath = `${socketDir}/${sessionName}.sock`;
1208
+ // Use SSH proxy with image interception for dtach attach
1209
+ const proc = (0, ssh_proxy_1.attachWithProxy)({
1266
1210
  ipAddress: genbox.ipAddress,
1267
1211
  keyPath,
1268
1212
  genboxName: genbox.name,
1213
+ socketPath,
1269
1214
  });
1270
- const socketDir = getRemoteDtachSocketDir();
1271
- const socketPath = `${socketDir}/${sessionName}.sock`;
1272
- const sshArgs = [
1273
- '-t',
1274
- '-i', keyPath,
1275
- '-o', 'StrictHostKeyChecking=no',
1276
- '-o', 'UserKnownHostsFile=/dev/null',
1277
- `dev@${genbox.ipAddress}`,
1278
- `dtach -a ${socketPath}`
1279
- ];
1280
- const proc = (0, child_process_1.spawn)('ssh', sshArgs, { stdio: 'inherit' });
1281
- await new Promise(resolve => proc.on('close', () => {
1282
- clearInterval(clipboardInterval);
1283
- resolve();
1284
- }));
1215
+ await new Promise(resolve => proc.on('close', () => resolve()));
1285
1216
  console.log(chalk_1.default.dim('\nDetached from session.'));
1286
1217
  }
1287
1218
  }
@@ -1333,36 +1264,20 @@ async function createNewSessionInGenbox(genbox) {
1333
1264
  console.log(chalk_1.default.green(`Session created: ${sessionName}`));
1334
1265
  console.log(chalk_1.default.dim('\nAttaching to session...'));
1335
1266
  console.log(chalk_1.default.dim('Tip: Detach with Ctrl+\\'));
1336
- console.log('');
1337
- console.log(chalk_1.default.yellow('Image Upload Active') + chalk_1.default.dim(' - Cmd+C image file, auto-uploads, Cmd+V'));
1338
- console.log(chalk_1.default.dim('Setting up connection...\n'));
1339
- // Set up SSH multiplexing for faster uploads
1340
- (0, image_upload_1.setupSshMultiplexing)(vmIp, keyPath);
1341
- // Start clipboard watcher
1342
- const clipboardInterval = (0, image_upload_1.startClipboardWatcher)({
1343
- ipAddress: vmIp,
1344
- keyPath,
1345
- genboxName: genbox.name,
1346
- });
1347
- // Create and attach to dtach session
1267
+ console.log(chalk_1.default.dim(`Image auto-upload: ${chalk_1.default.green('enabled')} - Paste local image paths, they'll be uploaded automatically\n`));
1268
+ // Create and attach to dtach session with image interception
1348
1269
  // Note: We use grep to extract GENBOX_PROJECT_DIR from .bashrc since .bashrc may have early return for non-interactive shells
1349
1270
  const socketDir = getRemoteDtachSocketDir();
1350
1271
  const socketPath = `${socketDir}/${sessionName}.sock`;
1351
1272
  const cdToProjectCmd = 'eval $(grep GENBOX_PROJECT_DIR ~/.bashrc 2>/dev/null); cd $GENBOX_PROJECT_DIR 2>/dev/null;';
1352
- const sshArgs = [
1353
- '-t',
1354
- '-i', keyPath,
1355
- '-o', 'StrictHostKeyChecking=no',
1356
- '-o', 'UserKnownHostsFile=/dev/null',
1357
- '-o', 'LogLevel=ERROR',
1358
- `dev@${vmIp}`,
1359
- `mkdir -p ${socketDir} && dtach -A ${socketPath} bash -c '${cdToProjectCmd} source ~/.nvm/nvm.sh 2>/dev/null; ${cliCommand}'`
1360
- ];
1361
- const proc = (0, child_process_1.spawn)('ssh', sshArgs, { stdio: 'inherit' });
1362
- await new Promise(resolve => proc.on('close', () => {
1363
- clearInterval(clipboardInterval);
1364
- resolve();
1365
- }));
1273
+ const remoteCmd = `mkdir -p ${socketDir} && dtach -A ${socketPath} bash -c '${cdToProjectCmd} source ~/.nvm/nvm.sh 2>/dev/null; ${cliCommand}'`;
1274
+ const proc = (0, ssh_proxy_1.createSshProxy)({
1275
+ ipAddress: vmIp,
1276
+ keyPath,
1277
+ genboxName: genbox.name,
1278
+ command: remoteCmd,
1279
+ });
1280
+ await new Promise(resolve => proc.on('close', () => resolve()));
1366
1281
  console.log(chalk_1.default.dim('\nDetached from session.'));
1367
1282
  }
1368
1283
  }
@@ -1373,35 +1288,20 @@ async function createNewSessionInGenbox(genbox) {
1373
1288
  console.log(chalk_1.default.green(`Session created: ${sessionName}`));
1374
1289
  console.log(chalk_1.default.dim('\nAttaching to session...'));
1375
1290
  console.log(chalk_1.default.dim('Tip: Detach with Ctrl+\\'));
1376
- console.log('');
1377
- console.log(chalk_1.default.yellow('Image Upload Active') + chalk_1.default.dim(' - Cmd+C image file, auto-uploads, Cmd+V'));
1378
- console.log(chalk_1.default.dim('Setting up connection...\n'));
1379
- // Set up SSH multiplexing for faster uploads
1380
- (0, image_upload_1.setupSshMultiplexing)(genbox.ipAddress, keyPath);
1381
- // Start clipboard watcher
1382
- const clipboardInterval = (0, image_upload_1.startClipboardWatcher)({
1383
- ipAddress: genbox.ipAddress,
1384
- keyPath,
1385
- genboxName: genbox.name,
1386
- });
1387
- // Create and attach to dtach session
1291
+ console.log(chalk_1.default.dim(`Image auto-upload: ${chalk_1.default.green('enabled')} - Paste local image paths, they'll be uploaded automatically\n`));
1292
+ // Create and attach to dtach session with image interception
1388
1293
  // Note: We use grep to extract GENBOX_PROJECT_DIR from .bashrc since .bashrc may have early return for non-interactive shells
1389
1294
  const socketDir = getRemoteDtachSocketDir();
1390
1295
  const socketPath = `${socketDir}/${sessionName}.sock`;
1391
1296
  const cdToProjectCmd = 'eval $(grep GENBOX_PROJECT_DIR ~/.bashrc 2>/dev/null); cd $GENBOX_PROJECT_DIR 2>/dev/null;';
1392
- const sshArgs = [
1393
- '-t',
1394
- '-i', keyPath,
1395
- '-o', 'StrictHostKeyChecking=no',
1396
- '-o', 'UserKnownHostsFile=/dev/null',
1397
- `dev@${genbox.ipAddress}`,
1398
- `mkdir -p ${socketDir} && dtach -A ${socketPath} bash -c '${cdToProjectCmd} source ~/.nvm/nvm.sh 2>/dev/null; ${cliCommand}'`
1399
- ];
1400
- const proc = (0, child_process_1.spawn)('ssh', sshArgs, { stdio: 'inherit' });
1401
- await new Promise(resolve => proc.on('close', () => {
1402
- clearInterval(clipboardInterval);
1403
- resolve();
1404
- }));
1297
+ const remoteCmd = `mkdir -p ${socketDir} && dtach -A ${socketPath} bash -c '${cdToProjectCmd} source ~/.nvm/nvm.sh 2>/dev/null; ${cliCommand}'`;
1298
+ const proc = (0, ssh_proxy_1.createSshProxy)({
1299
+ ipAddress: genbox.ipAddress,
1300
+ keyPath,
1301
+ genboxName: genbox.name,
1302
+ command: remoteCmd,
1303
+ });
1304
+ await new Promise(resolve => proc.on('close', () => resolve()));
1405
1305
  console.log(chalk_1.default.dim('\nDetached from session.'));
1406
1306
  }
1407
1307
  }