Run prettier on everything (#104)
This commit is contained in:
@@ -74,7 +74,7 @@ async function executeAppLocalNode({
|
||||
shell: true,
|
||||
stdio: "pipe", // Ensure stdio is piped so we can capture output/errors and detect close
|
||||
detached: false, // Ensure child process is attached to the main process lifecycle unless explicitly backgrounded
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Check if process spawned correctly
|
||||
@@ -86,7 +86,7 @@ async function executeAppLocalNode({
|
||||
throw new Error(
|
||||
`Failed to spawn process for app ${appId}. Error: ${
|
||||
errorOutput || "Unknown spawn error"
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ async function executeAppLocalNode({
|
||||
// Handle process exit/close
|
||||
process.on("close", (code, signal) => {
|
||||
logger.log(
|
||||
`App ${appId} (PID: ${process.pid}) process closed with code ${code}, signal ${signal}.`
|
||||
`App ${appId} (PID: ${process.pid}) process closed with code ${code}, signal ${signal}.`,
|
||||
);
|
||||
removeAppIfCurrentProcess(appId, process);
|
||||
});
|
||||
@@ -126,7 +126,7 @@ async function executeAppLocalNode({
|
||||
// Handle errors during process lifecycle (e.g., command not found)
|
||||
process.on("error", (err) => {
|
||||
logger.error(
|
||||
`Error in app ${appId} (PID: ${process.pid}) process: ${err.message}`
|
||||
`Error in app ${appId} (PID: ${process.pid}) process: ${err.message}`,
|
||||
);
|
||||
removeAppIfCurrentProcess(appId, process);
|
||||
// Note: We don't throw here as the error is asynchronous. The caller got a success response already.
|
||||
@@ -173,7 +173,7 @@ export function registerAppHandlers() {
|
||||
// Copy scaffold asynchronously
|
||||
await copyDirectoryRecursive(
|
||||
path.join(__dirname, "..", "..", "scaffold"),
|
||||
fullAppPath
|
||||
fullAppPath,
|
||||
);
|
||||
// Initialize git repo and create first commit
|
||||
await git.init({
|
||||
@@ -285,7 +285,7 @@ export function registerAppHandlers() {
|
||||
logger.error(`Error reading file ${filePath} for app ${appId}:`, error);
|
||||
throw new Error("Failed to read file");
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle("get-env-vars", async () => {
|
||||
@@ -300,7 +300,7 @@ export function registerAppHandlers() {
|
||||
"run-app",
|
||||
async (
|
||||
event: Electron.IpcMainInvokeEvent,
|
||||
{ appId }: { appId: number }
|
||||
{ appId }: { appId: number },
|
||||
) => {
|
||||
return withLock(appId, async () => {
|
||||
// Check if app is already running
|
||||
@@ -337,19 +337,19 @@ export function registerAppHandlers() {
|
||||
throw new Error(`Failed to run app ${appId}: ${error.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle("stop-app", async (_, { appId }: { appId: number }) => {
|
||||
logger.log(
|
||||
`Attempting to stop app ${appId}. Current running apps: ${runningApps.size}`
|
||||
`Attempting to stop app ${appId}. Current running apps: ${runningApps.size}`,
|
||||
);
|
||||
return withLock(appId, async () => {
|
||||
const appInfo = runningApps.get(appId);
|
||||
|
||||
if (!appInfo) {
|
||||
logger.log(
|
||||
`App ${appId} not found in running apps map. Assuming already stopped.`
|
||||
`App ${appId} not found in running apps map. Assuming already stopped.`,
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
@@ -359,13 +359,13 @@ export function registerAppHandlers() {
|
||||
|
||||
const { process, processId } = appInfo;
|
||||
logger.log(
|
||||
`Found running app ${appId} with processId ${processId} (PID: ${process.pid}). Attempting to stop.`
|
||||
`Found running app ${appId} with processId ${processId} (PID: ${process.pid}). Attempting to stop.`,
|
||||
);
|
||||
|
||||
// Check if the process is already exited or closed
|
||||
if (process.exitCode !== null || process.signalCode !== null) {
|
||||
logger.log(
|
||||
`Process for app ${appId} (PID: ${process.pid}) already exited (code: ${process.exitCode}, signal: ${process.signalCode}). Cleaning up map.`
|
||||
`Process for app ${appId} (PID: ${process.pid}) already exited (code: ${process.exitCode}, signal: ${process.signalCode}). Cleaning up map.`,
|
||||
);
|
||||
runningApps.delete(appId); // Ensure cleanup if somehow missed
|
||||
return { success: true, message: "Process already exited." };
|
||||
@@ -382,7 +382,7 @@ export function registerAppHandlers() {
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`Error stopping app ${appId} (PID: ${process.pid}, processId: ${processId}):`,
|
||||
error
|
||||
error,
|
||||
);
|
||||
// Attempt cleanup even if an error occurred during the stop process
|
||||
removeAppIfCurrentProcess(appId, process);
|
||||
@@ -398,7 +398,7 @@ export function registerAppHandlers() {
|
||||
{
|
||||
appId,
|
||||
removeNodeModules,
|
||||
}: { appId: number; removeNodeModules?: boolean }
|
||||
}: { appId: number; removeNodeModules?: boolean },
|
||||
) => {
|
||||
logger.log(`Restarting app ${appId}`);
|
||||
return withLock(appId, async () => {
|
||||
@@ -408,7 +408,7 @@ export function registerAppHandlers() {
|
||||
if (appInfo) {
|
||||
const { process, processId } = appInfo;
|
||||
logger.log(
|
||||
`Stopping app ${appId} (processId ${processId}) before restart`
|
||||
`Stopping app ${appId} (processId ${processId}) before restart`,
|
||||
);
|
||||
|
||||
await killProcess(process);
|
||||
@@ -435,7 +435,7 @@ export function registerAppHandlers() {
|
||||
if (removeNodeModules) {
|
||||
const nodeModulesPath = path.join(appPath, "node_modules");
|
||||
logger.log(
|
||||
`Removing node_modules for app ${appId} at ${nodeModulesPath}`
|
||||
`Removing node_modules for app ${appId} at ${nodeModulesPath}`,
|
||||
);
|
||||
if (fs.existsSync(nodeModulesPath)) {
|
||||
await fsPromises.rm(nodeModulesPath, {
|
||||
@@ -449,7 +449,7 @@ export function registerAppHandlers() {
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`Executing app ${appId} in path ${app.path} after restart request`
|
||||
`Executing app ${appId} in path ${app.path} after restart request`,
|
||||
); // Adjusted log
|
||||
|
||||
await executeApp({ appPath, appId, event }); // This will handle starting either mode
|
||||
@@ -460,7 +460,7 @@ export function registerAppHandlers() {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
@@ -471,7 +471,7 @@ export function registerAppHandlers() {
|
||||
appId,
|
||||
filePath,
|
||||
content,
|
||||
}: { appId: number; filePath: string; content: string }
|
||||
}: { appId: number; filePath: string; content: string },
|
||||
) => {
|
||||
const app = await db.query.apps.findFirst({
|
||||
where: eq(apps.id, appId),
|
||||
@@ -517,7 +517,7 @@ export function registerAppHandlers() {
|
||||
logger.error(`Error writing file ${filePath} for app ${appId}:`, error);
|
||||
throw new Error(`Failed to write file: ${error.message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle("delete-app", async (_, { appId }: { appId: number }) => {
|
||||
@@ -575,7 +575,7 @@ export function registerAppHandlers() {
|
||||
appId,
|
||||
appName,
|
||||
appPath,
|
||||
}: { appId: number; appName: string; appPath: string }
|
||||
}: { appId: number; appName: string; appPath: string },
|
||||
) => {
|
||||
return withLock(appId, async () => {
|
||||
// Check if app exists
|
||||
@@ -613,7 +613,7 @@ export function registerAppHandlers() {
|
||||
} catch (error: any) {
|
||||
logger.error(`Error stopping app ${appId} before renaming:`, error);
|
||||
throw new Error(
|
||||
`Failed to stop app before renaming: ${error.message}`
|
||||
`Failed to stop app before renaming: ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -627,7 +627,7 @@ export function registerAppHandlers() {
|
||||
// Check if destination directory already exists
|
||||
if (fs.existsSync(newAppPath)) {
|
||||
throw new Error(
|
||||
`Destination path '${newAppPath}' already exists`
|
||||
`Destination path '${newAppPath}' already exists`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -641,7 +641,7 @@ export function registerAppHandlers() {
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`Error moving app files from ${oldAppPath} to ${newAppPath}:`,
|
||||
error
|
||||
error,
|
||||
);
|
||||
throw new Error(`Failed to move app files: ${error.message}`);
|
||||
}
|
||||
@@ -667,7 +667,7 @@ export function registerAppHandlers() {
|
||||
} catch (rollbackError) {
|
||||
logger.error(
|
||||
`Failed to rollback file move during rename error:`,
|
||||
rollbackError
|
||||
rollbackError,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -676,7 +676,7 @@ export function registerAppHandlers() {
|
||||
throw new Error(`Failed to update app in database: ${error.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle("reset-all", async () => {
|
||||
|
||||
@@ -52,7 +52,7 @@ export function registerChatHandlers() {
|
||||
"for app:",
|
||||
appId,
|
||||
"with initial commit hash:",
|
||||
initialCommitHash
|
||||
initialCommitHash,
|
||||
);
|
||||
return chat.id;
|
||||
});
|
||||
@@ -101,7 +101,7 @@ export function registerChatHandlers() {
|
||||
|
||||
const allChats = await query;
|
||||
return allChats;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle("delete-chat", async (_, chatId: number) => {
|
||||
|
||||
@@ -124,7 +124,7 @@ export function registerChatStreamHandlers() {
|
||||
await db
|
||||
.delete(messages)
|
||||
.where(
|
||||
eq(messages.id, chatMessages[lastUserMessageIndex + 1].id)
|
||||
eq(messages.id, chatMessages[lastUserMessageIndex + 1].id),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,7 @@ export function registerChatStreamHandlers() {
|
||||
req.chatId,
|
||||
testResponse,
|
||||
abortController,
|
||||
updatedChat
|
||||
updatedChat,
|
||||
);
|
||||
} else {
|
||||
// Normal AI processing for non-test prompts
|
||||
@@ -242,7 +242,7 @@ export function registerChatStreamHandlers() {
|
||||
"codebaseInfo: length",
|
||||
codebaseInfo.length,
|
||||
"estimated tokens",
|
||||
codebaseInfo.length / 4
|
||||
codebaseInfo.length / 4,
|
||||
);
|
||||
|
||||
// Prepare message history for the AI
|
||||
@@ -266,7 +266,7 @@ export function registerChatStreamHandlers() {
|
||||
systemPrompt += "\n\n" + SUPABASE_NOT_AVAILABLE_SYSTEM_PROMPT;
|
||||
}
|
||||
const isSummarizeIntent = req.prompt.startsWith(
|
||||
"Summarize from chat-id="
|
||||
"Summarize from chat-id=",
|
||||
);
|
||||
if (isSummarizeIntent) {
|
||||
systemPrompt = SUMMARIZE_CHAT_SYSTEM_PROMPT;
|
||||
@@ -276,7 +276,7 @@ export function registerChatStreamHandlers() {
|
||||
const hasImageAttachments =
|
||||
req.attachments &&
|
||||
req.attachments.some((attachment) =>
|
||||
attachment.type.startsWith("image/")
|
||||
attachment.type.startsWith("image/"),
|
||||
);
|
||||
|
||||
if (hasImageAttachments) {
|
||||
@@ -316,7 +316,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
// Replace the last message with one that includes attachments
|
||||
chatMessages[lastUserIndex] = await prepareMessageWithAttachments(
|
||||
lastUserMessage,
|
||||
attachmentPaths
|
||||
attachmentPaths,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -353,7 +353,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
(error as any)?.error?.message || JSON.stringify(error);
|
||||
event.sender.send(
|
||||
"chat:response:error",
|
||||
`Sorry, there was an error from the AI: ${message}`
|
||||
`Sorry, there was an error from the AI: ${message}`,
|
||||
);
|
||||
// Clean up the abort controller
|
||||
activeStreams.delete(req.chatId);
|
||||
@@ -374,7 +374,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
});
|
||||
fullResponse = fullResponse.replace(
|
||||
"$$SUPABASE_CLIENT_CODE$$",
|
||||
supabaseClientCode
|
||||
supabaseClientCode,
|
||||
);
|
||||
}
|
||||
// Store the current partial response
|
||||
@@ -421,13 +421,13 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
.where(eq(messages.id, placeholderAssistantMessage.id));
|
||||
|
||||
logger.log(
|
||||
`Updated cancelled response for placeholder message ${placeholderAssistantMessage.id} in chat ${chatId}`
|
||||
`Updated cancelled response for placeholder message ${placeholderAssistantMessage.id} in chat ${chatId}`,
|
||||
);
|
||||
partialResponses.delete(req.chatId);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Error saving partial response for chat ${chatId}:`,
|
||||
error
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -441,7 +441,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
if (!abortController.signal.aborted && fullResponse) {
|
||||
// Scrape from: <dyad-chat-summary>Renaming profile file</dyad-chat-title>
|
||||
const chatTitle = fullResponse.match(
|
||||
/<dyad-chat-summary>(.*?)<\/dyad-chat-summary>/
|
||||
/<dyad-chat-summary>(.*?)<\/dyad-chat-summary>/,
|
||||
);
|
||||
if (chatTitle) {
|
||||
await db
|
||||
@@ -461,7 +461,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
const status = await processFullResponseActions(
|
||||
fullResponse,
|
||||
req.chatId,
|
||||
{ chatSummary, messageId: placeholderAssistantMessage.id } // Use placeholder ID
|
||||
{ chatSummary, messageId: placeholderAssistantMessage.id }, // Use placeholder ID
|
||||
);
|
||||
|
||||
const chat = await db.query.chats.findFirst({
|
||||
@@ -481,7 +481,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
if (status.error) {
|
||||
event.sender.send(
|
||||
"chat:response:error",
|
||||
`Sorry, there was an error applying the AI's changes: ${status.error}`
|
||||
`Sorry, there was an error applying the AI's changes: ${status.error}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -505,12 +505,15 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
try {
|
||||
// We don't immediately delete files because they might be needed for reference
|
||||
// Instead, schedule them for deletion after some time
|
||||
setTimeout(async () => {
|
||||
if (fs.existsSync(filePath)) {
|
||||
await unlink(filePath);
|
||||
logger.log(`Deleted temporary file: ${filePath}`);
|
||||
}
|
||||
}, 30 * 60 * 1000); // Delete after 30 minutes
|
||||
setTimeout(
|
||||
async () => {
|
||||
if (fs.existsSync(filePath)) {
|
||||
await unlink(filePath);
|
||||
logger.log(`Deleted temporary file: ${filePath}`);
|
||||
}
|
||||
},
|
||||
30 * 60 * 1000,
|
||||
); // Delete after 30 minutes
|
||||
} catch (error) {
|
||||
logger.error(`Error scheduling file deletion: ${error}`);
|
||||
}
|
||||
@@ -523,7 +526,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
logger.error("Error calling LLM:", error);
|
||||
event.sender.send(
|
||||
"chat:response:error",
|
||||
`Sorry, there was an error processing your request: ${error}`
|
||||
`Sorry, there was an error processing your request: ${error}`,
|
||||
);
|
||||
// Clean up the abort controller
|
||||
activeStreams.delete(req.chatId);
|
||||
@@ -555,7 +558,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
}
|
||||
|
||||
export function formatMessages(
|
||||
messages: { role: string; content: string | undefined }[]
|
||||
messages: { role: string; content: string | undefined }[],
|
||||
) {
|
||||
return messages
|
||||
.map((m) => `<message role="${m.role}">${m.content}</message>`)
|
||||
@@ -566,7 +569,7 @@ export function formatMessages(
|
||||
async function replaceTextAttachmentWithContent(
|
||||
text: string,
|
||||
filePath: string,
|
||||
fileName: string
|
||||
fileName: string,
|
||||
): Promise<string> {
|
||||
try {
|
||||
if (await isTextFile(filePath)) {
|
||||
@@ -577,16 +580,16 @@ async function replaceTextAttachmentWithContent(
|
||||
const escapedPath = filePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const tagPattern = new RegExp(
|
||||
`<dyad-text-attachment filename="[^"]*" type="[^"]*" path="${escapedPath}">\\s*<\\/dyad-text-attachment>`,
|
||||
"g"
|
||||
"g",
|
||||
);
|
||||
|
||||
const replacedText = text.replace(
|
||||
tagPattern,
|
||||
`Full content of ${fileName}:\n\`\`\`\n${fullContent}\n\`\`\``
|
||||
`Full content of ${fileName}:\n\`\`\`\n${fullContent}\n\`\`\``,
|
||||
);
|
||||
|
||||
logger.log(
|
||||
`Replaced text attachment content for: ${fileName} - length before: ${text.length} - length after: ${replacedText.length}`
|
||||
`Replaced text attachment content for: ${fileName} - length before: ${text.length} - length after: ${replacedText.length}`,
|
||||
);
|
||||
return replacedText;
|
||||
}
|
||||
@@ -600,13 +603,13 @@ async function replaceTextAttachmentWithContent(
|
||||
// Helper function to convert traditional message to one with proper image attachments
|
||||
async function prepareMessageWithAttachments(
|
||||
message: CoreMessage,
|
||||
attachmentPaths: string[]
|
||||
attachmentPaths: string[],
|
||||
): Promise<CoreMessage> {
|
||||
let textContent = message.content;
|
||||
// Get the original text content
|
||||
if (typeof textContent !== "string") {
|
||||
logger.warn(
|
||||
"Message content is not a string - shouldn't happen but using message as-is"
|
||||
"Message content is not a string - shouldn't happen but using message as-is",
|
||||
);
|
||||
return message;
|
||||
}
|
||||
@@ -617,7 +620,7 @@ async function prepareMessageWithAttachments(
|
||||
textContent = await replaceTextAttachmentWithContent(
|
||||
textContent,
|
||||
filePath,
|
||||
fileName
|
||||
fileName,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ export function registerDebugHandlers() {
|
||||
async (): Promise<SystemDebugInfo> => {
|
||||
console.log("IPC: get-system-debug-info called");
|
||||
return getSystemDebugInfo();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
@@ -151,7 +151,7 @@ export function registerDebugHandlers() {
|
||||
console.error(`Error in get-chat-logs:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
console.log("Registered debug IPC handlers");
|
||||
|
||||
@@ -10,7 +10,7 @@ export function registerDependencyHandlers() {
|
||||
"chat:add-dep",
|
||||
async (
|
||||
_event,
|
||||
{ chatId, packages }: { chatId: number; packages: string[] }
|
||||
{ chatId, packages }: { chatId: number; packages: string[] },
|
||||
) => {
|
||||
// Find the message from the database
|
||||
const foundMessages = await db.query.messages.findMany({
|
||||
@@ -39,13 +39,13 @@ export function registerDependencyHandlers() {
|
||||
.reverse()
|
||||
.find((m) =>
|
||||
m.content.includes(
|
||||
`<dyad-add-dependency packages="${packages.join(" ")}">`
|
||||
)
|
||||
`<dyad-add-dependency packages="${packages.join(" ")}">`,
|
||||
),
|
||||
);
|
||||
|
||||
if (!message) {
|
||||
throw new Error(
|
||||
`Message with packages ${packages.join(", ")} not found`
|
||||
`Message with packages ${packages.join(", ")} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,6 @@ export function registerDependencyHandlers() {
|
||||
message,
|
||||
appPath: getDyadAppPath(app.path),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ async function pollForAccessToken(event: IpcMainInvokeEvent) {
|
||||
// Schedule next poll
|
||||
currentFlowState.timeoutId = setTimeout(
|
||||
() => pollForAccessToken(event),
|
||||
interval * 1000
|
||||
interval * 1000,
|
||||
);
|
||||
break;
|
||||
case "slow_down":
|
||||
@@ -143,7 +143,7 @@ async function pollForAccessToken(event: IpcMainInvokeEvent) {
|
||||
});
|
||||
currentFlowState.timeoutId = setTimeout(
|
||||
() => pollForAccessToken(event),
|
||||
newInterval * 1000
|
||||
newInterval * 1000,
|
||||
);
|
||||
break;
|
||||
case "expired_token":
|
||||
@@ -162,7 +162,7 @@ async function pollForAccessToken(event: IpcMainInvokeEvent) {
|
||||
break;
|
||||
default:
|
||||
logger.error(
|
||||
`Unknown GitHub error: ${data.error_description || data.error}`
|
||||
`Unknown GitHub error: ${data.error_description || data.error}`,
|
||||
);
|
||||
event.sender.send("github:flow-error", {
|
||||
error: `GitHub authorization error: ${
|
||||
@@ -202,7 +202,7 @@ function stopPolling() {
|
||||
|
||||
function handleStartGithubFlow(
|
||||
event: IpcMainInvokeEvent,
|
||||
args: { appId: number | null }
|
||||
args: { appId: number | null },
|
||||
) {
|
||||
logger.debug(`Received github:start-flow for appId: ${args.appId}`);
|
||||
|
||||
@@ -249,7 +249,7 @@ function handleStartGithubFlow(
|
||||
if (!res.ok) {
|
||||
return res.json().then((errData) => {
|
||||
throw new Error(
|
||||
`GitHub API Error: ${errData.error_description || res.statusText}`
|
||||
`GitHub API Error: ${errData.error_description || res.statusText}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -273,7 +273,7 @@ function handleStartGithubFlow(
|
||||
// Start polling after the initial interval
|
||||
currentFlowState.timeoutId = setTimeout(
|
||||
() => pollForAccessToken(event),
|
||||
currentFlowState.interval * 1000
|
||||
currentFlowState.interval * 1000,
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -289,7 +289,7 @@ function handleStartGithubFlow(
|
||||
// --- GitHub Repo Availability Handler ---
|
||||
async function handleIsRepoAvailable(
|
||||
event: IpcMainInvokeEvent,
|
||||
{ org, repo }: { org: string; repo: string }
|
||||
{ org, repo }: { org: string; repo: string },
|
||||
) {
|
||||
try {
|
||||
// Get access token from settings
|
||||
@@ -327,7 +327,7 @@ async function handleIsRepoAvailable(
|
||||
// --- GitHub Create Repo Handler ---
|
||||
async function handleCreateRepo(
|
||||
event: IpcMainInvokeEvent,
|
||||
{ org, repo, appId }: { org: string; repo: string; appId: number }
|
||||
{ org, repo, appId }: { org: string; repo: string; appId: number },
|
||||
) {
|
||||
try {
|
||||
// Get access token from settings
|
||||
@@ -376,7 +376,7 @@ async function handleCreateRepo(
|
||||
// --- GitHub Push Handler ---
|
||||
async function handlePushToGithub(
|
||||
event: IpcMainInvokeEvent,
|
||||
{ appId }: { appId: number }
|
||||
{ appId }: { appId: number },
|
||||
) {
|
||||
try {
|
||||
// Get access token from settings
|
||||
@@ -424,7 +424,7 @@ async function handlePushToGithub(
|
||||
|
||||
async function handleDisconnectGithubRepo(
|
||||
event: IpcMainInvokeEvent,
|
||||
{ appId }: { appId: number }
|
||||
{ appId }: { appId: number },
|
||||
) {
|
||||
try {
|
||||
logger.log(`Disconnecting GitHub repo for appId: ${appId}`);
|
||||
@@ -464,6 +464,6 @@ export function registerGithubHandlers() {
|
||||
ipcMain.handle("github:create-repo", handleCreateRepo);
|
||||
ipcMain.handle("github:push", handlePushToGithub);
|
||||
ipcMain.handle("github:disconnect", (event, args: { appId: number }) =>
|
||||
handleDisconnectGithubRepo(event, args)
|
||||
handleDisconnectGithubRepo(event, args),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,15 +11,17 @@ export interface LMStudioModel {
|
||||
publisher: string;
|
||||
state: "loaded" | "not-loaded";
|
||||
max_context_length: number;
|
||||
quantization: string
|
||||
compatibility_type: string
|
||||
quantization: string;
|
||||
compatibility_type: string;
|
||||
arch: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export async function fetchLMStudioModels(): Promise<LocalModelListResponse> {
|
||||
try {
|
||||
const modelsResponse: Response = await fetch("http://localhost:1234/api/v0/models");
|
||||
const modelsResponse: Response = await fetch(
|
||||
"http://localhost:1234/api/v0/models",
|
||||
);
|
||||
if (!modelsResponse.ok) {
|
||||
throw new Error("Failed to fetch models from LM Studio");
|
||||
}
|
||||
@@ -30,7 +32,7 @@ export async function fetchLMStudioModels(): Promise<LocalModelListResponse> {
|
||||
.map((model: any) => ({
|
||||
modelName: model.id,
|
||||
displayName: model.id,
|
||||
provider: "lmstudio"
|
||||
provider: "lmstudio",
|
||||
}));
|
||||
|
||||
logger.info(`Successfully fetched ${models.length} models from LM Studio`);
|
||||
@@ -41,7 +43,10 @@ export async function fetchLMStudioModels(): Promise<LocalModelListResponse> {
|
||||
}
|
||||
|
||||
export function registerLMStudioHandlers() {
|
||||
ipcMain.handle('local-models:list-lmstudio', async (): Promise<LocalModelListResponse> => {
|
||||
return fetchLMStudioModels();
|
||||
});
|
||||
}
|
||||
ipcMain.handle(
|
||||
"local-models:list-lmstudio",
|
||||
async (): Promise<LocalModelListResponse> => {
|
||||
return fetchLMStudioModels();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,12 +31,13 @@ export async function fetchOllamaModels(): Promise<LocalModelListResponse> {
|
||||
const ollamaModels: OllamaModel[] = data.models || [];
|
||||
|
||||
const models: LocalModel[] = ollamaModels.map((model: OllamaModel) => {
|
||||
const displayName = model.name.split(':')[0]
|
||||
.replace(/-/g, ' ')
|
||||
.replace(/(\d+)/, ' $1 ')
|
||||
.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
const displayName = model.name
|
||||
.split(":")[0]
|
||||
.replace(/-/g, " ")
|
||||
.replace(/(\d+)/, " $1 ")
|
||||
.split(" ")
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
return {
|
||||
@@ -45,14 +46,18 @@ export async function fetchOllamaModels(): Promise<LocalModelListResponse> {
|
||||
provider: "ollama",
|
||||
};
|
||||
});
|
||||
logger.info(`Successfully fetched ${models.length} models from Ollama`);
|
||||
return { models, error: null };
|
||||
logger.info(`Successfully fetched ${models.length} models from Ollama`);
|
||||
return { models, error: null };
|
||||
} catch (error) {
|
||||
if (error instanceof TypeError && (error as Error).message.includes('fetch failed')) {
|
||||
if (
|
||||
error instanceof TypeError &&
|
||||
(error as Error).message.includes("fetch failed")
|
||||
) {
|
||||
logger.error("Could not connect to Ollama");
|
||||
return {
|
||||
models: [],
|
||||
error: "Could not connect to Ollama. Make sure it's running at http://localhost:11434"
|
||||
error:
|
||||
"Could not connect to Ollama. Make sure it's running at http://localhost:11434",
|
||||
};
|
||||
}
|
||||
return { models: [], error: "Failed to fetch models from Ollama" };
|
||||
@@ -60,7 +65,10 @@ export async function fetchOllamaModels(): Promise<LocalModelListResponse> {
|
||||
}
|
||||
|
||||
export function registerOllamaHandlers() {
|
||||
ipcMain.handle('local-models:list-ollama', async (): Promise<LocalModelListResponse> => {
|
||||
return fetchOllamaModels();
|
||||
});
|
||||
}
|
||||
ipcMain.handle(
|
||||
"local-models:list-ollama",
|
||||
async (): Promise<LocalModelListResponse> => {
|
||||
return fetchOllamaModels();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export function registerNodeHandlers() {
|
||||
"handling ipc: nodejs-status for platform:",
|
||||
platform(),
|
||||
"and arch:",
|
||||
arch()
|
||||
arch(),
|
||||
);
|
||||
// Run checks in parallel
|
||||
const [nodeVersion, pnpmVersion] = await Promise.all([
|
||||
@@ -23,7 +23,7 @@ export function registerNodeHandlers() {
|
||||
// If not, try to install it using corepack.
|
||||
// If both fail, then pnpm is not available.
|
||||
runShellCommand(
|
||||
"pnpm --version || (corepack enable pnpm && pnpm --version) || (npm install -g pnpm@latest-10 && pnpm --version)"
|
||||
"pnpm --version || (corepack enable pnpm && pnpm --version) || (npm install -g pnpm@latest-10 && pnpm --version)",
|
||||
),
|
||||
]);
|
||||
// Default to mac download url.
|
||||
|
||||
@@ -80,7 +80,7 @@ function cleanupExpiredCacheEntries() {
|
||||
|
||||
if (expiredCount > 0) {
|
||||
logger.log(
|
||||
`Cleaned up ${expiredCount} expired codebase token cache entries`
|
||||
`Cleaned up ${expiredCount} expired codebase token cache entries`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ async function getCodebaseTokenCount(
|
||||
chatId: number,
|
||||
messageId: number,
|
||||
messageContent: string,
|
||||
appPath: string
|
||||
appPath: string,
|
||||
): Promise<number> {
|
||||
// Clean up expired cache entries first
|
||||
cleanupExpiredCacheEntries();
|
||||
@@ -128,7 +128,7 @@ async function getCodebaseTokenCount(
|
||||
|
||||
const getProposalHandler = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
{ chatId }: { chatId: number }
|
||||
{ chatId }: { chatId: number },
|
||||
): Promise<ProposalResult | null> => {
|
||||
return withLock("get-proposal:" + chatId, async () => {
|
||||
logger.log(`IPC: get-proposal called for chatId: ${chatId}`);
|
||||
@@ -152,7 +152,7 @@ const getProposalHandler = async (
|
||||
) {
|
||||
const messageId = latestAssistantMessage.id; // Get the message ID
|
||||
logger.log(
|
||||
`Found latest assistant message (ID: ${messageId}), parsing content...`
|
||||
`Found latest assistant message (ID: ${messageId}), parsing content...`,
|
||||
);
|
||||
const messageContent = latestAssistantMessage.content;
|
||||
|
||||
@@ -211,7 +211,7 @@ const getProposalHandler = async (
|
||||
"files=",
|
||||
proposal.filesChanged.length,
|
||||
"packages=",
|
||||
proposal.packagesAdded.length
|
||||
proposal.packagesAdded.length,
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -221,19 +221,23 @@ const getProposalHandler = async (
|
||||
};
|
||||
} else {
|
||||
logger.log(
|
||||
"No relevant tags found in the latest assistant message content."
|
||||
"No relevant tags found in the latest assistant message content.",
|
||||
);
|
||||
}
|
||||
}
|
||||
const actions: ActionProposal["actions"] = [];
|
||||
if (latestAssistantMessage?.content) {
|
||||
const writeTags = getDyadWriteTags(latestAssistantMessage.content);
|
||||
const refactorTarget = writeTags.reduce((largest, tag) => {
|
||||
const lineCount = tag.content.split("\n").length;
|
||||
return lineCount > 500 && (!largest || lineCount > largest.lineCount)
|
||||
? { path: tag.path, lineCount }
|
||||
: largest;
|
||||
}, null as { path: string; lineCount: number } | null);
|
||||
const refactorTarget = writeTags.reduce(
|
||||
(largest, tag) => {
|
||||
const lineCount = tag.content.split("\n").length;
|
||||
return lineCount > 500 &&
|
||||
(!largest || lineCount > largest.lineCount)
|
||||
? { path: tag.path, lineCount }
|
||||
: largest;
|
||||
},
|
||||
null as { path: string; lineCount: number } | null,
|
||||
);
|
||||
if (refactorTarget) {
|
||||
actions.push({
|
||||
id: "refactor-file",
|
||||
@@ -288,7 +292,7 @@ const getProposalHandler = async (
|
||||
chatId,
|
||||
latestAssistantMessage.id,
|
||||
latestAssistantMessage.content || "",
|
||||
chat.app.path
|
||||
chat.app.path,
|
||||
);
|
||||
|
||||
const totalTokens = messagesTokenCount + codebaseTokenCount;
|
||||
@@ -296,13 +300,13 @@ const getProposalHandler = async (
|
||||
logger.log(
|
||||
`Token usage: ${totalTokens}/${contextWindow} (${
|
||||
(totalTokens / contextWindow) * 100
|
||||
}%)`
|
||||
}%)`,
|
||||
);
|
||||
|
||||
// If we're using more than 80% of the context window, suggest summarizing
|
||||
if (totalTokens > contextWindow * 0.8) {
|
||||
logger.log(
|
||||
`Token usage high (${totalTokens}/${contextWindow}), suggesting summarize action`
|
||||
`Token usage high (${totalTokens}/${contextWindow}), suggesting summarize action`,
|
||||
);
|
||||
actions.push({
|
||||
id: "summarize-in-new-chat",
|
||||
@@ -330,14 +334,14 @@ const getProposalHandler = async (
|
||||
// Handler to approve a proposal (process actions and update message)
|
||||
const approveProposalHandler = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
{ chatId, messageId }: { chatId: number; messageId: number }
|
||||
{ chatId, messageId }: { chatId: number; messageId: number },
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
error?: string;
|
||||
uncommittedFiles?: string[];
|
||||
}> => {
|
||||
logger.log(
|
||||
`IPC: approve-proposal called for chatId: ${chatId}, messageId: ${messageId}`
|
||||
`IPC: approve-proposal called for chatId: ${chatId}, messageId: ${messageId}`,
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -346,7 +350,7 @@ const approveProposalHandler = async (
|
||||
where: and(
|
||||
eq(messages.id, messageId),
|
||||
eq(messages.chatId, chatId),
|
||||
eq(messages.role, "assistant")
|
||||
eq(messages.role, "assistant"),
|
||||
),
|
||||
columns: {
|
||||
content: true,
|
||||
@@ -355,7 +359,7 @@ const approveProposalHandler = async (
|
||||
|
||||
if (!messageToApprove?.content) {
|
||||
logger.error(
|
||||
`Assistant message not found for chatId: ${chatId}, messageId: ${messageId}`
|
||||
`Assistant message not found for chatId: ${chatId}, messageId: ${messageId}`,
|
||||
);
|
||||
return { success: false, error: "Assistant message not found." };
|
||||
}
|
||||
@@ -368,13 +372,13 @@ const approveProposalHandler = async (
|
||||
{
|
||||
chatSummary: chatSummary ?? undefined,
|
||||
messageId,
|
||||
} // Pass summary if found
|
||||
}, // Pass summary if found
|
||||
);
|
||||
|
||||
if (processResult.error) {
|
||||
logger.error(
|
||||
`Error processing actions for message ${messageId}:`,
|
||||
processResult.error
|
||||
processResult.error,
|
||||
);
|
||||
// Optionally: Update message state to 'error' or similar?
|
||||
// For now, just return error to frontend
|
||||
@@ -397,10 +401,10 @@ const approveProposalHandler = async (
|
||||
// Handler to reject a proposal (just update message state)
|
||||
const rejectProposalHandler = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
{ chatId, messageId }: { chatId: number; messageId: number }
|
||||
{ chatId, messageId }: { chatId: number; messageId: number },
|
||||
): Promise<{ success: boolean; error?: string }> => {
|
||||
logger.log(
|
||||
`IPC: reject-proposal called for chatId: ${chatId}, messageId: ${messageId}`
|
||||
`IPC: reject-proposal called for chatId: ${chatId}, messageId: ${messageId}`,
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -409,14 +413,14 @@ const rejectProposalHandler = async (
|
||||
where: and(
|
||||
eq(messages.id, messageId),
|
||||
eq(messages.chatId, chatId),
|
||||
eq(messages.role, "assistant")
|
||||
eq(messages.role, "assistant"),
|
||||
),
|
||||
columns: { id: true },
|
||||
});
|
||||
|
||||
if (!messageToReject) {
|
||||
logger.error(
|
||||
`Assistant message not found for chatId: ${chatId}, messageId: ${messageId}`
|
||||
`Assistant message not found for chatId: ${chatId}, messageId: ${messageId}`,
|
||||
);
|
||||
return { success: false, error: "Assistant message not found." };
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export function registerSettingsHandlers() {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
settings.providerSettings,
|
||||
providerKey
|
||||
providerKey,
|
||||
)
|
||||
) {
|
||||
const providerSetting = settings.providerSettings[providerKey];
|
||||
@@ -35,6 +35,6 @@ export function registerSettingsHandlers() {
|
||||
async (_, settings: Partial<UserSettings>) => {
|
||||
writeSettings(settings);
|
||||
return readSettings();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export function registerSupabaseHandlers() {
|
||||
logger.error("Error setting Supabase project for app:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Unset app project - removes the link between a Dyad app and a Supabase project
|
||||
@@ -58,6 +58,6 @@ export function registerSupabaseHandlers() {
|
||||
logger.error("Error unsetting Supabase project for app:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function streamTestResponse(
|
||||
chatId: number,
|
||||
testResponse: string,
|
||||
abortController: AbortController,
|
||||
updatedChat: any
|
||||
updatedChat: any,
|
||||
): Promise<string> {
|
||||
console.log(`Using canned response for test prompt`);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export function registerTokenCountHandlers() {
|
||||
}
|
||||
|
||||
const systemPromptTokens = estimateTokens(
|
||||
systemPrompt + supabaseContext
|
||||
systemPrompt + supabaseContext,
|
||||
);
|
||||
|
||||
// Extract codebase information if app is associated with the chat
|
||||
@@ -75,7 +75,7 @@ export function registerTokenCountHandlers() {
|
||||
codebaseInfo = await extractCodebase(appPath);
|
||||
codebaseTokens = estimateTokens(codebaseInfo);
|
||||
logger.log(
|
||||
`Extracted codebase information from ${appPath}, tokens: ${codebaseTokens}`
|
||||
`Extracted codebase information from ${appPath}, tokens: ${codebaseTokens}`,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error("Error extracting codebase:", error);
|
||||
@@ -101,6 +101,6 @@ export function registerTokenCountHandlers() {
|
||||
logger.error("Error counting tokens:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export function registerUploadHandlers() {
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Upload failed with status ${response.status}: ${response.statusText}`
|
||||
`Upload failed with status ${response.status}: ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function registerUploadHandlers() {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
logger.debug("Registered upload IPC handlers");
|
||||
|
||||
@@ -95,14 +95,17 @@ export function registerVersionHandlers() {
|
||||
errorMessage: `Failed to get current branch: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
"revert-version",
|
||||
async (
|
||||
_,
|
||||
{ appId, previousVersionId }: { appId: number; previousVersionId: string }
|
||||
{
|
||||
appId,
|
||||
previousVersionId,
|
||||
}: { appId: number; previousVersionId: string },
|
||||
) => {
|
||||
return withLock(appId, async () => {
|
||||
const app = await db.query.apps.findFirst({
|
||||
@@ -198,13 +201,13 @@ export function registerVersionHandlers() {
|
||||
const messagesToDelete = await db.query.messages.findMany({
|
||||
where: and(
|
||||
eq(messages.chatId, chatId),
|
||||
gt(messages.id, messageWithCommit.id)
|
||||
gt(messages.id, messageWithCommit.id),
|
||||
),
|
||||
orderBy: desc(messages.id),
|
||||
});
|
||||
|
||||
logger.log(
|
||||
`Deleting ${messagesToDelete.length} messages after commit ${previousVersionId} from chat ${chatId}`
|
||||
`Deleting ${messagesToDelete.length} messages after commit ${previousVersionId} from chat ${chatId}`,
|
||||
);
|
||||
|
||||
// Delete the messages
|
||||
@@ -214,8 +217,8 @@ export function registerVersionHandlers() {
|
||||
.where(
|
||||
and(
|
||||
eq(messages.chatId, chatId),
|
||||
gt(messages.id, messageWithCommit.id)
|
||||
)
|
||||
gt(messages.id, messageWithCommit.id),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -224,12 +227,12 @@ export function registerVersionHandlers() {
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`Error reverting to version ${previousVersionId} for app ${appId}:`,
|
||||
error
|
||||
error,
|
||||
);
|
||||
throw new Error(`Failed to revert version: ${error.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
@@ -259,11 +262,11 @@ export function registerVersionHandlers() {
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`Error checking out version ${versionId} for app ${appId}:`,
|
||||
error
|
||||
error,
|
||||
);
|
||||
throw new Error(`Failed to checkout version: ${error.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user