fix: Handle missing Claude projects directory and Node.js compatibility issues

- Add proper error handling for missing ~/.claude/projects directory
- Fix TypeError when d.parentPath is undefined (Node.js compatibility)
- Replace toSorted() with sort() for Node.js 18 compatibility
- Handle readdir errors gracefully and return empty arrays

These changes prevent the application from crashing with 'Internal Server Error'
when the Claude projects directory doesn't exist or when using Node.js < 20.
This commit is contained in:
kouyaman345
2025-09-07 07:17:36 +09:00
parent ec0225612c
commit 42d028b06d
6 changed files with 9419 additions and 46 deletions

View File

@@ -50,11 +50,11 @@ export const getProjectMeta = async (
.map(
(d) =>
({
fullPath: resolve(d.parentPath, d.name),
stats: statSync(resolve(d.parentPath, d.name)),
fullPath: resolve(claudeProjectPath, d.name),
stats: statSync(resolve(claudeProjectPath, d.name)),
}) as const,
)
.toSorted((a, b) => {
.sort((a, b) => {
return a.stats.ctime.getTime() - b.stats.ctime.getTime();
});

View File

@@ -1,5 +1,6 @@
import { readdir } from "node:fs/promises";
import { readdir, access } from "node:fs/promises";
import { resolve } from "node:path";
import { constants } from "node:fs";
import { claudeProjectPath } from "../paths";
import type { Project } from "../types";
@@ -7,28 +8,42 @@ import { getProjectMeta } from "./getProjectMeta";
import { encodeProjectId } from "./id";
export const getProjects = async (): Promise<{ projects: Project[] }> => {
const dirents = await readdir(claudeProjectPath, { withFileTypes: true });
const projects = await Promise.all(
dirents
.filter((d) => d.isDirectory())
.map(async (d) => {
const fullPath = resolve(d.parentPath, d.name);
const id = encodeProjectId(fullPath);
try {
// Check if the claude projects directory exists
await access(claudeProjectPath, constants.F_OK);
} catch (error) {
// Directory doesn't exist, return empty array
console.warn(`Claude projects directory not found at ${claudeProjectPath}`);
return { projects: [] };
}
return {
id,
claudeProjectPath: fullPath,
meta: await getProjectMeta(fullPath),
};
try {
const dirents = await readdir(claudeProjectPath, { withFileTypes: true });
const projects = await Promise.all(
dirents
.filter((d) => d.isDirectory())
.map(async (d) => {
const fullPath = resolve(claudeProjectPath, d.name);
const id = encodeProjectId(fullPath);
return {
id,
claudeProjectPath: fullPath,
meta: await getProjectMeta(fullPath),
};
}),
);
return {
projects: projects.sort((a, b) => {
return (
(b.meta.lastModifiedAt?.getTime() ?? 0) -
(a.meta.lastModifiedAt?.getTime() ?? 0)
);
}),
);
return {
projects: projects.sort((a, b) => {
return (
(b.meta.lastModifiedAt?.getTime() ?? 0) -
(a.meta.lastModifiedAt?.getTime() ?? 0)
);
}),
};
};
} catch (error) {
console.error('Error reading projects:', error);
return { projects: [] };
}
};

View File

@@ -15,24 +15,29 @@ export const getSessions = async (
): Promise<{ sessions: Session[] }> => {
const claudeProjectPath = decodeProjectId(projectId);
const dirents = await readdir(claudeProjectPath, { withFileTypes: true });
const sessions = await Promise.all(
dirents
.filter((d) => d.isFile() && d.name.endsWith(".jsonl"))
.map(async (d): Promise<Session> => {
const fullPath = resolve(d.parentPath, d.name);
try {
const dirents = await readdir(claudeProjectPath, { withFileTypes: true });
const sessions = await Promise.all(
dirents
.filter((d) => d.isFile() && d.name.endsWith(".jsonl"))
.map(async (d): Promise<Session> => {
const fullPath = resolve(claudeProjectPath, d.name);
return {
id: basename(fullPath, extname(fullPath)),
jsonlFilePath: fullPath,
meta: await getSessionMeta(fullPath),
};
return {
id: basename(fullPath, extname(fullPath)),
jsonlFilePath: fullPath,
meta: await getSessionMeta(fullPath),
};
}),
);
return {
sessions: sessions.sort((a, b) => {
return getTime(b.meta.lastModifiedAt) - getTime(a.meta.lastModifiedAt);
}),
);
return {
sessions: sessions.sort((a, b) => {
return getTime(b.meta.lastModifiedAt) - getTime(a.meta.lastModifiedAt);
}),
};
};
} catch (error) {
console.warn(`Failed to read sessions for project ${projectId}:`, error);
return { sessions: [] };
}
};