Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
55c0190f
提交
55c0190f
authored
4月 23, 2025
作者:
Will Chen
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Support rename/delete edge function & standardize output
上级
6e1935bb
隐藏空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
295 行增加
和
67 行删除
+295
-67
ChatInput.tsx
src/components/chat/ChatInput.tsx
+29
-2
DyadMarkdownParser.tsx
src/components/chat/DyadMarkdownParser.tsx
+13
-0
DyadOutput.tsx
src/components/chat/DyadOutput.tsx
+77
-0
supabase_handlers.ts
src/ipc/handlers/supabase_handlers.ts
+0
-2
executeAddDependency.ts
src/ipc/processors/executeAddDependency.ts
+23
-47
response_processor.ts
src/ipc/processors/response_processor.ts
+127
-16
supabase_management_client.ts
src/supabase_admin/supabase_management_client.ts
+26
-0
没有找到文件。
src/components/chat/ChatInput.tsx
浏览文件 @
55c0190f
...
@@ -330,12 +330,20 @@ function ChatInputActions({
...
@@ -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({
...
@@ -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"
>
...
...
src/components/chat/DyadMarkdownParser.tsx
浏览文件 @
55c0190f
...
@@ -11,6 +11,7 @@ import { CodeHighlight } from "./CodeHighlight";
...
@@ -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): {
...
@@ -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[] {
...
@@ -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(
...
@@ -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
;
}
}
...
...
src/components/chat/DyadOutput.tsx
0 → 100644
浏览文件 @
55c0190f
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
>
);
};
src/ipc/handlers/supabase_handlers.ts
浏览文件 @
55c0190f
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"
;
...
...
src/ipc/processors/executeAddDependency.ts
浏览文件 @
55c0190f
...
@@ -17,55 +17,31 @@ export async function executeAddDependency({
...
@@ -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"
),
// Save the updated message back to the database
`<dyad-add-dependency packages="
${
packages
.
join
(
await
db
" "
.
update
(
messages
)
)}
">
${
installResults
}
</dyad-add-dependency>`
.
set
({
content
:
updatedContent
})
);
.
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
;
// Save the updated message back to the database
}
await
db
.
update
(
messages
)
.
set
({
content
:
updatedContent
})
.
where
(
eq
(
messages
.
id
,
message
.
id
));
}
}
src/ipc/processors/response_processor.ts
浏览文件 @
55c0190f
...
@@ -10,11 +10,13 @@ import { getGitAuthor } from "../utils/git_author";
...
@@ -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[] {
...
@@ -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(
...
@@ -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(
...
@@ -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(
...
@@ -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(
...
@@ -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(
...
@@ -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(
...
@@ -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(
...
@@ -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
));
}
}
}
}
}
src/supabase_admin/supabase_management_client.ts
浏览文件 @
55c0190f
...
@@ -4,6 +4,9 @@ import {
...
@@ -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({
...
@@ -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({
...
@@ -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({
...
@@ -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
();
}
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论