Files
PaperClipAI/server/src/services/chat-channels/github/setup.ts
T
DottaandPaperclip eb06c9fedd refactor(server): split chat channel workflows into provider and shared modules (#16080)
Move provider operations into channel folders and shared chat work into named interaction, setup, admission, delivery and publication workflows. Consolidate reviewed PRs #16085, #16089, #16096 and #16105. Preserve upstream execution-default and recovery behavior. The main service is 7,917 lines.

All 55 current-head CI checks pass, with two optional Storybook skips. Greptile is 5/5 with no findings. All 1,294 current chat integration cases, repository-wide typecheck and build pass locally. Broad local tool-route timing/socket failures are recorded in the PR.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 07:26:32 -05:00

70 lines
2.9 KiB
TypeScript

import { createGitHubAppSetup } from "./app-setup.js";
import type { EndpointRow } from "../runtime-types.js";
import { type CredentialMutationLeaseGuard } from "../../chat-credential-mutation-lease.js";
import { githubAppJwt } from "../../chat-github-client.js";
import type { Db } from "@paperclipai/db";
import {
forbidden,
} from "../../../errors.js";
import {
logActivity,
} from "../../activity-log.js";
import {
resyncGitHubAppWebhook,
} from "../../chat-github-webhook-config.js";
type GitHubSetupOptions = {
fetchImpl: typeof globalThis.fetch;
getWebhookPublicBaseUrl: () => string | null;
githubWizard: Pick<ReturnType<typeof createGitHubAppSetup>["githubWizard"], "repairCloud">;
};
export function createGitHubSetup(db: Db, options: GitHubSetupOptions) {
const { fetchImpl, getWebhookPublicBaseUrl } = options;
async function repairGitHubCloudSetup(endpoint: EndpointRow, credentialLease: CredentialMutationLeaseGuard, actorUserId?: string | null) {
if (!actorUserId) throw forbidden("A configuring member is required to recover this App");
await credentialLease.assertOwned();
await options.githubWizard.repairCloud(endpoint.id, actorUserId);
await credentialLease.assertOwned();
}
async function repairGitHubWebhookSetup(endpoint: EndpointRow, credentials: Record<string, string>, credentialLease: CredentialMutationLeaseGuard, actorUserId?: string | null) {
// The immutable App and its installation were verified above. Repair
// only its callback using the stored secret; never reinstall it or
// change repository access. Keep historical signed-ping evidence, but
// require the fresh test round trip established above before activation.
const auditWebhookSync = async (action: string) => {
await db.transaction(async (tx) => {
await credentialLease.assertOwned(tx);
await logActivity(tx as unknown as Db, {
companyId: endpoint.companyId,
actorType: "user",
actorId: actorUserId ?? "board",
action,
entityType: "tool_connection",
entityId: endpoint.connectionId,
details: { endpointId: endpoint.id, provider: "github" },
});
await credentialLease.assertOwned(tx);
});
};
await auditWebhookSync("chat_endpoint.webhook_sync_started");
await credentialLease.assertOwned();
await resyncGitHubAppWebhook({
fetch: fetchImpl,
appToken: githubAppJwt(credentials.appId, credentials.privateKey),
webhookUrl: `${getWebhookPublicBaseUrl()}/api/chat-webhooks/${endpoint.publicId}/github`,
webhookSecret: credentials.webhookSecret,
});
await auditWebhookSync("chat_endpoint.webhook_synced");
}
return { repairGitHubCloudSetup, repairGitHubWebhookSetup };
}