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.
- package/dist/commands/provider-command.js +225 -4
- package/dist/commands/session/index.js +53 -153
- package/dist/utils/image-upload.js +0 -231
- package/dist/utils/interactive-prompt.js +419 -0
- package/dist/utils/ssh-proxy.js +53 -4
- package/package.json +1 -1
|
@@ -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,
|
|
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
|
-
|
|
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
|
|
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
|
|
708
|
+
// Fetch all genboxes (same approach as gb claude/gemini - no project filtering)
|
|
710
709
|
const response = await (0, api_1.fetchApi)('/genboxes');
|
|
711
|
-
|
|
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
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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
|
|
998
|
-
const
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
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
|
-
|
|
1229
|
-
|
|
1230
|
-
//
|
|
1231
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
1263
|
-
|
|
1264
|
-
//
|
|
1265
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
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
|
-
|
|
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
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
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
|
}
|