Supabase support: client, auth & SQL
This commit is contained in:
@@ -37,7 +37,7 @@ import { getGitAuthor } from "../utils/git_author";
|
||||
import killPort from "kill-port";
|
||||
import util from "util";
|
||||
import log from "electron-log";
|
||||
import { getSupabaseProjectName } from "../utils/supabase_management_client";
|
||||
import { getSupabaseProjectName } from "../../supabase_admin/supabase_management_client";
|
||||
|
||||
const logger = log.scope("app_handlers");
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@ import { db } from "../../db";
|
||||
import { chats, messages } from "../../db/schema";
|
||||
import { and, eq, isNull } from "drizzle-orm";
|
||||
import { SYSTEM_PROMPT } from "../../prompts/system_prompt";
|
||||
import {
|
||||
SUPABASE_AVAILABLE_SYSTEM_PROMPT,
|
||||
SUPABASE_NOT_AVAILABLE_SYSTEM_PROMPT,
|
||||
} from "../../prompts/supabase_prompt";
|
||||
import { getDyadAppPath } from "../../paths/paths";
|
||||
import { readSettings } from "../../main/settings";
|
||||
import type { ChatResponseEnd, ChatStreamParams } from "../ipc_types";
|
||||
@@ -13,6 +17,10 @@ import { streamTestResponse } from "./testing_chat_handlers";
|
||||
import { getTestResponse } from "./testing_chat_handlers";
|
||||
import { getModelClient } from "../utils/get_model_client";
|
||||
import log from "electron-log";
|
||||
import {
|
||||
getSupabaseContext,
|
||||
getSupabaseClientCode,
|
||||
} from "../../supabase_admin/supabase_context";
|
||||
|
||||
const logger = log.scope("chat_stream_handlers");
|
||||
|
||||
@@ -158,12 +166,23 @@ export function registerChatStreamHandlers() {
|
||||
) {
|
||||
messageHistory.pop();
|
||||
}
|
||||
|
||||
let systemPrompt = SYSTEM_PROMPT;
|
||||
if (updatedChat.app?.supabaseProjectId) {
|
||||
systemPrompt +=
|
||||
"\n\n" +
|
||||
SUPABASE_AVAILABLE_SYSTEM_PROMPT +
|
||||
"\n\n" +
|
||||
(await getSupabaseContext({
|
||||
supabaseProjectId: updatedChat.app.supabaseProjectId,
|
||||
}));
|
||||
} else {
|
||||
systemPrompt += "\n\n" + SUPABASE_NOT_AVAILABLE_SYSTEM_PROMPT;
|
||||
}
|
||||
const { textStream } = streamText({
|
||||
maxTokens: 8_000,
|
||||
temperature: 0,
|
||||
model: modelClient,
|
||||
system: SYSTEM_PROMPT,
|
||||
system: systemPrompt,
|
||||
messages: [
|
||||
...messageHistory,
|
||||
// Add the enhanced user prompt
|
||||
@@ -190,6 +209,18 @@ export function registerChatStreamHandlers() {
|
||||
try {
|
||||
for await (const textPart of textStream) {
|
||||
fullResponse += textPart;
|
||||
if (
|
||||
fullResponse.includes("$$SUPABASE_CLIENT_CODE$$") &&
|
||||
updatedChat.app?.supabaseProjectId
|
||||
) {
|
||||
const supabaseClientCode = await getSupabaseClientCode({
|
||||
projectId: updatedChat.app?.supabaseProjectId,
|
||||
});
|
||||
fullResponse = fullResponse.replace(
|
||||
"$$SUPABASE_CLIENT_CODE$$",
|
||||
supabaseClientCode
|
||||
);
|
||||
}
|
||||
// Store the current partial response
|
||||
partialResponses.set(req.chatId, fullResponse);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
getDyadAddDependencyTags,
|
||||
getDyadChatSummaryTag,
|
||||
getDyadDeleteTags,
|
||||
getDyadExecuteSqlTags,
|
||||
getDyadRenameTags,
|
||||
getDyadWriteTags,
|
||||
processFullResponseActions,
|
||||
@@ -76,7 +77,7 @@ const getProposalHandler = async (
|
||||
const proposalWriteFiles = getDyadWriteTags(messageContent);
|
||||
const proposalRenameFiles = getDyadRenameTags(messageContent);
|
||||
const proposalDeleteFiles = getDyadDeleteTags(messageContent);
|
||||
|
||||
const proposalExecuteSqlQueries = getDyadExecuteSqlTags(messageContent);
|
||||
const packagesAdded = getDyadAddDependencyTags(messageContent);
|
||||
|
||||
const filesChanged = [
|
||||
@@ -108,6 +109,7 @@ const getProposalHandler = async (
|
||||
securityRisks: [], // Keep empty
|
||||
filesChanged,
|
||||
packagesAdded,
|
||||
sqlQueries: proposalExecuteSqlQueries,
|
||||
};
|
||||
logger.log(
|
||||
"Generated code proposal. title=",
|
||||
|
||||
@@ -5,7 +5,7 @@ import log from "electron-log";
|
||||
import { db } from "../../db";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { apps } from "../../db/schema";
|
||||
import { getSupabaseClient } from "../utils/supabase_management_client";
|
||||
import { getSupabaseClient } from "../../supabase_admin/supabase_management_client";
|
||||
|
||||
const logger = log.scope("supabase_handlers");
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getGithubUser } from "../handlers/github_handlers";
|
||||
import { getGitAuthor } from "../utils/git_author";
|
||||
import log from "electron-log";
|
||||
import { executeAddDependency } from "./executeAddDependency";
|
||||
import { executeSupabaseSql } from "../../supabase_admin/supabase_management_client";
|
||||
|
||||
const logger = log.scope("response_processor");
|
||||
|
||||
@@ -101,6 +102,31 @@ export function getDyadChatSummaryTag(fullResponse: string): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getDyadExecuteSqlTags(fullResponse: string): string[] {
|
||||
const dyadExecuteSqlRegex =
|
||||
/<dyad-execute-sql>([\s\S]*?)<\/dyad-execute-sql>/g;
|
||||
let match;
|
||||
const queries: string[] = [];
|
||||
|
||||
while ((match = dyadExecuteSqlRegex.exec(fullResponse)) !== null) {
|
||||
let content = match[1].trim();
|
||||
|
||||
// Handle markdown code blocks if present
|
||||
const contentLines = content.split("\n");
|
||||
if (contentLines[0]?.startsWith("```")) {
|
||||
contentLines.shift();
|
||||
}
|
||||
if (contentLines[contentLines.length - 1]?.startsWith("```")) {
|
||||
contentLines.pop();
|
||||
}
|
||||
content = contentLines.join("\n");
|
||||
|
||||
queries.push(content);
|
||||
}
|
||||
|
||||
return queries;
|
||||
}
|
||||
|
||||
export async function processFullResponseActions(
|
||||
fullResponse: string,
|
||||
chatId: number,
|
||||
@@ -134,6 +160,9 @@ export async function processFullResponseActions(
|
||||
const dyadRenameTags = getDyadRenameTags(fullResponse);
|
||||
const dyadDeletePaths = getDyadDeleteTags(fullResponse);
|
||||
const dyadAddDependencyPackages = getDyadAddDependencyTags(fullResponse);
|
||||
const dyadExecuteSqlQueries = chatWithApp.app.supabaseProjectId
|
||||
? getDyadExecuteSqlTags(fullResponse)
|
||||
: [];
|
||||
|
||||
const message = await db.query.messages.findFirst({
|
||||
where: and(
|
||||
@@ -148,6 +177,17 @@ export async function processFullResponseActions(
|
||||
return {};
|
||||
}
|
||||
|
||||
// Handle SQL execution tags
|
||||
if (dyadExecuteSqlQueries.length > 0) {
|
||||
for (const query of dyadExecuteSqlQueries) {
|
||||
const result = await executeSupabaseSql({
|
||||
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
||||
query,
|
||||
});
|
||||
}
|
||||
logger.log(`Executed ${dyadExecuteSqlQueries.length} SQL queries`);
|
||||
}
|
||||
|
||||
// TODO: Handle add dependency tags
|
||||
if (dyadAddDependencyPackages.length > 0) {
|
||||
await executeAddDependency({
|
||||
@@ -249,7 +289,8 @@ export async function processFullResponseActions(
|
||||
writtenFiles.length > 0 ||
|
||||
renamedFiles.length > 0 ||
|
||||
deletedFiles.length > 0 ||
|
||||
dyadAddDependencyPackages.length > 0;
|
||||
dyadAddDependencyPackages.length > 0 ||
|
||||
dyadExecuteSqlQueries.length > 0;
|
||||
if (hasChanges) {
|
||||
// Stage all written files
|
||||
for (const file of writtenFiles) {
|
||||
@@ -272,6 +313,8 @@ export async function processFullResponseActions(
|
||||
changes.push(
|
||||
`added ${dyadAddDependencyPackages.join(", ")} package(s)`
|
||||
);
|
||||
if (dyadExecuteSqlQueries.length > 0)
|
||||
changes.push(`executed ${dyadExecuteSqlQueries.length} SQL queries`);
|
||||
|
||||
// Use chat summary, if provided, or default for commit message
|
||||
await git.commit({
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { readSettings } from "../../main/settings";
|
||||
import { SupabaseManagementAPI } from "supabase-management-js";
|
||||
|
||||
// Function to get the Supabase Management API client
|
||||
export async function getSupabaseClient(): Promise<SupabaseManagementAPI> {
|
||||
const settings = readSettings();
|
||||
// Check if Supabase token exists in settings
|
||||
const supabaseAccessToken = settings.supabase?.accessToken?.value;
|
||||
|
||||
if (!supabaseAccessToken) {
|
||||
throw new Error(
|
||||
"Supabase access token not found. Please authenticate first."
|
||||
);
|
||||
}
|
||||
|
||||
return new SupabaseManagementAPI({
|
||||
accessToken: supabaseAccessToken,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getSupabaseProjectName(
|
||||
projectId: string
|
||||
): Promise<string> {
|
||||
const supabase = await getSupabaseClient();
|
||||
const projects = await supabase.getProjects();
|
||||
const project = projects?.find((p) => p.id === projectId);
|
||||
return project?.name || `<project not found for: ${projectId}>`;
|
||||
}
|
||||
Reference in New Issue
Block a user