Skip to content

Commit 53c2847

Browse files
committed
update devin status handler and types
1 parent 0a10075 commit 53c2847

File tree

8 files changed

+147
-288
lines changed

8 files changed

+147
-288
lines changed

apps/bot/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"zod": "^4.1.13"
1515
},
1616
"devDependencies": {
17+
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
1718
"@types/node": "^20.19.25",
1819
"nock": "^14.0.10",
1920
"smee-client": "^2.0.4",

apps/bot/src/devin/detail.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// https://docs.devin.ai/api-reference/sessions/retrieve-details-about-an-existing-session
2+
import { DEVIN_API_BASE_URL, fetchFromDevin } from "./shared.js";
3+
4+
export const DevinSessionStatus = {
5+
Working: "working",
6+
Blocked: "blocked",
7+
Expired: "expired",
8+
Finished: "finished",
9+
SuspendRequested: "suspend_requested",
10+
SuspendRequestedFrontend: "suspend_requested_frontend",
11+
ResumeRequested: "resume_requested",
12+
ResumeRequestedFrontend: "resume_requested_frontend",
13+
Resumed: "resumed",
14+
} as const;
15+
16+
export type DevinSessionStatus =
17+
(typeof DevinSessionStatus)[keyof typeof DevinSessionStatus];
18+
19+
export interface DevinSessionDetail {
20+
session_id: string;
21+
status: string;
22+
title: string | null;
23+
created_at: string;
24+
updated_at: string;
25+
snapshot_id: string | null;
26+
playbook_id: string | null;
27+
tags: string[] | null;
28+
pull_request: { url: string } | null;
29+
structured_output: Record<string, unknown> | null;
30+
status_enum: DevinSessionStatus | null;
31+
}
32+
33+
export async function getDevinSessionDetail(
34+
sessionId: string,
35+
): Promise<DevinSessionDetail> {
36+
const url = `${DEVIN_API_BASE_URL}/sessions/${sessionId}`;
37+
38+
const response = await fetchFromDevin(url, {
39+
method: "GET",
40+
});
41+
42+
return (await response.json()) as DevinSessionDetail;
43+
}
44+
45+
export function isDevinSessionWorking(detail: DevinSessionDetail): boolean {
46+
return detail.status_enum === DevinSessionStatus.Working;
47+
}

apps/bot/src/devin/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from "./detail.js";
12
export * from "./list.js";
23
export * from "./send.js";
34
export * from "./shared.js";
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Probot } from "probot";
2+
3+
import {
4+
findRunningSessionForPR,
5+
getDevinSessionDetail,
6+
isDevinSessionWorking,
7+
} from "../devin/index.js";
8+
import { createOrUpdateCheck, ProbotContext } from "../lib/github-checks.js";
9+
10+
const CHECK_NAME = "Devin";
11+
12+
export function registerDevinStatusHandler(app: Probot): void {
13+
app.on(
14+
[
15+
"pull_request.opened",
16+
"pull_request.synchronize",
17+
"pull_request.reopened",
18+
],
19+
async (context) => {
20+
const pr = context.payload.pull_request;
21+
const owner = context.payload.repository.owner.login;
22+
const repo = context.payload.repository.name;
23+
const headSha = pr.head.sha;
24+
const prUrl = pr.html_url;
25+
26+
context.log.info(
27+
`[Devin] PR ${context.payload.action} for ${owner}/${repo}#${pr.number}`,
28+
);
29+
30+
try {
31+
await checkDevinSession(context, owner, repo, headSha, prUrl);
32+
} catch (error) {
33+
context.log.error(`[Devin] Failed to check Devin session: ${error}`);
34+
}
35+
},
36+
);
37+
}
38+
39+
async function checkDevinSession(
40+
context: ProbotContext,
41+
owner: string,
42+
repo: string,
43+
headSha: string,
44+
prUrl: string,
45+
): Promise<void> {
46+
const session = await findRunningSessionForPR(prUrl);
47+
if (!session) {
48+
return;
49+
}
50+
51+
const detail = await getDevinSessionDetail(session.session_id);
52+
if (!isDevinSessionWorking(detail)) {
53+
return;
54+
}
55+
56+
await createOrUpdateCheck(context, {
57+
owner,
58+
repo,
59+
name: CHECK_NAME,
60+
head_sha: headSha,
61+
status: "in_progress",
62+
output: {
63+
title: "Devin is working",
64+
summary: `Devin session ${session.session_id} is currently working on this PR.`,
65+
},
66+
});
67+
}

apps/bot/src/features/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1+
export * from "./devin-status.js";
12
export * from "./fix-merge-conflict.js";
2-
export * from "./mergeable.js";
33
export * from "./pr-closed.js";

apps/bot/src/features/mergeable.ts

Lines changed: 0 additions & 185 deletions
This file was deleted.

apps/bot/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ApplicationFunctionOptions, Probot } from "probot";
22

33
import {
4+
registerDevinStatusHandler,
45
registerFixMergeConflictHandler,
56
registerPrClosedHandler,
67
} from "./features/index.js";
@@ -14,6 +15,7 @@ export default (app: Probot, { getRouter }: ApplicationFunctionOptions) => {
1415
});
1516
}
1617

18+
registerDevinStatusHandler(app);
1719
registerFixMergeConflictHandler(app);
1820
registerPrClosedHandler(app);
1921
};

0 commit comments

Comments
 (0)