Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/ade-cli/src/headlessLinearServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,15 @@ export function createHeadlessGitHubService(
})
).data ?? null;
},
async updateIssueComment(owner, name, commentId, body) {
return (
await apiRequest<GitHubIssueComment>({
method: "PATCH",
path: `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/comments/${commentId}`,
body: { body },
})
).data ?? null;
},
async setIssueLabels(owner, name, number, labels) {
const { data } = await apiRequest<GitHubLabel[]>({
method: "PUT",
Expand Down
24 changes: 24 additions & 0 deletions apps/ade-cli/src/services/sync/syncRemoteCommandService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import type {
LandQueueNextArgs,
PauseQueueAutomationArgs,
PipelineSettings,
PrGithubCoords,
PrConvergenceStatePatch,
LaneEnvInitConfig,
LaneEnvInitProgress,
Expand Down Expand Up @@ -1164,6 +1165,16 @@ function requirePrId(value: Record<string, unknown>, action: string): string {
return requireString(value.prId, `${action} requires prId.`);
}

function requirePrGithubCoords(value: Record<string, unknown>, action: string): PrGithubCoords {
const repoOwner = requireString(value.repoOwner, `${action} requires repoOwner.`);
const repoName = requireString(value.repoName, `${action} requires repoName.`);
const githubPrNumber = asOptionalNumber(value.githubPrNumber);
if (githubPrNumber == null || !Number.isInteger(githubPrNumber) || githubPrNumber <= 0) {
throw new Error(`${action} requires a positive integer githubPrNumber.`);
}
return { repoOwner, repoName, githubPrNumber };
}

function parseCreatePrArgs(value: Record<string, unknown>): CreatePrFromLaneArgs {
const laneId = asTrimmedString(value.laneId);
const title = asTrimmedString(value.title);
Expand Down Expand Up @@ -2845,6 +2856,19 @@ function registerPrAndDeeplinkRemoteCommands({ args, register }: RemoteCommandRe
register("prs.getActionRuns", { viewerAllowed: true }, async (payload) => args.prService.getActionRuns(requirePrId(payload, "prs.getActionRuns")));
register("prs.getActivity", { viewerAllowed: true }, async (payload) => args.prService.getActivity(requirePrId(payload, "prs.getActivity")));
register("prs.getDeployments", { viewerAllowed: true }, async (payload) => args.prService.getDeployments(requirePrId(payload, "prs.getDeployments")));
// Coordinate-based PR reads for PRs that are not mapped to an ADE lane (no DB
// row). The preload sends these `*ByGithub` runtime actions before falling
// back to IPC, so the socket runtime must register them alongside the
// row-based reads. Args are GitHub coordinates: { repoOwner, repoName, githubPrNumber }.
register("prs.getDetailByGithub", { viewerAllowed: true }, async (payload) => args.prService.getDetailByGithub(requirePrGithubCoords(payload, "prs.getDetailByGithub")));
register("prs.getFilesByGithub", { viewerAllowed: true }, async (payload) => args.prService.getFilesByGithub(requirePrGithubCoords(payload, "prs.getFilesByGithub")));
register("prs.getCommitsByGithub", { viewerAllowed: true }, async (payload) => args.prService.getCommitsByGithub(requirePrGithubCoords(payload, "prs.getCommitsByGithub")));
register("prs.getActionRunsByGithub", { viewerAllowed: true }, async (payload) => args.prService.getActionRunsByGithub(requirePrGithubCoords(payload, "prs.getActionRunsByGithub")));
register("prs.getActivityByGithub", { viewerAllowed: true }, async (payload) => args.prService.getActivityByGithub(requirePrGithubCoords(payload, "prs.getActivityByGithub")));
register("prs.getChecksByGithub", { viewerAllowed: true }, async (payload) => args.prService.getChecksByGithub(requirePrGithubCoords(payload, "prs.getChecksByGithub")));
register("prs.getReviewsByGithub", { viewerAllowed: true }, async (payload) => args.prService.getReviewsByGithub(requirePrGithubCoords(payload, "prs.getReviewsByGithub")));
register("prs.getCommentsByGithub", { viewerAllowed: true }, async (payload) => args.prService.getCommentsByGithub(requirePrGithubCoords(payload, "prs.getCommentsByGithub")));
register("prs.getReviewThreadsByGithub", { viewerAllowed: true }, async (payload) => args.prService.getReviewThreadsByGithub(requirePrGithubCoords(payload, "prs.getReviewThreadsByGithub")));
register("prs.createFromLane", { viewerAllowed: true, queueable: true }, async (payload) => args.prService.createFromLane(parseCreatePrArgs(payload)));
register("prs.createQueue", { viewerAllowed: true, queueable: true }, async (payload) => args.prService.createQueuePrs(parseCreateQueuePrsArgs(payload)));
register("prs.linkToLane", { viewerAllowed: true, queueable: true }, async (payload) => args.prService.linkToLane(parseLinkPrToLaneArgs(payload)));
Expand Down
146 changes: 11 additions & 135 deletions apps/desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"lottie-react": "^2.4.1",
"lucide-react": "^0.563.0",
"mdast-util-find-and-replace": "^3.0.2",
"mermaid": "^11.15.0",
"monaco-editor": "^0.55.1",
"motion": "^12.34.2",
"node-cron": "^3.0.3",
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop/src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2524,6 +2524,14 @@ app.whenReady().then(async () => {
});
};

// Wire auto-map-by-branch: the PR service emits Undo-able toasts through the
// PR event channel, and a freshly created worktree lane triggers a
// best-effort auto-map of any existing open PR on its branch (Trigger #1).
prService.setEventEmitter(emitPrEvent);
laneService.setOnWorktreeLaneCreated((lane) => {
void prService.tryAutoMapLaneByBranch(lane.id);
});

const prPollingService = createPrPollingService({
logger,
prService,
Expand Down
10 changes: 9 additions & 1 deletion apps/desktop/src/main/rendererCsp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ export function buildRendererCspPolicy(isDevMode: boolean): string {
const cspWsSources = isDevMode ? " ws://localhost:* ws://127.0.0.1:*" : "";
const cspLocalSources = " http://localhost:* http://127.0.0.1:*";
const cspConnectLocalSources = isDevMode ? "" : cspLocalSources;
const cspImageSources = `${cspSources}${cspLocalSources} https://avatars.githubusercontent.com https://*.githubusercontent.com https://github.githubassets.com https://opengraph.githubassets.com https://github.com https://vercel.com https://*.vercel.com https://img.shields.io https://*.s3.amazonaws.com`;
// GitHub serves comment-body images from a spread of hosts: avatars and the
// `*.githubusercontent.com` family (user-images, private-user-images, media,
// camo, objects), plus `github.com/user-attachments/...` (served under
// github.com, which then 302s to private-user-images.githubusercontent.com).
// The `*.githubusercontent.com` wildcard already covers the subdomain family;
// we list the common ones explicitly for clarity/self-documentation. We keep
// the allowlist host-scoped (no blanket `https:`) to preserve the existing
// posture of not allowing arbitrary public image beacons.
const cspImageSources = `${cspSources}${cspLocalSources} https://avatars.githubusercontent.com https://*.githubusercontent.com https://user-images.githubusercontent.com https://private-user-images.githubusercontent.com https://media.githubusercontent.com https://camo.githubusercontent.com https://objects.githubusercontent.com https://github.githubassets.com https://opengraph.githubassets.com https://github.com https://vercel.com https://*.vercel.com https://img.shields.io https://*.s3.amazonaws.com https://ade-app.dev`;
const cspScriptSources = isDevMode ? `${cspSources} 'unsafe-inline'` : cspSources;
return [
`default-src ${cspSources}`,
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop/src/main/services/adeActions/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,15 +364,22 @@ export const ADE_ACTION_ALLOWLIST: Partial<Record<AdeActionDomain, readonly stri
"dismissIntegrationCleanup",
"draftDescription",
"getActionRuns",
"getActionRunsByGithub",
"getActivity",
"getActivityByGithub",
"getChecks",
"getChecksByGithub",
"getComments",
"getCommentsByGithub",
"getCommits",
"getCommitsByGithub",
"getConflictAnalysis",
"getDetail",
"getDetailByGithub",
"getDeployments",
"getForLane",
"getFiles",
"getFilesByGithub",
"getGithubSnapshot",
"getIntegrationResolutionState",
"getMergeContext",
Expand All @@ -382,7 +389,9 @@ export const ADE_ACTION_ALLOWLIST: Partial<Record<AdeActionDomain, readonly stri
"getQueueState",
"getAiSummary",
"getReviewThreads",
"getReviewThreadsByGithub",
"getReviews",
"getReviewsByGithub",
"getStatus",
"land",
"landQueueNext",
Expand Down Expand Up @@ -425,6 +434,7 @@ export const ADE_ACTION_ALLOWLIST: Partial<Record<AdeActionDomain, readonly stri
"rebaseResolutionStart",
"submitReview",
"updateBody",
"updateComment",
"updateDescription",
"updateIntegrationProposal",
"updateTitle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1930,13 +1930,17 @@ function coerceGithubConfig(value: unknown): ProjectConfigFile["github"] {
const prTranscriptGistsEnabled = rawTranscriptGists
? asBool(rawTranscriptGists.enabled)
: null;
const autoMapByBranch = asBool(value.autoMapByBranch);
const github: NonNullable<ProjectConfigFile["github"]> = {};
if (prPollingIntervalSeconds != null) {
github.prPollingIntervalSeconds = prPollingIntervalSeconds;
}
if (prTranscriptGistsEnabled != null) {
github.prTranscriptGists = { enabled: prTranscriptGistsEnabled };
}
if (autoMapByBranch != null) {
github.autoMapByBranch = autoMapByBranch;
}
return Object.keys(github).length ? github : undefined;
}

Expand All @@ -1954,6 +1958,10 @@ function mergeGithubConfig(
if (prTranscriptGistsEnabled != null) {
github.prTranscriptGists = { enabled: prTranscriptGistsEnabled };
}
const autoMapByBranch = local?.autoMapByBranch ?? shared?.autoMapByBranch;
if (autoMapByBranch != null) {
github.autoMapByBranch = autoMapByBranch;
}
return Object.keys(github).length ? github : undefined;
}

Expand Down
Loading