Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
d6d6918d
Unverified
提交
d6d6918d
authored
6月 17, 2025
作者:
Will Chen
提交者:
GitHub
6月 17, 2025
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Safe send (#421)
上级
30b5c0d0
显示空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
50 行增加
和
13 行删除
+50
-13
app_handlers.ts
src/ipc/handlers/app_handlers.ts
+4
-3
chat_stream_handlers.ts
src/ipc/handlers/chat_stream_handlers.ts
+12
-8
supabase_handlers.ts
src/ipc/handlers/supabase_handlers.ts
+2
-1
testing_chat_handlers.ts
src/ipc/handlers/testing_chat_handlers.ts
+3
-1
safe_sender.ts
src/ipc/utils/safe_sender.ts
+29
-0
没有找到文件。
src/ipc/handlers/app_handlers.ts
浏览文件 @
d6d6918d
...
@@ -39,6 +39,7 @@ import { startProxy } from "../utils/start_proxy_server";
...
@@ -39,6 +39,7 @@ import { startProxy } from "../utils/start_proxy_server";
import
{
Worker
}
from
"worker_threads"
;
import
{
Worker
}
from
"worker_threads"
;
import
{
createFromTemplate
}
from
"./createFromTemplate"
;
import
{
createFromTemplate
}
from
"./createFromTemplate"
;
import
{
gitCommit
}
from
"../utils/git_utils"
;
import
{
gitCommit
}
from
"../utils/git_utils"
;
import
{
safeSend
}
from
"../utils/safe_sender"
;
async
function
copyDir
(
async
function
copyDir
(
source
:
string
,
source
:
string
,
...
@@ -126,7 +127,7 @@ async function executeAppLocalNode({
...
@@ -126,7 +127,7 @@ async function executeAppLocalNode({
const
message
=
util
.
stripVTControlCharacters
(
data
.
toString
());
const
message
=
util
.
stripVTControlCharacters
(
data
.
toString
());
logger
.
debug
(
`App
${
appId
}
(PID:
${
process
.
pid
}
) stdout:
${
message
}
`
);
logger
.
debug
(
`App
${
appId
}
(PID:
${
process
.
pid
}
) stdout:
${
message
}
`
);
event
.
sender
.
send
(
"app:output"
,
{
safeSend
(
event
.
sender
,
"app:output"
,
{
type
:
"stdout"
,
type
:
"stdout"
,
message
,
message
,
appId
,
appId
,
...
@@ -135,7 +136,7 @@ async function executeAppLocalNode({
...
@@ -135,7 +136,7 @@ async function executeAppLocalNode({
if
(
urlMatch
)
{
if
(
urlMatch
)
{
proxyWorker
=
await
startProxy
(
urlMatch
[
1
],
{
proxyWorker
=
await
startProxy
(
urlMatch
[
1
],
{
onStarted
:
(
proxyUrl
)
=>
{
onStarted
:
(
proxyUrl
)
=>
{
event
.
sender
.
send
(
"app:output"
,
{
safeSend
(
event
.
sender
,
"app:output"
,
{
type
:
"stdout"
,
type
:
"stdout"
,
message
:
`[dyad-proxy-server]started=[
${
proxyUrl
}
] original=[
${
urlMatch
[
1
]}
]`
,
message
:
`[dyad-proxy-server]started=[
${
proxyUrl
}
] original=[
${
urlMatch
[
1
]}
]`
,
appId
,
appId
,
...
@@ -148,7 +149,7 @@ async function executeAppLocalNode({
...
@@ -148,7 +149,7 @@ async function executeAppLocalNode({
process
.
stderr
?.
on
(
"data"
,
(
data
)
=>
{
process
.
stderr
?.
on
(
"data"
,
(
data
)
=>
{
const
message
=
util
.
stripVTControlCharacters
(
data
.
toString
());
const
message
=
util
.
stripVTControlCharacters
(
data
.
toString
());
logger
.
error
(
`App
${
appId
}
(PID:
${
process
.
pid
}
) stderr:
${
message
}
`
);
logger
.
error
(
`App
${
appId
}
(PID:
${
process
.
pid
}
) stderr:
${
message
}
`
);
event
.
sender
.
send
(
"app:output"
,
{
safeSend
(
event
.
sender
,
"app:output"
,
{
type
:
"stderr"
,
type
:
"stderr"
,
message
,
message
,
appId
,
appId
,
...
...
src/ipc/handlers/chat_stream_handlers.ts
浏览文件 @
d6d6918d
...
@@ -37,6 +37,8 @@ import { GoogleGenerativeAIProviderOptions } from "@ai-sdk/google";
...
@@ -37,6 +37,8 @@ import { GoogleGenerativeAIProviderOptions } from "@ai-sdk/google";
import
{
getExtraProviderOptions
}
from
"../utils/thinking_utils"
;
import
{
getExtraProviderOptions
}
from
"../utils/thinking_utils"
;
import
{
safeSend
}
from
"../utils/safe_sender"
;
const
logger
=
log
.
scope
(
"chat_stream_handlers"
);
const
logger
=
log
.
scope
(
"chat_stream_handlers"
);
// Track active streams for cancellation
// Track active streams for cancellation
...
@@ -237,7 +239,7 @@ ${componentSnippet}
...
@@ -237,7 +239,7 @@ ${componentSnippet}
}
}
// Send the messages right away so that the loading state is shown for the message.
// Send the messages right away so that the loading state is shown for the message.
event
.
sender
.
send
(
"chat:response:chunk"
,
{
safeSend
(
event
.
sender
,
"chat:response:chunk"
,
{
chatId
:
req
.
chatId
,
chatId
:
req
.
chatId
,
messages
:
updatedChat
.
messages
,
messages
:
updatedChat
.
messages
,
});
});
...
@@ -540,7 +542,7 @@ This conversation includes one or more image attachments. When the user uploads
...
@@ -540,7 +542,7 @@ This conversation includes one or more image attachments. When the user uploads
}
}
// Update the assistant message in the database
// Update the assistant message in the database
event
.
sender
.
send
(
"chat:response:chunk"
,
{
safeSend
(
event
.
sender
,
"chat:response:chunk"
,
{
chatId
:
req
.
chatId
,
chatId
:
req
.
chatId
,
messages
:
currentMessages
,
messages
:
currentMessages
,
});
});
...
@@ -622,27 +624,28 @@ This conversation includes one or more image attachments. When the user uploads
...
@@ -622,27 +624,28 @@ This conversation includes one or more image attachments. When the user uploads
},
},
});
});
event
.
sender
.
send
(
"chat:response:chunk"
,
{
safeSend
(
event
.
sender
,
"chat:response:chunk"
,
{
chatId
:
req
.
chatId
,
chatId
:
req
.
chatId
,
messages
:
chat
!
.
messages
,
messages
:
chat
!
.
messages
,
});
});
if
(
status
.
error
)
{
if
(
status
.
error
)
{
event
.
sender
.
send
(
safeSend
(
event
.
sender
,
"chat:response:error"
,
"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
}
`
,
);
);
}
}
// Signal that the stream has completed
// Signal that the stream has completed
event
.
sender
.
send
(
"chat:response:end"
,
{
safeSend
(
event
.
sender
,
"chat:response:end"
,
{
chatId
:
req
.
chatId
,
chatId
:
req
.
chatId
,
updatedFiles
:
status
.
updatedFiles
??
false
,
updatedFiles
:
status
.
updatedFiles
??
false
,
extraFiles
:
status
.
extraFiles
,
extraFiles
:
status
.
extraFiles
,
extraFilesError
:
status
.
extraFilesError
,
extraFilesError
:
status
.
extraFilesError
,
}
satisfies
ChatResponseEnd
);
}
satisfies
ChatResponseEnd
);
}
else
{
}
else
{
event
.
sender
.
send
(
"chat:response:end"
,
{
safeSend
(
event
.
sender
,
"chat:response:end"
,
{
chatId
:
req
.
chatId
,
chatId
:
req
.
chatId
,
updatedFiles
:
false
,
updatedFiles
:
false
,
}
satisfies
ChatResponseEnd
);
}
satisfies
ChatResponseEnd
);
...
@@ -674,7 +677,8 @@ This conversation includes one or more image attachments. When the user uploads
...
@@ -674,7 +677,8 @@ This conversation includes one or more image attachments. When the user uploads
return
req
.
chatId
;
return
req
.
chatId
;
}
catch
(
error
)
{
}
catch
(
error
)
{
logger
.
error
(
"Error calling LLM:"
,
error
);
logger
.
error
(
"Error calling LLM:"
,
error
);
event
.
sender
.
send
(
safeSend
(
event
.
sender
,
"chat:response:error"
,
"chat:response:error"
,
`Sorry, there was an error processing your request:
${
error
}
`
,
`Sorry, there was an error processing your request:
${
error
}
`
,
);
);
...
@@ -698,7 +702,7 @@ This conversation includes one or more image attachments. When the user uploads
...
@@ -698,7 +702,7 @@ This conversation includes one or more image attachments. When the user uploads
}
}
// Send the end event to the renderer
// Send the end event to the renderer
event
.
sender
.
send
(
"chat:response:end"
,
{
safeSend
(
event
.
sender
,
"chat:response:end"
,
{
chatId
,
chatId
,
updatedFiles
:
false
,
updatedFiles
:
false
,
}
satisfies
ChatResponseEnd
);
}
satisfies
ChatResponseEnd
);
...
...
src/ipc/handlers/supabase_handlers.ts
浏览文件 @
d6d6918d
...
@@ -8,6 +8,7 @@ import {
...
@@ -8,6 +8,7 @@ import {
createTestOnlyLoggedHandler
,
createTestOnlyLoggedHandler
,
}
from
"./safe_handle"
;
}
from
"./safe_handle"
;
import
{
handleSupabaseOAuthReturn
}
from
"../../supabase_admin/supabase_return_handler"
;
import
{
handleSupabaseOAuthReturn
}
from
"../../supabase_admin/supabase_return_handler"
;
import
{
safeSend
}
from
"../utils/safe_sender"
;
const
logger
=
log
.
scope
(
"supabase_handlers"
);
const
logger
=
log
.
scope
(
"supabase_handlers"
);
const
handle
=
createLoggedHandler
(
logger
);
const
handle
=
createLoggedHandler
(
logger
);
...
@@ -70,7 +71,7 @@ export function registerSupabaseHandlers() {
...
@@ -70,7 +71,7 @@ export function registerSupabaseHandlers() {
);
);
// Simulate the deep link event
// Simulate the deep link event
event
.
sender
.
send
(
"deep-link-received"
,
{
safeSend
(
event
.
sender
,
"deep-link-received"
,
{
type
:
"supabase-oauth-return"
,
type
:
"supabase-oauth-return"
,
url
:
"https://supabase-oauth.dyad.sh/api/connect-supabase/login"
,
url
:
"https://supabase-oauth.dyad.sh/api/connect-supabase/login"
,
});
});
...
...
src/ipc/handlers/testing_chat_handlers.ts
浏览文件 @
d6d6918d
import
{
safeSend
}
from
"../utils/safe_sender"
;
// e.g. [dyad-qa=add-dep]
// e.g. [dyad-qa=add-dep]
// Canned responses for test prompts
// Canned responses for test prompts
const
TEST_RESPONSES
:
Record
<
string
,
string
>
=
{
const
TEST_RESPONSES
:
Record
<
string
,
string
>
=
{
...
@@ -64,7 +66,7 @@ export async function streamTestResponse(
...
@@ -64,7 +66,7 @@ export async function streamTestResponse(
fullResponse
+=
chunk
+
" "
;
fullResponse
+=
chunk
+
" "
;
// Send the current accumulated response
// Send the current accumulated response
event
.
sender
.
send
(
"chat:response:chunk"
,
{
safeSend
(
event
.
sender
,
"chat:response:chunk"
,
{
chatId
:
chatId
,
chatId
:
chatId
,
messages
:
[
messages
:
[
...
updatedChat
.
messages
,
...
updatedChat
.
messages
,
...
...
src/ipc/utils/safe_sender.ts
0 → 100644
浏览文件 @
d6d6918d
import
type
{
WebContents
}
from
"electron"
;
import
log
from
"electron-log"
;
/**
* Sends an IPC message to the renderer only if the provided `WebContents` is
* still alive. This prevents `Object has been destroyed` errors that can occur
* when asynchronous callbacks attempt to communicate after the window has
* already been closed (e.g. during e2e test teardown).
*/
export
function
safeSend
(
sender
:
WebContents
|
null
|
undefined
,
channel
:
string
,
...
args
:
unknown
[]
):
void
{
if
(
!
sender
)
return
;
if
(
sender
.
isDestroyed
())
return
;
// @ts-ignore – `isCrashed` exists at runtime but is not in the type defs
if
(
typeof
sender
.
isCrashed
===
"function"
&&
sender
.
isCrashed
())
return
;
try
{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore – allow variadic args beyond `data`
sender
.
send
(
channel
,
...
args
);
}
catch
(
error
)
{
log
.
debug
(
`safeSend: failed to send on channel "
${
channel
}
" because:
${(
error
as
Error
).
message
}
`
,
);
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论