Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
1c325ecc
提交
1c325ecc
authored
4月 15, 2025
作者:
Will Chen
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Commit with GitHub user email (needed for vercel deployment)
上级
d55d6781
隐藏空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
145 行增加
和
100 行删除
+145
-100
GitHubConnector.tsx
src/components/GitHubConnector.tsx
+84
-84
app_handlers.ts
src/ipc/handlers/app_handlers.ts
+4
-12
github_handlers.ts
src/ipc/handlers/github_handlers.ts
+32
-0
response_processor.ts
src/ipc/processors/response_processor.ts
+3
-4
git_author.ts
src/ipc/utils/git_author.ts
+15
-0
schemas.ts
src/lib/schemas.ts
+6
-0
settings.ts
src/main/settings.ts
+1
-0
没有找到文件。
src/components/GitHubConnector.tsx
浏览文件 @
1c325ecc
...
@@ -171,6 +171,89 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
...
@@ -171,6 +171,89 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
}
}
};
};
if
(
!
settings
?.
githubSettings
.
secrets
?.
accessToken
)
{
return
(
<
div
className=
"mt-4 w-full"
>
{
" "
}
<
Button
onClick=
{
handleConnectToGithub
}
className=
"cursor-pointer w-full py-6 flex justify-center items-center gap-2 text-lg"
size=
"lg"
variant=
"outline"
disabled=
{
isConnectingToGithub
||
!
appId
}
// Also disable if appId is null
>
Connect to GitHub
<
Github
className=
"h-5 w-5"
/>
{
isConnectingToGithub
&&
(
<
svg
className=
"animate-spin h-5 w-5 ml-2"
xmlns=
"http://www.w3.org/2000/svg"
fill=
"none"
viewBox=
"0 0 24 24"
>
<
circle
className=
"opacity-25"
cx=
"12"
cy=
"12"
r=
"10"
stroke=
"currentColor"
strokeWidth=
"4"
></
circle
>
<
path
className=
"opacity-75"
fill=
"currentColor"
d=
"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></
path
>
</
svg
>
)
}
</
Button
>
{
/* GitHub Connection Status/Instructions */
}
{
(
githubUserCode
||
githubStatusMessage
||
githubError
)
&&
(
<
div
className=
"mt-6 p-4 border rounded-md bg-gray-50 dark:bg-gray-700/50 border-gray-200 dark:border-gray-600"
>
<
h4
className=
"font-medium mb-2"
>
GitHub Connection
</
h4
>
{
githubError
&&
(
<
p
className=
"text-red-600 dark:text-red-400 mb-2"
>
Error:
{
githubError
}
</
p
>
)
}
{
githubUserCode
&&
githubVerificationUri
&&
(
<
div
className=
"mb-2"
>
<
p
>
1. Go to:
<
a
href=
{
githubVerificationUri
}
// Make it a direct link
onClick=
{
(
e
)
=>
{
e
.
preventDefault
();
IpcClient
.
getInstance
().
openExternalUrl
(
githubVerificationUri
);
}
}
target=
"_blank"
rel=
"noopener noreferrer"
className=
"ml-1 text-blue-600 hover:underline dark:text-blue-400"
>
{
githubVerificationUri
}
</
a
>
</
p
>
<
p
>
2. Enter code:
<
strong
className=
"ml-1 font-mono text-lg tracking-wider bg-gray-200 dark:bg-gray-600 px-2 py-0.5 rounded"
>
{
githubUserCode
}
</
strong
>
</
p
>
</
div
>
)
}
{
githubStatusMessage
&&
(
<
p
className=
"text-sm text-gray-600 dark:text-gray-300"
>
{
githubStatusMessage
}
</
p
>
)
}
</
div
>
)
}
</
div
>
);
}
if
(
app
?.
githubOrg
&&
app
?.
githubRepo
)
{
if
(
app
?.
githubOrg
&&
app
?.
githubRepo
)
{
const
handleSyncToGithub
=
async
()
=>
{
const
handleSyncToGithub
=
async
()
=>
{
setIsSyncing
(
true
);
setIsSyncing
(
true
);
...
@@ -244,9 +327,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
...
@@ -244,9 +327,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
)
}
)
}
</
div
>
</
div
>
);
);
}
}
else
{
if
(
settings
?.
githubSettings
.
secrets
)
{
return
(
return
(
<
div
className=
"mt-4 w-full border border-gray-200 rounded-md p-4"
>
<
div
className=
"mt-4 w-full border border-gray-200 rounded-md p-4"
>
<
p
>
Set up your GitHub repo
</
p
>
<
p
>
Set up your GitHub repo
</
p
>
...
@@ -290,85 +371,4 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
...
@@ -290,85 +371,4 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
</
div
>
</
div
>
);
);
}
}
return
(
<
div
className=
"mt-4 w-full"
>
{
" "
}
<
Button
onClick=
{
handleConnectToGithub
}
className=
"cursor-pointer w-full py-6 flex justify-center items-center gap-2 text-lg"
size=
"lg"
variant=
"outline"
disabled=
{
isConnectingToGithub
||
!
appId
}
// Also disable if appId is null
>
Connect to GitHub
<
Github
className=
"h-5 w-5"
/>
{
isConnectingToGithub
&&
(
<
svg
className=
"animate-spin h-5 w-5 ml-2"
xmlns=
"http://www.w3.org/2000/svg"
fill=
"none"
viewBox=
"0 0 24 24"
>
<
circle
className=
"opacity-25"
cx=
"12"
cy=
"12"
r=
"10"
stroke=
"currentColor"
strokeWidth=
"4"
></
circle
>
<
path
className=
"opacity-75"
fill=
"currentColor"
d=
"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></
path
>
</
svg
>
)
}
</
Button
>
{
/* GitHub Connection Status/Instructions */
}
{
(
githubUserCode
||
githubStatusMessage
||
githubError
)
&&
(
<
div
className=
"mt-6 p-4 border rounded-md bg-gray-50 dark:bg-gray-700/50 border-gray-200 dark:border-gray-600"
>
<
h4
className=
"font-medium mb-2"
>
GitHub Connection
</
h4
>
{
githubError
&&
(
<
p
className=
"text-red-600 dark:text-red-400 mb-2"
>
Error:
{
githubError
}
</
p
>
)
}
{
githubUserCode
&&
githubVerificationUri
&&
(
<
div
className=
"mb-2"
>
<
p
>
1. Go to:
<
a
href=
{
githubVerificationUri
}
// Make it a direct link
onClick=
{
(
e
)
=>
{
e
.
preventDefault
();
IpcClient
.
getInstance
().
openExternalUrl
(
githubVerificationUri
);
}
}
target=
"_blank"
rel=
"noopener noreferrer"
className=
"ml-1 text-blue-600 hover:underline dark:text-blue-400"
>
{
githubVerificationUri
}
</
a
>
</
p
>
<
p
>
2. Enter code:
<
strong
className=
"ml-1 font-mono text-lg tracking-wider bg-gray-200 dark:bg-gray-600 px-2 py-0.5 rounded"
>
{
githubUserCode
}
</
strong
>
</
p
>
</
div
>
)
}
{
githubStatusMessage
&&
(
<
p
className=
"text-sm text-gray-600 dark:text-gray-300"
>
{
githubStatusMessage
}
</
p
>
)
}
</
div
>
)
}
</
div
>
);
}
}
src/ipc/handlers/app_handlers.ts
浏览文件 @
1c325ecc
...
@@ -33,6 +33,7 @@ import { getEnvVar } from "../utils/read_env";
...
@@ -33,6 +33,7 @@ import { getEnvVar } from "../utils/read_env";
import
{
readSettings
}
from
"../../main/settings"
;
import
{
readSettings
}
from
"../../main/settings"
;
import
{
Worker
}
from
"worker_threads"
;
import
{
Worker
}
from
"worker_threads"
;
import
fixPath
from
"fix-path"
;
import
fixPath
from
"fix-path"
;
import
{
getGitAuthor
}
from
"../utils/git_author"
;
// Needed, otherwise electron in MacOS/Linux will not be able
// Needed, otherwise electron in MacOS/Linux will not be able
// to find "npm".
// to find "npm".
...
@@ -346,10 +347,7 @@ export function registerAppHandlers() {
...
@@ -346,10 +347,7 @@ export function registerAppHandlers() {
fs
:
fs
,
fs
:
fs
,
dir
:
fullAppPath
,
dir
:
fullAppPath
,
message
:
"Init from react vite template"
,
message
:
"Init from react vite template"
,
author
:
{
author
:
await
getGitAuthor
(),
name
:
"Dyad"
,
email
:
"dyad@example.com"
,
},
});
});
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
"Error in background app initialization:"
,
error
);
console
.
error
(
"Error in background app initialization:"
,
error
);
...
@@ -708,10 +706,7 @@ export function registerAppHandlers() {
...
@@ -708,10 +706,7 @@ export function registerAppHandlers() {
fs
,
fs
,
dir
:
appPath
,
dir
:
appPath
,
message
:
`Reverted all changes back to version
${
previousVersionId
}
`
,
message
:
`Reverted all changes back to version
${
previousVersionId
}
`
,
author
:
{
author
:
await
getGitAuthor
(),
name
:
"Dyad"
,
email
:
"hi@dyad.sh"
,
},
});
});
return
{
success
:
true
};
return
{
success
:
true
};
...
@@ -845,10 +840,7 @@ export function registerAppHandlers() {
...
@@ -845,10 +840,7 @@ export function registerAppHandlers() {
fs
,
fs
,
dir
:
appPath
,
dir
:
appPath
,
message
:
`Updated
${
filePath
}
`
,
message
:
`Updated
${
filePath
}
`
,
author
:
{
author
:
await
getGitAuthor
(),
name
:
"Dyad"
,
email
:
"hi@dyad.sh"
,
},
});
});
}
}
...
...
src/ipc/handlers/github_handlers.ts
浏览文件 @
1c325ecc
...
@@ -15,6 +15,7 @@ import { getDyadAppPath } from "../../paths/paths";
...
@@ -15,6 +15,7 @@ import { getDyadAppPath } from "../../paths/paths";
import
{
db
}
from
"../../db"
;
import
{
db
}
from
"../../db"
;
import
{
apps
}
from
"../../db/schema"
;
import
{
apps
}
from
"../../db/schema"
;
import
{
eq
}
from
"drizzle-orm"
;
import
{
eq
}
from
"drizzle-orm"
;
import
{
GithubUser
}
from
"../../lib/schemas"
;
// --- GitHub Device Flow Constants ---
// --- GitHub Device Flow Constants ---
// TODO: Fetch this securely, e.g., from environment variables or a config file
// TODO: Fetch this securely, e.g., from environment variables or a config file
...
@@ -38,6 +39,37 @@ let currentFlowState: DeviceFlowState | null = null;
...
@@ -38,6 +39,37 @@ let currentFlowState: DeviceFlowState | null = null;
// --- Helper Functions ---
// --- Helper Functions ---
/**
* Fetches the GitHub username of the currently authenticated user (using the stored access token).
* @returns {Promise<string|null>} The GitHub username, or null if not authenticated or on error.
*/
export
async
function
getGithubUser
():
Promise
<
GithubUser
|
null
>
{
const
settings
=
readSettings
();
const
email
=
settings
.
githubUser
?.
email
;
if
(
email
)
return
{
email
};
try
{
const
accessToken
=
settings
.
githubSettings
?.
secrets
?.
accessToken
;
if
(
!
accessToken
)
return
null
;
const
res
=
await
fetch
(
"https://api.github.com/user/emails"
,
{
headers
:
{
Authorization
:
`Bearer
${
accessToken
}
`
},
});
if
(
!
res
.
ok
)
return
null
;
const
emails
=
await
res
.
json
();
const
email
=
emails
.
find
((
e
:
any
)
=>
e
.
primary
)?.
email
;
if
(
!
email
)
return
null
;
writeSettings
({
githubUser
:
{
email
,
},
});
return
{
email
};
}
catch
(
err
)
{
console
.
error
(
"[GitHub Handler] Failed to get GitHub username:"
,
err
);
return
null
;
}
}
// function event.sender.send(channel: string, data: any) {
// function event.sender.send(channel: string, data: any) {
// if (currentFlowState?.window && !currentFlowState.window.isDestroyed()) {
// if (currentFlowState?.window && !currentFlowState.window.isDestroyed()) {
// currentFlowState.window.webContents.send(channel, data);
// currentFlowState.window.webContents.send(channel, data);
...
...
src/ipc/processors/response_processor.ts
浏览文件 @
1c325ecc
...
@@ -5,6 +5,8 @@ import fs from "node:fs";
...
@@ -5,6 +5,8 @@ import fs from "node:fs";
import
{
getDyadAppPath
}
from
"../../paths/paths"
;
import
{
getDyadAppPath
}
from
"../../paths/paths"
;
import
path
from
"node:path"
;
import
path
from
"node:path"
;
import
git
from
"isomorphic-git"
;
import
git
from
"isomorphic-git"
;
import
{
getGithubUser
}
from
"../handlers/github_handlers"
;
import
{
getGitAuthor
}
from
"../utils/git_author"
;
export
function
getDyadWriteTags
(
fullResponse
:
string
):
{
export
function
getDyadWriteTags
(
fullResponse
:
string
):
{
path
:
string
;
path
:
string
;
...
@@ -210,10 +212,7 @@ export async function processFullResponseActions(
...
@@ -210,10 +212,7 @@ export async function processFullResponseActions(
message
:
chatSummary
message
:
chatSummary
?
`[dyad]
${
chatSummary
}
-
${
changes
.
join
(
", "
)}
`
?
`[dyad]
${
chatSummary
}
-
${
changes
.
join
(
", "
)}
`
:
`[dyad]
${
changes
.
join
(
", "
)}
`
,
:
`[dyad]
${
changes
.
join
(
", "
)}
`
,
author
:
{
author
:
await
getGitAuthor
(),
name
:
"Dyad AI"
,
email
:
"dyad-ai@example.com"
,
},
});
});
console
.
log
(
`Successfully committed changes:
${
changes
.
join
(
", "
)}
`
);
console
.
log
(
`Successfully committed changes:
${
changes
.
join
(
", "
)}
`
);
return
{
updatedFiles
:
true
};
return
{
updatedFiles
:
true
};
...
...
src/ipc/utils/git_author.ts
0 → 100644
浏览文件 @
1c325ecc
import
{
getGithubUser
}
from
"../handlers/github_handlers"
;
export
async
function
getGitAuthor
()
{
const
user
=
await
getGithubUser
();
const
author
=
user
?
{
name
:
`[dyad]`
,
email
:
user
.
email
,
}
:
{
name
:
"[dyad]"
,
email
:
"git@dyad.sh"
,
};
return
author
;
}
src/lib/schemas.ts
浏览文件 @
1c325ecc
...
@@ -74,6 +74,11 @@ export const GitHubSettingsSchema = z.object({
...
@@ -74,6 +74,11 @@ export const GitHubSettingsSchema = z.object({
});
});
export
type
GitHubSettings
=
z
.
infer
<
typeof
GitHubSettingsSchema
>
;
export
type
GitHubSettings
=
z
.
infer
<
typeof
GitHubSettingsSchema
>
;
export
const
GithubUserSchema
=
z
.
object
({
email
:
z
.
string
(),
});
export
type
GithubUser
=
z
.
infer
<
typeof
GithubUserSchema
>
;
/**
/**
* Zod schema for user settings
* Zod schema for user settings
*/
*/
...
@@ -82,6 +87,7 @@ export const UserSettingsSchema = z.object({
...
@@ -82,6 +87,7 @@ export const UserSettingsSchema = z.object({
providerSettings
:
z
.
record
(
z
.
string
(),
ProviderSettingSchema
),
providerSettings
:
z
.
record
(
z
.
string
(),
ProviderSettingSchema
),
runtimeMode
:
RuntimeModeSchema
,
runtimeMode
:
RuntimeModeSchema
,
githubSettings
:
GitHubSettingsSchema
,
githubSettings
:
GitHubSettingsSchema
,
githubUser
:
GithubUserSchema
.
optional
(),
});
});
/**
/**
...
...
src/main/settings.ts
浏览文件 @
1c325ecc
...
@@ -3,6 +3,7 @@ import path from "node:path";
...
@@ -3,6 +3,7 @@ import path from "node:path";
import
{
getUserDataPath
}
from
"../paths/paths"
;
import
{
getUserDataPath
}
from
"../paths/paths"
;
import
{
UserSettingsSchema
,
type
UserSettings
}
from
"../lib/schemas"
;
import
{
UserSettingsSchema
,
type
UserSettings
}
from
"../lib/schemas"
;
import
{
safeStorage
}
from
"electron"
;
import
{
safeStorage
}
from
"electron"
;
const
DEFAULT_SETTINGS
:
UserSettings
=
{
const
DEFAULT_SETTINGS
:
UserSettings
=
{
selectedModel
:
{
selectedModel
:
{
name
:
"auto"
,
name
:
"auto"
,
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论