@framers/agentos-ext-github 0.4.0 → 0.4.2
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 +2 -2
- package/dist/__tests__/app.test.js +183 -0
- package/dist/__tests__/app.test.js.map +1 -1
- package/dist/__tests__/documents.test.js +46 -0
- package/dist/__tests__/documents.test.js.map +1 -1
- package/dist/app/http.d.ts +38 -5
- package/dist/app/http.d.ts.map +1 -1
- package/dist/app/http.js +129 -24
- package/dist/app/http.js.map +1 -1
- package/dist/app/installations.d.ts +2 -2
- package/dist/app/installations.js +3 -3
- package/dist/app/installations.js.map +1 -1
- package/dist/app/reconcile.js +1 -1
- package/dist/app/reconcile.js.map +1 -1
- package/dist/app/userFlow.d.ts +10 -4
- package/dist/app/userFlow.d.ts.map +1 -1
- package/dist/app/userFlow.js +46 -24
- package/dist/app/userFlow.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +1 -1
package/dist/app/userFlow.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
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
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
|
|
6
|
-
* whole under a cap before it is
|
|
5
|
+
* dropped, so the flow has no store. The exchange and the revocation end at their deadline; the token answer is read
|
|
6
|
+
* whole under a cap before it is parsed, its stream cancelled where the exchange ends, and a revocation's answer is
|
|
7
|
+
* told by its status alone.
|
|
7
8
|
* @module agentos-ext-github/app/userFlow
|
|
8
9
|
*/
|
|
9
|
-
import {
|
|
10
|
+
import { ReadCapExceeded } from '@framers/agentos-ext-account-sources';
|
|
10
11
|
import { OAuthGrantRefused, RedirectOAuthFlow } from '@framers/agentos/auth';
|
|
11
|
-
import { callDeadline, GITHUB_API_BASE, GITHUB_API_VERSION } from './http.js';
|
|
12
|
+
import { callDeadline, discardBody, GITHUB_API_BASE, GITHUB_API_VERSION, readBody, untilDeadline } from './http.js';
|
|
12
13
|
/** github.com's own authorize and token addresses. */
|
|
13
14
|
export const GITHUB_OAUTH_ENDPOINTS = {
|
|
14
15
|
authorize: 'https://github.com/login/oauth/authorize',
|
|
@@ -18,24 +19,25 @@ export const GITHUB_OAUTH_ENDPOINTS = {
|
|
|
18
19
|
const TOKEN_ANSWER_BYTES = 65_536;
|
|
19
20
|
/**
|
|
20
21
|
* A fetch that ends each call at its deadline and hands on the answer only once its whole body is read under
|
|
21
|
-
* {@link TOKEN_ANSWER_BYTES}, so the auth module never waits on GitHub past the deadline
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* {@link TOKEN_ANSWER_BYTES}, so the auth module never waits on GitHub past the deadline, even through a fetch that
|
|
23
|
+
* leaves the request or the body open past its signal; a body still being read at the deadline is cancelled there. A
|
|
24
|
+
* redirect is answered as it came and never followed: a 307 or 308 would send the code and the client secret on to its
|
|
25
|
+
* target, and the auth module refuses the redirect's status. A call that gets no answer, or whose body stops before its
|
|
26
|
+
* end, throws `GitHubNoAnswer`; a body past the cap throws an error that names the cap.
|
|
25
27
|
*/
|
|
26
28
|
function boundedFetch(fetchImpl, timeoutMs) {
|
|
27
29
|
return async (input, init) => {
|
|
28
30
|
const deadline = callDeadline(init?.signal ?? undefined, timeoutMs);
|
|
29
31
|
let response;
|
|
30
32
|
try {
|
|
31
|
-
response = await fetchImpl(input, { ...init, redirect: 'manual', signal: deadline.signal });
|
|
33
|
+
response = await untilDeadline(fetchImpl(input, { ...init, redirect: 'manual', signal: deadline.signal }), deadline, (late) => discardBody(late));
|
|
32
34
|
}
|
|
33
35
|
catch {
|
|
34
36
|
throw deadline.noAnswer();
|
|
35
37
|
}
|
|
36
38
|
let bytes;
|
|
37
39
|
try {
|
|
38
|
-
bytes = await
|
|
40
|
+
bytes = await readBody(response, TOKEN_ANSWER_BYTES, deadline);
|
|
39
41
|
}
|
|
40
42
|
catch (error) {
|
|
41
43
|
if (error instanceof ReadCapExceeded)
|
|
@@ -64,6 +66,10 @@ export class GitHubAppUserFlow extends RedirectOAuthFlow {
|
|
|
64
66
|
providerId = 'github';
|
|
65
67
|
/** GitHub's REST API, where `revoke` deletes a token. */
|
|
66
68
|
apiBase;
|
|
69
|
+
/** The fetch a revocation is sent with, as given: its answer is told by its status, its body never read. */
|
|
70
|
+
revocationFetch;
|
|
71
|
+
/** How long a revocation may take; {@link GITHUB_TIMEOUT_MS} when absent. */
|
|
72
|
+
revocationTimeoutMs;
|
|
67
73
|
/**
|
|
68
74
|
* @param client The app's client id and client secret.
|
|
69
75
|
* @param fetchImpl The fetch the code is exchanged and a token revoked with.
|
|
@@ -75,6 +81,8 @@ export class GitHubAppUserFlow extends RedirectOAuthFlow {
|
|
|
75
81
|
this.client = client;
|
|
76
82
|
this.endpoints = endpoints;
|
|
77
83
|
this.apiBase = options.apiBase ?? GITHUB_API_BASE;
|
|
84
|
+
this.revocationFetch = fetchImpl;
|
|
85
|
+
this.revocationTimeoutMs = options.timeoutMs;
|
|
78
86
|
}
|
|
79
87
|
/** GitHub's addresses and the app's client, with no scopes. */
|
|
80
88
|
getConfig() {
|
|
@@ -98,8 +106,9 @@ export class GitHubAppUserFlow extends RedirectOAuthFlow {
|
|
|
98
106
|
* Deletes a user access token at GitHub, so a token the caller is done with stops working at once instead of at its
|
|
99
107
|
* expiry: GitHub's "Delete an app token" (`DELETE /applications/{client_id}/token` with the token in a JSON body),
|
|
100
108
|
* authenticated as the app with Basic authentication, its client id and client secret
|
|
101
|
-
* (https://docs.github.com/en/rest/apps/oauth-applications#delete-an-app-token). GitHub answers 204
|
|
102
|
-
* throws `OAuthGrantRefused` (reason `revoke`, its status)
|
|
109
|
+
* (https://docs.github.com/en/rest/apps/oauth-applications#delete-an-app-token). GitHub answers 204, and 204 alone
|
|
110
|
+
* resolves: any other answer, another 2xx included, throws `OAuthGrantRefused` (reason `revoke`, its status), told
|
|
111
|
+
* by its status alone with its body released unread, however large or slow. A redirect is refused rather than
|
|
103
112
|
* followed so the client secret reaches GitHub's API alone, and a call that gets no answer by the flow's deadline
|
|
104
113
|
* throws `GitHubNoAnswer`.
|
|
105
114
|
*
|
|
@@ -107,18 +116,31 @@ export class GitHubAppUserFlow extends RedirectOAuthFlow {
|
|
|
107
116
|
*/
|
|
108
117
|
async revoke(token) {
|
|
109
118
|
const credentials = Buffer.from(`${this.client.clientId}:${this.client.clientSecret}`).toString('base64');
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
'
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
// Called as a plain function, as the fetch is everywhere else in the pack: a runtime's own fetch may refuse another receiver.
|
|
120
|
+
const send = this.revocationFetch;
|
|
121
|
+
const deadline = callDeadline(undefined, this.revocationTimeoutMs);
|
|
122
|
+
let response;
|
|
123
|
+
try {
|
|
124
|
+
response = await untilDeadline(send(`${this.apiBase}/applications/${encodeURIComponent(this.client.clientId)}/token`, {
|
|
125
|
+
method: 'DELETE',
|
|
126
|
+
headers: {
|
|
127
|
+
authorization: `Basic ${credentials}`,
|
|
128
|
+
accept: 'application/vnd.github+json',
|
|
129
|
+
'content-type': 'application/json',
|
|
130
|
+
'x-github-api-version': GITHUB_API_VERSION,
|
|
131
|
+
'user-agent': 'agentos-ext-github',
|
|
132
|
+
},
|
|
133
|
+
body: JSON.stringify({ access_token: token }),
|
|
134
|
+
redirect: 'manual',
|
|
135
|
+
signal: deadline.signal,
|
|
136
|
+
}), deadline, (late) => discardBody(late));
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
throw deadline.noAnswer();
|
|
140
|
+
}
|
|
141
|
+
// The status is the whole answer: the body is let go unread, and the revocation never waits on it.
|
|
142
|
+
discardBody(response);
|
|
143
|
+
if (response.status !== 204)
|
|
122
144
|
throw new OAuthGrantRefused('revoke', response.status);
|
|
123
145
|
}
|
|
124
146
|
}
|
package/dist/app/userFlow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userFlow.js","sourceRoot":"","sources":["../../src/app/userFlow.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"userFlow.js","sourceRoot":"","sources":["../../src/app/userFlow.ts"],"names":[],"mappings":"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"}
|
package/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://agentos.sh/schemas/extension-manifest-v1.json",
|
|
3
3
|
"id": "com.framers.integrations.github",
|
|
4
4
|
"name": "GitHub Extension",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.2",
|
|
6
6
|
"description": "GitHub API tools: 26 tools for repos, issues, PRs, branches, commits, releases, Actions, files, gists, and codebase indexing. Its ./documents entry adds a GitHub App's installation mode (the app's JWT, installation tokens, the user's web flow, webhook deliveries checked and read as typed events, a daily reconcile, every call under a deadline with rate limit timing) and a connector that reads a repository's documentation as account sources.",
|
|
7
7
|
"categories": ["tool", "developer"],
|
|
8
8
|
"extensions": [
|
package/package.json
CHANGED