fix: require valid project directory for session creation
- Backend: POST /api/chat/sessions now validates working_directory exists (400 MISSING_DIRECTORY / INVALID_DIRECTORY) - db.ts: remove process.cwd() fallback in createSession - ChatListPanel: validate last-working-directory before use, open FolderPicker when invalid; add SSR guard for localStorage - NewChatPage: block sending without a project directory selected Bump version to 0.10.5. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
ae8a905581
commit
916b8889b7
6 files changed
+63
-12
No files matched your search
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "codepilot",
|
||||
"version": "0.10.4",
|
||||
"version": "0.10.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codepilot",
|
||||
"version": "0.10.4",
|
||||
"version": "0.10.5",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.33",
|
||||
"@hugeicons/core-free-icons": "^3.1.1",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codepilot",
|
||||
"version": "0.10.4",
|
||||
"version": "0.10.5",
|
||||
"private": true,
|
||||
"author": {
|
||||
"name": "op7418",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
import fs from 'fs/promises';
|
||||
import { getAllSessions, createSession } from '@/lib/db';
|
||||
import type { CreateSessionRequest, SessionsResponse, SessionResponse } from '@/types';
|
||||
|
||||
@@ -17,6 +18,25 @@ export async function GET() {
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body: CreateSessionRequest = await request.json();
|
||||
|
||||
// Validate working_directory is provided
|
||||
if (!body.working_directory) {
|
||||
return Response.json(
|
||||
{ error: 'Working directory is required', code: 'MISSING_DIRECTORY' },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Validate directory actually exists on disk
|
||||
try {
|
||||
await fs.access(body.working_directory);
|
||||
} catch {
|
||||
return Response.json(
|
||||
{ error: 'Directory does not exist', code: 'INVALID_DIRECTORY' },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const session = createSession(
|
||||
body.title,
|
||||
body.model,
|
||||
|
||||
+16
-4
@@ -78,6 +78,20 @@ export default function NewChatPage() {
|
||||
async (content: string) => {
|
||||
if (isStreaming) return;
|
||||
|
||||
// Require a project directory before sending
|
||||
if (!workingDir.trim()) {
|
||||
const hint: Message = {
|
||||
id: 'hint-' + Date.now(),
|
||||
session_id: '',
|
||||
role: 'assistant',
|
||||
content: '**Please select a project directory first.** Use the folder picker in the toolbar below to choose a working directory before sending a message.',
|
||||
created_at: new Date().toISOString(),
|
||||
token_usage: null,
|
||||
};
|
||||
setMessages((prev) => [...prev, hint]);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsStreaming(true);
|
||||
setStreamingContent('');
|
||||
setToolUses([]);
|
||||
@@ -90,14 +104,12 @@ export default function NewChatPage() {
|
||||
let sessionId = '';
|
||||
|
||||
try {
|
||||
// Create a new session with optional working directory
|
||||
// Create a new session with working directory
|
||||
const createBody: Record<string, string> = {
|
||||
title: content.slice(0, 50),
|
||||
mode,
|
||||
working_directory: workingDir.trim(),
|
||||
};
|
||||
if (workingDir.trim()) {
|
||||
createBody.working_directory = workingDir.trim();
|
||||
}
|
||||
|
||||
const createRes = await fetch('/api/chat/sessions', {
|
||||
method: 'POST',
|
||||
|
||||
@@ -53,6 +53,7 @@ function formatRelativeTime(dateStr: string): string {
|
||||
const COLLAPSED_PROJECTS_KEY = "codepilot:collapsed-projects";
|
||||
|
||||
function loadCollapsedProjects(): Set<string> {
|
||||
if (typeof window === 'undefined') return new Set();
|
||||
try {
|
||||
const raw = localStorage.getItem(COLLAPSED_PROJECTS_KEY);
|
||||
if (raw) return new Set(JSON.parse(raw));
|
||||
@@ -129,27 +130,45 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) {
|
||||
const [creatingChat, setCreatingChat] = useState(false);
|
||||
|
||||
const handleNewChat = useCallback(async () => {
|
||||
const lastDir = localStorage.getItem("codepilot:last-working-directory");
|
||||
const lastDir = typeof window !== 'undefined'
|
||||
? localStorage.getItem("codepilot:last-working-directory")
|
||||
: null;
|
||||
|
||||
if (!lastDir) {
|
||||
router.push("/chat");
|
||||
// No saved directory — let user pick one
|
||||
setFolderPickerOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the saved directory still exists
|
||||
setCreatingChat(true);
|
||||
try {
|
||||
const checkRes = await fetch(
|
||||
`/api/files/browse?dir=${encodeURIComponent(lastDir)}`
|
||||
);
|
||||
if (!checkRes.ok) {
|
||||
// Directory is gone — clear stale value and prompt user
|
||||
localStorage.removeItem("codepilot:last-working-directory");
|
||||
setFolderPickerOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await fetch("/api/chat/sessions", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ working_directory: lastDir }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
router.push("/chat");
|
||||
// Backend rejected it (e.g. INVALID_DIRECTORY) — prompt user
|
||||
localStorage.removeItem("codepilot:last-working-directory");
|
||||
setFolderPickerOpen(true);
|
||||
return;
|
||||
}
|
||||
const data = await res.json();
|
||||
router.push(`/chat/${data.session.id}`);
|
||||
window.dispatchEvent(new CustomEvent("session-created"));
|
||||
} catch {
|
||||
router.push("/chat");
|
||||
setFolderPickerOpen(true);
|
||||
} finally {
|
||||
setCreatingChat(false);
|
||||
}
|
||||
|
||||
+1
-1
@@ -229,7 +229,7 @@ export function createSession(
|
||||
const db = getDb();
|
||||
const id = crypto.randomBytes(16).toString('hex');
|
||||
const now = new Date().toISOString().replace('T', ' ').split('.')[0];
|
||||
const wd = workingDirectory || process.cwd();
|
||||
const wd = workingDirectory || '';
|
||||
const projectName = path.basename(wd);
|
||||
|
||||
db.prepare(
|
||||
|
||||
Reference in new issue
Block a user