genbox 1.0.210 → 1.0.211
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.
|
@@ -65,6 +65,10 @@ const stop_1 = require("./stop");
|
|
|
65
65
|
const kill_1 = require("./kill");
|
|
66
66
|
const migrate_1 = require("./migrate");
|
|
67
67
|
const attach_1 = require("./attach");
|
|
68
|
+
const watch_1 = require("./watch");
|
|
69
|
+
const logs_1 = require("./logs");
|
|
70
|
+
const show_1 = require("./show");
|
|
71
|
+
const send_1 = require("./send");
|
|
68
72
|
const child_process_1 = require("child_process");
|
|
69
73
|
const os = __importStar(require("os"));
|
|
70
74
|
const path = __importStar(require("path"));
|
|
@@ -1768,6 +1772,10 @@ exports.sessionCommand = new commander_1.Command('session')
|
|
|
1768
1772
|
.addCommand(stop_1.sessionStopCommand)
|
|
1769
1773
|
.addCommand(kill_1.sessionKillCommand)
|
|
1770
1774
|
.addCommand(migrate_1.sessionMigrateCommand)
|
|
1775
|
+
.addCommand(watch_1.sessionWatchCommand)
|
|
1776
|
+
.addCommand(logs_1.sessionLogsCommand)
|
|
1777
|
+
.addCommand(show_1.sessionShowCommand)
|
|
1778
|
+
.addCommand(send_1.sessionSendCommand)
|
|
1771
1779
|
.action(async (sessionArg, providerArgs, options) => {
|
|
1772
1780
|
try {
|
|
1773
1781
|
// Clean up stale sockets on startup (silently)
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Logs Command
|
|
4
|
+
*
|
|
5
|
+
* Stream session transcript and activity logs.
|
|
6
|
+
* Shows real-time updates of what the AI is doing.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* gb session logs <session> # Show session transcript
|
|
10
|
+
* gb session logs <session> -f # Follow mode (like tail -f)
|
|
11
|
+
* gb session logs <session> --json # JSON output
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.sessionLogsCommand = void 0;
|
|
51
|
+
const commander_1 = require("commander");
|
|
52
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
const os = __importStar(require("os"));
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const unified_session_1 = require("../../lib/unified-session");
|
|
57
|
+
const api_1 = require("../../api");
|
|
58
|
+
/**
|
|
59
|
+
* Get SSH key path
|
|
60
|
+
*/
|
|
61
|
+
function getPrivateSshKey() {
|
|
62
|
+
const home = os.homedir();
|
|
63
|
+
const potentialKeys = [
|
|
64
|
+
path.join(home, '.ssh', 'id_ed25519'),
|
|
65
|
+
path.join(home, '.ssh', 'id_rsa'),
|
|
66
|
+
];
|
|
67
|
+
for (const keyPath of potentialKeys) {
|
|
68
|
+
if (fs.existsSync(keyPath)) {
|
|
69
|
+
return keyPath;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Format timestamp
|
|
76
|
+
*/
|
|
77
|
+
function formatTime(date) {
|
|
78
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
79
|
+
return d.toLocaleTimeString('en-US', { hour12: false });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Find session by name or ID
|
|
83
|
+
*/
|
|
84
|
+
async function findSession(nameOrId) {
|
|
85
|
+
const result = await (0, unified_session_1.listAllSessions)({ includeEnded: true });
|
|
86
|
+
// Check local sessions
|
|
87
|
+
for (const session of result.sessions) {
|
|
88
|
+
if (session.name === nameOrId ||
|
|
89
|
+
session.id === nameOrId ||
|
|
90
|
+
session.id.startsWith(nameOrId) ||
|
|
91
|
+
session.name.includes(nameOrId)) {
|
|
92
|
+
return { session };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Check remote sessions
|
|
96
|
+
for (const remote of result.remoteSessions) {
|
|
97
|
+
if (remote.name === nameOrId ||
|
|
98
|
+
remote.name.includes(nameOrId)) {
|
|
99
|
+
return { remoteSession: remote };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Fetch messages from API
|
|
106
|
+
*/
|
|
107
|
+
async function fetchMessages(sessionId, limit = 50) {
|
|
108
|
+
try {
|
|
109
|
+
const response = await (0, api_1.fetchApi)(`/sessions/v2/${sessionId}/messages?limit=${limit}`);
|
|
110
|
+
return response.messages || [];
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Fetch events from API
|
|
118
|
+
*/
|
|
119
|
+
async function fetchEvents(sessionId, limit = 100) {
|
|
120
|
+
try {
|
|
121
|
+
const response = await (0, api_1.fetchApi)(`/sessions/v2/${sessionId}/events?limit=${limit}`);
|
|
122
|
+
return response.events || [];
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get role icon
|
|
130
|
+
*/
|
|
131
|
+
function getRoleIcon(role) {
|
|
132
|
+
switch (role) {
|
|
133
|
+
case 'user':
|
|
134
|
+
return '💬';
|
|
135
|
+
case 'assistant':
|
|
136
|
+
return '🤖';
|
|
137
|
+
default:
|
|
138
|
+
return '📝';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get event icon
|
|
143
|
+
*/
|
|
144
|
+
function getEventIcon(event) {
|
|
145
|
+
switch (event) {
|
|
146
|
+
case 'session_start':
|
|
147
|
+
return '🚀';
|
|
148
|
+
case 'session_end':
|
|
149
|
+
return '🏁';
|
|
150
|
+
case 'prompt_submitted':
|
|
151
|
+
return '💬';
|
|
152
|
+
case 'response_started':
|
|
153
|
+
return '🤔';
|
|
154
|
+
case 'response_completed':
|
|
155
|
+
return '✅';
|
|
156
|
+
case 'tool_started':
|
|
157
|
+
return '⚙';
|
|
158
|
+
case 'tool_completed':
|
|
159
|
+
return '✓';
|
|
160
|
+
case 'tool_error':
|
|
161
|
+
return '❌';
|
|
162
|
+
case 'state_change':
|
|
163
|
+
return '🔄';
|
|
164
|
+
case 'error':
|
|
165
|
+
return '⚠';
|
|
166
|
+
default:
|
|
167
|
+
return '•';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Format a message for display
|
|
172
|
+
*/
|
|
173
|
+
function formatMessage(msg) {
|
|
174
|
+
const icon = getRoleIcon(msg.role);
|
|
175
|
+
const time = chalk_1.default.dim(`[${formatTime(msg.createdAt)}]`);
|
|
176
|
+
const roleColor = msg.role === 'user' ? chalk_1.default.cyan : chalk_1.default.green;
|
|
177
|
+
const roleLabel = roleColor(msg.role === 'user' ? 'User' : 'Assistant');
|
|
178
|
+
let output = `${time} ${icon} ${roleLabel}:`;
|
|
179
|
+
if (msg.textPreview) {
|
|
180
|
+
// Wrap text for readability
|
|
181
|
+
const preview = msg.textPreview.substring(0, 200);
|
|
182
|
+
output += ` ${preview}${msg.textPreview.length > 200 ? '...' : ''}`;
|
|
183
|
+
}
|
|
184
|
+
if (msg.toolsUsed && msg.toolsUsed.length > 0) {
|
|
185
|
+
output += chalk_1.default.dim(` [Tools: ${msg.toolsUsed.join(', ')}]`);
|
|
186
|
+
}
|
|
187
|
+
return output;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Format an event for display
|
|
191
|
+
*/
|
|
192
|
+
function formatEvent(evt) {
|
|
193
|
+
const icon = getEventIcon(evt.event);
|
|
194
|
+
const time = chalk_1.default.dim(`[${formatTime(evt.createdAt)}]`);
|
|
195
|
+
const eventLabel = evt.event.replace(/_/g, ' ');
|
|
196
|
+
let output = `${time} ${icon} ${eventLabel}`;
|
|
197
|
+
if (evt.data) {
|
|
198
|
+
if (evt.data.toolName) {
|
|
199
|
+
output += chalk_1.default.cyan(` ${evt.data.toolName}`);
|
|
200
|
+
}
|
|
201
|
+
if (evt.data.filePath) {
|
|
202
|
+
output += chalk_1.default.dim(` ${evt.data.filePath}`);
|
|
203
|
+
}
|
|
204
|
+
if (evt.data.currentState) {
|
|
205
|
+
output += chalk_1.default.yellow(` → ${evt.data.currentState}`);
|
|
206
|
+
}
|
|
207
|
+
if (evt.data.error) {
|
|
208
|
+
output += chalk_1.default.red(` ${evt.data.error}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return output;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Format for JSON output
|
|
215
|
+
*/
|
|
216
|
+
function formatJson(data) {
|
|
217
|
+
console.log(JSON.stringify(data, null, 2));
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Display transcript from API
|
|
221
|
+
*/
|
|
222
|
+
async function displayApiTranscript(session, options) {
|
|
223
|
+
const sessionId = session.apiSessionIdV2;
|
|
224
|
+
if (!sessionId) {
|
|
225
|
+
console.log(chalk_1.default.yellow('\nSession is not synced to API. No transcript available.'));
|
|
226
|
+
console.log(chalk_1.default.dim('Enable sync with: gb session start --sync'));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const limit = parseInt(options.tail || '50');
|
|
230
|
+
if (options.json) {
|
|
231
|
+
const messages = await fetchMessages(sessionId, limit);
|
|
232
|
+
const events = options.events ? await fetchEvents(sessionId, limit) : undefined;
|
|
233
|
+
formatJson({ messages, events });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (options.events) {
|
|
237
|
+
// Show events
|
|
238
|
+
console.log(chalk_1.default.bold(`\nSession Events: ${session.name}`));
|
|
239
|
+
console.log(chalk_1.default.dim('─'.repeat(70)));
|
|
240
|
+
const events = await fetchEvents(sessionId, limit);
|
|
241
|
+
if (events.length === 0) {
|
|
242
|
+
console.log(chalk_1.default.dim('\nNo events recorded.\n'));
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
for (const evt of events.reverse()) {
|
|
246
|
+
console.log(formatEvent(evt));
|
|
247
|
+
}
|
|
248
|
+
console.log('');
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// Show messages
|
|
252
|
+
console.log(chalk_1.default.bold(`\nSession Transcript: ${session.name}`));
|
|
253
|
+
console.log(chalk_1.default.dim('─'.repeat(70)));
|
|
254
|
+
const messages = await fetchMessages(sessionId, limit);
|
|
255
|
+
if (messages.length === 0) {
|
|
256
|
+
console.log(chalk_1.default.dim('\nNo messages recorded.\n'));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
for (const msg of messages.reverse()) {
|
|
260
|
+
console.log(formatMessage(msg));
|
|
261
|
+
}
|
|
262
|
+
console.log('');
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Follow mode - continuously poll for updates
|
|
266
|
+
*/
|
|
267
|
+
async function followMode(session, options) {
|
|
268
|
+
const sessionId = session.apiSessionIdV2;
|
|
269
|
+
if (!sessionId) {
|
|
270
|
+
console.log(chalk_1.default.yellow('\nSession is not synced to API. Follow mode unavailable.'));
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
console.log(chalk_1.default.bold(`\nFollowing: ${session.name}`));
|
|
274
|
+
console.log(chalk_1.default.dim('─'.repeat(70)));
|
|
275
|
+
console.log(chalk_1.default.dim('Press Ctrl+C to stop\n'));
|
|
276
|
+
let lastMessageId = null;
|
|
277
|
+
let lastEventTime = null;
|
|
278
|
+
while (true) {
|
|
279
|
+
try {
|
|
280
|
+
if (options.events || options.tools) {
|
|
281
|
+
// Poll events
|
|
282
|
+
const events = await fetchEvents(sessionId, 20);
|
|
283
|
+
for (const evt of events.reverse()) {
|
|
284
|
+
if (lastEventTime && new Date(evt.createdAt) <= new Date(lastEventTime)) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
// Filter for tools only if --tools specified
|
|
288
|
+
if (options.tools &&
|
|
289
|
+
!evt.event.includes('tool') &&
|
|
290
|
+
evt.event !== 'state_change') {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
console.log(formatEvent(evt));
|
|
294
|
+
lastEventTime = evt.createdAt;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
// Poll messages
|
|
299
|
+
const messages = await fetchMessages(sessionId, 10);
|
|
300
|
+
for (const msg of messages.reverse()) {
|
|
301
|
+
if (lastMessageId === msg.messageId)
|
|
302
|
+
continue;
|
|
303
|
+
if (lastMessageId) {
|
|
304
|
+
console.log(formatMessage(msg));
|
|
305
|
+
}
|
|
306
|
+
lastMessageId = msg.messageId;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
// Silently continue on errors
|
|
312
|
+
}
|
|
313
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Display local session info when no API sync
|
|
318
|
+
*/
|
|
319
|
+
function displayLocalSessionInfo(session) {
|
|
320
|
+
console.log(chalk_1.default.bold(`\nSession: ${session.name}`));
|
|
321
|
+
console.log(chalk_1.default.dim('─'.repeat(50)));
|
|
322
|
+
console.log(` Type: ${session.type}`);
|
|
323
|
+
console.log(` Provider: ${session.provider}`);
|
|
324
|
+
console.log(` Status: ${session.status}`);
|
|
325
|
+
console.log(` Created: ${new Date(session.createdAt).toLocaleString()}`);
|
|
326
|
+
if (session.infrastructure?.dtachSocketPath) {
|
|
327
|
+
console.log(` Socket: ${session.infrastructure.dtachSocketPath}`);
|
|
328
|
+
}
|
|
329
|
+
console.log(chalk_1.default.dim('\nThis session is not synced to API. Transcript not available.'));
|
|
330
|
+
console.log(chalk_1.default.dim('To enable recording, start sessions with --sync flag.'));
|
|
331
|
+
console.log('');
|
|
332
|
+
console.log(chalk_1.default.bold('To attach to this session:'));
|
|
333
|
+
console.log(chalk_1.default.cyan(` gb ${session.provider} attach ${session.name}\n`));
|
|
334
|
+
}
|
|
335
|
+
exports.sessionLogsCommand = new commander_1.Command('logs')
|
|
336
|
+
.description('View session transcript and activity logs')
|
|
337
|
+
.argument('<session>', 'Session name or ID')
|
|
338
|
+
.option('-f, --follow', 'Follow mode - continuously show new activity')
|
|
339
|
+
.option('--json', 'Output as JSON')
|
|
340
|
+
.option('-n, --tail <lines>', 'Number of messages to show (default: 50)')
|
|
341
|
+
.option('--events', 'Show events instead of messages')
|
|
342
|
+
.option('--tools', 'Show only tool executions (with -f)')
|
|
343
|
+
.addHelpText('after', `
|
|
344
|
+
Examples:
|
|
345
|
+
gb session logs claude-swift-fox # View transcript
|
|
346
|
+
gb session logs claude-swift-fox -f # Follow mode
|
|
347
|
+
gb session logs abc123 --events # View events
|
|
348
|
+
gb session logs abc123 --json # JSON output
|
|
349
|
+
`)
|
|
350
|
+
.action(async (sessionArg, options) => {
|
|
351
|
+
try {
|
|
352
|
+
const found = await findSession(sessionArg);
|
|
353
|
+
if (!found) {
|
|
354
|
+
console.log(chalk_1.default.red(`\nSession not found: ${sessionArg}`));
|
|
355
|
+
console.log(chalk_1.default.dim('\nRun `gb session list` to see available sessions.\n'));
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
if (found.remoteSession) {
|
|
359
|
+
console.log(chalk_1.default.yellow(`\nRemote session: ${found.remoteSession.name}`));
|
|
360
|
+
console.log(chalk_1.default.dim(`Running on: ${found.remoteSession.genboxName}`));
|
|
361
|
+
console.log(chalk_1.default.dim('\nRemote sessions must be attached to view logs.'));
|
|
362
|
+
console.log(chalk_1.default.cyan(`\n gb ${found.remoteSession.name.split('-')[0]} attach ${found.remoteSession.name}\n`));
|
|
363
|
+
process.exit(0);
|
|
364
|
+
}
|
|
365
|
+
const session = found.session;
|
|
366
|
+
// Check if session has API sync
|
|
367
|
+
if (!session.syncEnabled || !session.apiSessionIdV2) {
|
|
368
|
+
displayLocalSessionInfo(session);
|
|
369
|
+
process.exit(0);
|
|
370
|
+
}
|
|
371
|
+
if (options.follow) {
|
|
372
|
+
await followMode(session, options);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
await displayApiTranscript(session, options);
|
|
376
|
+
}
|
|
377
|
+
process.exit(0);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
381
|
+
console.error(chalk_1.default.red('\nNot logged in. Run `gb login` first.\n'));
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
});
|