diff --git a/src/components/GitHubConnector.tsx b/src/components/GitHubConnector.tsx index 6f0bb7e..c389420 100644 --- a/src/components/GitHubConnector.tsx +++ b/src/components/GitHubConnector.tsx @@ -171,6 +171,89 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) { } }; + if (!settings?.githubSettings.secrets?.accessToken) { + return ( +
+ {" "} + + {/* GitHub Connection Status/Instructions */} + {(githubUserCode || githubStatusMessage || githubError) && ( +
+

GitHub Connection

+ {githubError && ( +

+ Error: {githubError} +

+ )} + {githubUserCode && githubVerificationUri && ( +
+

+ 1. Go to: + { + e.preventDefault(); + IpcClient.getInstance().openExternalUrl( + githubVerificationUri + ); + }} + target="_blank" + rel="noopener noreferrer" + className="ml-1 text-blue-600 hover:underline dark:text-blue-400" + > + {githubVerificationUri} + +

+

+ 2. Enter code: + + {githubUserCode} + +

+
+ )} + {githubStatusMessage && ( +

+ {githubStatusMessage} +

+ )} +
+ )} +
+ ); + } + if (app?.githubOrg && app?.githubRepo) { const handleSyncToGithub = async () => { setIsSyncing(true); @@ -244,9 +327,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) { )} ); - } - - if (settings?.githubSettings.secrets) { + } else { return (

Set up your GitHub repo

@@ -290,85 +371,4 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
); } - - return ( -
- {" "} - - {/* GitHub Connection Status/Instructions */} - {(githubUserCode || githubStatusMessage || githubError) && ( -
-

GitHub Connection

- {githubError && ( -

- Error: {githubError} -

- )} - {githubUserCode && githubVerificationUri && ( -
-

- 1. Go to: - { - e.preventDefault(); - IpcClient.getInstance().openExternalUrl( - githubVerificationUri - ); - }} - target="_blank" - rel="noopener noreferrer" - className="ml-1 text-blue-600 hover:underline dark:text-blue-400" - > - {githubVerificationUri} - -

-

- 2. Enter code: - - {githubUserCode} - -

-
- )} - {githubStatusMessage && ( -

- {githubStatusMessage} -

- )} -
- )} -
- ); } diff --git a/src/ipc/handlers/app_handlers.ts b/src/ipc/handlers/app_handlers.ts index ef27065..e670904 100644 --- a/src/ipc/handlers/app_handlers.ts +++ b/src/ipc/handlers/app_handlers.ts @@ -33,6 +33,7 @@ import { getEnvVar } from "../utils/read_env"; import { readSettings } from "../../main/settings"; import { Worker } from "worker_threads"; import fixPath from "fix-path"; +import { getGitAuthor } from "../utils/git_author"; // Needed, otherwise electron in MacOS/Linux will not be able // to find "npm". @@ -346,10 +347,7 @@ export function registerAppHandlers() { fs: fs, dir: fullAppPath, message: "Init from react vite template", - author: { - name: "Dyad", - email: "dyad@example.com", - }, + author: await getGitAuthor(), }); } catch (error) { console.error("Error in background app initialization:", error); @@ -708,10 +706,7 @@ export function registerAppHandlers() { fs, dir: appPath, message: `Reverted all changes back to version ${previousVersionId}`, - author: { - name: "Dyad", - email: "hi@dyad.sh", - }, + author: await getGitAuthor(), }); return { success: true }; @@ -845,10 +840,7 @@ export function registerAppHandlers() { fs, dir: appPath, message: `Updated ${filePath}`, - author: { - name: "Dyad", - email: "hi@dyad.sh", - }, + author: await getGitAuthor(), }); } diff --git a/src/ipc/handlers/github_handlers.ts b/src/ipc/handlers/github_handlers.ts index e872824..7893834 100644 --- a/src/ipc/handlers/github_handlers.ts +++ b/src/ipc/handlers/github_handlers.ts @@ -15,6 +15,7 @@ import { getDyadAppPath } from "../../paths/paths"; import { db } from "../../db"; import { apps } from "../../db/schema"; import { eq } from "drizzle-orm"; +import { GithubUser } from "../../lib/schemas"; // --- GitHub Device Flow Constants --- // TODO: Fetch this securely, e.g., from environment variables or a config file @@ -38,6 +39,37 @@ let currentFlowState: DeviceFlowState | null = null; // --- Helper Functions --- +/** + * Fetches the GitHub username of the currently authenticated user (using the stored access token). + * @returns {Promise} The GitHub username, or null if not authenticated or on error. + */ +export async function getGithubUser(): Promise { + 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) { // if (currentFlowState?.window && !currentFlowState.window.isDestroyed()) { // currentFlowState.window.webContents.send(channel, data); diff --git a/src/ipc/processors/response_processor.ts b/src/ipc/processors/response_processor.ts index c318d78..0210273 100644 --- a/src/ipc/processors/response_processor.ts +++ b/src/ipc/processors/response_processor.ts @@ -5,6 +5,8 @@ import fs from "node:fs"; import { getDyadAppPath } from "../../paths/paths"; import path from "node:path"; import git from "isomorphic-git"; +import { getGithubUser } from "../handlers/github_handlers"; +import { getGitAuthor } from "../utils/git_author"; export function getDyadWriteTags(fullResponse: string): { path: string; @@ -210,10 +212,7 @@ export async function processFullResponseActions( message: chatSummary ? `[dyad] ${chatSummary} - ${changes.join(", ")}` : `[dyad] ${changes.join(", ")}`, - author: { - name: "Dyad AI", - email: "dyad-ai@example.com", - }, + author: await getGitAuthor(), }); console.log(`Successfully committed changes: ${changes.join(", ")}`); return { updatedFiles: true }; diff --git a/src/ipc/utils/git_author.ts b/src/ipc/utils/git_author.ts new file mode 100644 index 0000000..556a58d --- /dev/null +++ b/src/ipc/utils/git_author.ts @@ -0,0 +1,15 @@ +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; +} diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts index a5c1c7e..b902b27 100644 --- a/src/lib/schemas.ts +++ b/src/lib/schemas.ts @@ -74,6 +74,11 @@ export const GitHubSettingsSchema = z.object({ }); export type GitHubSettings = z.infer; +export const GithubUserSchema = z.object({ + email: z.string(), +}); +export type GithubUser = z.infer; + /** * Zod schema for user settings */ @@ -82,6 +87,7 @@ export const UserSettingsSchema = z.object({ providerSettings: z.record(z.string(), ProviderSettingSchema), runtimeMode: RuntimeModeSchema, githubSettings: GitHubSettingsSchema, + githubUser: GithubUserSchema.optional(), }); /** diff --git a/src/main/settings.ts b/src/main/settings.ts index 6005270..b7e5d9f 100644 --- a/src/main/settings.ts +++ b/src/main/settings.ts @@ -3,6 +3,7 @@ import path from "node:path"; import { getUserDataPath } from "../paths/paths"; import { UserSettingsSchema, type UserSettings } from "../lib/schemas"; import { safeStorage } from "electron"; + const DEFAULT_SETTINGS: UserSettings = { selectedModel: { name: "auto",