authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-12 21:14:31-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-12 22:20:01-08:00
logd27b08bae68ac514f5f3a497bc4aa91c0cbfc101
treeff6073170063f3eb81ec9b985d25614b8072ba99
parent485747b892fc9825a7b87719d70211fd8d794c5b

Migrate legacy terminal history to terminal-scoped log path

- On open, read legacy transcript files and write them to the new terminal-scoped history path - Remove legacy files after migration and warn if cleanup fails - Update tests to assert migration, content preservation, and legacy file removal

2 files changed, 21 insertions(+), 8 deletions(-)

apps/server/src/terminalManager.test.ts+5-2
......@@ -334,15 +334,18 @@ describe("TerminalManager", () => {
334334 manager.dispose();
335335 });
336336
337 it("loads existing legacy transcript filenames and keeps new naming for writes", async () => {
337 it("migrates legacy transcript filenames to terminal-scoped history path on open", async () => {
338338 const { manager, logsDir } = makeManager();
339339 const legacyPath = path.join(logsDir, "thread-1.log");
340 const nextPath = historyLogPath(logsDir);
340341 fs.writeFileSync(legacyPath, "legacy-line\n", "utf8");
341342
342343 const snapshot = await manager.open(openInput());
343344
344345 expect(snapshot.history).toBe("legacy-line\n");
345 expect(fs.existsSync(legacyPath)).toBe(true);
346 expect(fs.existsSync(nextPath)).toBe(true);
347 expect(fs.readFileSync(nextPath, "utf8")).toBe("legacy-line\n");
348 expect(fs.existsSync(legacyPath)).toBe(false);
346349
347350 manager.dispose();
348351 });
apps/server/src/terminalManager.ts+16-6
......@@ -582,12 +582,12 @@ export class TerminalManager extends EventEmitter<TerminalManagerEvents> {
582582 }
583583
584584 private async readHistory(threadId: string, terminalId: string): Promise<string> {
585 const historyPath = this.historyPath(threadId, terminalId);
585 const nextPath = this.historyPath(threadId, terminalId);
586586 try {
587 const raw = await fs.promises.readFile(historyPath, "utf8");
587 const raw = await fs.promises.readFile(nextPath, "utf8");
588588 const capped = capHistory(raw, this.historyLineLimit);
589589 if (capped !== raw) {
590 await fs.promises.writeFile(historyPath, capped, "utf8");
590 await fs.promises.writeFile(nextPath, capped, "utf8");
591591 }
592592 return capped;
593593 } catch (error) {
......@@ -600,12 +600,22 @@ export class TerminalManager extends EventEmitter<TerminalManagerEvents> {
600600 return "";
601601 }
602602
603 const legacyPath = this.legacyHistoryPath(threadId);
603604 try {
604 const raw = await fs.promises.readFile(this.legacyHistoryPath(threadId), "utf8");
605 const raw = await fs.promises.readFile(legacyPath, "utf8");
605606 const capped = capHistory(raw, this.historyLineLimit);
606 if (capped !== raw) {
607 await fs.promises.writeFile(this.legacyHistoryPath(threadId), capped, "utf8");
607
608 // Migrate legacy transcript filename to the terminal-scoped path.
609 await fs.promises.writeFile(nextPath, capped, "utf8");
610 try {
611 await fs.promises.rm(legacyPath, { force: true });
612 } catch (cleanupError) {
613 this.logger.warn("failed to remove legacy terminal history", {
614 threadId,
615 error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),
616 });
608617 }
618
609619 return capped;
610620 } catch (error) {
611621 if ((error as NodeJS.ErrnoException).code === "ENOENT") {