@framers/agentos-ext-github 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -12
- package/dist/__tests__/app.test.js +229 -1
- package/dist/__tests__/app.test.js.map +1 -1
- package/dist/__tests__/documents.test.js +86 -1
- package/dist/__tests__/documents.test.js.map +1 -1
- package/dist/__tests__/entry.test.d.ts +2 -1
- package/dist/__tests__/entry.test.d.ts.map +1 -1
- package/dist/__tests__/entry.test.js +19 -2
- package/dist/__tests__/entry.test.js.map +1 -1
- package/dist/__tests__/events.test.d.ts +8 -0
- package/dist/__tests__/events.test.d.ts.map +1 -0
- package/dist/__tests__/events.test.js +304 -0
- package/dist/__tests__/events.test.js.map +1 -0
- package/dist/app/credential.d.ts +11 -2
- package/dist/app/credential.d.ts.map +1 -1
- package/dist/app/credential.js +6 -2
- package/dist/app/credential.js.map +1 -1
- package/dist/app/events.d.ts +142 -0
- package/dist/app/events.d.ts.map +1 -0
- package/dist/app/events.js +182 -0
- package/dist/app/events.js.map +1 -0
- package/dist/app/http.d.ts +83 -13
- package/dist/app/http.d.ts.map +1 -1
- package/dist/app/http.js +169 -23
- package/dist/app/http.js.map +1 -1
- package/dist/app/installations.d.ts +8 -6
- package/dist/app/installations.d.ts.map +1 -1
- package/dist/app/installations.js +21 -15
- package/dist/app/installations.js.map +1 -1
- package/dist/app/reconcile.d.ts +3 -2
- package/dist/app/reconcile.d.ts.map +1 -1
- package/dist/app/reconcile.js +11 -9
- package/dist/app/reconcile.js.map +1 -1
- package/dist/app/userFlow.d.ts +32 -7
- package/dist/app/userFlow.d.ts.map +1 -1
- package/dist/app/userFlow.js +80 -9
- package/dist/app/userFlow.js.map +1 -1
- package/dist/documents/GitHubDocumentsConnector.d.ts +9 -4
- package/dist/documents/GitHubDocumentsConnector.d.ts.map +1 -1
- package/dist/documents/GitHubDocumentsConnector.js +47 -22
- package/dist/documents/GitHubDocumentsConnector.js.map +1 -1
- package/dist/documents/index.d.ts +12 -7
- package/dist/documents/index.d.ts.map +1 -1
- package/dist/documents/index.js +11 -6
- package/dist/documents/index.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/manifest.json +30 -28
- package/package.json +5 -4
package/dist/app/http.js
CHANGED
|
@@ -1,21 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview GitHub's REST API over an injected `fetch`: one call with the headers every call
|
|
3
|
-
* of a listing followed through its `Link` header, and the small readers that turn an answer into
|
|
4
|
-
* answer outside 2xx throws {@link GitHubApiError}, whose message names the status and nothing of
|
|
2
|
+
* @fileoverview GitHub's REST API over an injected `fetch`: one call under a deadline with the headers every call
|
|
3
|
+
* carries, the pages of a listing followed through its `Link` header, and the small readers that turn an answer into
|
|
4
|
+
* typed fields. An answer outside 2xx throws {@link GitHubApiError}, whose message names the status and nothing of
|
|
5
|
+
* the answer's body and which says whether GitHub refused for a rate limit and when to try again; a call that gets no
|
|
6
|
+
* answer throws {@link GitHubNoAnswer}. An answer's body is read under a byte cap.
|
|
5
7
|
* @module agentos-ext-github/app/http
|
|
6
8
|
*/
|
|
9
|
+
import { readCapped, ReadCapExceeded } from '@framers/agentos-ext-account-sources';
|
|
7
10
|
/** The REST API version every call pins (`X-GitHub-Api-Version`). */
|
|
8
11
|
export const GITHUB_API_VERSION = '2026-03-10';
|
|
9
12
|
/** GitHub's REST API. */
|
|
10
13
|
export const GITHUB_API_BASE = 'https://api.github.com';
|
|
11
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* How long one call may take when its caller sets no other deadline, its answer's body included: 30 seconds. GitHub
|
|
16
|
+
* ends a request it takes more than 10 seconds to process.
|
|
17
|
+
*/
|
|
18
|
+
export const GITHUB_TIMEOUT_MS = 30_000;
|
|
19
|
+
/** The largest JSON answer read: 16 MiB, above the 7 MB at which GitHub cuts a recursive tree. */
|
|
20
|
+
export const GITHUB_MAX_ANSWER_BYTES = 16_777_216;
|
|
21
|
+
/** How much of a refused call's body is read to tell a rate limit from another 403. */
|
|
22
|
+
const REFUSAL_READ_BYTES = 16_384;
|
|
23
|
+
/** The wait after a rate limit that names no time: GitHub asks for at least a minute. */
|
|
24
|
+
const RATE_LIMIT_WAIT_MS = 60_000;
|
|
25
|
+
/**
|
|
26
|
+
* GitHub answered a status outside 2xx. The message holds the status alone, never the answer's body. `rateLimited`
|
|
27
|
+
* and `retryAt` follow GitHub's rules for a refused call
|
|
28
|
+
* (https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#exceeding-the-rate-limit):
|
|
29
|
+
* `retry-after` first, then `x-ratelimit-reset` when `x-ratelimit-remaining` is 0, else a minute for a rate limit
|
|
30
|
+
* that names no time.
|
|
31
|
+
*/
|
|
12
32
|
export class GitHubApiError extends Error {
|
|
13
33
|
status;
|
|
14
|
-
/**
|
|
15
|
-
|
|
34
|
+
/** True when GitHub refused the call for a rate limit. */
|
|
35
|
+
rateLimited;
|
|
36
|
+
/** Epoch milliseconds before which the call should not be made again, or null when GitHub asked for no wait. */
|
|
37
|
+
retryAt;
|
|
38
|
+
/**
|
|
39
|
+
* @param status The HTTP status GitHub answered.
|
|
40
|
+
* @param timing Whether it was a rate limit and when to try again; neither when absent.
|
|
41
|
+
*/
|
|
42
|
+
constructor(status, timing = {}) {
|
|
16
43
|
super(`GitHub answered ${status}`);
|
|
17
44
|
this.status = status;
|
|
18
45
|
this.name = 'GitHubApiError';
|
|
46
|
+
this.rateLimited = timing.rateLimited ?? false;
|
|
47
|
+
this.retryAt = timing.retryAt ?? null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** A call that got no answer from GitHub, or whose answer's body stopped before its end. Names no address or cause. */
|
|
51
|
+
export class GitHubNoAnswer extends Error {
|
|
52
|
+
reason;
|
|
53
|
+
/** @param reason Why the call got no answer. */
|
|
54
|
+
constructor(reason) {
|
|
55
|
+
super(`GitHub gave no answer: ${reason}`);
|
|
56
|
+
this.reason = reason;
|
|
57
|
+
this.name = 'GitHubNoAnswer';
|
|
19
58
|
}
|
|
20
59
|
}
|
|
21
60
|
/** The headers of a call: the bearer, the media type, the pinned API version and the pack's user agent. */
|
|
@@ -36,25 +75,132 @@ export async function discardBody(response) {
|
|
|
36
75
|
// The body is not needed; a failed cancel changes nothing for the caller.
|
|
37
76
|
}
|
|
38
77
|
}
|
|
39
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* The deadline of one call: its own timeout joined with the caller's signal, so the call and the reading of its body
|
|
80
|
+
* end at whichever comes first.
|
|
81
|
+
*
|
|
82
|
+
* @param signal The caller's signal, if any.
|
|
83
|
+
* @param timeoutMs How long the call may take; {@link GITHUB_TIMEOUT_MS} when absent.
|
|
84
|
+
*/
|
|
85
|
+
export function callDeadline(signal, timeoutMs = GITHUB_TIMEOUT_MS) {
|
|
86
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
87
|
+
return {
|
|
88
|
+
signal: signal === undefined ? timeout : AbortSignal.any([signal, timeout]),
|
|
89
|
+
noAnswer: () => new GitHubNoAnswer(signal?.aborted === true ? 'cancelled' : timeout.aborted ? 'timeout' : 'network'),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* True when a refused call's body names a rate limit, read up to {@link REFUSAL_READ_BYTES}; the body is never kept. A
|
|
94
|
+
* body that stops before its end (the deadline, the caller's cancel or the connection) throws the call's
|
|
95
|
+
* {@link GitHubNoAnswer}, since the refusal it would have named is unknown.
|
|
96
|
+
*/
|
|
97
|
+
async function bodyNamesRateLimit(response, deadline) {
|
|
98
|
+
let bytes;
|
|
99
|
+
try {
|
|
100
|
+
bytes = await readCapped(response, REFUSAL_READ_BYTES);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// An oversized refusal is told by its status and headers alone.
|
|
104
|
+
if (error instanceof ReadCapExceeded)
|
|
105
|
+
return false;
|
|
106
|
+
throw deadline.noAnswer();
|
|
107
|
+
}
|
|
108
|
+
let fields;
|
|
109
|
+
try {
|
|
110
|
+
fields = JSON.parse(new TextDecoder().decode(bytes));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// A refusal that is not JSON is told by its status and headers alone.
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (typeof fields !== 'object' || fields === null || Array.isArray(fields))
|
|
117
|
+
return false;
|
|
118
|
+
const { message, documentation_url: documentation } = fields;
|
|
119
|
+
return (typeof message === 'string' && /rate limit/i.test(message)) || (typeof documentation === 'string' && /rate-limit/i.test(documentation));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The time a `retry-after` header gives, as delay-seconds or an HTTP date, in epoch milliseconds, or null when it
|
|
123
|
+
* gives none that can be read. The wait is kept whole, however long: GitHub asks that no call be made before it ends.
|
|
124
|
+
* A date already past answers `now`.
|
|
125
|
+
*/
|
|
126
|
+
function retryAfterAt(header, now) {
|
|
127
|
+
const value = (header ?? '').trim();
|
|
128
|
+
if (/^\d+$/.test(value)) {
|
|
129
|
+
const at = now + Number(value) * 1000;
|
|
130
|
+
return Number.isSafeInteger(at) ? at : null;
|
|
131
|
+
}
|
|
132
|
+
const date = value === '' ? Number.NaN : Date.parse(value);
|
|
133
|
+
return Number.isFinite(date) ? Math.max(date, now) : null;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The refusal an answer outside 2xx reads as: its status, whether it was a rate limit, and when to try again. A 403
|
|
137
|
+
* whose headers do not tell is read on to its body, under the call's deadline.
|
|
138
|
+
*/
|
|
139
|
+
async function refusalOf(response, deadline) {
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
const retryAfter = retryAfterAt(response.headers.get('retry-after'), now);
|
|
142
|
+
const exhausted = response.headers.get('x-ratelimit-remaining')?.trim() === '0';
|
|
143
|
+
const reset = (response.headers.get('x-ratelimit-reset') ?? '').trim();
|
|
144
|
+
let rateLimited = response.status === 429 || (response.status === 403 && (retryAfter !== null || exhausted));
|
|
145
|
+
if (!rateLimited && response.status === 403)
|
|
146
|
+
rateLimited = await bodyNamesRateLimit(response, deadline);
|
|
147
|
+
else
|
|
148
|
+
await discardBody(response);
|
|
149
|
+
let retryAt = retryAfter;
|
|
150
|
+
if (retryAt === null && rateLimited) {
|
|
151
|
+
retryAt = exhausted && /^\d+$/.test(reset) ? Math.max(Number(reset) * 1000, now + 1000) : now + RATE_LIMIT_WAIT_MS;
|
|
152
|
+
}
|
|
153
|
+
return new GitHubApiError(response.status, { rateLimited, retryAt });
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Sends one call under its deadline and the caller's signal. An answer outside 2xx is read only as far as telling a
|
|
157
|
+
* rate limit needs and thrown as {@link GitHubApiError}; a call that gets no answer, or whose refusal's body stops
|
|
158
|
+
* while it is read, throws {@link GitHubNoAnswer}.
|
|
159
|
+
*/
|
|
40
160
|
export async function githubSend(fetchImpl, url, request) {
|
|
41
161
|
const headers = githubHeaders(request.token, request.accept);
|
|
42
162
|
if (request.body !== undefined)
|
|
43
163
|
headers['content-type'] = 'application/json';
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
164
|
+
const deadline = callDeadline(request.signal, request.timeoutMs);
|
|
165
|
+
let response;
|
|
166
|
+
try {
|
|
167
|
+
response = await fetchImpl(url, { method: request.method ?? 'GET', headers, body: request.body, signal: deadline.signal });
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// The fetch's own error would name the address or its cause; the reason is all a caller acts on.
|
|
171
|
+
throw deadline.noAnswer();
|
|
48
172
|
}
|
|
49
|
-
|
|
173
|
+
if (!response.ok)
|
|
174
|
+
throw await refusalOf(response, deadline);
|
|
175
|
+
return {
|
|
176
|
+
response,
|
|
177
|
+
async bytes(maxBytes) {
|
|
178
|
+
try {
|
|
179
|
+
return await readCapped(response, maxBytes);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (error instanceof ReadCapExceeded)
|
|
183
|
+
throw error;
|
|
184
|
+
throw deadline.noAnswer();
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
};
|
|
50
188
|
}
|
|
51
|
-
/** An answer's JSON, or `undefined` when it has no body (a 202 or a 204). */
|
|
52
|
-
async function answerOf(
|
|
53
|
-
|
|
54
|
-
|
|
189
|
+
/** An answer's JSON, read under {@link GITHUB_MAX_ANSWER_BYTES}, or `undefined` when it has no body (a 202 or a 204). */
|
|
190
|
+
async function answerOf(answer) {
|
|
191
|
+
let bytes;
|
|
192
|
+
try {
|
|
193
|
+
bytes = await answer.bytes(GITHUB_MAX_ANSWER_BYTES);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error instanceof ReadCapExceeded)
|
|
197
|
+
throw new Error('GitHub answered a body past the size cap');
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
if (bytes.byteLength === 0)
|
|
55
201
|
return undefined;
|
|
56
202
|
try {
|
|
57
|
-
return JSON.parse(
|
|
203
|
+
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
58
204
|
}
|
|
59
205
|
catch {
|
|
60
206
|
// The parser's message would quote the body.
|
|
@@ -64,7 +210,7 @@ async function answerOf(response) {
|
|
|
64
210
|
/**
|
|
65
211
|
* One call to `url` (an address under the API's base), answering its JSON, or `undefined` for an answer without a
|
|
66
212
|
* body. Sends `Authorization: Bearer <token>`, `Accept: application/vnd.github+json` unless told otherwise,
|
|
67
|
-
* `X-GitHub-Api-Version` and `User-Agent
|
|
213
|
+
* `X-GitHub-Api-Version` and `User-Agent`, and ends at its deadline.
|
|
68
214
|
*/
|
|
69
215
|
export async function githubFetch(fetchImpl, url, request) {
|
|
70
216
|
return answerOf(await githubSend(fetchImpl, url, request));
|
|
@@ -77,16 +223,16 @@ export function nextPage(link) {
|
|
|
77
223
|
return match === null ? null : match[1];
|
|
78
224
|
}
|
|
79
225
|
/**
|
|
80
|
-
* Each page of a listing as its JSON, following `Link: <...>; rel="next"
|
|
81
|
-
* first is refused, so the bearer never leaves the API it was sent to.
|
|
226
|
+
* Each page of a listing as its JSON, following `Link: <...>; rel="next"`, each page under a deadline of its own. A
|
|
227
|
+
* next page on another origin than the first is refused, so the bearer never leaves the API it was sent to.
|
|
82
228
|
*/
|
|
83
229
|
export async function* githubPages(fetchImpl, url, request) {
|
|
84
230
|
const origin = new URL(url).origin;
|
|
85
231
|
let next = url;
|
|
86
232
|
while (next !== null) {
|
|
87
|
-
const
|
|
88
|
-
const link = response.headers.get('link');
|
|
89
|
-
yield await answerOf(
|
|
233
|
+
const answer = await githubSend(fetchImpl, next, request);
|
|
234
|
+
const link = answer.response.headers.get('link');
|
|
235
|
+
yield await answerOf(answer);
|
|
90
236
|
next = nextPage(link);
|
|
91
237
|
if (next !== null && new URL(next).origin !== origin)
|
|
92
238
|
throw new Error('GitHub named a next page on another origin');
|
package/dist/app/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/app/http.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/app/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview What a user token reaches (the person, the app's installations they can access, and the repositories
|
|
3
3
|
* of an installation they can read) and the app's own installations, listed and removed with its JWT. Each function
|
|
4
|
-
* takes `{ fetchImpl, apiBase }` last: `fetch
|
|
4
|
+
* takes `{ fetchImpl, apiBase, timeoutMs }` last: `fetch`, GitHub's API and a 30-second deadline a call when absent.
|
|
5
5
|
* @module agentos-ext-github/app/installations
|
|
6
6
|
*/
|
|
7
7
|
import type { GitHubAppCredential } from './credential.js';
|
|
@@ -27,22 +27,24 @@ export interface UserRepository {
|
|
|
27
27
|
fullName: string;
|
|
28
28
|
defaultBranch: string;
|
|
29
29
|
}
|
|
30
|
+
/** An installation's account, as GitHub answers it and as a webhook event names it: a login, or an enterprise's slug, and an id. */
|
|
31
|
+
export declare function accountOf(value: unknown): GitHubAccount;
|
|
30
32
|
/** The person a user token belongs to (`GET /user`): the GitHub id and login a link is made by. */
|
|
31
|
-
export declare function userOfToken(token: string, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<GitHubUser>;
|
|
33
|
+
export declare function userOfToken(token: string, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<GitHubUser>;
|
|
32
34
|
/** The app's installations a user token can access (`GET /user/installations`), across every page. */
|
|
33
|
-
export declare function installationsOfUser(token: string, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<GitHubInstallation[]>;
|
|
35
|
+
export declare function installationsOfUser(token: string, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<GitHubInstallation[]>;
|
|
34
36
|
/**
|
|
35
37
|
* The repositories of an installation a user token can read (`GET /user/installations/{installation_id}/repositories`),
|
|
36
38
|
* across every page: those the person owns, collaborates on or reaches through an organization.
|
|
37
39
|
*/
|
|
38
|
-
export declare function repositoriesOfUser(token: string, installationId: number, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<UserRepository[]>;
|
|
40
|
+
export declare function repositoriesOfUser(token: string, installationId: number, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<UserRepository[]>;
|
|
39
41
|
/** Every installation of the app (`GET /app/installations`, with its JWT), across every page. */
|
|
40
|
-
export declare function listInstallations(credential: GitHubAppCredential, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<GitHubInstallation[]>;
|
|
42
|
+
export declare function listInstallations(credential: GitHubAppCredential, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<GitHubInstallation[]>;
|
|
41
43
|
/**
|
|
42
44
|
* Uninstalls the app from an account (`DELETE /app/installations/{installation_id}`, with its JWT). Answers without an
|
|
43
45
|
* error on 202 (GitHub has taken the removal and does it in the background at the pinned API version), on 204 (the
|
|
44
46
|
* answer of earlier versions) and on 404 (the installation is already gone); the answer's body is never read. Any
|
|
45
47
|
* other status throws {@link GitHubApiError}.
|
|
46
48
|
*/
|
|
47
|
-
export declare function uninstallInstallation(credential: GitHubAppCredential, installationId: number, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<void>;
|
|
49
|
+
export declare function uninstallInstallation(credential: GitHubAppCredential, installationId: number, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<void>;
|
|
48
50
|
//# sourceMappingURL=installations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/app/installations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAEnB,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uGAAuG;AACvG,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;CAC/B;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/app/installations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAEnB,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uGAAuG;AACvG,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;CAC/B;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,oIAAoI;AACpI,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAMvD;AAUD,mGAAmG;AACnG,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAGxJ;AAED,sGAAsG;AACtG,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GACjF,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAI/B;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GACjF,OAAO,CAAC,cAAc,EAAE,CAAC,CAW3B;AAED,iGAAiG;AACjG,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,mBAAmB,EAC/B,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GACjF,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAI/B;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,mBAAmB,EAC/B,cAAc,EAAE,MAAM,EACtB,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GACjF,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -1,35 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview What a user token reaches (the person, the app's installations they can access, and the repositories
|
|
3
3
|
* of an installation they can read) and the app's own installations, listed and removed with its JWT. Each function
|
|
4
|
-
* takes `{ fetchImpl, apiBase }` last: `fetch
|
|
4
|
+
* takes `{ fetchImpl, apiBase, timeoutMs }` last: `fetch`, GitHub's API and a 30-second deadline a call when absent.
|
|
5
5
|
* @module agentos-ext-github/app/installations
|
|
6
6
|
*/
|
|
7
7
|
import { arrayOf, discardBody, GITHUB_API_BASE, GitHubApiError, githubFetch, githubSend, numberIn, objectOf, pages, stringIn, } from './http.js';
|
|
8
|
+
/** An installation's account, as GitHub answers it and as a webhook event names it: a login, or an enterprise's slug, and an id. */
|
|
9
|
+
export function accountOf(value) {
|
|
10
|
+
const account = objectOf(value, "an installation's account");
|
|
11
|
+
return {
|
|
12
|
+
login: stringIn(account, typeof account.login === 'string' ? 'login' : 'slug', "an installation's account"),
|
|
13
|
+
id: numberIn(account, 'id', "an installation's account"),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
8
16
|
function installationOf(value) {
|
|
9
17
|
const fields = objectOf(value, 'an installation');
|
|
10
|
-
const account = fields.account === null || fields.account === undefined ? null : objectOf(fields.account, "an installation's account");
|
|
11
18
|
return {
|
|
12
19
|
id: numberIn(fields, 'id', 'an installation'),
|
|
13
|
-
account: account === null
|
|
14
|
-
? null
|
|
15
|
-
: { login: stringIn(account, typeof account.login === 'string' ? 'login' : 'slug', "an installation's account"), id: numberIn(account, 'id', "an installation's account") },
|
|
20
|
+
account: fields.account === null || fields.account === undefined ? null : accountOf(fields.account),
|
|
16
21
|
};
|
|
17
22
|
}
|
|
18
23
|
/** The person a user token belongs to (`GET /user`): the GitHub id and login a link is made by. */
|
|
19
|
-
export async function userOfToken(token, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
|
|
20
|
-
const user = objectOf(await githubFetch(fetchImpl, `${apiBase}/user`, { token }), 'a user');
|
|
24
|
+
export async function userOfToken(token, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
|
|
25
|
+
const user = objectOf(await githubFetch(fetchImpl, `${apiBase}/user`, { token, timeoutMs }), 'a user');
|
|
21
26
|
return { id: numberIn(user, 'id', 'a user'), login: stringIn(user, 'login', 'a user') };
|
|
22
27
|
}
|
|
23
28
|
/** The app's installations a user token can access (`GET /user/installations`), across every page. */
|
|
24
|
-
export async function installationsOfUser(token, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
|
|
25
|
-
return pages(fetchImpl, `${apiBase}/user/installations?per_page=100`, { token }, (page) => arrayOf(objectOf(page, 'a page of installations').installations, 'a page of installations').map(installationOf));
|
|
29
|
+
export async function installationsOfUser(token, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
|
|
30
|
+
return pages(fetchImpl, `${apiBase}/user/installations?per_page=100`, { token, timeoutMs }, (page) => arrayOf(objectOf(page, 'a page of installations').installations, 'a page of installations').map(installationOf));
|
|
26
31
|
}
|
|
27
32
|
/**
|
|
28
33
|
* The repositories of an installation a user token can read (`GET /user/installations/{installation_id}/repositories`),
|
|
29
34
|
* across every page: those the person owns, collaborates on or reaches through an organization.
|
|
30
35
|
*/
|
|
31
|
-
export async function repositoriesOfUser(token, installationId, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
|
|
32
|
-
return pages(fetchImpl, `${apiBase}/user/installations/${installationId}/repositories?per_page=100`, { token }, (page) => arrayOf(objectOf(page, 'a page of repositories').repositories, 'a page of repositories').map((value) => {
|
|
36
|
+
export async function repositoriesOfUser(token, installationId, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
|
|
37
|
+
return pages(fetchImpl, `${apiBase}/user/installations/${installationId}/repositories?per_page=100`, { token, timeoutMs }, (page) => arrayOf(objectOf(page, 'a page of repositories').repositories, 'a page of repositories').map((value) => {
|
|
33
38
|
const repository = objectOf(value, 'a repository');
|
|
34
39
|
return {
|
|
35
40
|
id: numberIn(repository, 'id', 'a repository'),
|
|
@@ -39,8 +44,8 @@ export async function repositoriesOfUser(token, installationId, { fetchImpl = fe
|
|
|
39
44
|
}));
|
|
40
45
|
}
|
|
41
46
|
/** Every installation of the app (`GET /app/installations`, with its JWT), across every page. */
|
|
42
|
-
export async function listInstallations(credential, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
|
|
43
|
-
return pages(fetchImpl, `${apiBase}/app/installations?per_page=100`, { token: credential.jwt() }, (page) => arrayOf(page, 'a page of installations').map(installationOf));
|
|
47
|
+
export async function listInstallations(credential, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
|
|
48
|
+
return pages(fetchImpl, `${apiBase}/app/installations?per_page=100`, { token: credential.jwt(), timeoutMs }, (page) => arrayOf(page, 'a page of installations').map(installationOf));
|
|
44
49
|
}
|
|
45
50
|
/**
|
|
46
51
|
* Uninstalls the app from an account (`DELETE /app/installations/{installation_id}`, with its JWT). Answers without an
|
|
@@ -48,9 +53,10 @@ export async function listInstallations(credential, { fetchImpl = fetch, apiBase
|
|
|
48
53
|
* answer of earlier versions) and on 404 (the installation is already gone); the answer's body is never read. Any
|
|
49
54
|
* other status throws {@link GitHubApiError}.
|
|
50
55
|
*/
|
|
51
|
-
export async function uninstallInstallation(credential, installationId, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
|
|
56
|
+
export async function uninstallInstallation(credential, installationId, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
|
|
52
57
|
try {
|
|
53
|
-
|
|
58
|
+
const answer = await githubSend(fetchImpl, `${apiBase}/app/installations/${installationId}`, { token: credential.jwt(), method: 'DELETE', timeoutMs });
|
|
59
|
+
await discardBody(answer.response);
|
|
54
60
|
}
|
|
55
61
|
catch (error) {
|
|
56
62
|
if (error instanceof GitHubApiError && error.status === 404)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installations.js","sourceRoot":"","sources":["../../src/app/installations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,OAAO,EACP,WAAW,EACX,eAAe,EACf,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,GAET,MAAM,WAAW,CAAC;AA2BnB,SAAS,
|
|
1
|
+
{"version":3,"file":"installations.js","sourceRoot":"","sources":["../../src/app/installations.ts"],"names":[],"mappings":"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"}
|
package/dist/app/reconcile.d.ts
CHANGED
|
@@ -68,7 +68,8 @@ export interface ReconcileOutcome {
|
|
|
68
68
|
* answered gone; each repository of the other links has its default branch's head read with an installation token
|
|
69
69
|
* narrowed to it and is answered moved when the head differs from the one last read, or unreadable with GitHub's
|
|
70
70
|
* status; and the app's failed deliveries since `since` are asked for again
|
|
71
|
-
* (`GET /app/hook/deliveries`, `POST /app/hook/deliveries/{delivery_id}/attempts`).
|
|
71
|
+
* (`GET /app/hook/deliveries`, `POST /app/hook/deliveries/{delivery_id}/attempts`). `timeoutMs` bounds every call,
|
|
72
|
+
* the installation token requests among them.
|
|
72
73
|
*
|
|
73
74
|
* A delivery whose newest attempt the receiving route answered with a 4xx other than 408 and 429 is not asked for
|
|
74
75
|
* again: such a refusal (a body past the route's size cap, a 413) answers the same on every attempt, and each new
|
|
@@ -81,5 +82,5 @@ export interface ReconcileOutcome {
|
|
|
81
82
|
* @param links The links to check.
|
|
82
83
|
* @param options The window's start, and where calls go.
|
|
83
84
|
*/
|
|
84
|
-
export declare function reconcileInstallations(credential: GitHubAppCredential, links: readonly ReconcileLink[], { since, fetchImpl, apiBase }: ReconcileOptions): Promise<ReconcileOutcome>;
|
|
85
|
+
export declare function reconcileInstallations(credential: GitHubAppCredential, links: readonly ReconcileLink[], { since, fetchImpl, apiBase, timeoutMs }: ReconcileOptions): Promise<ReconcileOutcome>;
|
|
85
86
|
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/app/reconcile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAGnB,2GAA2G;AAC3G,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC9C;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD,iFAAiF;IACjF,KAAK,EAAE,IAAI,CAAC;CACb;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8HAA8H;AAC9H,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oEAAoE;IACpE,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6EAA6E;IAC7E,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,kCAAkC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,uDAAuD;IACvD,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,kFAAkF;IAClF,UAAU,EAAE,iBAAiB,CAAC;CAC/B;
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/app/reconcile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAGnB,2GAA2G;AAC3G,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC9C;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD,iFAAiF;IACjF,KAAK,EAAE,IAAI,CAAC;CACb;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8HAA8H;AAC9H,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oEAAoE;IACpE,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6EAA6E;IAC7E,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,kCAAkC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,uDAAuD;IACvD,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,kFAAkF;IAClF,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAkFD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,mBAAmB,EAC/B,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,EAAE,KAAK,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,EAAE,gBAAgB,GACnF,OAAO,CAAC,gBAAgB,CAAC,CA2B3B"}
|
package/dist/app/reconcile.js
CHANGED
|
@@ -32,10 +32,10 @@ function refusedByRoute(attempt) {
|
|
|
32
32
|
* may lie on the pages not read; a redelivery GitHub refuses is answered with its status, and the others are still
|
|
33
33
|
* asked for.
|
|
34
34
|
*/
|
|
35
|
-
async function redeliverFailures(credential, since, fetchImpl, apiBase) {
|
|
35
|
+
async function redeliverFailures(credential, since, fetchImpl, apiBase, timeoutMs) {
|
|
36
36
|
const attemptsByGuid = new Map();
|
|
37
37
|
try {
|
|
38
|
-
for await (const page of githubPages(fetchImpl, `${apiBase}/app/hook/deliveries?per_page=100`, { token: credential.jwt() })) {
|
|
38
|
+
for await (const page of githubPages(fetchImpl, `${apiBase}/app/hook/deliveries?per_page=100`, { token: credential.jwt(), timeoutMs })) {
|
|
39
39
|
const deliveries = arrayOf(page, 'a page of deliveries').map(deliveryOf);
|
|
40
40
|
for (const delivery of deliveries) {
|
|
41
41
|
if (!(delivery.deliveredAt >= since))
|
|
@@ -64,7 +64,8 @@ async function redeliverFailures(credential, since, fetchImpl, apiBase) {
|
|
|
64
64
|
if (refusedByRoute(latest))
|
|
65
65
|
continue;
|
|
66
66
|
try {
|
|
67
|
-
|
|
67
|
+
const answer = await githubSend(fetchImpl, `${apiBase}/app/hook/deliveries/${latest.id}/attempts`, { token: credential.jwt(), method: 'POST', timeoutMs });
|
|
68
|
+
await discardBody(answer.response);
|
|
68
69
|
redelivered.push(latest.id);
|
|
69
70
|
}
|
|
70
71
|
catch (error) {
|
|
@@ -80,7 +81,8 @@ async function redeliverFailures(credential, since, fetchImpl, apiBase) {
|
|
|
80
81
|
* answered gone; each repository of the other links has its default branch's head read with an installation token
|
|
81
82
|
* narrowed to it and is answered moved when the head differs from the one last read, or unreadable with GitHub's
|
|
82
83
|
* status; and the app's failed deliveries since `since` are asked for again
|
|
83
|
-
* (`GET /app/hook/deliveries`, `POST /app/hook/deliveries/{delivery_id}/attempts`).
|
|
84
|
+
* (`GET /app/hook/deliveries`, `POST /app/hook/deliveries/{delivery_id}/attempts`). `timeoutMs` bounds every call,
|
|
85
|
+
* the installation token requests among them.
|
|
84
86
|
*
|
|
85
87
|
* A delivery whose newest attempt the receiving route answered with a 4xx other than 408 and 429 is not asked for
|
|
86
88
|
* again: such a refusal (a body past the route's size cap, a 413) answers the same on every attempt, and each new
|
|
@@ -93,8 +95,8 @@ async function redeliverFailures(credential, since, fetchImpl, apiBase) {
|
|
|
93
95
|
* @param links The links to check.
|
|
94
96
|
* @param options The window's start, and where calls go.
|
|
95
97
|
*/
|
|
96
|
-
export async function reconcileInstallations(credential, links, { since, fetchImpl = fetch, apiBase = GITHUB_API_BASE }) {
|
|
97
|
-
const listed = new Set((await listInstallations(credential, { fetchImpl, apiBase })).map((installation) => installation.id));
|
|
98
|
+
export async function reconcileInstallations(credential, links, { since, fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs }) {
|
|
99
|
+
const listed = new Set((await listInstallations(credential, { fetchImpl, apiBase, timeoutMs })).map((installation) => installation.id));
|
|
98
100
|
const gone = [];
|
|
99
101
|
const moved = [];
|
|
100
102
|
const unreadable = [];
|
|
@@ -106,8 +108,8 @@ export async function reconcileInstallations(credential, links, { since, fetchIm
|
|
|
106
108
|
}
|
|
107
109
|
for (const repository of link.repositories) {
|
|
108
110
|
try {
|
|
109
|
-
const token = await credential.installationToken(link.installationId, [repository.id]);
|
|
110
|
-
const branch = objectOf(await githubFetch(fetchImpl, `${apiBase}/repos/${repository.fullName}/branches/${encodeURIComponent(repository.defaultBranch)}`, { token }), 'a branch');
|
|
111
|
+
const token = await credential.installationToken(link.installationId, [repository.id], { timeoutMs });
|
|
112
|
+
const branch = objectOf(await githubFetch(fetchImpl, `${apiBase}/repos/${repository.fullName}/branches/${encodeURIComponent(repository.defaultBranch)}`, { token, timeoutMs }), 'a branch');
|
|
111
113
|
const head = stringIn(objectOf(branch.commit, "a branch's commit"), 'sha', "a branch's commit");
|
|
112
114
|
if (head !== repository.headCommit)
|
|
113
115
|
moved.push({ installationId: link.installationId, repositoryId: repository.id, head });
|
|
@@ -119,7 +121,7 @@ export async function reconcileInstallations(credential, links, { since, fetchIm
|
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
|
-
const { redelivered, deliveries } = await redeliverFailures(credential, since.getTime(), fetchImpl, apiBase);
|
|
124
|
+
const { redelivered, deliveries } = await redeliverFailures(credential, since.getTime(), fetchImpl, apiBase, timeoutMs);
|
|
123
125
|
return { gone, moved, redelivered, unreadable, deliveries };
|
|
124
126
|
}
|
|
125
127
|
//# sourceMappingURL=reconcile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/app/reconcile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,OAAO,EACP,WAAW,EACX,eAAe,EACf,cAAc,EACd,WAAW,EACX,WAAW,EACX,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,QAAQ,GAET,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AA4EvD,sFAAsF;AACtF,MAAM,yBAAyB,GAAwB,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAE3E,SAAS,UAAU,CAAC,KAAc;IAChC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC7C,OAAO;QACL,EAAE,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC;QACxC,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC;QAC5C,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC;QACvE,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,YAAY,CAAC;KAC1D,CAAC;AACJ,CAAC;AAED,oDAAoD;AACpD,SAAS,SAAS,CAAC,OAAiB;IAClC,OAAO,OAAO,CAAC,UAAU,IAAI,GAAG,IAAI,OAAO,CAAC,UAAU,GAAG,GAAG,CAAC;AAC/D,CAAC;AAED,wGAAwG;AACxG,SAAS,cAAc,CAAC,OAAiB;IACvC,OAAO,OAAO,CAAC,UAAU,IAAI,GAAG,IAAI,OAAO,CAAC,UAAU,GAAG,GAAG,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AACrH,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,iBAAiB,CAC9B,UAA+B,EAC/B,KAAa,EACb,SAAuB,EACvB,OAAe;
|
|
1
|
+
{"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/app/reconcile.ts"],"names":[],"mappings":"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"}
|
package/dist/app/userFlow.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview A GitHub App's user authorization on a web server: GitHub's authorize address with `state` and PKCE,
|
|
3
3
|
* and the code exchanged for a user access token, on the auth module's `RedirectOAuthFlow`. The token proves who the
|
|
4
|
-
* person is and which installations and repositories they can reach; it is used once
|
|
5
|
-
* store.
|
|
4
|
+
* person is and which installations and repositories they can reach; it is used once, then revoked at GitHub and
|
|
5
|
+
* dropped, so the flow has no store. The exchange and the revocation run under a deadline, and each answer is read
|
|
6
|
+
* whole under a cap before it is read.
|
|
6
7
|
* @module agentos-ext-github/app/userFlow
|
|
7
8
|
*/
|
|
8
9
|
import { RedirectOAuthFlow, type RedirectBegin, type RedirectOAuthConfig } from '@framers/agentos/auth';
|
|
@@ -16,28 +17,52 @@ export interface GitHubOAuthEndpoints {
|
|
|
16
17
|
authorize: string;
|
|
17
18
|
token: string;
|
|
18
19
|
}
|
|
20
|
+
/** How the flow reaches GitHub's token address and its REST API. */
|
|
21
|
+
export interface GitHubAppUserFlowOptions {
|
|
22
|
+
/** How long the code exchange or a revocation may take, its answer included; {@link GITHUB_TIMEOUT_MS} when absent. */
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
/** GitHub's REST API, where `revoke` deletes a token; {@link GITHUB_API_BASE} when absent, a stand-in's address in tests. */
|
|
25
|
+
apiBase?: string;
|
|
26
|
+
}
|
|
19
27
|
/** github.com's own authorize and token addresses. */
|
|
20
28
|
export declare const GITHUB_OAUTH_ENDPOINTS: GitHubOAuthEndpoints;
|
|
21
29
|
/**
|
|
22
30
|
* The user's web flow for a GitHub App: `begin` answers the authorize address with the state and the PKCE verifier
|
|
23
|
-
* for the caller to keep,
|
|
24
|
-
*
|
|
25
|
-
* no `scope`.
|
|
31
|
+
* for the caller to keep, `complete` checks the state and exchanges the code (with `code_verifier`, asking for a JSON
|
|
32
|
+
* answer) for the user's token, and `revoke` deletes that token at GitHub once the caller is done with it. A GitHub
|
|
33
|
+
* App's permissions are the app's, not the token's, so the address carries no `scope`. The exchange and a revocation
|
|
34
|
+
* end at their deadline with `GitHubNoAnswer`, and a redirect from either address is refused with `OAuthGrantRefused`
|
|
35
|
+
* (reason `exchange` or `revoke`, the redirect's status), never followed.
|
|
26
36
|
*/
|
|
27
37
|
export declare class GitHubAppUserFlow extends RedirectOAuthFlow {
|
|
28
38
|
private readonly client;
|
|
29
39
|
private readonly endpoints;
|
|
30
40
|
/** The provider's id. */
|
|
31
41
|
readonly providerId = "github";
|
|
42
|
+
/** GitHub's REST API, where `revoke` deletes a token. */
|
|
43
|
+
private readonly apiBase;
|
|
32
44
|
/**
|
|
33
45
|
* @param client The app's client id and client secret.
|
|
34
|
-
* @param fetchImpl The fetch the code is exchanged with.
|
|
46
|
+
* @param fetchImpl The fetch the code is exchanged and a token revoked with.
|
|
35
47
|
* @param endpoints GitHub's authorize and token addresses.
|
|
48
|
+
* @param options The deadline of the exchange and of a revocation, and GitHub's REST API.
|
|
36
49
|
*/
|
|
37
|
-
constructor(client: GitHubAppClient, fetchImpl?: typeof fetch, endpoints?: GitHubOAuthEndpoints);
|
|
50
|
+
constructor(client: GitHubAppClient, fetchImpl?: typeof fetch, endpoints?: GitHubOAuthEndpoints, options?: GitHubAppUserFlowOptions);
|
|
38
51
|
/** GitHub's addresses and the app's client, with no scopes. */
|
|
39
52
|
protected getConfig(): RedirectOAuthConfig;
|
|
40
53
|
/** The authorize address with the state and the PKCE challenge, leaving out the empty `scope` the base would send. */
|
|
41
54
|
begin(input: Parameters<RedirectOAuthFlow['begin']>[0]): RedirectBegin;
|
|
55
|
+
/**
|
|
56
|
+
* Deletes a user access token at GitHub, so a token the caller is done with stops working at once instead of at its
|
|
57
|
+
* expiry: GitHub's "Delete an app token" (`DELETE /applications/{client_id}/token` with the token in a JSON body),
|
|
58
|
+
* authenticated as the app with Basic authentication, its client id and client secret
|
|
59
|
+
* (https://docs.github.com/en/rest/apps/oauth-applications#delete-an-app-token). GitHub answers 204. Any other answer
|
|
60
|
+
* throws `OAuthGrantRefused` (reason `revoke`, its status) without its body, a redirect is refused rather than
|
|
61
|
+
* followed so the client secret reaches GitHub's API alone, and a call that gets no answer by the flow's deadline
|
|
62
|
+
* throws `GitHubNoAnswer`.
|
|
63
|
+
*
|
|
64
|
+
* @param token The user access token to delete.
|
|
65
|
+
*/
|
|
66
|
+
revoke(token: string): Promise<void>;
|
|
42
67
|
}
|
|
43
68
|
//# sourceMappingURL=userFlow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userFlow.d.ts","sourceRoot":"","sources":["../../src/app/userFlow.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"userFlow.d.ts","sourceRoot":"","sources":["../../src/app/userFlow.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAqB,iBAAiB,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAG3H,+DAA+D;AAC/D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,oEAAoE;AACpE,MAAM,WAAW,wBAAwB;IACvC,uHAAuH;IACvH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6HAA6H;IAC7H,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,sDAAsD;AACtD,eAAO,MAAM,sBAAsB,EAAE,oBAGpC,CAAC;AAoCF;;;;;;;GAOG;AACH,qBAAa,iBAAkB,SAAQ,iBAAiB;IAcpD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAf5B,yBAAyB;IACzB,QAAQ,CAAC,UAAU,YAAY;IAE/B,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC;;;;;OAKG;gBAEgB,MAAM,EAAE,eAAe,EACxC,SAAS,GAAE,OAAO,KAAa,EACd,SAAS,GAAE,oBAA6C,EACzE,OAAO,GAAE,wBAA6B;IAMxC,+DAA+D;IAC/D,SAAS,CAAC,SAAS,IAAI,mBAAmB;IAU1C,sHAAsH;IAC7G,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa;IAO/E;;;;;;;;;;OAUG;IACY,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAepD"}
|