@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/credential.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ export interface GitHubAppIdentity {
|
|
|
8
8
|
clientId: string;
|
|
9
9
|
privateKey: string;
|
|
10
10
|
}
|
|
11
|
+
/** How a token request ends: the caller's signal, and its deadline. */
|
|
12
|
+
export interface InstallationTokenOptions {
|
|
13
|
+
/** The caller's signal: the request ends with `GitHubNoAnswer` (`cancelled`) when it aborts. */
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
/** How long the request may take, its answer included; `GITHUB_TIMEOUT_MS` (30 seconds) when absent. */
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
}
|
|
11
18
|
/**
|
|
12
19
|
* The app's JWT: RS256, issued 60 seconds back against clock drift, valid nine minutes (GitHub takes at most ten),
|
|
13
20
|
* with the client id as its issuer.
|
|
@@ -32,13 +39,15 @@ export declare class GitHubAppCredential {
|
|
|
32
39
|
jwt(): string;
|
|
33
40
|
/**
|
|
34
41
|
* An installation token that reads only the repositories asked for, held until five minutes before it ends, so a
|
|
35
|
-
* second call within that time makes no request. GitHub's tokens last an hour.
|
|
42
|
+
* second call within that time makes no request. GitHub's tokens last an hour. A refusal throws `GitHubApiError`
|
|
43
|
+
* (404 for an installation GitHub no longer has), and a request that gets no answer throws `GitHubNoAnswer`.
|
|
36
44
|
*
|
|
37
45
|
* @param installationId The installation the token acts for.
|
|
38
46
|
* @param repositoryIds The repositories it may read: at least one.
|
|
47
|
+
* @param options The caller's signal and the request's deadline.
|
|
39
48
|
* @throws TypeError when `repositoryIds` is empty, before any request: GitHub answers a token request that names no
|
|
40
49
|
* repository with a token for every repository the installation reaches.
|
|
41
50
|
*/
|
|
42
|
-
installationToken(installationId: number, repositoryIds: readonly number[]): Promise<string>;
|
|
51
|
+
installationToken(installationId: number, repositoryIds: readonly number[], options?: InstallationTokenOptions): Promise<string>;
|
|
43
52
|
}
|
|
44
53
|
//# sourceMappingURL=credential.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../src/app/credential.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AASD;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,iBAAiB,EAAE,UAAU,SAAgC,GAAG,MAAM,CAKtG;AAED,kHAAkH;AAClH,qBAAa,mBAAmB;IAQlB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAAqB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAAwB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2D;IAEhF;;;;OAIG;gBAC0B,QAAQ,EAAE,iBAAiB,EAAmB,SAAS,GAAE,OAAO,KAAa,EAAmB,OAAO,SAAkB;IAEtJ,2CAA2C;IAC3C,GAAG,IAAI,MAAM;IAIb
|
|
1
|
+
{"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../src/app/credential.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,uEAAuE;AACvE,MAAM,WAAW,wBAAwB;IACvC,gGAAgG;IAChG,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,wGAAwG;IACxG,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AASD;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,iBAAiB,EAAE,UAAU,SAAgC,GAAG,MAAM,CAKtG;AAED,kHAAkH;AAClH,qBAAa,mBAAmB;IAQlB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAAqB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAAwB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2D;IAEhF;;;;OAIG;gBAC0B,QAAQ,EAAE,iBAAiB,EAAmB,SAAS,GAAE,OAAO,KAAa,EAAmB,OAAO,SAAkB;IAEtJ,2CAA2C;IAC3C,GAAG,IAAI,MAAM;IAIb;;;;;;;;;;OAUG;IACG,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,MAAM,CAAC;CAwB3I"}
|
package/dist/app/credential.js
CHANGED
|
@@ -45,14 +45,16 @@ export class GitHubAppCredential {
|
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* An installation token that reads only the repositories asked for, held until five minutes before it ends, so a
|
|
48
|
-
* second call within that time makes no request. GitHub's tokens last an hour.
|
|
48
|
+
* second call within that time makes no request. GitHub's tokens last an hour. A refusal throws `GitHubApiError`
|
|
49
|
+
* (404 for an installation GitHub no longer has), and a request that gets no answer throws `GitHubNoAnswer`.
|
|
49
50
|
*
|
|
50
51
|
* @param installationId The installation the token acts for.
|
|
51
52
|
* @param repositoryIds The repositories it may read: at least one.
|
|
53
|
+
* @param options The caller's signal and the request's deadline.
|
|
52
54
|
* @throws TypeError when `repositoryIds` is empty, before any request: GitHub answers a token request that names no
|
|
53
55
|
* repository with a token for every repository the installation reaches.
|
|
54
56
|
*/
|
|
55
|
-
async installationToken(installationId, repositoryIds) {
|
|
57
|
+
async installationToken(installationId, repositoryIds, options = {}) {
|
|
56
58
|
if (repositoryIds.length === 0) {
|
|
57
59
|
throw new TypeError('An installation token needs at least one repository id: with none, GitHub mints one for every repository of the installation');
|
|
58
60
|
}
|
|
@@ -69,6 +71,8 @@ export class GitHubAppCredential {
|
|
|
69
71
|
token: this.jwt(),
|
|
70
72
|
method: 'POST',
|
|
71
73
|
body: JSON.stringify({ repository_ids: repositoryIds }),
|
|
74
|
+
signal: options.signal,
|
|
75
|
+
timeoutMs: options.timeoutMs,
|
|
72
76
|
}), 'an installation token');
|
|
73
77
|
const token = stringIn(answer, 'token', 'an installation token');
|
|
74
78
|
this.held.set(key, { token, expiresAt: Date.parse(stringIn(answer, 'expires_at', 'an installation token')) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential.js","sourceRoot":"","sources":["../../src/app/credential.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"credential.js","sourceRoot":"","sources":["../../src/app/credential.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A GitHub App's webhook deliveries read as typed events. A delivery's size is checked first, then its
|
|
3
|
+
* `X-Hub-Signature-256` on the raw body, then its `X-GitHub-Delivery` id, and only then is its body parsed; each event
|
|
4
|
+
* an app reading its installations' repositories acts on (a push, an installation's actions, the repositories it
|
|
5
|
+
* reaches, its account renamed, a ping) is answered with the fields that reading needs, and every other delivery as
|
|
6
|
+
* `other`. No text of a payload reaches an error, and nothing of a payload but those fields leaves the reader.
|
|
7
|
+
* Payloads as https://docs.github.com/en/webhooks/webhook-events-and-payloads documents them.
|
|
8
|
+
* @module agentos-ext-github/app/events
|
|
9
|
+
*/
|
|
10
|
+
import { type GitHubAccount } from './installations.js';
|
|
11
|
+
/** The largest delivery read by default: 25 MiB (26,214,400 bytes). GitHub delivers no payload past 25 MB. */
|
|
12
|
+
export declare const DEFAULT_MAX_DELIVERY_BYTES = 26214400;
|
|
13
|
+
/**
|
|
14
|
+
* Why a delivery was refused: `too_large` past the cap (answer 413), `signature` for a signature that does not hold
|
|
15
|
+
* or is missing (answer 401), `delivery` for a missing or malformed `X-GitHub-Delivery` (answer 400), `body` for a
|
|
16
|
+
* signed body that is not a JSON object in UTF-8 (answer 400).
|
|
17
|
+
*/
|
|
18
|
+
export type WebhookRefusalReason = 'too_large' | 'signature' | 'delivery' | 'body';
|
|
19
|
+
/** A delivery the reader refused. The message names the reason alone, never the body or a header's value. */
|
|
20
|
+
export declare class WebhookDeliveryRefused extends Error {
|
|
21
|
+
readonly reason: WebhookRefusalReason;
|
|
22
|
+
/** @param reason Why the delivery was refused. */
|
|
23
|
+
constructor(reason: WebhookRefusalReason);
|
|
24
|
+
}
|
|
25
|
+
/** A repository an event names. */
|
|
26
|
+
export interface GitHubEventRepository {
|
|
27
|
+
id: number;
|
|
28
|
+
/** `owner/name`. */
|
|
29
|
+
fullName: string;
|
|
30
|
+
}
|
|
31
|
+
/** A push to a repository the installation reaches. */
|
|
32
|
+
export interface GitHubPushEvent {
|
|
33
|
+
kind: 'push';
|
|
34
|
+
installationId: number;
|
|
35
|
+
repository: GitHubEventRepository & {
|
|
36
|
+
defaultBranch: string;
|
|
37
|
+
};
|
|
38
|
+
/** The full ref pushed, such as `refs/heads/main` or `refs/tags/v1.0.0`. */
|
|
39
|
+
ref: string;
|
|
40
|
+
/** The ref's commit before the push; zeros when the push created the ref. */
|
|
41
|
+
before: string;
|
|
42
|
+
/** The ref's commit after the push; zeros when the push deleted the ref. */
|
|
43
|
+
after: string;
|
|
44
|
+
created: boolean;
|
|
45
|
+
deleted: boolean;
|
|
46
|
+
forced: boolean;
|
|
47
|
+
/** True when the push moved the repository's default branch: `ref` is `refs/heads/<default branch>` and it was not deleted. */
|
|
48
|
+
toDefaultBranch: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** An installation's action: installed, uninstalled, suspended, unsuspended, or new permissions accepted. */
|
|
51
|
+
export interface GitHubInstallationEvent {
|
|
52
|
+
kind: 'installation';
|
|
53
|
+
action: 'created' | 'deleted' | 'suspend' | 'unsuspend' | 'new_permissions_accepted';
|
|
54
|
+
installationId: number;
|
|
55
|
+
/** The account the app is installed on, or null when GitHub sends none. */
|
|
56
|
+
account: GitHubAccount | null;
|
|
57
|
+
/** The repositories the installation can access, where GitHub lists them (an installation on selected repositories); else empty. */
|
|
58
|
+
repositories: GitHubEventRepository[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Repositories added to or removed from an installation. When the installation goes from all of an account's
|
|
62
|
+
* repositories to a selection, GitHub sends `removed` with no repository listed, so `removed` can be empty while
|
|
63
|
+
* repositories left: read the installation's repositories again.
|
|
64
|
+
*/
|
|
65
|
+
export interface GitHubInstallationRepositoriesEvent {
|
|
66
|
+
kind: 'installation_repositories';
|
|
67
|
+
action: 'added' | 'removed';
|
|
68
|
+
installationId: number;
|
|
69
|
+
/** Whether the installation now reaches all of the account's repositories or a selection. */
|
|
70
|
+
selection: 'all' | 'selected';
|
|
71
|
+
added: GitHubEventRepository[];
|
|
72
|
+
removed: GitHubEventRepository[];
|
|
73
|
+
}
|
|
74
|
+
/** The account an installation is on was renamed. */
|
|
75
|
+
export interface GitHubInstallationTargetEvent {
|
|
76
|
+
kind: 'installation_target';
|
|
77
|
+
action: 'renamed';
|
|
78
|
+
installationId: number;
|
|
79
|
+
/** The account under its new login, or an enterprise's new slug. */
|
|
80
|
+
account: GitHubAccount;
|
|
81
|
+
}
|
|
82
|
+
/** GitHub's check that the webhook is set up. */
|
|
83
|
+
export interface GitHubPingEvent {
|
|
84
|
+
kind: 'ping';
|
|
85
|
+
}
|
|
86
|
+
/** A delivery the reader does not read: another event, another action, or a payload without the fields above. */
|
|
87
|
+
export interface GitHubOtherEvent {
|
|
88
|
+
kind: 'other';
|
|
89
|
+
}
|
|
90
|
+
/** A delivery's event. */
|
|
91
|
+
export type GitHubEvent = GitHubPushEvent | GitHubInstallationEvent | GitHubInstallationRepositoriesEvent | GitHubInstallationTargetEvent | GitHubPingEvent | GitHubOtherEvent;
|
|
92
|
+
/** A delivery read. */
|
|
93
|
+
export interface GitHubDelivery {
|
|
94
|
+
/** The `X-GitHub-Delivery` GUID: the same for a redelivery, so a receiver applies each id once. */
|
|
95
|
+
id: string;
|
|
96
|
+
/** The `X-GitHub-Event` name when it is an event name (lower case letters, digits and underscores), else `''`. */
|
|
97
|
+
name: string;
|
|
98
|
+
event: GitHubEvent;
|
|
99
|
+
}
|
|
100
|
+
/** What a delivery is read from: its headers and its raw body exactly as received. */
|
|
101
|
+
export interface WebhookDeliveryInput {
|
|
102
|
+
headers: {
|
|
103
|
+
get(name: string): string | null;
|
|
104
|
+
};
|
|
105
|
+
body: Uint8Array;
|
|
106
|
+
}
|
|
107
|
+
/** What a request is read from: a `Request`, or anything with its headers and its body's stream. */
|
|
108
|
+
export interface WebhookRequestInput {
|
|
109
|
+
headers: {
|
|
110
|
+
get(name: string): string | null;
|
|
111
|
+
};
|
|
112
|
+
body: ReadableStream<Uint8Array> | null;
|
|
113
|
+
}
|
|
114
|
+
/** The reader's bound. */
|
|
115
|
+
export interface ReadWebhookOptions {
|
|
116
|
+
/** The largest body read, in bytes ({@link DEFAULT_MAX_DELIVERY_BYTES} when absent). */
|
|
117
|
+
maxBytes?: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Reads a delivery already received whole: refuses it past `maxBytes` (`too_large`), when its signature under
|
|
121
|
+
* `secret` does not hold (`signature`), when it has no usable `X-GitHub-Delivery` (`delivery`), or when its signed
|
|
122
|
+
* body is not a JSON object in UTF-8 (`body`), each with {@link WebhookDeliveryRefused}; answers its id, its event's
|
|
123
|
+
* name and its typed event otherwise. A receiver answers GitHub at once and applies each delivery id once: GitHub
|
|
124
|
+
* expects a 2xx within 10 seconds, and a redelivery carries the id of the delivery it repeats.
|
|
125
|
+
*
|
|
126
|
+
* @param input The delivery's headers and its raw body exactly as received.
|
|
127
|
+
* @param secret The webhook's secret, never empty.
|
|
128
|
+
* @param options The largest body read.
|
|
129
|
+
* @throws TypeError when the secret is empty or missing: anyone can sign a delivery under an empty key.
|
|
130
|
+
*/
|
|
131
|
+
export declare function readWebhookDelivery(input: WebhookDeliveryInput, secret: string, options?: ReadWebhookOptions): GitHubDelivery;
|
|
132
|
+
/**
|
|
133
|
+
* Reads a delivery from a request: its body read as it streams and refused at `maxBytes` (`too_large`), before any
|
|
134
|
+
* check that needs it whole, the stream then cancelled unread; a `Content-Length` past the cap is refused before a byte
|
|
135
|
+
* is read. Then as {@link readWebhookDelivery}.
|
|
136
|
+
*
|
|
137
|
+
* @param request The request, or its headers and body stream.
|
|
138
|
+
* @param secret The webhook's secret, never empty.
|
|
139
|
+
* @param options The largest body read.
|
|
140
|
+
*/
|
|
141
|
+
export declare function readWebhookRequest(request: WebhookRequestInput, secret: string, options?: ReadWebhookOptions): Promise<GitHubDelivery>;
|
|
142
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/app/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A GitHub App's webhook deliveries read as typed events. A delivery's size is checked first, then its
|
|
3
|
+
* `X-Hub-Signature-256` on the raw body, then its `X-GitHub-Delivery` id, and only then is its body parsed; each event
|
|
4
|
+
* an app reading its installations' repositories acts on (a push, an installation's actions, the repositories it
|
|
5
|
+
* reaches, its account renamed, a ping) is answered with the fields that reading needs, and every other delivery as
|
|
6
|
+
* `other`. No text of a payload reaches an error, and nothing of a payload but those fields leaves the reader.
|
|
7
|
+
* Payloads as https://docs.github.com/en/webhooks/webhook-events-and-payloads documents them.
|
|
8
|
+
* @module agentos-ext-github/app/events
|
|
9
|
+
*/
|
|
10
|
+
import { readCapped, ReadCapExceeded } from '@framers/agentos-ext-account-sources';
|
|
11
|
+
import { arrayOf, numberIn, objectOf, stringIn } from './http.js';
|
|
12
|
+
import { accountOf } from './installations.js';
|
|
13
|
+
import { verifyWebhookSignature } from './webhook.js';
|
|
14
|
+
/** The largest delivery read by default: 25 MiB (26,214,400 bytes). GitHub delivers no payload past 25 MB. */
|
|
15
|
+
export const DEFAULT_MAX_DELIVERY_BYTES = 26_214_400;
|
|
16
|
+
/** A delivery the reader refused. The message names the reason alone, never the body or a header's value. */
|
|
17
|
+
export class WebhookDeliveryRefused extends Error {
|
|
18
|
+
reason;
|
|
19
|
+
/** @param reason Why the delivery was refused. */
|
|
20
|
+
constructor(reason) {
|
|
21
|
+
super(`the webhook delivery was refused: ${reason}`);
|
|
22
|
+
this.reason = reason;
|
|
23
|
+
this.name = 'WebhookDeliveryRefused';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const INSTALLATION_ACTIONS = new Set(['created', 'deleted', 'suspend', 'unsuspend', 'new_permissions_accepted']);
|
|
27
|
+
/** A delivery id: a GUID's hexadecimal digits and hyphens. */
|
|
28
|
+
const DELIVERY_ID = /^[0-9A-Fa-f-]{8,64}$/;
|
|
29
|
+
/** An event name as GitHub sends it. */
|
|
30
|
+
const EVENT_NAME = /^[a-z][a-z0-9_]{0,63}$/;
|
|
31
|
+
/** A commit's SHA-1 or SHA-256 name. */
|
|
32
|
+
const COMMIT = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
33
|
+
function booleanIn(fields, key, what) {
|
|
34
|
+
const value = fields[key];
|
|
35
|
+
if (typeof value !== 'boolean')
|
|
36
|
+
throw new Error(`GitHub sent ${what} without a boolean ${key}`);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function commitIn(fields, key) {
|
|
40
|
+
const value = stringIn(fields, key, 'a push');
|
|
41
|
+
if (!COMMIT.test(value))
|
|
42
|
+
throw new Error(`GitHub sent a push whose ${key} is not a commit`);
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
function installationIdOf(payload) {
|
|
46
|
+
return numberIn(objectOf(payload.installation, "an event's installation"), 'id', "an event's installation");
|
|
47
|
+
}
|
|
48
|
+
function repositoriesIn(value, what) {
|
|
49
|
+
return arrayOf(value, what).map((entry) => {
|
|
50
|
+
const repository = objectOf(entry, what);
|
|
51
|
+
return { id: numberIn(repository, 'id', what), fullName: stringIn(repository, 'full_name', what) };
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function pushOf(payload) {
|
|
55
|
+
const repository = objectOf(payload.repository, "a push's repository");
|
|
56
|
+
const defaultBranch = stringIn(repository, 'default_branch', "a push's repository");
|
|
57
|
+
const ref = stringIn(payload, 'ref', 'a push');
|
|
58
|
+
const deleted = booleanIn(payload, 'deleted', 'a push');
|
|
59
|
+
return {
|
|
60
|
+
kind: 'push',
|
|
61
|
+
installationId: installationIdOf(payload),
|
|
62
|
+
repository: { id: numberIn(repository, 'id', "a push's repository"), fullName: stringIn(repository, 'full_name', "a push's repository"), defaultBranch },
|
|
63
|
+
ref,
|
|
64
|
+
before: commitIn(payload, 'before'),
|
|
65
|
+
after: commitIn(payload, 'after'),
|
|
66
|
+
created: booleanIn(payload, 'created', 'a push'),
|
|
67
|
+
deleted,
|
|
68
|
+
forced: booleanIn(payload, 'forced', 'a push'),
|
|
69
|
+
toDefaultBranch: !deleted && ref === `refs/heads/${defaultBranch}`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function installationOf(payload) {
|
|
73
|
+
const action = stringIn(payload, 'action', 'an installation event');
|
|
74
|
+
if (!INSTALLATION_ACTIONS.has(action))
|
|
75
|
+
return { kind: 'other' };
|
|
76
|
+
const installation = objectOf(payload.installation, "an event's installation");
|
|
77
|
+
return {
|
|
78
|
+
kind: 'installation',
|
|
79
|
+
action: action,
|
|
80
|
+
installationId: numberIn(installation, 'id', "an event's installation"),
|
|
81
|
+
account: installation.account === null || installation.account === undefined ? null : accountOf(installation.account),
|
|
82
|
+
repositories: payload.repositories === undefined || payload.repositories === null ? [] : repositoriesIn(payload.repositories, 'an installed repository'),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function installationRepositoriesOf(payload) {
|
|
86
|
+
const action = stringIn(payload, 'action', 'an installation_repositories event');
|
|
87
|
+
if (action !== 'added' && action !== 'removed')
|
|
88
|
+
return { kind: 'other' };
|
|
89
|
+
const selection = stringIn(payload, 'repository_selection', 'an installation_repositories event');
|
|
90
|
+
if (selection !== 'all' && selection !== 'selected')
|
|
91
|
+
return { kind: 'other' };
|
|
92
|
+
return {
|
|
93
|
+
kind: 'installation_repositories',
|
|
94
|
+
action,
|
|
95
|
+
installationId: installationIdOf(payload),
|
|
96
|
+
selection,
|
|
97
|
+
added: repositoriesIn(payload.repositories_added, 'an added repository'),
|
|
98
|
+
removed: repositoriesIn(payload.repositories_removed, 'a removed repository'),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function installationTargetOf(payload) {
|
|
102
|
+
if (stringIn(payload, 'action', 'an installation_target event') !== 'renamed')
|
|
103
|
+
return { kind: 'other' };
|
|
104
|
+
return { kind: 'installation_target', action: 'renamed', installationId: installationIdOf(payload), account: accountOf(payload.account) };
|
|
105
|
+
}
|
|
106
|
+
/** The event a payload holds, by the delivery's event name; `other` for a name or a payload it does not read. */
|
|
107
|
+
function eventOf(name, payload) {
|
|
108
|
+
try {
|
|
109
|
+
if (name === 'push')
|
|
110
|
+
return pushOf(payload);
|
|
111
|
+
if (name === 'installation')
|
|
112
|
+
return installationOf(payload);
|
|
113
|
+
if (name === 'installation_repositories')
|
|
114
|
+
return installationRepositoriesOf(payload);
|
|
115
|
+
if (name === 'installation_target')
|
|
116
|
+
return installationTargetOf(payload);
|
|
117
|
+
if (name === 'ping')
|
|
118
|
+
return { kind: 'ping' };
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// A payload without a field the event needs is an event this reader does not read; its text is not kept.
|
|
122
|
+
}
|
|
123
|
+
return { kind: 'other' };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Reads a delivery already received whole: refuses it past `maxBytes` (`too_large`), when its signature under
|
|
127
|
+
* `secret` does not hold (`signature`), when it has no usable `X-GitHub-Delivery` (`delivery`), or when its signed
|
|
128
|
+
* body is not a JSON object in UTF-8 (`body`), each with {@link WebhookDeliveryRefused}; answers its id, its event's
|
|
129
|
+
* name and its typed event otherwise. A receiver answers GitHub at once and applies each delivery id once: GitHub
|
|
130
|
+
* expects a 2xx within 10 seconds, and a redelivery carries the id of the delivery it repeats.
|
|
131
|
+
*
|
|
132
|
+
* @param input The delivery's headers and its raw body exactly as received.
|
|
133
|
+
* @param secret The webhook's secret, never empty.
|
|
134
|
+
* @param options The largest body read.
|
|
135
|
+
* @throws TypeError when the secret is empty or missing: anyone can sign a delivery under an empty key.
|
|
136
|
+
*/
|
|
137
|
+
export function readWebhookDelivery(input, secret, options = {}) {
|
|
138
|
+
if (input.body.byteLength > (options.maxBytes ?? DEFAULT_MAX_DELIVERY_BYTES))
|
|
139
|
+
throw new WebhookDeliveryRefused('too_large');
|
|
140
|
+
if (!verifyWebhookSignature(input.body, input.headers.get('x-hub-signature-256'), secret))
|
|
141
|
+
throw new WebhookDeliveryRefused('signature');
|
|
142
|
+
const id = (input.headers.get('x-github-delivery') ?? '').trim();
|
|
143
|
+
if (!DELIVERY_ID.test(id))
|
|
144
|
+
throw new WebhookDeliveryRefused('delivery');
|
|
145
|
+
let payload;
|
|
146
|
+
try {
|
|
147
|
+
payload = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(input.body));
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// The parser's message would quote the body.
|
|
151
|
+
throw new WebhookDeliveryRefused('body');
|
|
152
|
+
}
|
|
153
|
+
if (typeof payload !== 'object' || payload === null || Array.isArray(payload))
|
|
154
|
+
throw new WebhookDeliveryRefused('body');
|
|
155
|
+
const header = (input.headers.get('x-github-event') ?? '').trim();
|
|
156
|
+
const name = EVENT_NAME.test(header) ? header : '';
|
|
157
|
+
return { id, name, event: eventOf(name, payload) };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Reads a delivery from a request: its body read as it streams and refused at `maxBytes` (`too_large`), before any
|
|
161
|
+
* check that needs it whole, the stream then cancelled unread; a `Content-Length` past the cap is refused before a byte
|
|
162
|
+
* is read. Then as {@link readWebhookDelivery}.
|
|
163
|
+
*
|
|
164
|
+
* @param request The request, or its headers and body stream.
|
|
165
|
+
* @param secret The webhook's secret, never empty.
|
|
166
|
+
* @param options The largest body read.
|
|
167
|
+
*/
|
|
168
|
+
export async function readWebhookRequest(request, secret, options = {}) {
|
|
169
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_DELIVERY_BYTES;
|
|
170
|
+
const declared = request.headers.get('content-length');
|
|
171
|
+
let body;
|
|
172
|
+
try {
|
|
173
|
+
body = await readCapped(new Response(request.body, { headers: declared === null ? {} : { 'content-length': declared } }), maxBytes);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (error instanceof ReadCapExceeded)
|
|
177
|
+
throw new WebhookDeliveryRefused('too_large');
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
return readWebhookDelivery({ headers: request.headers, body }, secret, { maxBytes });
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/app/events.ts"],"names":[],"mappings":"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"}
|
package/dist/app/http.d.ts
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
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
|
*/
|
|
7
9
|
/** The REST API version every call pins (`X-GitHub-Api-Version`). */
|
|
8
10
|
export declare const GITHUB_API_VERSION = "2026-03-10";
|
|
9
11
|
/** GitHub's REST API. */
|
|
10
12
|
export declare const GITHUB_API_BASE = "https://api.github.com";
|
|
11
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* How long one call may take when its caller sets no other deadline, its answer's body included: 30 seconds. GitHub
|
|
15
|
+
* ends a request it takes more than 10 seconds to process.
|
|
16
|
+
*/
|
|
17
|
+
export declare const GITHUB_TIMEOUT_MS = 30000;
|
|
18
|
+
/** The largest JSON answer read: 16 MiB, above the 7 MB at which GitHub cuts a recursive tree. */
|
|
19
|
+
export declare const GITHUB_MAX_ANSWER_BYTES = 16777216;
|
|
20
|
+
/** Where calls go: the fetch they are made with, the API's base address and each call's deadline. */
|
|
12
21
|
export interface GitHubApiOptions {
|
|
22
|
+
/** The fetch calls are made with; the global `fetch` when absent. */
|
|
13
23
|
fetchImpl?: typeof fetch;
|
|
24
|
+
/** GitHub's REST API, or a stand-in's address in tests; {@link GITHUB_API_BASE} when absent. */
|
|
14
25
|
apiBase?: string;
|
|
26
|
+
/** How long each call may take, its answer's body included; {@link GITHUB_TIMEOUT_MS} when absent. */
|
|
27
|
+
timeoutMs?: number;
|
|
15
28
|
}
|
|
16
|
-
/** One call: the bearer it carries, and its method, JSON body, media type
|
|
29
|
+
/** One call: the bearer it carries, and its method, JSON body, media type, abort signal and deadline when not the defaults. */
|
|
17
30
|
export interface GitHubRequest {
|
|
18
31
|
token: string;
|
|
19
32
|
method?: string;
|
|
@@ -22,30 +35,87 @@ export interface GitHubRequest {
|
|
|
22
35
|
/** The `Accept` media type; `application/vnd.github+json` when absent. */
|
|
23
36
|
accept?: string;
|
|
24
37
|
signal?: AbortSignal;
|
|
38
|
+
/** How long the call may take, its answer's body included; {@link GITHUB_TIMEOUT_MS} when absent. */
|
|
39
|
+
timeoutMs?: number;
|
|
40
|
+
}
|
|
41
|
+
/** Whether a refusal was a rate limit, and when the call may be made again. */
|
|
42
|
+
export interface GitHubRefusalTiming {
|
|
43
|
+
/** True for a rate limit: a 429, or a 403 with `retry-after`, `x-ratelimit-remaining: 0` or a body naming a rate limit. */
|
|
44
|
+
rateLimited: boolean;
|
|
45
|
+
/** Epoch milliseconds before which the call should not be made again, or null when GitHub asked for no wait. */
|
|
46
|
+
retryAt: number | null;
|
|
25
47
|
}
|
|
26
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* GitHub answered a status outside 2xx. The message holds the status alone, never the answer's body. `rateLimited`
|
|
50
|
+
* and `retryAt` follow GitHub's rules for a refused call
|
|
51
|
+
* (https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#exceeding-the-rate-limit):
|
|
52
|
+
* `retry-after` first, then `x-ratelimit-reset` when `x-ratelimit-remaining` is 0, else a minute for a rate limit
|
|
53
|
+
* that names no time.
|
|
54
|
+
*/
|
|
27
55
|
export declare class GitHubApiError extends Error {
|
|
28
56
|
readonly status: number;
|
|
29
|
-
/**
|
|
30
|
-
|
|
57
|
+
/** True when GitHub refused the call for a rate limit. */
|
|
58
|
+
readonly rateLimited: boolean;
|
|
59
|
+
/** Epoch milliseconds before which the call should not be made again, or null when GitHub asked for no wait. */
|
|
60
|
+
readonly retryAt: number | null;
|
|
61
|
+
/**
|
|
62
|
+
* @param status The HTTP status GitHub answered.
|
|
63
|
+
* @param timing Whether it was a rate limit and when to try again; neither when absent.
|
|
64
|
+
*/
|
|
65
|
+
constructor(status: number, timing?: Partial<GitHubRefusalTiming>);
|
|
66
|
+
}
|
|
67
|
+
/** Why a call got no answer: its deadline passed, its caller cancelled it, or the connection failed. */
|
|
68
|
+
export type GitHubNoAnswerReason = 'timeout' | 'cancelled' | 'network';
|
|
69
|
+
/** A call that got no answer from GitHub, or whose answer's body stopped before its end. Names no address or cause. */
|
|
70
|
+
export declare class GitHubNoAnswer extends Error {
|
|
71
|
+
readonly reason: GitHubNoAnswerReason;
|
|
72
|
+
/** @param reason Why the call got no answer. */
|
|
73
|
+
constructor(reason: GitHubNoAnswerReason);
|
|
31
74
|
}
|
|
32
75
|
/** The headers of a call: the bearer, the media type, the pinned API version and the pack's user agent. */
|
|
33
76
|
export declare function githubHeaders(token: string, accept?: string): Record<string, string>;
|
|
34
77
|
/** Releases an answer's body unread. */
|
|
35
78
|
export declare function discardBody(response: Response): Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
export
|
|
79
|
+
/** A call's signal (the caller's, ended at the deadline) and the failure its transport errors read as. */
|
|
80
|
+
export interface CallDeadline {
|
|
81
|
+
signal: AbortSignal;
|
|
82
|
+
/** The failure a fetch or a body read that threw reads as: the caller's cancel, the deadline, or the network. */
|
|
83
|
+
noAnswer(): GitHubNoAnswer;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The deadline of one call: its own timeout joined with the caller's signal, so the call and the reading of its body
|
|
87
|
+
* end at whichever comes first.
|
|
88
|
+
*
|
|
89
|
+
* @param signal The caller's signal, if any.
|
|
90
|
+
* @param timeoutMs How long the call may take; {@link GITHUB_TIMEOUT_MS} when absent.
|
|
91
|
+
*/
|
|
92
|
+
export declare function callDeadline(signal: AbortSignal | undefined, timeoutMs?: number): CallDeadline;
|
|
93
|
+
/** A 2xx answer, and its body read under the call's deadline. */
|
|
94
|
+
export interface GitHubAnswer {
|
|
95
|
+
readonly response: Response;
|
|
96
|
+
/**
|
|
97
|
+
* The answer's body, read as it streams and cut at `maxBytes` ({@link ReadCapExceeded} past it, the rest cancelled
|
|
98
|
+
* unread). A body that stops before its end, at the deadline or the caller's cancel, throws {@link GitHubNoAnswer}.
|
|
99
|
+
*/
|
|
100
|
+
bytes(maxBytes: number): Promise<Uint8Array>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Sends one call under its deadline and the caller's signal. An answer outside 2xx is read only as far as telling a
|
|
104
|
+
* rate limit needs and thrown as {@link GitHubApiError}; a call that gets no answer, or whose refusal's body stops
|
|
105
|
+
* while it is read, throws {@link GitHubNoAnswer}.
|
|
106
|
+
*/
|
|
107
|
+
export declare function githubSend(fetchImpl: typeof fetch, url: string, request: GitHubRequest): Promise<GitHubAnswer>;
|
|
38
108
|
/**
|
|
39
109
|
* One call to `url` (an address under the API's base), answering its JSON, or `undefined` for an answer without a
|
|
40
110
|
* body. Sends `Authorization: Bearer <token>`, `Accept: application/vnd.github+json` unless told otherwise,
|
|
41
|
-
* `X-GitHub-Api-Version` and `User-Agent
|
|
111
|
+
* `X-GitHub-Api-Version` and `User-Agent`, and ends at its deadline.
|
|
42
112
|
*/
|
|
43
113
|
export declare function githubFetch(fetchImpl: typeof fetch, url: string, request: GitHubRequest): Promise<unknown>;
|
|
44
114
|
/** The address a `Link` header names as `rel="next"`, or null. */
|
|
45
115
|
export declare function nextPage(link: string | null): string | null;
|
|
46
116
|
/**
|
|
47
|
-
* Each page of a listing as its JSON, following `Link: <...>; rel="next"
|
|
48
|
-
* first is refused, so the bearer never leaves the API it was sent to.
|
|
117
|
+
* Each page of a listing as its JSON, following `Link: <...>; rel="next"`, each page under a deadline of its own. A
|
|
118
|
+
* next page on another origin than the first is refused, so the bearer never leaves the API it was sent to.
|
|
49
119
|
*/
|
|
50
120
|
export declare function githubPages(fetchImpl: typeof fetch, url: string, request: GitHubRequest): AsyncGenerator<unknown, void, undefined>;
|
|
51
121
|
/** Every item of a listing across its pages; `items` takes a page's JSON and answers its items. */
|
package/dist/app/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/app/http.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/app/http.ts"],"names":[],"mappings":"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"}
|