Improve Foundry auth and task flows (#240)

This commit is contained in:
Nathan Flurry 2026-03-11 18:13:31 -07:00 committed by GitHub
parent d75e8c31d1
commit dbc2ff0682
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 621 additions and 137 deletions

View file

@ -73,6 +73,14 @@ export interface GitHubAppClientOptions {
webhookSecret?: string;
}
function normalizePem(value: string | undefined): string | undefined {
if (!value) {
return value;
}
return value.includes("\\n") ? value.replace(/\\n/g, "\n") : value;
}
export class GitHubAppClient {
private readonly apiBaseUrl: string;
private readonly authBaseUrl: string;
@ -90,7 +98,7 @@ export class GitHubAppClient {
this.clientSecret = options.clientSecret ?? process.env.GITHUB_CLIENT_SECRET;
this.redirectUri = options.redirectUri ?? process.env.GITHUB_REDIRECT_URI;
this.appId = options.appId ?? process.env.GITHUB_APP_ID;
this.appPrivateKey = options.appPrivateKey ?? process.env.GITHUB_APP_PRIVATE_KEY;
this.appPrivateKey = normalizePem(options.appPrivateKey ?? process.env.GITHUB_APP_PRIVATE_KEY);
this.webhookSecret = options.webhookSecret ?? process.env.GITHUB_WEBHOOK_SECRET;
}
@ -143,7 +151,7 @@ export class GitHubAppClient {
const url = new URL(`${this.authBaseUrl}/login/oauth/authorize`);
url.searchParams.set("client_id", this.clientId);
url.searchParams.set("redirect_uri", this.redirectUri);
url.searchParams.set("scope", "read:user user:email read:org");
url.searchParams.set("scope", "read:user user:email read:org repo");
url.searchParams.set("state", state);
return url.toString();
}
@ -273,7 +281,7 @@ export class GitHubAppClient {
full_name: string;
clone_url: string;
private: boolean;
}>("/user/repos?per_page=100&affiliation=owner&sort=updated", accessToken);
}>("/user/repos?per_page=100&affiliation=owner,collaborator,organization_member&sort=updated", accessToken);
return repositories.map((repository) => ({
fullName: repository.full_name,

View file

@ -0,0 +1,30 @@
import { getOrCreateWorkspace } from "../actors/handles.js";
import { APP_SHELL_WORKSPACE_ID } from "../actors/workspace/app-shell.js";
export interface ResolvedGithubAuth {
githubToken: string;
scopes: string[];
}
export async function resolveWorkspaceGithubAuth(c: any, workspaceId: string): Promise<ResolvedGithubAuth | null> {
if (!workspaceId || workspaceId === APP_SHELL_WORKSPACE_ID) {
return null;
}
try {
const appWorkspace = await getOrCreateWorkspace(c, APP_SHELL_WORKSPACE_ID);
const resolved = await appWorkspace.resolveAppGithubToken({
organizationId: workspaceId,
requireRepoScope: true,
});
if (!resolved?.accessToken) {
return null;
}
return {
githubToken: resolved.accessToken,
scopes: Array.isArray(resolved.scopes) ? resolved.scopes : [],
};
} catch {
return null;
}
}