2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import { LRUCache } from '../../../../base/common/map.js';
7
>
import { createDecorator } from '../../../instantiation/common/instantiation.js';
8
>
import { ILogService } from '../../../log/common/log.js';
9
>
import { IAgentHostGitHubEndpointService } from '../agentHostGitHubEndpointService.js';
10
>
11
>
export type FetchFunction = typeof globalThis.fetch;
12
>
13
>
/**
14
>
* Successful result of {@link IAgentHostOctoKitService.createPullRequest}.
15
>
*
16
>
* Mirrors the `CreatedPullRequest` type returned by `OctoKitService` in
17
>
* `extensions/copilot/src/platform/github/common/githubService.ts` so the
18
>
* shapes line up if/when the two are ported together.
19
>
*/
20
>
export interface CreatedPullRequest {
21
>
readonly url: string;
22
>
readonly number: number;
23
>
readonly nodeId?: string;
24
>
}
25
>
26
>
/**
27
>
* Merge strategy used when enabling auto-merge on a pull request.
28
>
* Mirrors the GitHub GraphQL `PullRequestMergeMethod` enum.
29
>
*/
30
>
export type AutoMergeMethod = 'MERGE' | 'SQUASH' | 'REBASE';
31
>
32
>
interface GitHubPullRequestResponseItem {
33
>
readonly number?: unknown;
34
>
readonly html_url?: unknown;
35
>
readonly node_id?: unknown;
36
>
}
37
>
38
>
export interface IGitHubApiResponse<T> {
39
>
readonly data: T | undefined;
40
>
readonly statusCode: number;
41
>
readonly etag?: string;
42
>
}
43
>
44
>
/**
45
>
* Minimal GitHub REST client living in the agent-host process.
46
>
*
47
>
* The agent host runs headless and has no access to the workbench
48
>
* `IOctoKitService` / Octokit / VS Code auth providers. This service is a
49
>
* deliberately small re-implementation of the bits we need, modelled on
50
>
* `OctoKitService` from the Copilot extension so the API surface is
51
>
* familiar. Only operations the agent host actually needs are exposed —
52
>
* extend this interface as new changeset operations are added.
53
>
*
54
>
* The caller is responsible for supplying a GitHub OAuth token with the
55
>
* scopes required by the operation (e.g. `repo` for {@link createPullRequest}).
56
>
* Tokens are typically obtained from the agent host's
57
>
* `authenticate(resource, token)` token store, which the workbench pushes
58
>
* on session create via the same channel used for `ICopilotApiService`.
59
>
*/
60
>
export interface IAgentHostOctoKitService {
61
>
readonly _serviceBrand: undefined;
62
>
63
>
/**
64
>
* Creates a pull request on github.com.
65
>
*
66
>
* Mirrors `OctoKitService.createPullRequest` from the Copilot extension.
67
>
* Throws on non-2xx responses or malformed payloads.
68
>
*/
69
>
createPullRequest(
70
>
owner: string,
71
>
repo: string,
72
>
title: string,
73
>
body: string,
74
>
head: string,
75
>
base: string,
76
>
draft: boolean,
77
>
token: string,
78
>
signal: AbortSignal,
79
>
): Promise<CreatedPullRequest>;
80
>
81
>
/** Finds the most recently updated pull request for `owner:branch`, if any. */
82
>
findPullRequestByHeadBranch(owner: string, repo: string, branch: string, token: string, signal: AbortSignal): Promise<CreatedPullRequest | undefined>;
83
>
84
>
/**
85
>
* Enables auto-merge on a pull request so GitHub merges it automatically
86
>
* once all required reviews and status checks pass.
87
>
*
88
>
* Issues the GraphQL `enablePullRequestAutoMerge` mutation. `pullRequestId`
89
>
* is the pull request's GraphQL global node id (see
90
>
* {@link CreatedPullRequest.nodeId}). Throws on GraphQL or transport errors,
91
>
* including when the repository does not allow the requested merge method or
92
>
* auto-merge is not enabled for the repository.
93
>
*/
94
>
enablePullRequestAutoMerge(pullRequestId: string, mergeMethod: AutoMergeMethod, token: string, signal: AbortSignal): Promise<void>;
95
>
}
96
>
97
>
export const IAgentHostOctoKitService = createDecorator<IAgentHostOctoKitService>('agentHostOctoKitService');
98
>
99
>
const GITHUB_API_VERSION = '2022-11-28';
100
>
const MAX_ERROR_RESPONSE_BODY_LENGTH = 500;
101
>
102
>
const ENABLE_AUTO_MERGE_MUTATION = `mutation EnableAutoMerge($pullRequestId: ID!, $mergeMethod: PullRequestMergeMethod!) {
103
>
enablePullRequestAutoMerge(input: { pullRequestId: $pullRequestId, mergeMethod: $mergeMethod }) {
104
>
pullRequest { id }
105
>
}
106
>
}`;
107
>
108
>
export class AgentHostOctoKitService implements IAgentHostOctoKitService {
109
>
110
>
declare readonly _serviceBrand: undefined;
111
>
112
>
private readonly _fetch: FetchFunction;
113
>
114
>
/**
115
>
* A cache of ETags for pull request search results.
116
>
*/
117
>
private readonly pullRequestSearchEtags = new LRUCache<string, string>(100);
118
>
119
>
constructor(
120
fetchFn: FetchFunction | undefined,
121
@ILogService private readonly _logService: ILogService,