Support rename/delete edge function & standardize output
This commit is contained in:
@@ -330,12 +330,20 @@ function ChatInputActions({
|
|||||||
}: ChatInputActionsProps) {
|
}: ChatInputActionsProps) {
|
||||||
const [autoApprove, setAutoApprove] = useState(false);
|
const [autoApprove, setAutoApprove] = useState(false);
|
||||||
const [isDetailsVisible, setIsDetailsVisible] = useState(false);
|
const [isDetailsVisible, setIsDetailsVisible] = useState(false);
|
||||||
|
|
||||||
if (proposal.type === "tip-proposal") {
|
if (proposal.type === "tip-proposal") {
|
||||||
return <div>Tip proposal</div>;
|
return <div>Tip proposal</div>;
|
||||||
}
|
}
|
||||||
if (proposal.type === "action-proposal") {
|
if (proposal.type === "action-proposal") {
|
||||||
return <ActionProposalActions proposal={proposal}></ActionProposalActions>;
|
return <ActionProposalActions proposal={proposal}></ActionProposalActions>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Split files into server functions and other files - only for CodeProposal
|
||||||
|
const serverFunctions =
|
||||||
|
proposal.filesChanged?.filter((f: FileChange) => f.isServerFunction) ?? [];
|
||||||
|
const otherFilesChanged =
|
||||||
|
proposal.filesChanged?.filter((f: FileChange) => !f.isServerFunction) ?? [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-b border-border">
|
<div className="border-b border-border">
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
@@ -462,11 +470,30 @@ function ChatInputActions({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{proposal.filesChanged?.length > 0 && (
|
{serverFunctions.length > 0 && (
|
||||||
|
<div className="mb-3">
|
||||||
|
<h4 className="font-semibold mb-1">Server Functions Changed</h4>
|
||||||
|
<ul className="space-y-1">
|
||||||
|
{serverFunctions.map((file: FileChange, index: number) => (
|
||||||
|
<li key={index} className="flex items-center space-x-2">
|
||||||
|
{getIconForFileChange(file)}
|
||||||
|
<span title={file.path} className="truncate cursor-default">
|
||||||
|
{file.name}
|
||||||
|
</span>
|
||||||
|
<span className="text-muted-foreground text-xs truncate">
|
||||||
|
- {file.summary}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{otherFilesChanged.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold mb-1">Files Changed</h4>
|
<h4 className="font-semibold mb-1">Files Changed</h4>
|
||||||
<ul className="space-y-1">
|
<ul className="space-y-1">
|
||||||
{proposal.filesChanged.map((file, index) => (
|
{otherFilesChanged.map((file: FileChange, index: number) => (
|
||||||
<li key={index} className="flex items-center space-x-2">
|
<li key={index} className="flex items-center space-x-2">
|
||||||
{getIconForFileChange(file)}
|
{getIconForFileChange(file)}
|
||||||
<span title={file.path} className="truncate cursor-default">
|
<span title={file.path} className="truncate cursor-default">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { CodeHighlight } from "./CodeHighlight";
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { isStreamingAtom } from "@/atoms/chatAtoms";
|
import { isStreamingAtom } from "@/atoms/chatAtoms";
|
||||||
import { CustomTagState } from "./stateTypes";
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
import { DyadOutput } from "./DyadOutput";
|
||||||
|
|
||||||
interface DyadMarkdownParserProps {
|
interface DyadMarkdownParserProps {
|
||||||
content: string;
|
content: string;
|
||||||
@@ -77,6 +78,7 @@ function preprocessUnclosedTags(content: string): {
|
|||||||
"dyad-add-dependency",
|
"dyad-add-dependency",
|
||||||
"dyad-execute-sql",
|
"dyad-execute-sql",
|
||||||
"dyad-add-integration",
|
"dyad-add-integration",
|
||||||
|
"dyad-output",
|
||||||
];
|
];
|
||||||
|
|
||||||
let processedContent = content;
|
let processedContent = content;
|
||||||
@@ -137,6 +139,7 @@ function parseCustomTags(content: string): ContentPiece[] {
|
|||||||
"dyad-add-dependency",
|
"dyad-add-dependency",
|
||||||
"dyad-execute-sql",
|
"dyad-execute-sql",
|
||||||
"dyad-add-integration",
|
"dyad-add-integration",
|
||||||
|
"dyad-output",
|
||||||
];
|
];
|
||||||
|
|
||||||
const tagPattern = new RegExp(
|
const tagPattern = new RegExp(
|
||||||
@@ -303,6 +306,16 @@ function renderCustomTag(
|
|||||||
</DyadAddIntegration>
|
</DyadAddIntegration>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case "dyad-output":
|
||||||
|
return (
|
||||||
|
<DyadOutput
|
||||||
|
type={attributes.type as "warning" | "error"}
|
||||||
|
message={attributes.message}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</DyadOutput>
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
77
src/components/chat/DyadOutput.tsx
Normal file
77
src/components/chat/DyadOutput.tsx
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
ChevronsDownUp,
|
||||||
|
ChevronsUpDown,
|
||||||
|
AlertTriangle,
|
||||||
|
XCircle,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
|
interface DyadOutputProps {
|
||||||
|
type: "error" | "warning";
|
||||||
|
message?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DyadOutput: React.FC<DyadOutputProps> = ({
|
||||||
|
type,
|
||||||
|
message,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||||
|
|
||||||
|
// If the type is not warning, it is an error (in case LLM gives a weird "type")
|
||||||
|
const isError = type !== "warning";
|
||||||
|
const borderColor = isError ? "border-red-500" : "border-amber-500";
|
||||||
|
const iconColor = isError ? "text-red-500" : "text-amber-500";
|
||||||
|
const icon = isError ? (
|
||||||
|
<XCircle size={16} className={iconColor} />
|
||||||
|
) : (
|
||||||
|
<AlertTriangle size={16} className={iconColor} />
|
||||||
|
);
|
||||||
|
const label = isError ? "Error" : "Warning";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${borderColor}`}
|
||||||
|
onClick={() => setIsContentVisible(!isContentVisible)}
|
||||||
|
>
|
||||||
|
{/* Top-left label badge */}
|
||||||
|
<div
|
||||||
|
className={`absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold ${iconColor} bg-white dark:bg-gray-900`}
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
<span>{label}</span>
|
||||||
|
</div>
|
||||||
|
{/* Main content, padded to avoid label */}
|
||||||
|
<div className="flex items-center justify-between pl-20">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{message && (
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
{message.slice(0, isContentVisible ? undefined : 80) +
|
||||||
|
(!isContentVisible ? "..." : "")}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
{isContentVisible ? (
|
||||||
|
<ChevronsDownUp
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronsUpDown
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isContentVisible && children && (
|
||||||
|
<div className="text-sm mt-2 text-gray-800 dark:text-gray-200 pl-20">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
import { SupabaseManagementAPI } from "supabase-management-js";
|
|
||||||
import { readSettings, writeSettings } from "../../main/settings";
|
|
||||||
import log from "electron-log";
|
import log from "electron-log";
|
||||||
import { db } from "../../db";
|
import { db } from "../../db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|||||||
@@ -17,55 +17,31 @@ export async function executeAddDependency({
|
|||||||
appPath: string;
|
appPath: string;
|
||||||
}) {
|
}) {
|
||||||
const packageStr = packages.join(" ");
|
const packageStr = packages.join(" ");
|
||||||
try {
|
|
||||||
const { stdout, stderr } = await execPromise(
|
|
||||||
`(pnpm add ${packageStr}) || (npm install ${packageStr})`,
|
|
||||||
{
|
|
||||||
cwd: appPath,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const installResults = stdout + (stderr ? `\n${stderr}` : "");
|
|
||||||
|
|
||||||
// Update the message content with the installation results
|
const { stdout, stderr } = await execPromise(
|
||||||
const updatedContent = message.content.replace(
|
`(pnpm add ${packageStr}) || (npm install ${packageStr})`,
|
||||||
new RegExp(
|
{
|
||||||
`<dyad-add-dependency packages="${packages.join(
|
cwd: appPath,
|
||||||
" "
|
}
|
||||||
)}">[^<]*</dyad-add-dependency>`,
|
);
|
||||||
"g"
|
const installResults = stdout + (stderr ? `\n${stderr}` : "");
|
||||||
),
|
|
||||||
|
// Update the message content with the installation results
|
||||||
|
const updatedContent = message.content.replace(
|
||||||
|
new RegExp(
|
||||||
`<dyad-add-dependency packages="${packages.join(
|
`<dyad-add-dependency packages="${packages.join(
|
||||||
" "
|
" "
|
||||||
)}">${installResults}</dyad-add-dependency>`
|
)}">[^<]*</dyad-add-dependency>`,
|
||||||
);
|
"g"
|
||||||
|
),
|
||||||
|
`<dyad-add-dependency packages="${packages.join(
|
||||||
|
" "
|
||||||
|
)}">${installResults}</dyad-add-dependency>`
|
||||||
|
);
|
||||||
|
|
||||||
// Save the updated message back to the database
|
// Save the updated message back to the database
|
||||||
await db
|
await db
|
||||||
.update(messages)
|
.update(messages)
|
||||||
.set({ content: updatedContent })
|
.set({ content: updatedContent })
|
||||||
.where(eq(messages.id, message.id));
|
.where(eq(messages.id, message.id));
|
||||||
|
|
||||||
// Return undefined implicitly
|
|
||||||
} catch (error: any) {
|
|
||||||
// Update the message with the error
|
|
||||||
const updatedContent = message.content.replace(
|
|
||||||
new RegExp(
|
|
||||||
`<dyad-add-dependency packages="${packages.join(
|
|
||||||
" "
|
|
||||||
)}">[^<]*</dyad-add-dependency>`,
|
|
||||||
"g"
|
|
||||||
),
|
|
||||||
`<dyad-add-dependency packages="${packages.join(" ")}"><dyad-error>${
|
|
||||||
error.message
|
|
||||||
}</dyad-error></dyad-add-dependency>`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Save the updated message back to the database
|
|
||||||
await db
|
|
||||||
.update(messages)
|
|
||||||
.set({ content: updatedContent })
|
|
||||||
.where(eq(messages.id, message.id));
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ import { getGitAuthor } from "../utils/git_author";
|
|||||||
import log from "electron-log";
|
import log from "electron-log";
|
||||||
import { executeAddDependency } from "./executeAddDependency";
|
import { executeAddDependency } from "./executeAddDependency";
|
||||||
import {
|
import {
|
||||||
|
deleteSupabaseFunction,
|
||||||
deploySupabaseFunctions,
|
deploySupabaseFunctions,
|
||||||
executeSupabaseSql,
|
executeSupabaseSql,
|
||||||
} from "../../supabase_admin/supabase_management_client";
|
} from "../../supabase_admin/supabase_management_client";
|
||||||
import { isServerFunction } from "../../supabase_admin/supabase_utils";
|
import { isServerFunction } from "../../supabase_admin/supabase_utils";
|
||||||
|
|
||||||
|
const readFile = fs.promises.readFile;
|
||||||
const logger = log.scope("response_processor");
|
const logger = log.scope("response_processor");
|
||||||
|
|
||||||
export function getDyadWriteTags(fullResponse: string): {
|
export function getDyadWriteTags(fullResponse: string): {
|
||||||
@@ -131,6 +133,23 @@ export function getDyadExecuteSqlTags(fullResponse: string): string[] {
|
|||||||
return queries;
|
return queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Output {
|
||||||
|
message: string;
|
||||||
|
error: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFunctionNameFromPath(input: string): string {
|
||||||
|
return path.basename(path.extname(input) ? path.dirname(input) : input);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readFileFromFunctionPath(input: string): Promise<string> {
|
||||||
|
// Sometimes, the path given is a directory, sometimes it's the file itself.
|
||||||
|
if (path.extname(input) === "") {
|
||||||
|
return readFile(path.join(input, "index.ts"), "utf8");
|
||||||
|
}
|
||||||
|
return readFile(input, "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
export async function processFullResponseActions(
|
export async function processFullResponseActions(
|
||||||
fullResponse: string,
|
fullResponse: string,
|
||||||
chatId: number,
|
chatId: number,
|
||||||
@@ -158,6 +177,9 @@ export async function processFullResponseActions(
|
|||||||
const deletedFiles: string[] = [];
|
const deletedFiles: string[] = [];
|
||||||
let hasChanges = false;
|
let hasChanges = false;
|
||||||
|
|
||||||
|
const warnings: Output[] = [];
|
||||||
|
const errors: Output[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Extract all tags
|
// Extract all tags
|
||||||
const dyadWriteTags = getDyadWriteTags(fullResponse);
|
const dyadWriteTags = getDyadWriteTags(fullResponse);
|
||||||
@@ -184,21 +206,37 @@ export async function processFullResponseActions(
|
|||||||
// Handle SQL execution tags
|
// Handle SQL execution tags
|
||||||
if (dyadExecuteSqlQueries.length > 0) {
|
if (dyadExecuteSqlQueries.length > 0) {
|
||||||
for (const query of dyadExecuteSqlQueries) {
|
for (const query of dyadExecuteSqlQueries) {
|
||||||
const result = await executeSupabaseSql({
|
try {
|
||||||
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
const result = await executeSupabaseSql({
|
||||||
query,
|
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
||||||
});
|
query,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
errors.push({
|
||||||
|
message: `Failed to execute SQL query: ${query}`,
|
||||||
|
error: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logger.log(`Executed ${dyadExecuteSqlQueries.length} SQL queries`);
|
logger.log(`Executed ${dyadExecuteSqlQueries.length} SQL queries`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle add dependency tags
|
// TODO: Handle add dependency tags
|
||||||
if (dyadAddDependencyPackages.length > 0) {
|
if (dyadAddDependencyPackages.length > 0) {
|
||||||
await executeAddDependency({
|
try {
|
||||||
packages: dyadAddDependencyPackages,
|
await executeAddDependency({
|
||||||
message: message,
|
packages: dyadAddDependencyPackages,
|
||||||
appPath,
|
message: message,
|
||||||
});
|
appPath,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
errors.push({
|
||||||
|
message: `Failed to add dependencies: ${dyadAddDependencyPackages.join(
|
||||||
|
", "
|
||||||
|
)}`,
|
||||||
|
error: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
writtenFiles.push("package.json");
|
writtenFiles.push("package.json");
|
||||||
const pnpmFilename = "pnpm-lock.yaml";
|
const pnpmFilename = "pnpm-lock.yaml";
|
||||||
if (fs.existsSync(path.join(appPath, pnpmFilename))) {
|
if (fs.existsSync(path.join(appPath, pnpmFilename))) {
|
||||||
@@ -225,11 +263,18 @@ export async function processFullResponseActions(
|
|||||||
logger.log(`Successfully wrote file: ${fullFilePath}`);
|
logger.log(`Successfully wrote file: ${fullFilePath}`);
|
||||||
writtenFiles.push(filePath);
|
writtenFiles.push(filePath);
|
||||||
if (isServerFunction(filePath)) {
|
if (isServerFunction(filePath)) {
|
||||||
await deploySupabaseFunctions({
|
try {
|
||||||
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
await deploySupabaseFunctions({
|
||||||
functionName: path.basename(path.dirname(filePath)),
|
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
||||||
content: content,
|
functionName: path.basename(path.dirname(filePath)),
|
||||||
});
|
content: content,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
errors.push({
|
||||||
|
message: `Failed to deploy Supabase function: ${filePath}`,
|
||||||
|
error: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,6 +312,33 @@ export async function processFullResponseActions(
|
|||||||
} else {
|
} else {
|
||||||
logger.warn(`Source file for rename does not exist: ${fromPath}`);
|
logger.warn(`Source file for rename does not exist: ${fromPath}`);
|
||||||
}
|
}
|
||||||
|
if (isServerFunction(tag.from)) {
|
||||||
|
try {
|
||||||
|
await deleteSupabaseFunction({
|
||||||
|
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
||||||
|
functionName: getFunctionNameFromPath(tag.from),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
warnings.push({
|
||||||
|
message: `Failed to delete Supabase function: ${tag.from} as part of renaming ${tag.from} to ${tag.to}`,
|
||||||
|
error: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isServerFunction(tag.to)) {
|
||||||
|
try {
|
||||||
|
await deploySupabaseFunctions({
|
||||||
|
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
||||||
|
functionName: getFunctionNameFromPath(tag.to),
|
||||||
|
content: await readFileFromFunctionPath(toPath),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
errors.push({
|
||||||
|
message: `Failed to deploy Supabase function: ${tag.to} as part of renaming ${tag.from} to ${tag.to}`,
|
||||||
|
error: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process all file deletions
|
// Process all file deletions
|
||||||
@@ -275,7 +347,11 @@ export async function processFullResponseActions(
|
|||||||
|
|
||||||
// Delete the file if it exists
|
// Delete the file if it exists
|
||||||
if (fs.existsSync(fullFilePath)) {
|
if (fs.existsSync(fullFilePath)) {
|
||||||
fs.unlinkSync(fullFilePath);
|
if (fs.lstatSync(fullFilePath).isDirectory()) {
|
||||||
|
fs.rmdirSync(fullFilePath, { recursive: true });
|
||||||
|
} else {
|
||||||
|
fs.unlinkSync(fullFilePath);
|
||||||
|
}
|
||||||
logger.log(`Successfully deleted file: ${fullFilePath}`);
|
logger.log(`Successfully deleted file: ${fullFilePath}`);
|
||||||
deletedFiles.push(filePath);
|
deletedFiles.push(filePath);
|
||||||
|
|
||||||
@@ -293,6 +369,19 @@ export async function processFullResponseActions(
|
|||||||
} else {
|
} else {
|
||||||
logger.warn(`File to delete does not exist: ${fullFilePath}`);
|
logger.warn(`File to delete does not exist: ${fullFilePath}`);
|
||||||
}
|
}
|
||||||
|
if (isServerFunction(filePath)) {
|
||||||
|
try {
|
||||||
|
await deleteSupabaseFunction({
|
||||||
|
supabaseProjectId: chatWithApp.app.supabaseProjectId!,
|
||||||
|
functionName: getFunctionNameFromPath(filePath),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
errors.push({
|
||||||
|
message: `Failed to delete Supabase function: ${filePath}`,
|
||||||
|
error: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have any file changes, commit them all at once
|
// If we have any file changes, commit them all at once
|
||||||
@@ -346,10 +435,32 @@ export async function processFullResponseActions(
|
|||||||
approvalState: "approved",
|
approvalState: "approved",
|
||||||
})
|
})
|
||||||
.where(eq(messages.id, messageId));
|
.where(eq(messages.id, messageId));
|
||||||
|
|
||||||
return { updatedFiles: hasChanges };
|
return { updatedFiles: hasChanges };
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
logger.error("Error processing files:", error);
|
logger.error("Error processing files:", error);
|
||||||
return { error: (error as any).toString() };
|
return { error: (error as any).toString() };
|
||||||
|
} finally {
|
||||||
|
const appendedContent = `
|
||||||
|
${warnings
|
||||||
|
.map(
|
||||||
|
(warning) =>
|
||||||
|
`<dyad-output type="warning" message="${warning.message}">${warning.error}</dyad-output>`
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
|
${errors
|
||||||
|
.map(
|
||||||
|
(error) =>
|
||||||
|
`<dyad-output type="error" message="${error.message}">${error.error}</dyad-output>`
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
|
`;
|
||||||
|
if (appendedContent.length > 0) {
|
||||||
|
await db
|
||||||
|
.update(messages)
|
||||||
|
.set({
|
||||||
|
content: fullResponse + "\n\n" + appendedContent,
|
||||||
|
})
|
||||||
|
.where(eq(messages.id, messageId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import {
|
|||||||
SupabaseManagementAPI,
|
SupabaseManagementAPI,
|
||||||
SupabaseManagementAPIError,
|
SupabaseManagementAPIError,
|
||||||
} from "@dyad-sh/supabase-management-js";
|
} from "@dyad-sh/supabase-management-js";
|
||||||
|
import log from "electron-log";
|
||||||
|
|
||||||
|
const logger = log.scope("supabase_management_client");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the Supabase access token is expired or about to expire
|
* Checks if the Supabase access token is expired or about to expire
|
||||||
@@ -137,6 +140,23 @@ export async function executeSupabaseSql({
|
|||||||
return JSON.stringify(result);
|
return JSON.stringify(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteSupabaseFunction({
|
||||||
|
supabaseProjectId,
|
||||||
|
functionName,
|
||||||
|
}: {
|
||||||
|
supabaseProjectId: string;
|
||||||
|
functionName: string;
|
||||||
|
}): Promise<void> {
|
||||||
|
logger.info(
|
||||||
|
`Deleting Supabase function: ${functionName} from project: ${supabaseProjectId}`
|
||||||
|
);
|
||||||
|
const supabase = await getSupabaseClient();
|
||||||
|
await supabase.deleteFunction(supabaseProjectId, functionName);
|
||||||
|
logger.info(
|
||||||
|
`Deleted Supabase function: ${functionName} from project: ${supabaseProjectId}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export async function deploySupabaseFunctions({
|
export async function deploySupabaseFunctions({
|
||||||
supabaseProjectId,
|
supabaseProjectId,
|
||||||
functionName,
|
functionName,
|
||||||
@@ -146,6 +166,9 @@ export async function deploySupabaseFunctions({
|
|||||||
functionName: string;
|
functionName: string;
|
||||||
content: string;
|
content: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
|
logger.info(
|
||||||
|
`Deploying Supabase function: ${functionName} to project: ${supabaseProjectId}`
|
||||||
|
);
|
||||||
const supabase = await getSupabaseClient();
|
const supabase = await getSupabaseClient();
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append(
|
formData.append(
|
||||||
@@ -172,6 +195,9 @@ export async function deploySupabaseFunctions({
|
|||||||
throw await createResponseError(response, "create function");
|
throw await createResponseError(response, "create function");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Deployed Supabase function: ${functionName} to project: ${supabaseProjectId}`
|
||||||
|
);
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user