latchkey 3.5.0 → 3.7.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/dist/skills/generic/latchkey/SKILL.md +1 -1
- package/dist/skills/openclaw/latchkey/SKILL.md +1 -1
- package/dist/src/apiCredentials/serialization.d.ts +12 -0
- package/dist/src/apiCredentials/serialization.d.ts.map +1 -1
- package/dist/src/apiCredentials/serialization.js +7 -0
- package/dist/src/apiCredentials/serialization.js.map +1 -1
- package/dist/src/oauthUtils.d.ts +2 -2
- package/dist/src/oauthUtils.d.ts.map +1 -1
- package/dist/src/oauthUtils.js +36 -15
- package/dist/src/oauthUtils.js.map +1 -1
- package/dist/src/serviceRegistry.d.ts.map +1 -1
- package/dist/src/serviceRegistry.js +4 -1
- package/dist/src/serviceRegistry.js.map +1 -1
- package/dist/src/services/fastmail-dav.d.ts +55 -0
- package/dist/src/services/fastmail-dav.d.ts.map +1 -0
- package/dist/src/services/fastmail-dav.js +98 -0
- package/dist/src/services/fastmail-dav.js.map +1 -0
- package/dist/src/services/fastmail.d.ts +79 -0
- package/dist/src/services/fastmail.d.ts.map +1 -0
- package/dist/src/services/fastmail.js +298 -0
- package/dist/src/services/fastmail.js.map +1 -0
- package/dist/src/services/google/gmail.d.ts.map +1 -1
- package/dist/src/services/google/gmail.js +6 -1
- package/dist/src/services/google/gmail.js.map +1 -1
- package/dist/src/services/index.d.ts +3 -0
- package/dist/src/services/index.d.ts.map +1 -1
- package/dist/src/services/index.js +3 -0
- package/dist/src/services/index.js.map +1 -1
- package/dist/src/services/tailscale.d.ts +74 -0
- package/dist/src/services/tailscale.d.ts.map +1 -0
- package/dist/src/services/tailscale.js +221 -0
- package/dist/src/services/tailscale.js.map +1 -0
- package/dist/src/sharedOperations.d.ts +1 -1
- package/dist/src/sharedOperations.d.ts.map +1 -1
- package/dist/src/sharedOperations.js +12 -1
- package/dist/src/sharedOperations.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/tests/serviceRegistry.test.js +32 -1
- package/dist/tests/serviceRegistry.test.js.map +1 -1
- package/dist/tests/servicesAgainstRecordings.test.js +12 -3
- package/dist/tests/servicesAgainstRecordings.test.js.map +1 -1
- package/dist/tests/tailscale.test.d.ts +2 -0
- package/dist/tests/tailscale.test.d.ts.map +1 -0
- package/dist/tests/tailscale.test.js +79 -0
- package/dist/tests/tailscale.test.js.map +1 -0
- package/package.json +2 -2
|
@@ -127,7 +127,7 @@ in the key means "unknown account".
|
|
|
127
127
|
Latchkey currently offers varying levels of support for the
|
|
128
128
|
following services: AWS, Calendly, Coolify, Discord, Dropbox, Figma, GitHub, GitLab,
|
|
129
129
|
Gmail, Google Analytics, Google Calendar, Google Docs, Google Drive, Google Sheets, Hugging Face,
|
|
130
|
-
Linear, Mailchimp, ngrok, Notion, OpenRouter, Ramp, Sentry, Slack, Stripe, Telegram, Todoist, Umami, Yelp, Zoom, and more.
|
|
130
|
+
Linear, Mailchimp, ngrok, Notion, OpenRouter, Ramp, Sentry, Slack, Stripe, Telegram, Tailscale, Todoist, Umami, Yelp, Zoom, and more.
|
|
131
131
|
|
|
132
132
|
### User-registered services
|
|
133
133
|
|
|
@@ -119,7 +119,7 @@ in the key means "unknown account".
|
|
|
119
119
|
Latchkey currently offers varying levels of support for the
|
|
120
120
|
following services: AWS, Calendly, Coolify, Discord, Dropbox, Figma, GitHub, GitLab,
|
|
121
121
|
Gmail, Google Analytics, Google Calendar, Google Docs, Google Drive, Google Sheets, Hugging Face,
|
|
122
|
-
Linear, Mailchimp, ngrok, Notion, OpenRouter, Ramp, Sentry, Slack, Stripe, Telegram, Todoist, Umami, Yelp, Zoom, and more.
|
|
122
|
+
Linear, Mailchimp, ngrok, Notion, OpenRouter, Ramp, Sentry, Slack, Stripe, Telegram, Tailscale, Todoist, Umami, Yelp, Zoom, and more.
|
|
123
123
|
|
|
124
124
|
### User-registered services
|
|
125
125
|
|
|
@@ -124,6 +124,18 @@ export declare const ApiCredentialsSchema: z.ZodDiscriminatedUnion<"objectType",
|
|
|
124
124
|
accountId: string;
|
|
125
125
|
accessToken?: string | undefined;
|
|
126
126
|
accessTokenExpiresAt?: string | undefined;
|
|
127
|
+
}>, z.ZodObject<{
|
|
128
|
+
objectType: z.ZodLiteral<"tailscale">;
|
|
129
|
+
token: z.ZodString;
|
|
130
|
+
tailnet: z.ZodString;
|
|
131
|
+
}, "strip", z.ZodTypeAny, {
|
|
132
|
+
objectType: "tailscale";
|
|
133
|
+
token: string;
|
|
134
|
+
tailnet: string;
|
|
135
|
+
}, {
|
|
136
|
+
objectType: "tailscale";
|
|
137
|
+
token: string;
|
|
138
|
+
tailnet: string;
|
|
127
139
|
}>]>;
|
|
128
140
|
export type ApiCredentialsData = z.infer<typeof ApiCredentialsSchema>;
|
|
129
141
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../../../src/apiCredentials/serialization.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,KAAK,cAAc,EASpB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../../../src/apiCredentials/serialization.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,KAAK,cAAc,EASpB,MAAM,WAAW,CAAC;AAWnB;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAW/B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEtE;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CA6B/E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,cAAc,GAAG,kBAAkB,CAgCpF;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC7C,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -12,6 +12,7 @@ import { GoogleApiKeyCredentials, GoogleApiKeyCredentialsSchema } from '../servi
|
|
|
12
12
|
import { SlackApiCredentials, SlackApiCredentialsSchema } from '../services/slack.js';
|
|
13
13
|
import { TelegramBotCredentials, TelegramBotCredentialsSchema } from '../services/telegram.js';
|
|
14
14
|
import { ZoomServerToServerCredentials, ZoomServerToServerCredentialsSchema, } from '../services/zoom.js';
|
|
15
|
+
import { TailscaleCredentials, TailscaleCredentialsSchema } from '../services/tailscale.js';
|
|
15
16
|
/**
|
|
16
17
|
* Union schema for all credential types.
|
|
17
18
|
*/
|
|
@@ -25,6 +26,7 @@ export const ApiCredentialsSchema = z.discriminatedUnion('objectType', [
|
|
|
25
26
|
AwsCredentialsSchema,
|
|
26
27
|
GoogleApiKeyCredentialsSchema,
|
|
27
28
|
ZoomServerToServerCredentialsSchema,
|
|
29
|
+
TailscaleCredentialsSchema,
|
|
28
30
|
]);
|
|
29
31
|
/**
|
|
30
32
|
* Deserialize credentials from JSON data.
|
|
@@ -49,6 +51,8 @@ export function deserializeCredentials(data) {
|
|
|
49
51
|
return GoogleApiKeyCredentials.fromJSON(data);
|
|
50
52
|
case 'zoomServerToServer':
|
|
51
53
|
return ZoomServerToServerCredentials.fromJSON(data);
|
|
54
|
+
case 'tailscale':
|
|
55
|
+
return TailscaleCredentials.fromJSON(data);
|
|
52
56
|
default: {
|
|
53
57
|
const exhaustiveCheck = data;
|
|
54
58
|
throw new ApiCredentialsSerializationError(`Unknown credential type: ${exhaustiveCheck.objectType}`);
|
|
@@ -86,6 +90,9 @@ export function serializeCredentials(credentials) {
|
|
|
86
90
|
if (credentials instanceof ZoomServerToServerCredentials) {
|
|
87
91
|
return credentials.toJSON();
|
|
88
92
|
}
|
|
93
|
+
if (credentials instanceof TailscaleCredentials) {
|
|
94
|
+
return credentials.toJSON();
|
|
95
|
+
}
|
|
89
96
|
throw new ApiCredentialsSerializationError(`Unknown credential type: ${credentials.objectType}`);
|
|
90
97
|
}
|
|
91
98
|
export class ApiCredentialsSerializationError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialization.js","sourceRoot":"","sources":["../../../src/apiCredentials/serialization.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,EACnB,yBAAyB,EACzB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAC;AAC/F,OAAO,EACL,6BAA6B,EAC7B,mCAAmC,GACpC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"serialization.js","sourceRoot":"","sources":["../../../src/apiCredentials/serialization.ts"],"names":[],"mappings":"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"}
|
package/dist/src/oauthUtils.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export declare function generateCodeChallenge(verifier: string): string;
|
|
|
48
48
|
* @param redirectUri - The redirect URI used in the authorization request
|
|
49
49
|
* @param codeVerifier - Optional PKCE code verifier
|
|
50
50
|
*/
|
|
51
|
-
export declare function exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, codeVerifier?: string): Promise<OAuthTokenExchangeResponse>;
|
|
51
|
+
export declare function exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, codeVerifier?: string, extraParams?: Readonly<Record<string, string>>): Promise<OAuthTokenExchangeResponse>;
|
|
52
52
|
/**
|
|
53
53
|
* Refresh OAuth access token using the refresh token.
|
|
54
54
|
* @param tokenEndpoint - The OAuth token endpoint URL
|
|
@@ -57,5 +57,5 @@ export declare function exchangeCodeForTokens(tokenEndpoint: string, code: strin
|
|
|
57
57
|
* @param clientSecret - The OAuth client secret
|
|
58
58
|
* @returns The new token response, or null if refresh failed
|
|
59
59
|
*/
|
|
60
|
-
export declare function refreshAccessToken(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string): Promise<OAuthTokenResponse | null>;
|
|
60
|
+
export declare function refreshAccessToken(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string, extraParams?: Readonly<Record<string, string>>): Promise<OAuthTokenResponse | null>;
|
|
61
61
|
//# sourceMappingURL=oauthUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauthUtils.d.ts","sourceRoot":"","sources":["../../src/oauthUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,0BAA2B,SAAQ,kBAAkB;IACpE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,+BAAgC,SAAQ,KAAK;;CAKzD;AAED,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,EACpB,YAAY,SAAoB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"oauthUtils.d.ts","sourceRoot":"","sources":["../../src/oauthUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,0BAA2B,SAAQ,kBAAkB;IACpE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,+BAAgC,SAAQ,KAAK;;CAKzD;AAED,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,EACpB,YAAY,SAAoB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CA8F9B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC7C,OAAO,CAAC,0BAA0B,CAAC,CA6DrC;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC7C,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAuCpC"}
|
package/dist/src/oauthUtils.js
CHANGED
|
@@ -72,11 +72,21 @@ export function startOAuthCallbackServer(timeoutMs, signal, callbackPath = '/oau
|
|
|
72
72
|
resolve(code);
|
|
73
73
|
}
|
|
74
74
|
else {
|
|
75
|
+
// No code means the authorization server refused. RFC 6749 §4.1.2.1
|
|
76
|
+
// puts the reason in `error` / `error_description`; reporting the
|
|
77
|
+
// bare absence of a code instead leaves the caller with nothing to
|
|
78
|
+
// act on.
|
|
79
|
+
const error = parsedUrl.searchParams.get('error');
|
|
80
|
+
const errorDescription = parsedUrl.searchParams.get('error_description');
|
|
81
|
+
const reason = error === null
|
|
82
|
+
? 'No authorization code received from OAuth callback.'
|
|
83
|
+
: `Authorization failed: ${error}` +
|
|
84
|
+
(errorDescription === null ? '' : ` (${errorDescription})`);
|
|
75
85
|
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
76
86
|
res.end('ERROR');
|
|
77
87
|
signal?.removeEventListener('abort', abortHandler);
|
|
78
88
|
cleanup();
|
|
79
|
-
reject(new LoginFailedError(
|
|
89
|
+
reject(new LoginFailedError(reason));
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
92
|
else {
|
|
@@ -120,13 +130,16 @@ export function generateCodeChallenge(verifier) {
|
|
|
120
130
|
* @param redirectUri - The redirect URI used in the authorization request
|
|
121
131
|
* @param codeVerifier - Optional PKCE code verifier
|
|
122
132
|
*/
|
|
123
|
-
export async function exchangeCodeForTokens(tokenEndpoint, code, clientId, clientSecret, redirectUri, codeVerifier) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
133
|
+
export async function exchangeCodeForTokens(tokenEndpoint, code, clientId, clientSecret, redirectUri, codeVerifier, extraParams) {
|
|
134
|
+
// Extras first, so the protocol fields below always win: a caller cannot
|
|
135
|
+
// accidentally replace `grant_type` or `code` with its own value. Servers that
|
|
136
|
+
// pin tokens to a named audience need their own parameters here, e.g. RFC
|
|
137
|
+
// 8707's `resource`.
|
|
138
|
+
const body = new URLSearchParams({ ...extraParams });
|
|
139
|
+
body.set('code', code);
|
|
140
|
+
body.set('client_id', clientId);
|
|
141
|
+
body.set('redirect_uri', redirectUri);
|
|
142
|
+
body.set('grant_type', 'authorization_code');
|
|
130
143
|
if (clientSecret) {
|
|
131
144
|
body.set('client_secret', clientSecret);
|
|
132
145
|
}
|
|
@@ -149,7 +162,13 @@ export async function exchangeCodeForTokens(tokenEndpoint, code, clientId, clien
|
|
|
149
162
|
try {
|
|
150
163
|
const response = JSON.parse(result.stdout);
|
|
151
164
|
if (!response.access_token || !response.refresh_token) {
|
|
152
|
-
|
|
165
|
+
// A refusal is reported as `error` / `error_description` (RFC 6749 §5.2);
|
|
166
|
+
// reporting only the absent fields discards the reason.
|
|
167
|
+
const reason = response.error === undefined
|
|
168
|
+
? 'Token response missing access_token or refresh_token.'
|
|
169
|
+
: `Token exchange failed: ${response.error}` +
|
|
170
|
+
(response.error_description === undefined ? '' : ` (${response.error_description})`);
|
|
171
|
+
throw new OAuthTokenExchangeError(reason);
|
|
153
172
|
}
|
|
154
173
|
return response;
|
|
155
174
|
}
|
|
@@ -168,12 +187,14 @@ export async function exchangeCodeForTokens(tokenEndpoint, code, clientId, clien
|
|
|
168
187
|
* @param clientSecret - The OAuth client secret
|
|
169
188
|
* @returns The new token response, or null if refresh failed
|
|
170
189
|
*/
|
|
171
|
-
export async function refreshAccessToken(tokenEndpoint, refreshToken, clientId, clientSecret) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
190
|
+
export async function refreshAccessToken(tokenEndpoint, refreshToken, clientId, clientSecret, extraParams) {
|
|
191
|
+
// Extras first so the protocol fields win (see exchangeCodeForTokens). A
|
|
192
|
+
// refresh has to name the same audience the original grant did, or a server
|
|
193
|
+
// that pins tokens to a resource will refuse it.
|
|
194
|
+
const body = new URLSearchParams({ ...extraParams });
|
|
195
|
+
body.set('refresh_token', refreshToken);
|
|
196
|
+
body.set('client_id', clientId);
|
|
197
|
+
body.set('grant_type', 'refresh_token');
|
|
177
198
|
if (clientSecret) {
|
|
178
199
|
body.set('client_secret', clientSecret);
|
|
179
200
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauthUtils.js","sourceRoot":"","sources":["../../src/oauthUtils.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"oauthUtils.js","sourceRoot":"","sources":["../../src/oauthUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceRegistry.d.ts","sourceRoot":"","sources":["../../src/serviceRegistry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EACL,OAAO,
|
|
1
|
+
{"version":3,"file":"serviceRegistry.d.ts","sourceRoot":"","sources":["../../src/serviceRegistry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EACL,OAAO,EAqCR,MAAM,qBAAqB,CAAC;AAE7B,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,IAAI,EAAE,MAAM;CAIzB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,IAAI,EAAE,MAAM;CAMzB;AAID,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM5D;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAE1B,QAAQ,EAAE,SAAS,OAAO,EAAE;IAIxC,IAAI,QAAQ,IAAI,SAAS,OAAO,EAAE,CAEjC;IAED,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAOlC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOjC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IASvC,OAAO,CAAC,UAAU;IAelB;;;;;;;;OAQG;IACH,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,OAAO,EAAE;IAInD;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;CAGtC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,eAAe,EACzB,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B,IAAI,CAIN;AAyBD,wBAAgB,yCAAyC,CACvD,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,eAAe,GACxB,IAAI,CAwBN;AAED,eAAO,MAAM,gBAAgB,iBAqC3B,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { loadRegisteredServices } from './configDataStore.js';
|
|
|
5
5
|
import { buildRegisteredServiceOptions, RegisteredService } from './services/core/registered.js';
|
|
6
6
|
import { resolveLoginFlow, UnknownLoginFlowError } from './services/core/loginFlows/registry.js';
|
|
7
7
|
import { LoginFlowParamsInvalidError } from './services/core/loginFlows/base.js';
|
|
8
|
-
import { SLACK, DISCORD, DROPBOX, GITHUB, LINEAR, NOTION, NOTION_MCP, GOOGLE_GMAIL, GOOGLE_CALENDAR, GOOGLE_DRIVE, GOOGLE_SHEETS, GOOGLE_DOCS, GOOGLE_SLIDES, GOOGLE_PEOPLE, MAILCHIMP, GITLAB, ZOOM, SENTRY, STRIPE, FIGMA, GOOGLE_ANALYTICS, CALENDLY, YELP, TELEGRAM, AWS, GOOGLE_DIRECTIONS, COOLIFY, UMAMI, RAMP, TODOIST, NGROK, HUGGINGFACE, OPENROUTER, } from './services/index.js';
|
|
8
|
+
import { SLACK, DISCORD, DROPBOX, GITHUB, LINEAR, NOTION, NOTION_MCP, GOOGLE_GMAIL, GOOGLE_CALENDAR, GOOGLE_DRIVE, GOOGLE_SHEETS, GOOGLE_DOCS, GOOGLE_SLIDES, GOOGLE_PEOPLE, MAILCHIMP, FASTMAIL, FASTMAIL_DAV, GITLAB, ZOOM, SENTRY, STRIPE, FIGMA, GOOGLE_ANALYTICS, CALENDLY, YELP, TELEGRAM, AWS, GOOGLE_DIRECTIONS, COOLIFY, UMAMI, RAMP, TODOIST, NGROK, HUGGINGFACE, OPENROUTER, TAILSCALE, } from './services/index.js';
|
|
9
9
|
export class DuplicateServiceNameError extends Error {
|
|
10
10
|
constructor(name) {
|
|
11
11
|
super(`A service with the name '${name}' already exists.`);
|
|
@@ -156,6 +156,8 @@ export const SERVICE_REGISTRY = new ServiceRegistry([
|
|
|
156
156
|
GOOGLE_SLIDES,
|
|
157
157
|
GOOGLE_PEOPLE,
|
|
158
158
|
MAILCHIMP,
|
|
159
|
+
FASTMAIL,
|
|
160
|
+
FASTMAIL_DAV,
|
|
159
161
|
GITLAB,
|
|
160
162
|
ZOOM,
|
|
161
163
|
SENTRY,
|
|
@@ -174,5 +176,6 @@ export const SERVICE_REGISTRY = new ServiceRegistry([
|
|
|
174
176
|
NGROK,
|
|
175
177
|
HUGGINGFACE,
|
|
176
178
|
OPENROUTER,
|
|
179
|
+
TAILSCALE,
|
|
177
180
|
]);
|
|
178
181
|
//# sourceMappingURL=serviceRegistry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceRegistry.js","sourceRoot":"","sources":["../../src/serviceRegistry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAA+B,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EAAE,6BAA6B,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AACjG,OAAO,EAAE,2BAA2B,EAAkB,MAAM,oCAAoC,CAAC;AACjG,OAAO,EAEL,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,MAAM,EACN,MAAM,EACN,UAAU,EACV,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,SAAS,EACT,MAAM,EACN,IAAI,EACJ,MAAM,EACN,MAAM,EACN,KAAK,EACL,gBAAgB,EAChB,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,iBAAiB,EACjB,OAAO,EACP,KAAK,EACL,IAAI,EACJ,OAAO,EACP,KAAK,EACL,WAAW,EACX,UAAU,
|
|
1
|
+
{"version":3,"file":"serviceRegistry.js","sourceRoot":"","sources":["../../src/serviceRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastmail CardDAV / CalDAV service implementation.
|
|
3
|
+
*
|
|
4
|
+
* Separate from the `fastmail` service, which speaks JMAP, because the two take
|
|
5
|
+
* different credentials against different hosts. Fastmail's OAuth access tokens
|
|
6
|
+
* are accepted only by the JMAP API; the DAV endpoints refuse them (401 on every
|
|
7
|
+
* collection, including the principal URL) and want an **app password** over
|
|
8
|
+
* HTTP Basic auth instead. Fastmail's own documentation draws the same line: an
|
|
9
|
+
* API token is "for JMAP access", an app password is "for everything else".
|
|
10
|
+
*
|
|
11
|
+
* Splitting by host also keeps URL matching unambiguous. `latchkey curl` picks
|
|
12
|
+
* among services that match a URL by registration order, with no way for a
|
|
13
|
+
* caller to name the one it wants, so two services claiming the same host would
|
|
14
|
+
* be a coin flip. These hosts are disjoint from the JMAP ones.
|
|
15
|
+
*
|
|
16
|
+
* The credential is an app password from Settings → Privacy & Security →
|
|
17
|
+
* "Connected apps & API tokens" → "Manage app passwords and access", used with
|
|
18
|
+
* the account's own e-mail address as the username. Its "DAV" access level is
|
|
19
|
+
* enough.
|
|
20
|
+
*
|
|
21
|
+
* One failure worth recognizing: on an account with no active subscription
|
|
22
|
+
* (observed on a lapsed trial) app passwords appear to be refused for every
|
|
23
|
+
* legacy protocol, and the refusal is a 401 reading "Incorrect username,
|
|
24
|
+
* password or access token" — indistinguishable from a typo — while JMAP over
|
|
25
|
+
* an API token or OAuth keeps working. Subscribing restored DAV with no change
|
|
26
|
+
* to any request. An IMAP login against imap.fastmail.com with the same
|
|
27
|
+
* credential tells the two apart: refused there too points at the account
|
|
28
|
+
* rather than the credential.
|
|
29
|
+
*/
|
|
30
|
+
import type { ApiCredentials } from '../apiCredentials/base.js';
|
|
31
|
+
import { Service } from './core/base.js';
|
|
32
|
+
export declare class FastmailDav extends Service {
|
|
33
|
+
readonly name = "fastmail-dav";
|
|
34
|
+
readonly displayName = "Fastmail (CardDAV/CalDAV)";
|
|
35
|
+
/**
|
|
36
|
+
* Contacts and calendars live on their own hosts, and — unlike the JMAP API —
|
|
37
|
+
* these are not region-sharded: `/.well-known/carddav` redirects within
|
|
38
|
+
* `carddav.fastmail.com` rather than off to a shard. The optional prefix is
|
|
39
|
+
* defensive, so a future shard would not need a new registration.
|
|
40
|
+
*/
|
|
41
|
+
readonly baseApiUrls: readonly [RegExp, RegExp];
|
|
42
|
+
readonly loginUrl = "https://app.fastmail.com/settings/security";
|
|
43
|
+
readonly info: string;
|
|
44
|
+
readonly credentialCheckCurlArguments: readonly ["-X", "PROPFIND", "-H", "Depth: 0", "-H", "Content-Type: application/xml; charset=utf-8", "--data-binary", string, "https://carddav.fastmail.com/dav/principals/"];
|
|
45
|
+
setCredentialsExample(serviceName: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* WebDAV answers a successful PROPFIND with 207 Multi-Status (RFC 4918 §13),
|
|
48
|
+
* so the inherited 200-only check would report every working app password as
|
|
49
|
+
* invalid.
|
|
50
|
+
*/
|
|
51
|
+
protected isCredentialCheckResponseValid(httpStatusCode: string): boolean;
|
|
52
|
+
getAccount(apiCredentials: ApiCredentials): Promise<string | null>;
|
|
53
|
+
}
|
|
54
|
+
export declare const FASTMAIL_DAV: FastmailDav;
|
|
55
|
+
//# sourceMappingURL=fastmail-dav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastmail-dav.d.ts","sourceRoot":"","sources":["../../../src/services/fastmail-dav.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAoCzC,qBAAa,WAAY,SAAQ,OAAO;IACtC,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,WAAW,+BAA+B;IAEnD;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,4BAGT;IAEX,QAAQ,CAAC,QAAQ,gDAAgD;IAEjE,QAAQ,CAAC,IAAI,SAKsB;IAEnC,QAAQ,CAAC,4BAA4B,+KAAkC;IAEvE,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAIlD;;;;OAIG;cACgB,8BAA8B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO;IAIzE,UAAU,CAAC,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAO5E;AAED,eAAO,MAAM,YAAY,aAAoB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastmail CardDAV / CalDAV service implementation.
|
|
3
|
+
*
|
|
4
|
+
* Separate from the `fastmail` service, which speaks JMAP, because the two take
|
|
5
|
+
* different credentials against different hosts. Fastmail's OAuth access tokens
|
|
6
|
+
* are accepted only by the JMAP API; the DAV endpoints refuse them (401 on every
|
|
7
|
+
* collection, including the principal URL) and want an **app password** over
|
|
8
|
+
* HTTP Basic auth instead. Fastmail's own documentation draws the same line: an
|
|
9
|
+
* API token is "for JMAP access", an app password is "for everything else".
|
|
10
|
+
*
|
|
11
|
+
* Splitting by host also keeps URL matching unambiguous. `latchkey curl` picks
|
|
12
|
+
* among services that match a URL by registration order, with no way for a
|
|
13
|
+
* caller to name the one it wants, so two services claiming the same host would
|
|
14
|
+
* be a coin flip. These hosts are disjoint from the JMAP ones.
|
|
15
|
+
*
|
|
16
|
+
* The credential is an app password from Settings → Privacy & Security →
|
|
17
|
+
* "Connected apps & API tokens" → "Manage app passwords and access", used with
|
|
18
|
+
* the account's own e-mail address as the username. Its "DAV" access level is
|
|
19
|
+
* enough.
|
|
20
|
+
*
|
|
21
|
+
* One failure worth recognizing: on an account with no active subscription
|
|
22
|
+
* (observed on a lapsed trial) app passwords appear to be refused for every
|
|
23
|
+
* legacy protocol, and the refusal is a 401 reading "Incorrect username,
|
|
24
|
+
* password or access token" — indistinguishable from a typo — while JMAP over
|
|
25
|
+
* an API token or OAuth keeps working. Subscribing restored DAV with no change
|
|
26
|
+
* to any request. An IMAP login against imap.fastmail.com with the same
|
|
27
|
+
* credential tells the two apart: refused there too points at the account
|
|
28
|
+
* rather than the credential.
|
|
29
|
+
*/
|
|
30
|
+
import { fetchAccountFromEndpoint } from '../apiCredentials/account.js';
|
|
31
|
+
import { Service } from './core/base.js';
|
|
32
|
+
/**
|
|
33
|
+
* A `current-user-principal` PROPFIND (RFC 5397). Depth 0 keeps it to the
|
|
34
|
+
* collection itself, and the response names the principal of whoever
|
|
35
|
+
* authenticated — which is how this doubles as the account lookup.
|
|
36
|
+
*/
|
|
37
|
+
const PRINCIPAL_PROPFIND_BODY = '<?xml version="1.0" encoding="utf-8"?>' +
|
|
38
|
+
'<D:propfind xmlns:D="DAV:"><D:prop><D:current-user-principal/></D:prop></D:propfind>';
|
|
39
|
+
const PRINCIPAL_CHECK_CURL_ARGUMENTS = [
|
|
40
|
+
'-X',
|
|
41
|
+
'PROPFIND',
|
|
42
|
+
'-H',
|
|
43
|
+
'Depth: 0',
|
|
44
|
+
'-H',
|
|
45
|
+
'Content-Type: application/xml; charset=utf-8',
|
|
46
|
+
'--data-binary',
|
|
47
|
+
PRINCIPAL_PROPFIND_BODY,
|
|
48
|
+
'https://carddav.fastmail.com/dav/principals/',
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* Pull the account out of a `current-user-principal` response. Fastmail's
|
|
52
|
+
* principal hrefs carry the account's e-mail, e.g.
|
|
53
|
+
* `/dav/principals/user/someone@fastmail.com/`.
|
|
54
|
+
*
|
|
55
|
+
* Matched with a regular expression rather than parsed as XML: latchkey has no
|
|
56
|
+
* XML parser, the shape is fixed by RFC 5397, and the alternative is taking on a
|
|
57
|
+
* dependency to read one href.
|
|
58
|
+
*/
|
|
59
|
+
function parseAccountFromPrincipalResponse(responseBody) {
|
|
60
|
+
return /\/dav\/principals\/user\/([^/<>"]+)/.exec(responseBody)?.[1] ?? null;
|
|
61
|
+
}
|
|
62
|
+
export class FastmailDav extends Service {
|
|
63
|
+
name = 'fastmail-dav';
|
|
64
|
+
displayName = 'Fastmail (CardDAV/CalDAV)';
|
|
65
|
+
/**
|
|
66
|
+
* Contacts and calendars live on their own hosts, and — unlike the JMAP API —
|
|
67
|
+
* these are not region-sharded: `/.well-known/carddav` redirects within
|
|
68
|
+
* `carddav.fastmail.com` rather than off to a shard. The optional prefix is
|
|
69
|
+
* defensive, so a future shard would not need a new registration.
|
|
70
|
+
*/
|
|
71
|
+
baseApiUrls = [
|
|
72
|
+
/^https:\/\/([a-z0-9-]+[.-])?carddav\.fastmail\.com\//,
|
|
73
|
+
/^https:\/\/([a-z0-9-]+[.-])?caldav\.fastmail\.com\//,
|
|
74
|
+
];
|
|
75
|
+
loginUrl = 'https://app.fastmail.com/settings/security';
|
|
76
|
+
info = 'https://www.fastmail.com/dev/ — contacts over CardDAV (RFC 6352) and calendars over ' +
|
|
77
|
+
'CalDAV (RFC 4791), rooted at /dav/addressbooks and /dav/calendars. ' +
|
|
78
|
+
'Credentials are an app password, rather than the OAuth token the `fastmail` service uses. ' +
|
|
79
|
+
'The `fastmail` service can also reach contacts over JMAP, which is the better route unless a ' +
|
|
80
|
+
'caller needs DAV specifically.';
|
|
81
|
+
credentialCheckCurlArguments = PRINCIPAL_CHECK_CURL_ARGUMENTS;
|
|
82
|
+
setCredentialsExample(serviceName) {
|
|
83
|
+
return `latchkey auth set ${serviceName} -u "you@fastmail.com:<app password>"`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* WebDAV answers a successful PROPFIND with 207 Multi-Status (RFC 4918 §13),
|
|
87
|
+
* so the inherited 200-only check would report every working app password as
|
|
88
|
+
* invalid.
|
|
89
|
+
*/
|
|
90
|
+
isCredentialCheckResponseValid(httpStatusCode) {
|
|
91
|
+
return httpStatusCode === '207' || httpStatusCode === '200';
|
|
92
|
+
}
|
|
93
|
+
getAccount(apiCredentials) {
|
|
94
|
+
return fetchAccountFromEndpoint(apiCredentials, this.credentialCheckCurlArguments, parseAccountFromPrincipalResponse);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export const FASTMAIL_DAV = new FastmailDav();
|
|
98
|
+
//# sourceMappingURL=fastmail-dav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastmail-dav.js","sourceRoot":"","sources":["../../../src/services/fastmail-dav.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC;;;;GAIG;AACH,MAAM,uBAAuB,GAC3B,wCAAwC;IACxC,sFAAsF,CAAC;AAEzF,MAAM,8BAA8B,GAAG;IACrC,IAAI;IACJ,UAAU;IACV,IAAI;IACJ,UAAU;IACV,IAAI;IACJ,8CAA8C;IAC9C,eAAe;IACf,uBAAuB;IACvB,8CAA8C;CACtC,CAAC;AAEX;;;;;;;;GAQG;AACH,SAAS,iCAAiC,CAAC,YAAoB;IAC7D,OAAO,qCAAqC,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC/E,CAAC;AAED,MAAM,OAAO,WAAY,SAAQ,OAAO;IAC7B,IAAI,GAAG,cAAc,CAAC;IACtB,WAAW,GAAG,2BAA2B,CAAC;IAEnD;;;;;OAKG;IACM,WAAW,GAAG;QACrB,sDAAsD;QACtD,qDAAqD;KAC7C,CAAC;IAEF,QAAQ,GAAG,4CAA4C,CAAC;IAExD,IAAI,GACX,sFAAsF;QACtF,qEAAqE;QACrE,4FAA4F;QAC5F,+FAA+F;QAC/F,gCAAgC,CAAC;IAE1B,4BAA4B,GAAG,8BAA8B,CAAC;IAEvE,qBAAqB,CAAC,WAAmB;QACvC,OAAO,qBAAqB,WAAW,uCAAuC,CAAC;IACjF,CAAC;IAED;;;;OAIG;IACgB,8BAA8B,CAAC,cAAsB;QACtE,OAAO,cAAc,KAAK,KAAK,IAAI,cAAc,KAAK,KAAK,CAAC;IAC9D,CAAC;IAEQ,UAAU,CAAC,cAA8B;QAChD,OAAO,wBAAwB,CAC7B,cAAc,EACd,IAAI,CAAC,4BAA4B,EACjC,iCAAiC,CAClC,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastmail service implementation.
|
|
3
|
+
*
|
|
4
|
+
* Fastmail's JMAP API is an OAuth 2.1 protected resource: it advertises an
|
|
5
|
+
* authorization server at `https://api.fastmail.com` (RFC 8414 metadata at
|
|
6
|
+
* `/.well-known/oauth-authorization-server`) supporting dynamic client
|
|
7
|
+
* registration, PKCE (S256) and refresh tokens, with public clients
|
|
8
|
+
* (`token_endpoint_auth_method: none`). So no API token has to be created by
|
|
9
|
+
* hand in the account's settings and pasted in — `latchkey auth browser
|
|
10
|
+
* fastmail` is enough.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import type { Browser, BrowserContext, Response } from 'playwright';
|
|
14
|
+
import { type ApiCredentials } from '../apiCredentials/base.js';
|
|
15
|
+
import { Service, ServiceSession, type LoginResult } from './core/base.js';
|
|
16
|
+
/**
|
|
17
|
+
* JSON accepted by `latchkey auth prepare fastmail`: an OAuth client id to
|
|
18
|
+
* reuse instead of registering a new client dynamically. Fastmail issues
|
|
19
|
+
* public clients, so no secret is needed. `.strict()` rejects unknown keys so
|
|
20
|
+
* typos are reported instead of silently ignored.
|
|
21
|
+
*/
|
|
22
|
+
export declare const FastmailPrepareInputSchema: z.ZodObject<{
|
|
23
|
+
clientId: z.ZodString;
|
|
24
|
+
}, "strict", z.ZodTypeAny, {
|
|
25
|
+
clientId: string;
|
|
26
|
+
}, {
|
|
27
|
+
clientId: string;
|
|
28
|
+
}>;
|
|
29
|
+
export type FastmailPrepareInput = z.infer<typeof FastmailPrepareInputSchema>;
|
|
30
|
+
declare class FastmailSession extends ServiceSession {
|
|
31
|
+
onResponse(_response: Response): void;
|
|
32
|
+
protected isLoginComplete(): boolean;
|
|
33
|
+
protected finalizeCredentials(_browser: Browser, _context: BrowserContext, _oldCredentials?: ApiCredentials): Promise<ApiCredentials | null>;
|
|
34
|
+
login(encryptedStorage: import('../encryptedStorage.js').EncryptedStorage, launchOptions?: import('../playwrightUtils.js').BrowserLaunchOptions, oldCredentials?: ApiCredentials): Promise<LoginResult>;
|
|
35
|
+
}
|
|
36
|
+
export declare class Fastmail extends Service {
|
|
37
|
+
readonly name = "fastmail";
|
|
38
|
+
readonly displayName = "Fastmail";
|
|
39
|
+
/**
|
|
40
|
+
* Four host shapes, which is why these are patterns rather than plain
|
|
41
|
+
* prefixes. Session discovery starts at `api.fastmail.com`, but the `apiUrl`
|
|
42
|
+
* and `downloadUrl` it returns point at the shard the account is homed on
|
|
43
|
+
* (`phl.api.fastmail.com`, `phl-www.fastmailusercontent.com`, …). Which
|
|
44
|
+
* shard that is only becomes known after discovery, so the service has to
|
|
45
|
+
* cover them all up front.
|
|
46
|
+
*
|
|
47
|
+
* The blob pattern is anchored on the `www` label rather than accepting any
|
|
48
|
+
* subdomain: the session document only ever names `www.fastmailusercontent.com`
|
|
49
|
+
* or `<shard>-www.…`, and this is the domain Fastmail uses to serve user
|
|
50
|
+
* content, so matching it loosely would offer the access token to hosts the
|
|
51
|
+
* credential has no business reaching.
|
|
52
|
+
*
|
|
53
|
+
* Deliberately NOT covering carddav.fastmail.com / caldav.fastmail.com. Those
|
|
54
|
+
* advertise `WWW-Authenticate: Basic realm=..., Bearer`, so adding them looks
|
|
55
|
+
* right, but Fastmail's OAuth access tokens are refused there (401 on every
|
|
56
|
+
* path tried, including the principal and per-user collections) — the DAV
|
|
57
|
+
* endpoints want an app password over Basic auth, which is a different
|
|
58
|
+
* credential type than this service issues. Contacts and calendars are
|
|
59
|
+
* reachable over JMAP on the hosts below instead.
|
|
60
|
+
*/
|
|
61
|
+
readonly baseApiUrls: readonly [RegExp, RegExp];
|
|
62
|
+
readonly loginUrl = "https://api.fastmail.com/oauth/authorize";
|
|
63
|
+
readonly info: string;
|
|
64
|
+
readonly credentialCheckCurlArguments: readonly ["https://api.fastmail.com/jmap/session"];
|
|
65
|
+
setCredentialsExample(serviceName: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Fastmail accepts an OAuth client id prepared in advance via
|
|
68
|
+
* `latchkey auth prepare`, stored as token-less OAuth credentials until
|
|
69
|
+
* login. The login flow reuses this client id instead of registering a new
|
|
70
|
+
* client dynamically.
|
|
71
|
+
*/
|
|
72
|
+
prepareFromJson(parsedJson: unknown): ApiCredentials;
|
|
73
|
+
getAccount(apiCredentials: ApiCredentials): Promise<string | null>;
|
|
74
|
+
getSession(appNamePrefix: string): FastmailSession;
|
|
75
|
+
refreshCredentials(apiCredentials: ApiCredentials): Promise<ApiCredentials | null>;
|
|
76
|
+
}
|
|
77
|
+
export declare const FASTMAIL: Fastmail;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=fastmail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastmail.d.ts","sourceRoot":"","sources":["../../../src/services/fastmail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,KAAK,cAAc,EAAoB,MAAM,2BAA2B,CAAC;AAclF,OAAO,EACL,OAAO,EACP,cAAc,EACd,KAAK,WAAW,EAKjB,MAAM,gBAAgB,CAAC;AAExB;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B;;;;;;EAI5B,CAAC;AAEZ,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AA8I9E,cAAM,eAAgB,SAAQ,cAAc;IAC1C,UAAU,CAAC,SAAS,EAAE,QAAQ,GAAG,IAAI;IAIrC,SAAS,CAAC,eAAe,IAAI,OAAO;IAKpC,SAAS,CAAC,mBAAmB,CAC3B,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,cAAc,EACxB,eAAe,CAAC,EAAE,cAAc,GAC/B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAKlB,KAAK,CAClB,gBAAgB,EAAE,OAAO,wBAAwB,EAAE,gBAAgB,EACnE,aAAa,GAAE,OAAO,uBAAuB,EAAE,oBAAyB,EACxE,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,WAAW,CAAC;CAiGxB;AAED,qBAAa,QAAS,SAAQ,OAAO;IACnC,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,WAAW,cAAc;IAElC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,WAAW,4BAGT;IAEX,QAAQ,CAAC,QAAQ,8CAA0B;IAE3C,QAAQ,CAAC,IAAI,SAI6D;IAE1E,QAAQ,CAAC,4BAA4B,qDAA+B;IAEpE,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAIlD;;;;;OAKG;IACM,eAAe,CAAC,UAAU,EAAE,OAAO,GAAG,cAAc;IASpD,UAAU,CAAC,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQlE,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,eAAe;IAI5C,kBAAkB,CAC/B,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;CAgClC;AAED,eAAO,MAAM,QAAQ,UAAiB,CAAC"}
|