@framers/agentos-ext-gmail-source 0.0.0-stage → 0.1.0
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/LICENSE +98 -0
- package/README.md +38 -2
- package/dist/GmailOAuthFlow.d.ts +78 -0
- package/dist/GmailOAuthFlow.d.ts.map +1 -0
- package/dist/GmailOAuthFlow.js +382 -0
- package/dist/GmailOAuthFlow.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/manifest.json +21 -0
- package/package.json +60 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
|
10
|
+
|
|
11
|
+
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
|
12
|
+
|
|
13
|
+
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
|
14
|
+
|
|
15
|
+
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
|
16
|
+
|
|
17
|
+
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
|
18
|
+
|
|
19
|
+
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
|
20
|
+
|
|
21
|
+
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
|
22
|
+
|
|
23
|
+
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
|
24
|
+
|
|
25
|
+
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
|
26
|
+
|
|
27
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
|
28
|
+
|
|
29
|
+
2. Grant of Copyright License.
|
|
30
|
+
|
|
31
|
+
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
|
32
|
+
|
|
33
|
+
3. Grant of Patent License.
|
|
34
|
+
|
|
35
|
+
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
|
36
|
+
|
|
37
|
+
4. Redistribution.
|
|
38
|
+
|
|
39
|
+
You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
|
40
|
+
|
|
41
|
+
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
|
42
|
+
|
|
43
|
+
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
|
44
|
+
|
|
45
|
+
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
|
46
|
+
|
|
47
|
+
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
|
48
|
+
|
|
49
|
+
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
|
50
|
+
|
|
51
|
+
5. Submission of Contributions.
|
|
52
|
+
|
|
53
|
+
Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
|
54
|
+
|
|
55
|
+
6. Trademarks.
|
|
56
|
+
|
|
57
|
+
This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
|
58
|
+
|
|
59
|
+
7. Disclaimer of Warranty.
|
|
60
|
+
|
|
61
|
+
Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
|
62
|
+
|
|
63
|
+
8. Limitation of Liability.
|
|
64
|
+
|
|
65
|
+
In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
|
66
|
+
|
|
67
|
+
9. Accepting Warranty or Additional Liability.
|
|
68
|
+
|
|
69
|
+
While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
|
70
|
+
|
|
71
|
+
END OF TERMS AND CONDITIONS
|
|
72
|
+
|
|
73
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
74
|
+
|
|
75
|
+
To apply the Apache License to your work, attach the following
|
|
76
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
77
|
+
replaced with your own identifying information. (Don't include
|
|
78
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
79
|
+
comment syntax for the file format. We also recommend that a
|
|
80
|
+
file or class name and description of purpose be included on the
|
|
81
|
+
same "printed page" as the copyright notice for easier
|
|
82
|
+
identification within third-party archives.
|
|
83
|
+
|
|
84
|
+
Copyright (c) 2025 Framers
|
|
85
|
+
|
|
86
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
87
|
+
you may not use this file except in compliance with the License.
|
|
88
|
+
You may obtain a copy of the License at
|
|
89
|
+
|
|
90
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
91
|
+
|
|
92
|
+
Unless required by applicable law or agreed to in writing, software
|
|
93
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
94
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
95
|
+
See the License for the specific language governing permissions and
|
|
96
|
+
limitations under the License.
|
|
97
|
+
|
|
98
|
+
|
package/README.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Gmail source
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This library provides read-only Gmail authorization on the AgentOS redirect grant and sealed-token contracts. It requests the sole Gmail read-only scope, offline access and incremental consent. It exports no send, reply, label-modification or attachment-download tool.
|
|
4
|
+
|
|
5
|
+
Install it next to `@framers/agentos` 0.13.134 or later and `@framers/agentos-ext-account-sources` 0.4.0 or later. Both are peer dependencies with a floor and no upper bound. Node.js 20.19 or later is required.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { GmailOAuthFlow } from '@framers/agentos-ext-gmail-source';
|
|
9
|
+
|
|
10
|
+
const callback = 'https://app.example.com/sources/gmail/callback';
|
|
11
|
+
const client = { clientId, clientSecret, redirectUris: [callback] };
|
|
12
|
+
|
|
13
|
+
// The request that sends the person to Google: keep the state and the verifier in a short-lived, single-use record.
|
|
14
|
+
const begun = new GmailOAuthFlow(client, null).begin({ redirectUri: callback });
|
|
15
|
+
|
|
16
|
+
// The request Google sends the person back to. `store` is a SealedTokenStore under the connection's namespace.
|
|
17
|
+
const flow = new GmailOAuthFlow(client, store);
|
|
18
|
+
const grant = await flow.complete({
|
|
19
|
+
code,
|
|
20
|
+
state,
|
|
21
|
+
expectedState: begun.state,
|
|
22
|
+
codeVerifier: begun.codeVerifier,
|
|
23
|
+
redirectUri: callback,
|
|
24
|
+
});
|
|
25
|
+
// After the host has consumed the start and rechecked the account: the store receives the refresh token and scope.
|
|
26
|
+
await flow.keep(connectionId, grant);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Construct `GmailOAuthFlow(client, store, options)` on the server. `client` contains the dedicated client id, client secret and exact registered callback URI list. Load credentials through the host's secret mechanism. The default endpoints are Google's HTTPS endpoints. Endpoint overrides are restricted to CI stand-ins; never derive them or callback URIs from a browser request.
|
|
30
|
+
|
|
31
|
+
`begin({ redirectUri })` returns the authorization URL, state and PKCE verifier. Keep state and verifier under a short-lived, single-use record tied to the signed-in account. `complete(input)` checks state, exchanges the code and verifies the granted scope and refresh material. It returns credentials in memory and does not persist them. The host must atomically consume the start and recheck account liveness and generation before installing the sealed grant. Do not log callback URLs, credentials or errors from arbitrary provider middleware.
|
|
32
|
+
|
|
33
|
+
Construct a flow for each scheduled token operation, with that operation's sealed store and abort signal, call `accessToken`, and discard the flow at the end of the operation. A store scoped to one operation must never be retained for another worker. The host's durable lifecycle owns disconnect, purge, refresh exclusion across workers and revocation. `forget` on an independently held flow is not a cross-worker lifecycle operation.
|
|
34
|
+
|
|
35
|
+
OAuth responses are capped at 65,536 bytes and must finish within 15 seconds. Parent cancellation stops dispatch and body consumption. Fixed `GmailOAuthFault` reasons contain no provider message or credential. When an exchange succeeds and `complete` does not return the grant, because the grant fails the checks or because the parent cancelled before `complete` returned, the flow asks Google to revoke the grant before it throws. That revocation has the same byte cap and a 15-second deadline of its own, and the parent's cancellation does not stop it. The fault's `cleanup` field then reads `confirmed` or `unconfirmed`. An unconfirmed outcome needs a provider-permission removal instruction. It does not authorize retaining the grant.
|
|
36
|
+
|
|
37
|
+
The host supplies informed consent, cache permission, retention policy, account isolation and a truthful availability state. Authorization alone does not establish permission to store or send mailbox content to a model. Do not infer those permissions from this package's successful tests.
|
|
38
|
+
|
|
39
|
+
Protocol references: https://developers.google.com/identity/protocols/oauth2/web-server and https://developers.google.com/workspace/gmail/api/auth/scopes.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Google's read-only Gmail grant on the AgentOS redirect OAuth flow: the read-only scope alone, asked for
|
|
3
|
+
* offline and incrementally with consent, for registered callbacks only; granted scopes and refresh material checked
|
|
4
|
+
* before a grant is accepted; every token and revocation call capped at 65,536 bytes, bounded by one 15-second
|
|
5
|
+
* deadline and stopped by the caller's cancellation, except the revocation of a grant the flow will not hand back.
|
|
6
|
+
* @module agentos-ext-gmail-source/GmailOAuthFlow
|
|
7
|
+
*/
|
|
8
|
+
import { RedirectOAuthFlow, type IOAuthTokenStore, type OAuthTokenSet, type RedirectOAuthConfig, type RedirectBegin, type RedirectCompleteInput } from '@framers/agentos/auth';
|
|
9
|
+
import { type GmailOAuthClient, type GmailOAuthOptions } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Google's read-only Gmail grant on the core redirect flow. `begin` asks for the read-only scope alone, offline, with
|
|
12
|
+
* incremental authorization and consent, for a registered callback; `complete` accepts only that scope with refresh
|
|
13
|
+
* material and revokes a grant it refuses, or one the caller cancels after the exchange; refreshes and revocations go
|
|
14
|
+
* through the same capped transport, which the caller's signal stops except during that revocation. A flow built with
|
|
15
|
+
* an operation's sealed store and signal serves that operation alone.
|
|
16
|
+
*/
|
|
17
|
+
export declare class GmailOAuthFlow extends RedirectOAuthFlow {
|
|
18
|
+
/** The provider's id. */
|
|
19
|
+
readonly providerId = "gmail";
|
|
20
|
+
private readonly client;
|
|
21
|
+
private readonly endpoints;
|
|
22
|
+
private readonly signal?;
|
|
23
|
+
/** The fetch and stand-in endpoints of a withheld grant's revocation, without the caller's signal. */
|
|
24
|
+
private readonly withheld;
|
|
25
|
+
/**
|
|
26
|
+
* @param client The dedicated client and its registered callback addresses (one to sixteen).
|
|
27
|
+
* @param store Where the grant is kept, a namespaced `SealedTokenStore` on a server; null for a flow that only
|
|
28
|
+
* begins, completes and revokes.
|
|
29
|
+
* @param options The fetch, the caller's cancellation and, on CI only, stand-in endpoints.
|
|
30
|
+
* @throws GmailOAuthFault `configuration` for a malformed client, callback address or endpoint override.
|
|
31
|
+
*/
|
|
32
|
+
constructor(client: GmailOAuthClient, store: IOAuthTokenStore | null, options?: GmailOAuthOptions);
|
|
33
|
+
/** Google's endpoints, the read-only scope and the client; a refresh starts a minute before expiry. */
|
|
34
|
+
protected getConfig(): RedirectOAuthConfig;
|
|
35
|
+
/** Throws `cancelled` once the caller's signal has aborted. */
|
|
36
|
+
private active;
|
|
37
|
+
/** Throws `configuration` for a callback address the client did not register. */
|
|
38
|
+
private redirect;
|
|
39
|
+
/**
|
|
40
|
+
* The authorization address, state and PKCE verifier for a registered callback, asking for the read-only scope
|
|
41
|
+
* alone with `access_type=offline`, `include_granted_scopes=true` and `prompt=consent`, and no identity hint.
|
|
42
|
+
* Throws `configuration` for any other scope, any extra parameter or an unregistered callback.
|
|
43
|
+
*/
|
|
44
|
+
begin(input: Parameters<RedirectOAuthFlow['begin']>[0]): RedirectBegin;
|
|
45
|
+
/**
|
|
46
|
+
* Checks the callback address and the inputs' shape before any call, then the state, then exchanges the code. It
|
|
47
|
+
* answers the accepted grant in memory and saves nothing. When the caller's signal aborts after Google has issued the
|
|
48
|
+
* grant, the grant is revoked before `cancelled` is thrown, and the fault's `cleanup` says whether that was confirmed.
|
|
49
|
+
*/
|
|
50
|
+
complete(input: RedirectCompleteInput): Promise<OAuthTokenSet>;
|
|
51
|
+
/**
|
|
52
|
+
* Accepts an exchanged grant only with the read-only scope alone, usable refresh material and an unexpired access
|
|
53
|
+
* token. A refused grant is revoked at Google first; the fault's `cleanup` says whether that was confirmed.
|
|
54
|
+
*/
|
|
55
|
+
protected postExchange(tokens: OAuthTokenSet): Promise<OAuthTokenSet>;
|
|
56
|
+
/**
|
|
57
|
+
* Revokes an exchanged grant the flow will not hand back, by its refresh token or else its access token, and answers
|
|
58
|
+
* whether Google confirmed it. The call goes through the same capped transport with a 15-second deadline of its own,
|
|
59
|
+
* on a flow without the caller's signal, so a cancellation cannot leave the grant at Google without an attempt.
|
|
60
|
+
*/
|
|
61
|
+
private revokeWithheld;
|
|
62
|
+
/**
|
|
63
|
+
* Refreshes and checks the answer: a scope it returns must be the read-only scope alone, and a refresh token it
|
|
64
|
+
* returns must be usable. An omitted scope keeps the kept grant's checked scope.
|
|
65
|
+
*/
|
|
66
|
+
protected refreshTokens(refreshToken: string): Promise<OAuthTokenSet>;
|
|
67
|
+
/** Keeps an accepted grant under `key` after the exchange's checks; the store receives its refresh token and scope. */
|
|
68
|
+
keep(key: string, tokens: OAuthTokenSet): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* An access token for the grant kept under `key`, refreshed when needed, only while the kept scope is the read-only
|
|
71
|
+
* scope alone. Throws `OAuthGrantRefused` with reason `missing` when nothing is kept or the grant changes during the
|
|
72
|
+
* refresh, and with reason `refresh` when Google refuses it.
|
|
73
|
+
*/
|
|
74
|
+
accessToken(key: string): Promise<string>;
|
|
75
|
+
/** Revokes a token at Google; an answer that is not a success throws `OAuthGrantRefused` with reason `revoke`. */
|
|
76
|
+
revoke(token: string): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=GmailOAuthFlow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GmailOAuthFlow.d.ts","sourceRoot":"","sources":["../src/GmailOAuthFlow.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EAAqB,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAC/E,KAAK,mBAAmB,EAAE,KAAK,aAAa,EAAE,KAAK,qBAAqB,EACzE,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACkC,KAAK,gBAAgB,EAClC,KAAK,iBAAiB,EACjD,MAAM,YAAY,CAAC;AAgKpB;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,iBAAiB;IACnD,yBAAyB;IACzB,QAAQ,CAAC,UAAU,WAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAc;IACtC,sGAAsG;IACtG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C;;;;;;OAMG;gBACS,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,EAAE,OAAO,GAAE,iBAAsB;IAYrG,uGAAuG;IACvG,SAAS,CAAC,SAAS,IAAI,mBAAmB;IAK1C,+DAA+D;IAC/D,OAAO,CAAC,MAAM;IACd,iFAAiF;IACjF,OAAO,CAAC,QAAQ;IAGhB;;;;OAIG;IACM,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa;IAO/E;;;;OAIG;IACY,QAAQ,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC;IAQ7E;;;OAGG;cACsB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAOpF;;;;OAIG;YACW,cAAc;IAO5B;;;OAGG;cACsB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAKpF,uHAAuH;IACxG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAGtE;;;;OAIG;IACY,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQxD,kHAAkH;IACnG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAIpD"}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Google's read-only Gmail grant on the AgentOS redirect OAuth flow: the read-only scope alone, asked for
|
|
3
|
+
* offline and incrementally with consent, for registered callbacks only; granted scopes and refresh material checked
|
|
4
|
+
* before a grant is accepted; every token and revocation call capped at 65,536 bytes, bounded by one 15-second
|
|
5
|
+
* deadline and stopped by the caller's cancellation, except the revocation of a grant the flow will not hand back.
|
|
6
|
+
* @module agentos-ext-gmail-source/GmailOAuthFlow
|
|
7
|
+
*/
|
|
8
|
+
import { RedirectOAuthFlow, OAuthGrantRefused, } from '@framers/agentos/auth';
|
|
9
|
+
import { readCapped, ReadCapExceeded } from '@framers/agentos-ext-account-sources';
|
|
10
|
+
import { GMAIL_READONLY_SCOPE, GmailOAuthFault, } from './types.js';
|
|
11
|
+
/** Google's production authorization, token and revocation endpoints. */
|
|
12
|
+
const endpoints = Object.freeze({
|
|
13
|
+
authorization: 'https://accounts.google.com/o/oauth2/v2/auth',
|
|
14
|
+
token: 'https://oauth2.googleapis.com/token',
|
|
15
|
+
revoke: 'https://oauth2.googleapis.com/revoke',
|
|
16
|
+
});
|
|
17
|
+
/** The byte cap on one OAuth response body. */
|
|
18
|
+
const maxBytes = 65_536;
|
|
19
|
+
/** One deadline for a call and the consumption of its body. */
|
|
20
|
+
const deadlineMs = 15_000;
|
|
21
|
+
/** A nonempty credential of bounded length with no whitespace or control character. */
|
|
22
|
+
const tokenString = (value) => typeof value === 'string'
|
|
23
|
+
&& value.length > 0 && value.length <= 16_384 && !/[\x00-\x20\x7f]/.test(value);
|
|
24
|
+
/** True when a granted scope string names the read-only scope and nothing else. */
|
|
25
|
+
function scopeOnly(value) {
|
|
26
|
+
if (typeof value !== 'string')
|
|
27
|
+
return false;
|
|
28
|
+
const scopes = value.trim().split(/\s+/);
|
|
29
|
+
return scopes.length === 1 && scopes[0] === GMAIL_READONLY_SCOPE;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* An absolute HTTPS address with no userinfo, query or fragment; loopback HTTP passes on CI only. A fixture-only
|
|
33
|
+
* address must also be a loopback or `.invalid` host on CI. The query and fragment marks are looked for in the string
|
|
34
|
+
* itself: the URL's `search` and `hash` read empty for a bare `?` or `#`.
|
|
35
|
+
*/
|
|
36
|
+
function validUrl(value, fixtureOnly) {
|
|
37
|
+
let url;
|
|
38
|
+
try {
|
|
39
|
+
url = new URL(value);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new GmailOAuthFault('configuration');
|
|
43
|
+
}
|
|
44
|
+
const local = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
45
|
+
const fixture = local || url.hostname.endsWith('.invalid');
|
|
46
|
+
if (url.username || url.password || /[?#]/.test(value)
|
|
47
|
+
|| (url.protocol !== 'https:' && !(process.env.CI === 'true' && local && url.protocol === 'http:'))
|
|
48
|
+
|| (fixtureOnly && (process.env.CI !== 'true' || !fixture)))
|
|
49
|
+
throw new GmailOAuthFault('configuration');
|
|
50
|
+
return url;
|
|
51
|
+
}
|
|
52
|
+
/** Google's endpoints, or three distinct stand-in endpoints that pass the fixture-only check. */
|
|
53
|
+
function selectedEndpoints(options) {
|
|
54
|
+
if (!options.endpoints)
|
|
55
|
+
return endpoints;
|
|
56
|
+
const chosen = { ...options.endpoints };
|
|
57
|
+
for (const value of [chosen.authorization, chosen.token, chosen.revoke])
|
|
58
|
+
validUrl(value, true);
|
|
59
|
+
if (new Set(Object.values(chosen)).size !== 3)
|
|
60
|
+
throw new GmailOAuthFault('configuration');
|
|
61
|
+
return Object.freeze(chosen);
|
|
62
|
+
}
|
|
63
|
+
/** Reject promptly; consume a fetch response that arrives after its caller has stopped. */
|
|
64
|
+
function abortable(work, signal, stopped, discard) {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
let done = false;
|
|
67
|
+
const abort = () => { if (!done) {
|
|
68
|
+
done = true;
|
|
69
|
+
reject(stopped());
|
|
70
|
+
} };
|
|
71
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
72
|
+
if (signal.aborted)
|
|
73
|
+
abort();
|
|
74
|
+
work.then((value) => {
|
|
75
|
+
signal.removeEventListener('abort', abort);
|
|
76
|
+
if (done) {
|
|
77
|
+
discard?.(value);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
done = true;
|
|
81
|
+
resolve(value);
|
|
82
|
+
}, (error) => {
|
|
83
|
+
signal.removeEventListener('abort', abort);
|
|
84
|
+
if (!done) {
|
|
85
|
+
done = true;
|
|
86
|
+
reject(error);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** Cancels a response's body without waiting, discarding any error. */
|
|
92
|
+
function cancel(response) { void response.body?.cancel().catch(() => { }); }
|
|
93
|
+
/**
|
|
94
|
+
* Reads a body through the released byte cap. The bridge stream turns the signal's abort into a fixed fault, even
|
|
95
|
+
* while no bytes arrive, and passes a cancellation through to the provider's stream.
|
|
96
|
+
*/
|
|
97
|
+
async function boundedBody(response, signal, stopped) {
|
|
98
|
+
if (signal.aborted) {
|
|
99
|
+
cancel(response);
|
|
100
|
+
throw stopped();
|
|
101
|
+
}
|
|
102
|
+
if (!response.body)
|
|
103
|
+
return new Uint8Array(0);
|
|
104
|
+
const reader = response.body.getReader();
|
|
105
|
+
let closed = false;
|
|
106
|
+
let stop;
|
|
107
|
+
const bridge = new ReadableStream({
|
|
108
|
+
start(controller) {
|
|
109
|
+
stop = () => {
|
|
110
|
+
if (closed)
|
|
111
|
+
return;
|
|
112
|
+
closed = true;
|
|
113
|
+
controller.error(stopped());
|
|
114
|
+
void reader.cancel().catch(() => { });
|
|
115
|
+
};
|
|
116
|
+
signal.addEventListener('abort', stop, { once: true });
|
|
117
|
+
if (signal.aborted)
|
|
118
|
+
stop();
|
|
119
|
+
},
|
|
120
|
+
async pull(controller) {
|
|
121
|
+
try {
|
|
122
|
+
const chunk = await reader.read();
|
|
123
|
+
if (closed)
|
|
124
|
+
return;
|
|
125
|
+
if (chunk.done) {
|
|
126
|
+
closed = true;
|
|
127
|
+
controller.close();
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
controller.enqueue(chunk.value);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
if (!closed) {
|
|
134
|
+
closed = true;
|
|
135
|
+
controller.error(signal.aborted ? stopped() : new GmailOAuthFault('network'));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
async cancel() { closed = true; try {
|
|
140
|
+
await reader.cancel();
|
|
141
|
+
}
|
|
142
|
+
catch { /* No provider text escapes. */ } },
|
|
143
|
+
});
|
|
144
|
+
try {
|
|
145
|
+
return await readCapped(new Response(bridge, { headers: response.headers }), maxBytes);
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
signal.removeEventListener('abort', stop);
|
|
149
|
+
try {
|
|
150
|
+
reader.releaseLock();
|
|
151
|
+
}
|
|
152
|
+
catch { /* A cancelled provider may still be settling. */ }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The fetch the core flow is given: POSTs to the chosen token and revocation endpoints only, no redirect followed, no
|
|
157
|
+
* credentials or referrer sent, one deadline over the call and its capped body, and fixed faults in place of
|
|
158
|
+
* transport errors. It answers a fresh response holding the bounded bytes and the provider's status.
|
|
159
|
+
*/
|
|
160
|
+
function oauthFetch(options, chosen) {
|
|
161
|
+
const send = options.fetchImpl ?? fetch;
|
|
162
|
+
return async (input, init) => {
|
|
163
|
+
const url = typeof input === 'string' || input instanceof URL ? String(input) : '';
|
|
164
|
+
if (![chosen.token, chosen.revoke].includes(url) || init?.method !== 'POST')
|
|
165
|
+
throw new GmailOAuthFault('configuration');
|
|
166
|
+
const parents = [options.signal, init?.signal].filter((s) => s !== undefined && s !== null);
|
|
167
|
+
if (parents.some((s) => s.aborted))
|
|
168
|
+
throw new GmailOAuthFault('cancelled');
|
|
169
|
+
const controller = new AbortController();
|
|
170
|
+
let timedOut = false;
|
|
171
|
+
const abort = () => controller.abort();
|
|
172
|
+
const stopped = () => new GmailOAuthFault(timedOut ? 'timeout' : 'cancelled');
|
|
173
|
+
for (const parent of parents)
|
|
174
|
+
parent.addEventListener('abort', abort, { once: true });
|
|
175
|
+
const timer = setTimeout(() => { timedOut = true; controller.abort(); }, deadlineMs);
|
|
176
|
+
try {
|
|
177
|
+
if (parents.some((s) => s.aborted))
|
|
178
|
+
abort();
|
|
179
|
+
if (controller.signal.aborted)
|
|
180
|
+
throw stopped();
|
|
181
|
+
const response = await abortable(send(url, { ...init, signal: controller.signal,
|
|
182
|
+
redirect: 'error', credentials: 'omit', cache: 'no-store', referrerPolicy: 'no-referrer' }), controller.signal, stopped, cancel);
|
|
183
|
+
if (controller.signal.aborted) {
|
|
184
|
+
cancel(response);
|
|
185
|
+
throw stopped();
|
|
186
|
+
}
|
|
187
|
+
if (response.status < 200 || response.status >= 600 || (response.status >= 300 && response.status < 400)) {
|
|
188
|
+
cancel(response);
|
|
189
|
+
throw new GmailOAuthFault('invalid_response');
|
|
190
|
+
}
|
|
191
|
+
const bytes = await boundedBody(response, controller.signal, stopped);
|
|
192
|
+
if (controller.signal.aborted)
|
|
193
|
+
throw stopped();
|
|
194
|
+
return new Response([204, 205, 304].includes(response.status) ? null : bytes, { status: response.status, headers: { 'content-type': 'application/json' } });
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
if (error instanceof GmailOAuthFault)
|
|
198
|
+
throw error;
|
|
199
|
+
if (controller.signal.aborted)
|
|
200
|
+
throw stopped();
|
|
201
|
+
if (error instanceof ReadCapExceeded)
|
|
202
|
+
throw new GmailOAuthFault('response_too_large');
|
|
203
|
+
throw new GmailOAuthFault('network');
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
clearTimeout(timer);
|
|
207
|
+
for (const parent of parents)
|
|
208
|
+
parent.removeEventListener('abort', abort);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Checks a token answer and keeps only the access token, the refresh token, the expiry and the read-only scope; an id
|
|
214
|
+
* token and any other field are dropped.
|
|
215
|
+
*/
|
|
216
|
+
function normalize(tokens, refreshRequired, scopeRequired) {
|
|
217
|
+
if (!tokenString(tokens.accessToken) || !Number.isFinite(tokens.expiresAt) || tokens.expiresAt <= Date.now()) {
|
|
218
|
+
throw new GmailOAuthFault('invalid_response');
|
|
219
|
+
}
|
|
220
|
+
const scope = tokens.metadata?.scope;
|
|
221
|
+
if ((scopeRequired || scope !== undefined) && !scopeOnly(scope))
|
|
222
|
+
throw new GmailOAuthFault('scope');
|
|
223
|
+
if ((refreshRequired || tokens.refreshToken !== undefined) && !tokenString(tokens.refreshToken)) {
|
|
224
|
+
throw new GmailOAuthFault('refresh_token');
|
|
225
|
+
}
|
|
226
|
+
return { accessToken: tokens.accessToken, refreshToken: tokens.refreshToken, expiresAt: tokens.expiresAt,
|
|
227
|
+
metadata: scope === undefined ? undefined : { scope: GMAIL_READONLY_SCOPE } };
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Google's read-only Gmail grant on the core redirect flow. `begin` asks for the read-only scope alone, offline, with
|
|
231
|
+
* incremental authorization and consent, for a registered callback; `complete` accepts only that scope with refresh
|
|
232
|
+
* material and revokes a grant it refuses, or one the caller cancels after the exchange; refreshes and revocations go
|
|
233
|
+
* through the same capped transport, which the caller's signal stops except during that revocation. A flow built with
|
|
234
|
+
* an operation's sealed store and signal serves that operation alone.
|
|
235
|
+
*/
|
|
236
|
+
export class GmailOAuthFlow extends RedirectOAuthFlow {
|
|
237
|
+
/** The provider's id. */
|
|
238
|
+
providerId = 'gmail';
|
|
239
|
+
client;
|
|
240
|
+
endpoints;
|
|
241
|
+
signal;
|
|
242
|
+
/** The fetch and stand-in endpoints of a withheld grant's revocation, without the caller's signal. */
|
|
243
|
+
withheld;
|
|
244
|
+
/**
|
|
245
|
+
* @param client The dedicated client and its registered callback addresses (one to sixteen).
|
|
246
|
+
* @param store Where the grant is kept, a namespaced `SealedTokenStore` on a server; null for a flow that only
|
|
247
|
+
* begins, completes and revokes.
|
|
248
|
+
* @param options The fetch, the caller's cancellation and, on CI only, stand-in endpoints.
|
|
249
|
+
* @throws GmailOAuthFault `configuration` for a malformed client, callback address or endpoint override.
|
|
250
|
+
*/
|
|
251
|
+
constructor(client, store, options = {}) {
|
|
252
|
+
const chosen = selectedEndpoints(options);
|
|
253
|
+
super(store, oauthFetch(options, chosen));
|
|
254
|
+
if (!tokenString(client.clientId) || !tokenString(client.clientSecret)
|
|
255
|
+
|| !Array.isArray(client.redirectUris) || client.redirectUris.length === 0 || client.redirectUris.length > 16) {
|
|
256
|
+
throw new GmailOAuthFault('configuration');
|
|
257
|
+
}
|
|
258
|
+
for (const uri of client.redirectUris)
|
|
259
|
+
validUrl(uri, false);
|
|
260
|
+
this.client = Object.freeze({ ...client, redirectUris: Object.freeze([...new Set(client.redirectUris)]) });
|
|
261
|
+
this.endpoints = chosen;
|
|
262
|
+
this.signal = options.signal;
|
|
263
|
+
this.withheld = Object.freeze({ fetchImpl: options.fetchImpl, endpoints: options.endpoints ? chosen : undefined });
|
|
264
|
+
}
|
|
265
|
+
/** Google's endpoints, the read-only scope and the client; a refresh starts a minute before expiry. */
|
|
266
|
+
getConfig() {
|
|
267
|
+
return { clientId: this.client.clientId, clientSecret: this.client.clientSecret, scopes: [GMAIL_READONLY_SCOPE],
|
|
268
|
+
authorizationEndpoint: this.endpoints.authorization, tokenEndpoint: this.endpoints.token,
|
|
269
|
+
revocationEndpoint: this.endpoints.revoke, refreshBufferMs: 60_000 };
|
|
270
|
+
}
|
|
271
|
+
/** Throws `cancelled` once the caller's signal has aborted. */
|
|
272
|
+
active() { if (this.signal?.aborted)
|
|
273
|
+
throw new GmailOAuthFault('cancelled'); }
|
|
274
|
+
/** Throws `configuration` for a callback address the client did not register. */
|
|
275
|
+
redirect(uri) {
|
|
276
|
+
if (!this.client.redirectUris.includes(uri))
|
|
277
|
+
throw new GmailOAuthFault('configuration');
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The authorization address, state and PKCE verifier for a registered callback, asking for the read-only scope
|
|
281
|
+
* alone with `access_type=offline`, `include_granted_scopes=true` and `prompt=consent`, and no identity hint.
|
|
282
|
+
* Throws `configuration` for any other scope, any extra parameter or an unregistered callback.
|
|
283
|
+
*/
|
|
284
|
+
begin(input) {
|
|
285
|
+
this.active();
|
|
286
|
+
this.redirect(input.redirectUri);
|
|
287
|
+
if ((input.scopes && (input.scopes.length !== 1 || input.scopes[0] !== GMAIL_READONLY_SCOPE))
|
|
288
|
+
|| (input.extraParams && Object.keys(input.extraParams).length > 0))
|
|
289
|
+
throw new GmailOAuthFault('configuration');
|
|
290
|
+
return super.begin({ redirectUri: input.redirectUri, scopes: [GMAIL_READONLY_SCOPE],
|
|
291
|
+
extraParams: { access_type: 'offline', include_granted_scopes: 'true', prompt: 'consent' } });
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Checks the callback address and the inputs' shape before any call, then the state, then exchanges the code. It
|
|
295
|
+
* answers the accepted grant in memory and saves nothing. When the caller's signal aborts after Google has issued the
|
|
296
|
+
* grant, the grant is revoked before `cancelled` is thrown, and the fault's `cleanup` says whether that was confirmed.
|
|
297
|
+
*/
|
|
298
|
+
async complete(input) {
|
|
299
|
+
this.active();
|
|
300
|
+
this.redirect(input.redirectUri);
|
|
301
|
+
if (!tokenString(input.code) || !/^[A-Za-z0-9._~-]{43,128}$/.test(input.codeVerifier)
|
|
302
|
+
|| input.state.length > 512 || input.expectedState.length > 512)
|
|
303
|
+
throw new GmailOAuthFault('configuration');
|
|
304
|
+
const tokens = await super.complete(input);
|
|
305
|
+
if (this.signal?.aborted)
|
|
306
|
+
throw new GmailOAuthFault('cancelled', await this.revokeWithheld(tokens));
|
|
307
|
+
return tokens;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Accepts an exchanged grant only with the read-only scope alone, usable refresh material and an unexpired access
|
|
311
|
+
* token. A refused grant is revoked at Google first; the fault's `cleanup` says whether that was confirmed.
|
|
312
|
+
*/
|
|
313
|
+
async postExchange(tokens) {
|
|
314
|
+
try {
|
|
315
|
+
return normalize(tokens, true, true);
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
throw new GmailOAuthFault(error instanceof GmailOAuthFault ? error.reason : 'invalid_response', await this.revokeWithheld(tokens));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Revokes an exchanged grant the flow will not hand back, by its refresh token or else its access token, and answers
|
|
323
|
+
* whether Google confirmed it. The call goes through the same capped transport with a 15-second deadline of its own,
|
|
324
|
+
* on a flow without the caller's signal, so a cancellation cannot leave the grant at Google without an attempt.
|
|
325
|
+
*/
|
|
326
|
+
async revokeWithheld(tokens) {
|
|
327
|
+
const token = tokenString(tokens.refreshToken) ? tokens.refreshToken
|
|
328
|
+
: tokenString(tokens.accessToken) ? tokens.accessToken : null;
|
|
329
|
+
if (token === null)
|
|
330
|
+
return 'unconfirmed';
|
|
331
|
+
try {
|
|
332
|
+
await new GmailOAuthFlow(this.client, null, this.withheld).revoke(token);
|
|
333
|
+
return 'confirmed';
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
return 'unconfirmed';
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Refreshes and checks the answer: a scope it returns must be the read-only scope alone, and a refresh token it
|
|
341
|
+
* returns must be usable. An omitted scope keeps the kept grant's checked scope.
|
|
342
|
+
*/
|
|
343
|
+
async refreshTokens(refreshToken) {
|
|
344
|
+
this.active();
|
|
345
|
+
const tokens = normalize(await super.refreshTokens(refreshToken), false, false);
|
|
346
|
+
this.active();
|
|
347
|
+
return tokens;
|
|
348
|
+
}
|
|
349
|
+
/** Keeps an accepted grant under `key` after the exchange's checks; the store receives its refresh token and scope. */
|
|
350
|
+
async keep(key, tokens) {
|
|
351
|
+
this.active();
|
|
352
|
+
await super.keep(key, normalize(tokens, true, true));
|
|
353
|
+
this.active();
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* An access token for the grant kept under `key`, refreshed when needed, only while the kept scope is the read-only
|
|
357
|
+
* scope alone. Throws `OAuthGrantRefused` with reason `missing` when nothing is kept or the grant changes during the
|
|
358
|
+
* refresh, and with reason `refresh` when Google refuses it.
|
|
359
|
+
*/
|
|
360
|
+
async accessToken(key) {
|
|
361
|
+
this.active();
|
|
362
|
+
if (this.store === null)
|
|
363
|
+
throw new GmailOAuthFault('configuration');
|
|
364
|
+
const saved = await this.store.load(key);
|
|
365
|
+
if (saved === null)
|
|
366
|
+
throw new OAuthGrantRefused('missing');
|
|
367
|
+
if (!scopeOnly(saved.metadata?.scope))
|
|
368
|
+
throw new GmailOAuthFault('scope');
|
|
369
|
+
const token = await super.accessToken(key);
|
|
370
|
+
this.active();
|
|
371
|
+
return token;
|
|
372
|
+
}
|
|
373
|
+
/** Revokes a token at Google; an answer that is not a success throws `OAuthGrantRefused` with reason `revoke`. */
|
|
374
|
+
async revoke(token) {
|
|
375
|
+
this.active();
|
|
376
|
+
if (!tokenString(token))
|
|
377
|
+
throw new GmailOAuthFault('configuration');
|
|
378
|
+
await super.revoke(token);
|
|
379
|
+
this.active();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
//# sourceMappingURL=GmailOAuthFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GmailOAuthFlow.js","sourceRoot":"","sources":["../src/GmailOAuthFlow.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A read-only Gmail source's grant: Google's read-only scope on the AgentOS redirect OAuth flow and
|
|
3
|
+
* sealed-token store, with capped, cancellable OAuth calls and fixed failures.
|
|
4
|
+
* @module agentos-ext-gmail-source
|
|
5
|
+
*/
|
|
6
|
+
export { GmailOAuthFlow } from './GmailOAuthFlow.js';
|
|
7
|
+
export { GMAIL_READONLY_SCOPE, GmailOAuthFault, type GmailOAuthFailure, type GmailGrantCleanup, type GmailOAuthClient, type GmailOAuthEndpoints, type GmailOAuthOptions, } from './types.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,oBAAoB,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EACrF,KAAK,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,GACxE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A read-only Gmail source's grant: Google's read-only scope on the AgentOS redirect OAuth flow and
|
|
3
|
+
* sealed-token store, with capped, cancellable OAuth calls and fixed failures.
|
|
4
|
+
* @module agentos-ext-gmail-source
|
|
5
|
+
*/
|
|
6
|
+
export { GmailOAuthFlow } from './GmailOAuthFlow.js';
|
|
7
|
+
export { GMAIL_READONLY_SCOPE, GmailOAuthFault, } from './types.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,oBAAoB,EAAE,eAAe,GAEtC,MAAM,YAAY,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The public types of the Gmail grant: Google's read-only scope, the dedicated client, the endpoints, the
|
|
3
|
+
* transport options and the fixed failures a grant operation answers.
|
|
4
|
+
* @module agentos-ext-gmail-source/types
|
|
5
|
+
*/
|
|
6
|
+
/** Google's read-only Gmail scope, the only scope the flow asks for or accepts; Google classes it as Restricted. */
|
|
7
|
+
export declare const GMAIL_READONLY_SCOPE = "https://www.googleapis.com/auth/gmail.readonly";
|
|
8
|
+
/** The dedicated web client: its id, its secret and the exact callback addresses registered for it. */
|
|
9
|
+
export interface GmailOAuthClient {
|
|
10
|
+
/** The client id. */
|
|
11
|
+
readonly clientId: string;
|
|
12
|
+
/** The client secret, loaded through the host's secret mechanism. */
|
|
13
|
+
readonly clientSecret: string;
|
|
14
|
+
/** The registered callback addresses, from trusted server configuration, with no query, fragment or userinfo. */
|
|
15
|
+
readonly redirectUris: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
/** Google's authorization, token and revocation addresses. */
|
|
18
|
+
export interface GmailOAuthEndpoints {
|
|
19
|
+
/** The authorization address the person is sent to. */
|
|
20
|
+
readonly authorization: string;
|
|
21
|
+
/** The token address the code is exchanged and the grant refreshed at. */
|
|
22
|
+
readonly token: string;
|
|
23
|
+
/** The revocation address. */
|
|
24
|
+
readonly revoke: string;
|
|
25
|
+
}
|
|
26
|
+
/** How the flow reaches Google: the fetch, the caller's cancellation and, on CI only, stand-in endpoints. */
|
|
27
|
+
export interface GmailOAuthOptions {
|
|
28
|
+
/** The fetch the token and revocation endpoints are called with; the global fetch by default. */
|
|
29
|
+
readonly fetchImpl?: typeof fetch;
|
|
30
|
+
/**
|
|
31
|
+
* The caller's cancellation: once aborted, a pending call or body read stops as `cancelled` and no other call starts,
|
|
32
|
+
* except the revocation of a grant the flow has already received, which runs under a 15-second deadline of its own.
|
|
33
|
+
*/
|
|
34
|
+
readonly signal?: AbortSignal;
|
|
35
|
+
/** Reserved for loopback or .invalid stand-ins on CI. Never take this from a request. */
|
|
36
|
+
readonly endpoints?: GmailOAuthEndpoints;
|
|
37
|
+
}
|
|
38
|
+
/** Why a grant operation failed, as a fixed word: never the provider's text, a code or a credential. */
|
|
39
|
+
export type GmailOAuthFailure = 'configuration' | 'cancelled' | 'timeout' | 'network' | 'response_too_large' | 'invalid_response' | 'scope' | 'refresh_token';
|
|
40
|
+
/**
|
|
41
|
+
* What became at Google of an exchanged grant the flow does not hand back, because its checks refused the grant or the
|
|
42
|
+
* caller cancelled before `complete` returned: `not_attempted` when the flow held no grant to revoke, `confirmed` when
|
|
43
|
+
* the revocation endpoint answered a success, `unconfirmed` when it did not, which calls for permission-removal
|
|
44
|
+
* instructions.
|
|
45
|
+
*/
|
|
46
|
+
export type GmailGrantCleanup = 'not_attempted' | 'confirmed' | 'unconfirmed';
|
|
47
|
+
/** A grant operation's fixed failure; its message is `gmail_oauth_<reason>`. */
|
|
48
|
+
export declare class GmailOAuthFault extends Error {
|
|
49
|
+
readonly reason: GmailOAuthFailure;
|
|
50
|
+
readonly cleanup: GmailGrantCleanup;
|
|
51
|
+
/**
|
|
52
|
+
* @param reason Why the operation failed.
|
|
53
|
+
* @param cleanup What became at Google of an exchanged grant the flow does not hand back.
|
|
54
|
+
*/
|
|
55
|
+
constructor(reason: GmailOAuthFailure, cleanup?: GmailGrantCleanup);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,oHAAoH;AACpH,eAAO,MAAM,oBAAoB,mDAAmD,CAAC;AACrF,uGAAuG;AACvG,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iHAAiH;IACjH,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AACD,8DAA8D;AAC9D,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AACD,6GAA6G;AAC7G,MAAM,WAAW,iBAAiB;IAChC,iGAAiG;IACjG,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,CAAC,EAAE,mBAAmB,CAAC;CAC1C;AACD,wGAAwG;AACxG,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GACjF,oBAAoB,GAAG,kBAAkB,GAAG,OAAO,GAAG,eAAe,CAAC;AAC1E;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,WAAW,GAAG,aAAa,CAAC;AAC9E,gFAAgF;AAChF,qBAAa,eAAgB,SAAQ,KAAK;IAK5B,QAAQ,CAAC,MAAM,EAAE,iBAAiB;IAAE,QAAQ,CAAC,OAAO,EAAE,iBAAiB;IAJnF;;;OAGG;gBACkB,MAAM,EAAE,iBAAiB,EAAW,OAAO,GAAE,iBAAmC;CAGtG"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The public types of the Gmail grant: Google's read-only scope, the dedicated client, the endpoints, the
|
|
3
|
+
* transport options and the fixed failures a grant operation answers.
|
|
4
|
+
* @module agentos-ext-gmail-source/types
|
|
5
|
+
*/
|
|
6
|
+
/** Google's read-only Gmail scope, the only scope the flow asks for or accepts; Google classes it as Restricted. */
|
|
7
|
+
export const GMAIL_READONLY_SCOPE = 'https://www.googleapis.com/auth/gmail.readonly';
|
|
8
|
+
/** A grant operation's fixed failure; its message is `gmail_oauth_<reason>`. */
|
|
9
|
+
export class GmailOAuthFault extends Error {
|
|
10
|
+
reason;
|
|
11
|
+
cleanup;
|
|
12
|
+
/**
|
|
13
|
+
* @param reason Why the operation failed.
|
|
14
|
+
* @param cleanup What became at Google of an exchanged grant the flow does not hand back.
|
|
15
|
+
*/
|
|
16
|
+
constructor(reason, cleanup = 'not_attempted') {
|
|
17
|
+
super(`gmail_oauth_${reason}`);
|
|
18
|
+
this.reason = reason;
|
|
19
|
+
this.cleanup = cleanup;
|
|
20
|
+
this.name = 'GmailOAuthFault';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,oHAAoH;AACpH,MAAM,CAAC,MAAM,oBAAoB,GAAG,gDAAgD,CAAC;AAyCrF,gFAAgF;AAChF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAKnB;IAAoC;IAJzD;;;OAGG;IACH,YAAqB,MAAyB,EAAW,UAA6B,eAAe;QACnG,KAAK,CAAC,eAAe,MAAM,EAAE,CAAC,CAAC;QADZ,WAAM,GAAN,MAAM,CAAmB;QAAW,YAAO,GAAP,OAAO,CAAqC;QACnE,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChE,CAAC;CACF"}
|
package/manifest.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agentos.sh/schemas/extension-manifest-v1.json",
|
|
3
|
+
"id": "com.framers.productivity.gmail-source",
|
|
4
|
+
"name": "Gmail Source",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "Read-only OAuth and bounded personal Gmail source helpers",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Frame",
|
|
9
|
+
"email": "team@frame.dev",
|
|
10
|
+
"url": "https://frame.dev"
|
|
11
|
+
},
|
|
12
|
+
"license": "Apache-2.0",
|
|
13
|
+
"keywords": ["gmail", "sources", "oauth"],
|
|
14
|
+
"agentosVersion": ">=0.13.134",
|
|
15
|
+
"categories": ["productivity"],
|
|
16
|
+
"repository": { "type": "git", "url": "https://github.com/framerslab/agentos-extensions" },
|
|
17
|
+
"extensions": [{
|
|
18
|
+
"kind": "library", "id": "gmailSource", "displayName": "Gmail source",
|
|
19
|
+
"description": "Read-only authorization for a personal Gmail source", "entry": "./dist/index.js"
|
|
20
|
+
}]
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@framers/agentos-ext-gmail-source",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Read-only OAuth and bounded personal Gmail source helpers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./manifest.json": "./manifest.json",
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"manifest.json",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"agentos",
|
|
24
|
+
"gmail",
|
|
25
|
+
"sources",
|
|
26
|
+
"oauth"
|
|
27
|
+
],
|
|
28
|
+
"author": {
|
|
29
|
+
"name": "Frame",
|
|
30
|
+
"email": "team@frame.dev",
|
|
31
|
+
"url": "https://frame.dev"
|
|
32
|
+
},
|
|
33
|
+
"license": "Apache-2.0",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/framerslab/agentos-extensions.git",
|
|
37
|
+
"directory": "registry/curated/productivity/gmail-source"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20.19.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@framers/agentos": ">=0.13.134",
|
|
47
|
+
"@framers/agentos-ext-account-sources": ">=0.4.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "22.20.1",
|
|
51
|
+
"@vitest/coverage-v8": "^3.2.7",
|
|
52
|
+
"typescript": "5.4.5",
|
|
53
|
+
"vitest": "^3.2.7",
|
|
54
|
+
"@framers/agentos": "^0.13.134",
|
|
55
|
+
"@framers/agentos-ext-account-sources": "^0.6.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsc",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:coverage": "vitest run --coverage",
|
|
61
|
+
"typecheck": "tsc --noEmit"
|
|
62
|
+
}
|
|
6
63
|
}
|