|
1 | 1 | import * as path from 'node:path'; |
| 2 | +import * as fs from 'node:fs'; |
2 | 3 | import { homedir } from 'node:os'; |
3 | 4 |
|
4 | 5 | import { spawnProcess } from '../../../../process/spawn.js'; |
@@ -41,6 +42,43 @@ export interface RunMistralResult { |
41 | 42 |
|
42 | 43 | const ANSI_ESCAPE_SEQUENCE = new RegExp(String.raw`\u001B\[[0-9;?]*[ -/]*[@-~]`, 'g'); |
43 | 44 |
|
| 45 | +/** |
| 46 | + * Find the most recent session file created after startTime |
| 47 | + * Session files are stored in VIBE_HOME/logs/session/ with format: |
| 48 | + * session_{date}_{time}_{short_id}.json |
| 49 | + * The full session_id is in the metadata.session_id field inside the file. |
| 50 | + */ |
| 51 | +function findLatestSessionId(vibeHome: string, startTime: number): string | null { |
| 52 | + const sessionDir = path.join(vibeHome, 'logs', 'session'); |
| 53 | + |
| 54 | + try { |
| 55 | + if (!fs.existsSync(sessionDir)) { |
| 56 | + return null; |
| 57 | + } |
| 58 | + |
| 59 | + const files = fs.readdirSync(sessionDir) |
| 60 | + .filter(f => f.startsWith('session_') && f.endsWith('.json')) |
| 61 | + .map(f => ({ |
| 62 | + name: f, |
| 63 | + path: path.join(sessionDir, f), |
| 64 | + mtime: fs.statSync(path.join(sessionDir, f)).mtimeMs, |
| 65 | + })) |
| 66 | + .filter(f => f.mtime >= startTime) |
| 67 | + .sort((a, b) => b.mtime - a.mtime); |
| 68 | + |
| 69 | + if (files.length === 0) { |
| 70 | + return null; |
| 71 | + } |
| 72 | + |
| 73 | + // Read the most recent session file and extract session_id from metadata |
| 74 | + const content = fs.readFileSync(files[0].path, 'utf-8'); |
| 75 | + const json = JSON.parse(content); |
| 76 | + return json.metadata?.session_id || null; |
| 77 | + } catch { |
| 78 | + return null; |
| 79 | + } |
| 80 | +} |
| 81 | + |
44 | 82 | /** |
45 | 83 | * Build the final resume prompt combining steering instruction with user message |
46 | 84 | */ |
@@ -236,6 +274,9 @@ export async function runMistral(options: RunMistralOptions): Promise<RunMistral |
236 | 274 | let capturedError: string | null = null; |
237 | 275 | let sessionIdCaptured = false; |
238 | 276 |
|
| 277 | + // Record start time to find session files created during this run |
| 278 | + const startTime = Date.now(); |
| 279 | + |
239 | 280 | let result; |
240 | 281 | try { |
241 | 282 | result = await spawnProcess({ |
@@ -315,14 +356,25 @@ export async function runMistral(options: RunMistralOptions): Promise<RunMistral |
315 | 356 | timeout, |
316 | 357 | }); |
317 | 358 | } catch (error) { |
318 | | - const err = error as unknown as { code?: string; message?: string }; |
| 359 | + const err = error as unknown as { code?: string; message?: string; name?: string }; |
319 | 360 | const message = err?.message ?? ''; |
320 | 361 | const notFound = err?.code === 'ENOENT' || /not recognized as an internal or external command/i.test(message) || /command not found/i.test(message); |
321 | 362 | if (notFound) { |
322 | 363 | const install = metadata.installCommand; |
323 | 364 | const name = metadata.name; |
324 | 365 | throw new Error(`'${command}' is not available on this system. Please install ${name} first:\n ${install}`); |
325 | 366 | } |
| 367 | + |
| 368 | + // Try to capture session ID even on abort - vibe saves session on exit |
| 369 | + // This is important for pause/resume functionality |
| 370 | + if (err?.name === 'AbortError' && !sessionIdCaptured && onSessionId) { |
| 371 | + const sessionId = findLatestSessionId(vibeHome, startTime); |
| 372 | + if (sessionId) { |
| 373 | + sessionIdCaptured = true; |
| 374 | + onSessionId(sessionId); |
| 375 | + } |
| 376 | + } |
| 377 | + |
326 | 378 | throw error; |
327 | 379 | } |
328 | 380 |
|
@@ -377,6 +429,16 @@ export async function runMistral(options: RunMistralOptions): Promise<RunMistral |
377 | 429 | throw new Error(errorMessage); |
378 | 430 | } |
379 | 431 |
|
| 432 | + // If session ID wasn't captured from streaming output, try to find it from session files |
| 433 | + // Vibe doesn't include session_id in streaming JSON output, but saves it to session files |
| 434 | + if (!sessionIdCaptured && onSessionId) { |
| 435 | + const sessionId = findLatestSessionId(vibeHome, startTime); |
| 436 | + if (sessionId) { |
| 437 | + sessionIdCaptured = true; |
| 438 | + onSessionId(sessionId); |
| 439 | + } |
| 440 | + } |
| 441 | + |
380 | 442 | // Log captured telemetry |
381 | 443 | telemetryCapture.logCapturedTelemetry(result.exitCode); |
382 | 444 |
|
|
0 commit comments