genbox 1.0.141 → 1.0.142
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 +57 -27
- package/package.json +1 -1
package/dist/commands/attach.js
CHANGED
|
@@ -45,6 +45,7 @@ const child_process_1 = require("child_process");
|
|
|
45
45
|
const os = __importStar(require("os"));
|
|
46
46
|
const path = __importStar(require("path"));
|
|
47
47
|
const fs = __importStar(require("fs"));
|
|
48
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
48
49
|
function getPrivateSshKey() {
|
|
49
50
|
const home = os.homedir();
|
|
50
51
|
const potentialKeys = [
|
|
@@ -85,13 +86,40 @@ set -g window-status-format ' #W '
|
|
|
85
86
|
`.trim();
|
|
86
87
|
async function listTmuxSessions(ipAddress, keyPath) {
|
|
87
88
|
try {
|
|
88
|
-
const result = (0, child_process_1.execSync)(`ssh -i "${keyPath}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dev@${ipAddress} "tmux list-sessions -F '#{session_name}'" 2>/dev/null`, { encoding: 'utf-8' });
|
|
89
|
-
return result.trim().split('\n').filter(s => s.length > 0)
|
|
89
|
+
const result = (0, child_process_1.execSync)(`ssh -i "${keyPath}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null dev@${ipAddress} "tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_created}|#{session_attached}'" 2>/dev/null`, { encoding: 'utf-8' });
|
|
90
|
+
return result.trim().split('\n').filter(s => s.length > 0).map(line => {
|
|
91
|
+
const [name, windows, created, attached] = line.split('|');
|
|
92
|
+
return {
|
|
93
|
+
name,
|
|
94
|
+
windows: parseInt(windows) || 1,
|
|
95
|
+
created: new Date(parseInt(created) * 1000).toLocaleTimeString(),
|
|
96
|
+
attached: attached === '1',
|
|
97
|
+
};
|
|
98
|
+
});
|
|
90
99
|
}
|
|
91
100
|
catch {
|
|
92
101
|
return [];
|
|
93
102
|
}
|
|
94
103
|
}
|
|
104
|
+
async function selectSession(sessions) {
|
|
105
|
+
const choices = sessions.map(s => ({
|
|
106
|
+
name: `${s.name}${s.attached ? chalk_1.default.green(' (attached)') : ''} ${chalk_1.default.dim(`- ${s.windows} window(s), started ${s.created}`)}`,
|
|
107
|
+
value: s.name,
|
|
108
|
+
}));
|
|
109
|
+
choices.push({
|
|
110
|
+
name: chalk_1.default.dim('Cancel'),
|
|
111
|
+
value: '__cancel__',
|
|
112
|
+
});
|
|
113
|
+
const { session } = await inquirer_1.default.prompt([
|
|
114
|
+
{
|
|
115
|
+
type: 'list',
|
|
116
|
+
name: 'session',
|
|
117
|
+
message: 'Select a session to attach:',
|
|
118
|
+
choices,
|
|
119
|
+
},
|
|
120
|
+
]);
|
|
121
|
+
return session === '__cancel__' ? null : session;
|
|
122
|
+
}
|
|
95
123
|
async function ensureTmuxConfig(ipAddress, keyPath) {
|
|
96
124
|
const configPath = '/home/dev/.tmux.conf';
|
|
97
125
|
const marker = '# GENBOX_BRANDED_CONFIG';
|
|
@@ -116,12 +144,11 @@ GENBOX_TMUX_EOF`, { encoding: 'utf-8' });
|
|
|
116
144
|
exports.attachCommand = new commander_1.Command('attach')
|
|
117
145
|
.description('Attach to an active Claude/AI session in a Genbox')
|
|
118
146
|
.argument('[name]', 'Name of the Genbox (optional - will prompt if not provided)')
|
|
119
|
-
.option('-s, --session <session>', 'Tmux session name to attach to')
|
|
147
|
+
.option('-s, --session <session>', 'Tmux session name to attach to directly')
|
|
120
148
|
.option('-a, --all', 'Select from all genboxes (not just current project)')
|
|
121
|
-
.option('-l, --list', 'List available sessions without attaching')
|
|
122
149
|
.action(async (name, options) => {
|
|
123
150
|
try {
|
|
124
|
-
// 1. Select Genbox
|
|
151
|
+
// 1. Select Genbox (interactive if no name provided)
|
|
125
152
|
const { genbox: target, cancelled } = await (0, genbox_selector_1.selectGenbox)(name, {
|
|
126
153
|
all: options.all,
|
|
127
154
|
selectMessage: 'Select a genbox to attach to:',
|
|
@@ -140,45 +167,48 @@ exports.attachCommand = new commander_1.Command('attach')
|
|
|
140
167
|
// 2. Get SSH key
|
|
141
168
|
const keyPath = getPrivateSshKey();
|
|
142
169
|
// 3. List available tmux sessions
|
|
170
|
+
console.log(chalk_1.default.dim(`Checking sessions on ${target.name}...`));
|
|
143
171
|
const sessions = await listTmuxSessions(target.ipAddress, keyPath);
|
|
144
172
|
if (sessions.length === 0) {
|
|
145
|
-
console.log(chalk_1.default.yellow('
|
|
146
|
-
console.log(chalk_1.default.dim('Start a new Claude session with: gb run-prompt
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
if (options.list) {
|
|
150
|
-
console.log(chalk_1.default.bold(`\nActive sessions in ${target.name}:\n`));
|
|
151
|
-
for (const session of sessions) {
|
|
152
|
-
console.log(` ${chalk_1.default.cyan('•')} ${session}`);
|
|
153
|
-
}
|
|
154
|
-
console.log('');
|
|
155
|
-
console.log(chalk_1.default.dim(`Attach with: gb attach ${target.name} -s <session>`));
|
|
173
|
+
console.log(chalk_1.default.yellow('\nNo active sessions found.'));
|
|
174
|
+
console.log(chalk_1.default.dim('Start a new Claude session with: gb run-prompt ' + target.name));
|
|
156
175
|
return;
|
|
157
176
|
}
|
|
158
177
|
// 4. Determine which session to attach to
|
|
159
178
|
let sessionName = options.session;
|
|
160
179
|
if (!sessionName) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
sessionName =
|
|
180
|
+
if (sessions.length === 1) {
|
|
181
|
+
// Only one session - attach directly
|
|
182
|
+
sessionName = sessions[0].name;
|
|
183
|
+
console.log(chalk_1.default.dim(`Found session: ${sessionName}`));
|
|
164
184
|
}
|
|
165
185
|
else {
|
|
166
|
-
|
|
186
|
+
// Multiple sessions - show interactive picker
|
|
187
|
+
console.log('');
|
|
188
|
+
sessionName = await selectSession(sessions);
|
|
189
|
+
if (!sessionName) {
|
|
190
|
+
console.log(chalk_1.default.dim('Cancelled.'));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
167
193
|
}
|
|
168
194
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
console.
|
|
195
|
+
else {
|
|
196
|
+
// Validate the provided session name
|
|
197
|
+
const sessionNames = sessions.map(s => s.name);
|
|
198
|
+
if (!sessionNames.includes(sessionName)) {
|
|
199
|
+
console.error(chalk_1.default.red(`\nSession '${sessionName}' not found.`));
|
|
200
|
+
console.log(chalk_1.default.dim('Available sessions:'));
|
|
201
|
+
for (const s of sessions) {
|
|
202
|
+
console.log(` ${chalk_1.default.cyan('•')} ${s.name}`);
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
174
205
|
}
|
|
175
|
-
return;
|
|
176
206
|
}
|
|
177
207
|
// 5. Ensure tmux is configured with branding
|
|
178
208
|
console.log(chalk_1.default.dim(`Setting up terminal...`));
|
|
179
209
|
await ensureTmuxConfig(target.ipAddress, keyPath);
|
|
180
210
|
// 6. Attach to session
|
|
181
|
-
console.log(chalk_1.default.dim(
|
|
211
|
+
console.log(chalk_1.default.dim(`\nAttaching to ${chalk_1.default.bold(sessionName)} on ${chalk_1.default.bold(target.name)}...`));
|
|
182
212
|
console.log(chalk_1.default.dim('Tip: Scroll with mouse wheel, detach with Ctrl+b d\n'));
|
|
183
213
|
const sshArgs = [
|
|
184
214
|
'-t', // Force pseudo-terminal allocation
|