mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 01:32:40 +00:00
fix(server): propagate Claude transcript replay read errors
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import pino from "pino";
|
||||
@@ -6,6 +6,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
import { ClaudeAgentClient } from "./agent.js";
|
||||
import { claudeProjectDirSync } from "./project-dir.js";
|
||||
import { readClaudeReplayHistory } from "./transcript-history.js";
|
||||
|
||||
/**
|
||||
* A resumed Claude agent whose transcript is missing or unreadable opens with an empty timeline.
|
||||
@@ -90,4 +91,14 @@ describe("ClaudeAgentSession persisted history load", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test("propagates read errors when a transcript disappears before replay", () => {
|
||||
const file = transcriptPath();
|
||||
mkdirSync(path.dirname(file), { recursive: true });
|
||||
writeFileSync(file, "");
|
||||
const history = readClaudeReplayHistory(file);
|
||||
rmSync(file);
|
||||
|
||||
expect(() => [...history.parentEntries]).toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,14 +26,18 @@ interface RecordLocation {
|
||||
function* locatedRecords(
|
||||
file: string,
|
||||
): Generator<{ entry: TranscriptEntry; location: RecordLocation }> {
|
||||
let fd: number;
|
||||
const fd = fs.openSync(file, "r");
|
||||
try {
|
||||
fd = fs.openSync(file, "r");
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let remaining = fs.fstatSync(fd).size;
|
||||
const stat = fs.fstatSync(fd);
|
||||
// Opening a directory succeeds on some platforms; it must never look like empty history.
|
||||
if (stat.isDirectory()) {
|
||||
throw Object.assign(new Error(`EISDIR: illegal operation on a directory, read '${file}'`), {
|
||||
code: "EISDIR",
|
||||
syscall: "read",
|
||||
path: file,
|
||||
});
|
||||
}
|
||||
let remaining = stat.size;
|
||||
let offset = 0;
|
||||
let pieces: Buffer[] = [];
|
||||
let length = 0;
|
||||
@@ -110,17 +114,12 @@ function workflowRecords(files: readonly string[]): Iterable<TranscriptEntry> {
|
||||
let fd: number | null = null;
|
||||
try {
|
||||
for (const location of locations) {
|
||||
if (currentFile !== location.file) {
|
||||
if (fd === null || currentFile !== location.file) {
|
||||
if (fd !== null) fs.closeSync(fd);
|
||||
fd = null;
|
||||
currentFile = location.file;
|
||||
try {
|
||||
fd = fs.openSync(currentFile, "r");
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
fd = fs.openSync(currentFile, "r");
|
||||
}
|
||||
if (fd === null) continue;
|
||||
const buffer = Buffer.allocUnsafe(location.length);
|
||||
let read = 0;
|
||||
while (read < buffer.length) {
|
||||
|
||||
Reference in New Issue
Block a user