Skip to content

Commit d3c406f

Browse files
committed
feat(route): implement dynamic skills directory handling for serverless environments
1 parent d336151 commit d3c406f

1 file changed

Lines changed: 40 additions & 4 deletions

File tree

src/app/api/skills/catalog/route.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,25 @@ function getGitHubToken(): string | undefined {
5858
return process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
5959
}
6060

61+
/**
62+
* Get the destination directory for installing skills.
63+
* On serverless environments (Vercel), falls back to a temp directory since
64+
* the home directory is read-only.
65+
*/
66+
function getSkillsDestDir(): string {
67+
// Check if we're in a serverless environment (Vercel sets this)
68+
const isServerless = process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME;
69+
70+
if (isServerless) {
71+
// On serverless, use /tmp which is the only writable location
72+
// Note: This is ephemeral and won't persist across invocations
73+
return path.join(os.tmpdir(), ".codex/skills");
74+
}
75+
76+
// On local/traditional servers, use the home directory
77+
return path.join(os.homedir(), ".codex/skills");
78+
}
79+
6180
async function githubFetch(url: string): Promise<Response> {
6281
const headers: Record<string, string> = {
6382
"User-Agent": "routa-skill-catalog",
@@ -277,8 +296,17 @@ async function handleSkillsShInstall(body: {
277296
);
278297
}
279298

280-
const destBase = path.join(os.homedir(), ".codex/skills");
281-
fs.mkdirSync(destBase, { recursive: true });
299+
// Use appropriate dest based on environment (Vercel serverless can't write to homedir)
300+
const destBase = getSkillsDestDir();
301+
try {
302+
fs.mkdirSync(destBase, { recursive: true });
303+
} catch (err) {
304+
console.error("[skills/catalog] Failed to create skills directory:", destBase, err);
305+
return NextResponse.json(
306+
{ error: `Cannot create skills directory: ${err instanceof Error ? err.message : String(err)}. On serverless environments, skill installation may not be supported.` },
307+
{ status: 500 }
308+
);
309+
}
282310

283311
const installed: string[] = [];
284312
const errors: string[] = [];
@@ -458,8 +486,16 @@ async function handleGithubInstall(body: {
458486
}
459487

460488
const repoRoot = path.join(tmpDir, topDirs[0].name);
461-
const destBase = path.join(os.homedir(), ".codex/skills");
462-
fs.mkdirSync(destBase, { recursive: true });
489+
const destBase = getSkillsDestDir();
490+
try {
491+
fs.mkdirSync(destBase, { recursive: true });
492+
} catch (err) {
493+
console.error("[skills/catalog] Failed to create skills directory:", destBase, err);
494+
return NextResponse.json(
495+
{ error: `Cannot create skills directory: ${err instanceof Error ? err.message : String(err)}. On serverless environments, skill installation may not be supported.` },
496+
{ status: 500 }
497+
);
498+
}
463499

464500
const installed: string[] = [];
465501
const errors: string[] = [];

0 commit comments

Comments
 (0)