mindweave 2.4.2 → 2.4.5

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.
Files changed (59) hide show
  1. package/dist/cli/App.js +213 -104
  2. package/dist/cli/App.js.map +1 -1
  3. package/dist/cli/attachments.js +14 -1
  4. package/dist/cli/attachments.js.map +1 -1
  5. package/dist/cli/blockSpacing.js +22 -1
  6. package/dist/cli/blockSpacing.js.map +1 -1
  7. package/dist/cli/components/ApprovalBox.js +16 -4
  8. package/dist/cli/components/ApprovalBox.js.map +1 -1
  9. package/dist/cli/components/BlockView.js +1 -1
  10. package/dist/cli/components/BlockView.js.map +1 -1
  11. package/dist/cli/components/KeyManager.js +3 -42
  12. package/dist/cli/components/KeyManager.js.map +1 -1
  13. package/dist/cli/components/McpMinitabs.js +689 -0
  14. package/dist/cli/components/McpMinitabs.js.map +1 -0
  15. package/dist/cli/components/ToolLine.js +12 -3
  16. package/dist/cli/components/ToolLine.js.map +1 -1
  17. package/dist/cli/components/minitabs.js +71 -0
  18. package/dist/cli/components/minitabs.js.map +1 -0
  19. package/dist/cli/framebuffer/writer.js +20 -3
  20. package/dist/cli/framebuffer/writer.js.map +1 -1
  21. package/dist/cli/toolDisplay.js +4 -1
  22. package/dist/cli/toolDisplay.js.map +1 -1
  23. package/dist/cli/transcript.js +1 -0
  24. package/dist/cli/transcript.js.map +1 -1
  25. package/dist/dynamo/engine.js +15 -0
  26. package/dist/dynamo/engine.js.map +1 -1
  27. package/dist/dynamo/prompt.js +1 -0
  28. package/dist/dynamo/prompt.js.map +1 -1
  29. package/dist/mcp/config.js +57 -3
  30. package/dist/mcp/config.js.map +1 -1
  31. package/dist/mcp/configWrite.js +40 -2
  32. package/dist/mcp/configWrite.js.map +1 -1
  33. package/dist/mcp/connection.js +51 -1
  34. package/dist/mcp/connection.js.map +1 -1
  35. package/dist/mcp/manager.js +65 -0
  36. package/dist/mcp/manager.js.map +1 -1
  37. package/dist/mcp/oauth/callback.js +171 -0
  38. package/dist/mcp/oauth/callback.js.map +1 -0
  39. package/dist/mcp/oauth/flow.js +398 -0
  40. package/dist/mcp/oauth/flow.js.map +1 -0
  41. package/dist/mcp/oauth/index.js +243 -0
  42. package/dist/mcp/oauth/index.js.map +1 -0
  43. package/dist/mcp/oauth/metadata.js +192 -0
  44. package/dist/mcp/oauth/metadata.js.map +1 -0
  45. package/dist/mcp/oauth/pkce.js +56 -0
  46. package/dist/mcp/oauth/pkce.js.map +1 -0
  47. package/dist/mcp/oauth/tokenStore.js +103 -0
  48. package/dist/mcp/oauth/tokenStore.js.map +1 -0
  49. package/dist/mcp/transport/http.js +33 -5
  50. package/dist/mcp/transport/http.js.map +1 -1
  51. package/dist/tools/askUser.js +26 -3
  52. package/dist/tools/askUser.js.map +1 -1
  53. package/dist/tools/backgroundShells.js +112 -7
  54. package/dist/tools/backgroundShells.js.map +1 -1
  55. package/dist/tools/mcpAdd.js +7 -0
  56. package/dist/tools/mcpAdd.js.map +1 -1
  57. package/dist/tools/shellTools.js +8 -1
  58. package/dist/tools/shellTools.js.map +1 -1
  59. package/package.json +74 -74
@@ -0,0 +1,398 @@
1
+ /**
2
+ * flow.ts — the network half of authorization: ask who issues tokens, get a client id,
3
+ * send the user to approve, redeem the code, and refresh it later.
4
+ *
5
+ * `metadata.ts` decides WHAT to fetch and how to read the answers; this file does the
6
+ * fetching and the four POSTs. Split that way because the branching is all in the
7
+ * parsing, and a test that has to stand up an HTTP server to check a fallback ordering is
8
+ * a test nobody keeps working.
9
+ *
10
+ * ONE RULE RUNS THROUGH ALL OF IT: every step reports the sentence a user can act on. An
11
+ * authorization failure is not a stack trace and is very rarely OUR bug — it is a
12
+ * consent screen that was declined, a server without dynamic registration, a token that
13
+ * was revoked in a dashboard an hour ago. The error strings here are the product.
14
+ */
15
+ import { authServerUrls, protectedResourceUrls, readAuthServerMetadata, readResourceMetadata, supportsS256, } from "./metadata.js";
16
+ /** How long any single metadata or token request may take. Generous rather than snappy:
17
+ * an authorization server is very often a cold serverless function, and a discovery that
18
+ * gives up at five seconds fails for a reason that has nothing to do with the user. */
19
+ const REQUEST_TIMEOUT_MS = 30_000;
20
+ export class TokenRequestError extends Error {
21
+ kind;
22
+ constructor(message, kind) {
23
+ super(message);
24
+ this.kind = kind;
25
+ this.name = "TokenRequestError";
26
+ }
27
+ }
28
+ /**
29
+ * Error codes that MEAN `invalid_grant` without saying it.
30
+ *
31
+ * Not pedantry: at least one widely used provider answers a dead refresh token with its
32
+ * own vocabulary, and treating that as an unknown failure means never clearing a
33
+ * credential that will never work again — the user sits in a silent retry loop instead of
34
+ * being offered the sign-in that would fix it.
35
+ */
36
+ const INVALID_GRANT_ALIASES = new Set(["invalid_refresh_token", "expired_refresh_token", "token_expired"]);
37
+ /** Statuses worth trying again. 408/429 and the 5xx family are the server saying "not
38
+ * now" rather than "no". */
39
+ function transientStatus(status) {
40
+ return status === 408 || status === 429 || status >= 500;
41
+ }
42
+ /** A thrown error that is the network rather than the server: a timeout, a reset, a DNS
43
+ * failure. All of them are "try again", none of them say anything about the credential. */
44
+ export function isTransientNetworkError(error) {
45
+ if (error instanceof TokenRequestError)
46
+ return error.kind === "transient";
47
+ const message = String(error?.message ?? error).toLowerCase();
48
+ return /timeout|timed out|etimedout|econnreset|econnrefused|enotfound|eai_again|socket hang up|network|fetch failed|aborted/.test(message);
49
+ }
50
+ /** What the client calls itself when it registers. Not a brand exercise: an
51
+ * authorization server shows this string on the consent screen and in the user's list of
52
+ * connected applications, so it has to say which program is asking. */
53
+ const CLIENT_NAME = "Mindweave";
54
+ async function getJson(url, fetchImpl) {
55
+ const controller = new AbortController();
56
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
57
+ timer.unref?.();
58
+ try {
59
+ const response = await fetchImpl(url, { headers: { accept: "application/json" }, signal: controller.signal });
60
+ if (!response.ok)
61
+ return null;
62
+ return (await response.json());
63
+ }
64
+ catch {
65
+ // A candidate URL that 404s, times out or is not JSON is not an error, it is a
66
+ // candidate that was wrong — the caller has more to try.
67
+ return null;
68
+ }
69
+ finally {
70
+ clearTimeout(timer);
71
+ }
72
+ }
73
+ /**
74
+ * Walk the chain: the 401's metadata URL (or the well-known guesses) to the resource
75
+ * document, the resource document to its authorization servers, and each of those to its
76
+ * own metadata.
77
+ *
78
+ * FALLS THROUGH TO THE SERVER ITSELF when there is no resource document anywhere. A great
79
+ * many deployed servers are their own authorization server and publish RFC 8414 metadata
80
+ * directly, having never implemented the newer RFC 9728 layer at all, and refusing those
81
+ * would be refusing a large part of the ecosystem to honour a document nobody promised.
82
+ */
83
+ export async function discover(serverUrl, challenge, fetchImpl = fetch,
84
+ /** A metadata document named in the config, for a server that publishes none of the
85
+ * discovery documents. Tried alone: if someone went to the trouble of naming it, a
86
+ * silent fallback to guessing would hide the fact that it was wrong. */
87
+ configuredMetadataUrl) {
88
+ if (configuredMetadataUrl) {
89
+ const meta = readAuthServerMetadata(await getJson(configuredMetadataUrl, fetchImpl));
90
+ if (!meta)
91
+ throw new Error(`no usable OAuth metadata at the configured authServerMetadataUrl (${configuredMetadataUrl})`);
92
+ if (!supportsS256(meta))
93
+ throw new Error(`${configuredMetadataUrl} does not offer PKCE with S256, which is required to sign in safely`);
94
+ return { authServer: meta, resource: null };
95
+ }
96
+ const resourceCandidates = challenge.resourceMetadata
97
+ ? [challenge.resourceMetadata, ...protectedResourceUrls(serverUrl)]
98
+ : protectedResourceUrls(serverUrl);
99
+ let resource = null;
100
+ for (const candidate of resourceCandidates) {
101
+ const parsed = readResourceMetadata(await getJson(candidate, fetchImpl));
102
+ if (parsed) {
103
+ resource = parsed;
104
+ break;
105
+ }
106
+ }
107
+ const issuers = resource ? resource.authorizationServers : [new URL(serverUrl).origin, serverUrl];
108
+ for (const issuer of issuers) {
109
+ for (const candidate of authServerUrls(issuer)) {
110
+ const meta = readAuthServerMetadata(await getJson(candidate, fetchImpl));
111
+ if (!meta)
112
+ continue;
113
+ if (!supportsS256(meta)) {
114
+ throw new Error(`${issuer} does not offer PKCE with S256, which is required to sign in safely`);
115
+ }
116
+ return { authServer: meta, resource };
117
+ }
118
+ }
119
+ throw new Error(resource
120
+ ? `this server names an authorization server (${issuers[0] ?? "unknown"}) that publishes no usable OAuth metadata`
121
+ : "this server asked for authorization but publishes no OAuth metadata, so there is nothing to sign in to");
122
+ }
123
+ /**
124
+ * Register as a new client (RFC 7591), because a CLI has no client id until it asks for
125
+ * one and there is nowhere to type one in.
126
+ *
127
+ * An authorization server with no registration endpoint is a genuine wall, and the error
128
+ * says so plainly rather than implying a retry would help: that server expects a client
129
+ * id created by hand in its dashboard, which is a different feature and a different
130
+ * conversation.
131
+ */
132
+ export async function registerClient(meta, redirect, fetchImpl = fetch) {
133
+ if (!meta.registrationEndpoint) {
134
+ throw new Error("this authorization server does not support automatic client registration");
135
+ }
136
+ const response = await fetchImpl(meta.registrationEndpoint, {
137
+ method: "POST",
138
+ headers: { "content-type": "application/json", accept: "application/json" },
139
+ body: JSON.stringify({
140
+ client_name: CLIENT_NAME,
141
+ redirect_uris: [redirect],
142
+ grant_types: ["authorization_code", "refresh_token"],
143
+ response_types: ["code"],
144
+ // A public client: see pkce.ts for why a CLI cannot hold a secret. Servers that
145
+ // issue one anyway are handled — we store it — but we do not claim to be able to
146
+ // keep it.
147
+ token_endpoint_auth_method: "none",
148
+ application_type: "native",
149
+ }),
150
+ });
151
+ if (!response.ok) {
152
+ throw new Error(`could not register with the authorization server (${response.status}): ${await errorText(response)}`);
153
+ }
154
+ const body = (await response.json());
155
+ const clientId = typeof body.client_id === "string" ? body.client_id : "";
156
+ if (!clientId)
157
+ throw new Error("the authorization server registered us but issued no client id");
158
+ const out = { clientId };
159
+ if (typeof body.client_secret === "string" && body.client_secret)
160
+ out.clientSecret = body.client_secret;
161
+ return out;
162
+ }
163
+ /** The URL to open in the browser (pure). */
164
+ export function authorizeUrl(options) {
165
+ const url = new URL(options.meta.authorizationEndpoint);
166
+ url.searchParams.set("response_type", "code");
167
+ url.searchParams.set("client_id", options.clientId);
168
+ url.searchParams.set("redirect_uri", options.redirect);
169
+ url.searchParams.set("code_challenge", options.pkce.challenge);
170
+ url.searchParams.set("code_challenge_method", options.pkce.method);
171
+ url.searchParams.set("state", options.state);
172
+ if (options.scope)
173
+ url.searchParams.set("scope", options.scope);
174
+ if (options.resource)
175
+ url.searchParams.set("resource", options.resource);
176
+ return url.toString();
177
+ }
178
+ async function postToken(endpoint, form, fetchImpl, signal) {
179
+ const controller = new AbortController();
180
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
181
+ timer.unref?.();
182
+ const onAbort = () => controller.abort();
183
+ signal?.addEventListener("abort", onAbort, { once: true });
184
+ if (signal?.aborted)
185
+ controller.abort();
186
+ let response;
187
+ let raw;
188
+ try {
189
+ response = await fetchImpl(endpoint, {
190
+ method: "POST",
191
+ headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
192
+ body: form.toString(),
193
+ signal: controller.signal,
194
+ });
195
+ raw = await response.text();
196
+ }
197
+ catch (error) {
198
+ // Never reached the server, or did not finish. Says nothing about the credential.
199
+ throw new TokenRequestError(`could not reach the authorization server: ${String(error?.message ?? error)}`, "transient");
200
+ }
201
+ finally {
202
+ clearTimeout(timer);
203
+ signal?.removeEventListener("abort", onAbort);
204
+ }
205
+ let body = {};
206
+ try {
207
+ body = JSON.parse(raw);
208
+ }
209
+ catch {
210
+ // Not JSON. Only meaningful if the status already said no.
211
+ }
212
+ // AN ERROR CAN ARRIVE WITH A 200. At least one widely used provider answers every
213
+ // request with 200 and puts the failure in the body, so status alone is not the test —
214
+ // a body carrying `error` is an error whatever the status line claims.
215
+ const declared = typeof body.error === "string" ? body.error : "";
216
+ if (!response.ok || declared) {
217
+ const code = INVALID_GRANT_ALIASES.has(declared) ? "invalid_grant" : declared;
218
+ const description = typeof body.error_description === "string" ? body.error_description : "";
219
+ const kind = code === "invalid_grant" ? "invalid_grant" : transientStatus(response.status) ? "transient" : "other";
220
+ const detail = [code, description].filter(Boolean).join(": ") || clip(raw) || response.statusText || "no detail";
221
+ throw new TokenRequestError(`the authorization server refused the request (${response.status}): ${detail}`, kind);
222
+ }
223
+ const accessToken = typeof body.access_token === "string" ? body.access_token : "";
224
+ if (!accessToken)
225
+ throw new TokenRequestError("the authorization server returned no access token", "other");
226
+ const out = { accessToken };
227
+ if (typeof body.refresh_token === "string" && body.refresh_token)
228
+ out.refreshToken = body.refresh_token;
229
+ if (typeof body.scope === "string" && body.scope)
230
+ out.scope = body.scope;
231
+ // `expires_in` is seconds from now. Converted to an absolute moment here because that
232
+ // is what survives being written to a file and read back tomorrow.
233
+ if (typeof body.expires_in === "number" && Number.isFinite(body.expires_in)) {
234
+ out.expiresAt = Date.now() + body.expires_in * 1000;
235
+ }
236
+ else if (out.refreshToken) {
237
+ // The server did not say, but gave us a way to renew. An hour is the near-universal
238
+ // default, and assuming it means we refresh QUIETLY on schedule rather than finding
239
+ // out through a 401 that sends the user back to a browser. Without a refresh token
240
+ // the guess would buy nothing, so it is not made: see `isExpired`.
241
+ out.expiresAt = Date.now() + 3_600_000;
242
+ }
243
+ return out;
244
+ }
245
+ /** Redeem the authorization code for tokens. */
246
+ export async function exchangeCode(options, fetchImpl = fetch) {
247
+ const form = new URLSearchParams({
248
+ grant_type: "authorization_code",
249
+ code: options.code,
250
+ redirect_uri: options.redirect,
251
+ client_id: options.clientId,
252
+ code_verifier: options.verifier,
253
+ });
254
+ if (options.clientSecret)
255
+ form.set("client_secret", options.clientSecret);
256
+ if (options.resource)
257
+ form.set("resource", options.resource);
258
+ return postToken(options.meta.tokenEndpoint, form, fetchImpl);
259
+ }
260
+ /**
261
+ * Trade a refresh token for a new access token.
262
+ *
263
+ * The refresh token is CARRIED FORWARD when the server does not send a new one: many
264
+ * rotate them and many do not, and dropping the old one on a server that does not would
265
+ * sign the user out an hour later for no reason.
266
+ */
267
+ export async function refreshTokens(options, fetchImpl = fetch) {
268
+ const form = new URLSearchParams({
269
+ grant_type: "refresh_token",
270
+ refresh_token: options.refreshToken,
271
+ client_id: options.clientId,
272
+ });
273
+ if (options.clientSecret)
274
+ form.set("client_secret", options.clientSecret);
275
+ if (options.resource)
276
+ form.set("resource", options.resource);
277
+ // RETRIED, unlike the code exchange. A refresh happens on its own, without anyone
278
+ // watching, against a server that may be briefly rate limiting or restarting — and the
279
+ // cost of giving up early is not a slow request, it is a credential thrown away and a
280
+ // browser round trip the user has to notice. The code exchange is not retried because
281
+ // an authorization code is single-use: a second attempt with the same code is refused
282
+ // by definition, and the user is right there and can simply try again.
283
+ let last;
284
+ for (let attempt = 1; attempt <= REFRESH_ATTEMPTS; attempt++) {
285
+ try {
286
+ const tokens = await postToken(options.tokenEndpoint, form, fetchImpl);
287
+ // Carried forward when the server sends no new one: many rotate refresh tokens and
288
+ // many do not, and dropping the old one on a server that does not would sign the
289
+ // user out an hour later for no reason.
290
+ if (!tokens.refreshToken)
291
+ tokens.refreshToken = options.refreshToken;
292
+ return tokens;
293
+ }
294
+ catch (error) {
295
+ last = error;
296
+ const retryable = error instanceof TokenRequestError ? error.kind === "transient" : isTransientNetworkError(error);
297
+ if (!retryable || attempt === REFRESH_ATTEMPTS)
298
+ throw error;
299
+ await sleep(REFRESH_BACKOFF_MS * 2 ** (attempt - 1));
300
+ }
301
+ }
302
+ throw last;
303
+ }
304
+ /** Three tries at 1s and 2s apart. Enough to ride out a restart or a rate limit, short
305
+ * enough that a genuinely dead server does not hold a request for a minute. */
306
+ const REFRESH_ATTEMPTS = 3;
307
+ const REFRESH_BACKOFF_MS = 1_000;
308
+ function sleep(ms) {
309
+ return new Promise((resolve) => {
310
+ const timer = setTimeout(resolve, ms);
311
+ timer.unref?.();
312
+ });
313
+ }
314
+ /**
315
+ * Tell the authorization server a token is finished with (RFC 7009).
316
+ *
317
+ * BEST EFFORT, and deliberately so: signing out has already succeeded locally by the time
318
+ * this runs, and a server that does not implement revocation, or is simply down, must not
319
+ * turn "signed out" into an error. What this buys is that a token we stop using also
320
+ * stops WORKING, rather than remaining valid on the server until it expires.
321
+ *
322
+ * The client id goes in the BODY rather than an Authorization header, which is what RFC
323
+ * 7009 asks of a public client: the header is for the resource owner, not for identifying
324
+ * which client is revoking.
325
+ */
326
+ export async function revokeToken(options, fetchImpl = fetch) {
327
+ const post = async (form, headers) => {
328
+ const controller = new AbortController();
329
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
330
+ timer.unref?.();
331
+ try {
332
+ return await fetchImpl(options.revocationEndpoint, {
333
+ method: "POST",
334
+ headers: { "content-type": "application/x-www-form-urlencoded", ...headers },
335
+ body: form.toString(),
336
+ signal: controller.signal,
337
+ });
338
+ }
339
+ catch {
340
+ return null; // best effort — see above
341
+ }
342
+ finally {
343
+ clearTimeout(timer);
344
+ }
345
+ };
346
+ const form = new URLSearchParams({ token: options.token, token_type_hint: options.hint });
347
+ // RFC 7009 §2.1 wants client authentication, and WHICH form matters. A client holding a
348
+ // secret is confidential, and strict authorization servers refuse a confidential
349
+ // client's token being revoked by something presenting as public — so a secret goes as
350
+ // HTTP Basic, the default RFC 6749 §2.3.1 form, rather than in the body. A public client
351
+ // identifies itself with `client_id` in the body and nothing else.
352
+ const headers = {};
353
+ if (options.clientSecret) {
354
+ const basic = Buffer.from(`${encodeURIComponent(options.clientId)}:${encodeURIComponent(options.clientSecret)}`).toString("base64");
355
+ headers.authorization = `Basic ${basic}`;
356
+ }
357
+ else {
358
+ form.set("client_id", options.clientId);
359
+ }
360
+ const response = await post(form, headers);
361
+ if (!response || response.ok || response.status !== 401)
362
+ return;
363
+ // Some servers are not RFC 7009 compliant and want the access token as a Bearer instead.
364
+ // RFC 6749 §2.3.1 forbids presenting two client authentication methods at once, so the
365
+ // body credential is dropped before switching.
366
+ if (!options.accessToken)
367
+ return;
368
+ const retry = new URLSearchParams({ token: options.token, token_type_hint: options.hint });
369
+ await post(retry, { authorization: `Bearer ${options.accessToken}` });
370
+ }
371
+ /** An OAuth error body names `error` and `error_description`; anything else is shown
372
+ * as-is, clipped, because an HTML error page in a terminal helps nobody. */
373
+ async function errorText(response) {
374
+ let raw = "";
375
+ try {
376
+ raw = await response.text();
377
+ }
378
+ catch {
379
+ return response.statusText || "no detail";
380
+ }
381
+ try {
382
+ const body = JSON.parse(raw);
383
+ const error = typeof body.error === "string" ? body.error : "";
384
+ const description = typeof body.error_description === "string" ? body.error_description : "";
385
+ if (error || description)
386
+ return [error, description].filter(Boolean).join(": ");
387
+ }
388
+ catch {
389
+ // Not JSON; fall through to the clipped body.
390
+ }
391
+ return clip(raw) || response.statusText || "no detail";
392
+ }
393
+ /** One line, bounded. A wall of HTML in a terminal helps nobody. */
394
+ function clip(raw) {
395
+ const flat = raw.replace(/\s+/g, " ").trim();
396
+ return flat.length > 200 ? `${flat.slice(0, 199)}…` : flat;
397
+ }
398
+ //# sourceMappingURL=flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow.js","sourceRoot":"","sources":["../../../src/mcp/oauth/flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * index.ts — the one door the rest of Mindweave uses to authorize an MCP server.
3
+ *
4
+ * Everything under `oauth/` is a step; this is the sequence. Two entry points, and the
5
+ * split between them is the whole design:
6
+ *
7
+ * - `authHeaders()` runs on EVERY request and must be silent, fast and never open a
8
+ * browser. It hands back a token if one is stored, refreshes it first if it has
9
+ * expired, and returns nothing at all if there is no credential — a request with no
10
+ * Authorization header is how we learn a server wants one, so failing to produce a
11
+ * token is a normal outcome rather than an error.
12
+ * - `authenticate()` runs when a PERSON asked for it, and is the only thing allowed to
13
+ * open a browser and hold a port. It never runs on its own: a connection that hits a
14
+ * 401 goes to `needs-auth` and STOPS. A background retry that pops a browser window
15
+ * for a server the user forgot they configured is a worse outcome than a server that
16
+ * plainly says it needs signing in.
17
+ *
18
+ * An expired refresh is treated as no credential rather than as a failure: refresh tokens
19
+ * are revoked from dashboards all the time, and the right response is to offer signing in
20
+ * again, not to report a broken connection.
21
+ */
22
+ import { spawn } from "node:child_process";
23
+ import { authorizeUrl, discover, exchangeCode, isTransientNetworkError, refreshTokens, registerClient, revokeToken, TokenRequestError } from "./flow.js";
24
+ import { createPkce, randomState } from "./pkce.js";
25
+ import { findAvailablePort, redirectUri, waitForCallback } from "./callback.js";
26
+ import { clearAuth, isExpired, readAuth, serverKey, writeAuth } from "./tokenStore.js";
27
+ import { clientSecretFor } from "../config.js";
28
+ export { serverKey, clearAuth } from "./tokenStore.js";
29
+ export { parseWwwAuthenticate } from "./metadata.js";
30
+ /**
31
+ * The Authorization header for a server, or `{}` when there is nothing stored.
32
+ *
33
+ * Refreshes in place and persists the result, so a long session does not accumulate one
34
+ * expired token per hour. A refresh that fails is classified before anything is thrown
35
+ * away: only `invalid_grant` — the refresh token itself being dead — clears the
36
+ * credential. See the catch below for why both directions of that judgement are costly.
37
+ */
38
+ export async function authHeaders(name, config, fetchImpl = fetch) {
39
+ const key = serverKey(name, config);
40
+ const stored = await readAuth(key);
41
+ if (!stored)
42
+ return {};
43
+ if (!isExpired(stored))
44
+ return { authorization: `Bearer ${stored.accessToken}` };
45
+ if (!stored.refreshToken) {
46
+ await clearAuth(key);
47
+ return {};
48
+ }
49
+ try {
50
+ const refreshed = await refreshTokens({
51
+ tokenEndpoint: stored.tokenEndpoint,
52
+ clientId: stored.clientId,
53
+ ...(stored.clientSecret ? { clientSecret: stored.clientSecret } : {}),
54
+ refreshToken: stored.refreshToken,
55
+ ...(stored.resource ? { resource: stored.resource } : {}),
56
+ }, fetchImpl);
57
+ const next = {
58
+ ...stored,
59
+ accessToken: refreshed.accessToken,
60
+ ...(refreshed.refreshToken ? { refreshToken: refreshed.refreshToken } : {}),
61
+ ...(refreshed.expiresAt ? { expiresAt: refreshed.expiresAt } : {}),
62
+ ...(refreshed.scope ? { scope: refreshed.scope } : {}),
63
+ };
64
+ await writeAuth(key, next);
65
+ return { authorization: `Bearer ${next.accessToken}` };
66
+ }
67
+ catch (error) {
68
+ // WHAT KIND of failure decides whether the credential survives, and getting this
69
+ // wrong in either direction is expensive: clear too eagerly and a dropped packet
70
+ // signs the user out, keep too stubbornly and a revoked token is re-sent forever
71
+ // while the sign-in that would fix it is never offered.
72
+ const kind = error instanceof TokenRequestError ? error.kind : isTransientNetworkError(error) ? "transient" : "other";
73
+ if (kind !== "invalid_grant") {
74
+ // Not the credential's fault. Send nothing this time — the request will 401 and the
75
+ // connection will say so — but keep what we have for the next attempt.
76
+ return {};
77
+ }
78
+ // `invalid_grant` usually means the refresh token is dead. It also means exactly this
79
+ // when ANOTHER Mindweave session refreshed a moment ago on a server that rotates
80
+ // refresh tokens: ours became invalid because theirs succeeded. Re-reading before
81
+ // clearing is what tells the two apart, and without it a second open session is a
82
+ // reliable way to sign yourself out of the first.
83
+ const current = await readAuth(key);
84
+ if (current && current.accessToken !== stored.accessToken && !isExpired(current)) {
85
+ return { authorization: `Bearer ${current.accessToken}` };
86
+ }
87
+ await clearAuth(key);
88
+ return {};
89
+ }
90
+ }
91
+ /** How long to let the browser get on with it before offering the raw URL. Long enough
92
+ * that a working launch never shows it, short enough that someone staring at a terminal
93
+ * which appears to have done nothing is not left guessing. */
94
+ const URL_FALLBACK_DELAY_MS = 4_000;
95
+ /**
96
+ * Sign in to one server, end to end, and persist what comes back.
97
+ *
98
+ * The port is bound BEFORE the authorize URL is built, because the redirect URI is part
99
+ * of what gets signed into the request and part of what the client registers with, so it
100
+ * has to be the real one rather than a guess corrected later.
101
+ */
102
+ export async function authenticate(options) {
103
+ const { name, config, challenge = {}, onUrl, signal, fetchImpl = fetch, openUrl = openInBrowser } = options;
104
+ if (config.type !== "http")
105
+ throw new Error("only http servers can be signed in to; a local command server has no authorization server");
106
+ const key = serverKey(name, config);
107
+ const overrides = config.oauth;
108
+ const { authServer, resource } = await discover(config.url, challenge, fetchImpl, overrides?.authServerMetadataUrl);
109
+ // A hand-registered client has an EXACT redirect URI recorded against it, so a
110
+ // configured client id and a configured port travel together — the random port the
111
+ // automatic flow picks would not match what was registered.
112
+ const port = overrides?.callbackPort ?? (await findAvailablePort());
113
+ const redirect = redirectUri(port);
114
+ // Three ways to have a client id, in order of how deliberate they are: named in the
115
+ // config by someone who registered it by hand, carried over from a previous sign-in, or
116
+ // registered now. Registering afresh on every attempt leaves a trail of one-shot clients
117
+ // on the authorization server, and several of them start refusing once that list gets long.
118
+ const configuredSecret = clientSecretFor(name);
119
+ const previous = await readAuth(key);
120
+ const registration = overrides?.clientId
121
+ ? { clientId: overrides.clientId, ...(configuredSecret ? { clientSecret: configuredSecret } : {}) }
122
+ : previous?.clientId && previous.tokenEndpoint === authServer.tokenEndpoint
123
+ ? { clientId: previous.clientId, ...(previous.clientSecret ?? configuredSecret ? { clientSecret: previous.clientSecret ?? configuredSecret } : {}) }
124
+ : await registerClient(authServer, redirect, fetchImpl);
125
+ const pkce = createPkce();
126
+ const state = randomState();
127
+ const scope = scopeFor(challenge, resource, authServer);
128
+ const url = authorizeUrl({
129
+ meta: authServer,
130
+ clientId: registration.clientId,
131
+ redirect,
132
+ pkce,
133
+ state,
134
+ ...(scope ? { scope } : {}),
135
+ ...(resource?.resource ? { resource: resource.resource } : {}),
136
+ });
137
+ // Start listening BEFORE the browser is told where to go. A fast redirect arriving at a
138
+ // port nobody is on yet is a flow that fails for no reason the user can see.
139
+ const waiting = waitForCallback({ port, state, ...(signal ? { signal } : {}) });
140
+ openUrl(url);
141
+ // THE URL IS A FALLBACK, NOT THE INSTRUCTION. It is 400 characters of query string, and
142
+ // printing it up front means every successful sign-in — the overwhelming majority —
143
+ // dumps a wall of unreadable text into the transcript to say something the browser
144
+ // window already said better. So it waits: if the browser opened, the callback lands
145
+ // first and the URL is never shown. If nothing opened, a few seconds of silence is
146
+ // exactly when someone starts wondering what to do, and that is when it appears.
147
+ const hint = setTimeout(() => onUrl?.(url), URL_FALLBACK_DELAY_MS);
148
+ hint.unref?.();
149
+ let code;
150
+ try {
151
+ ({ code } = await waiting);
152
+ }
153
+ finally {
154
+ clearTimeout(hint);
155
+ }
156
+ const tokens = await exchangeCode({
157
+ meta: authServer,
158
+ clientId: registration.clientId,
159
+ ...(registration.clientSecret ? { clientSecret: registration.clientSecret } : {}),
160
+ redirect,
161
+ code,
162
+ verifier: pkce.verifier,
163
+ ...(resource?.resource ? { resource: resource.resource } : {}),
164
+ }, fetchImpl);
165
+ await writeAuth(key, {
166
+ accessToken: tokens.accessToken,
167
+ ...(tokens.refreshToken ? { refreshToken: tokens.refreshToken } : {}),
168
+ ...(tokens.expiresAt ? { expiresAt: tokens.expiresAt } : {}),
169
+ ...(tokens.scope ? { scope: tokens.scope } : {}),
170
+ clientId: registration.clientId,
171
+ ...(registration.clientSecret ? { clientSecret: registration.clientSecret } : {}),
172
+ tokenEndpoint: authServer.tokenEndpoint,
173
+ // Kept so signing out can tell the server the token is finished with, rather than
174
+ // only forgetting it locally and leaving it valid until it expires on its own.
175
+ ...(authServer.revocationEndpoint ? { revocationEndpoint: authServer.revocationEndpoint } : {}),
176
+ ...(resource?.resource ? { resource: resource.resource } : {}),
177
+ });
178
+ }
179
+ /**
180
+ * Forget a server's credential, and tell the server so where it will listen.
181
+ *
182
+ * The local record goes FIRST and unconditionally. Revocation is a courtesy that makes
183
+ * the token stop working rather than merely stop being used, but a server that is down,
184
+ * or that never implemented RFC 7009, must not be able to keep someone signed in.
185
+ */
186
+ export async function signOut(name, config, fetchImpl = fetch) {
187
+ const key = serverKey(name, config);
188
+ const stored = await readAuth(key);
189
+ await clearAuth(key);
190
+ if (!stored?.revocationEndpoint)
191
+ return;
192
+ const base = {
193
+ revocationEndpoint: stored.revocationEndpoint,
194
+ clientId: stored.clientId,
195
+ ...(stored.clientSecret ? { clientSecret: stored.clientSecret } : {}),
196
+ };
197
+ // The refresh token first: it is the one that would otherwise keep minting new access
198
+ // tokens long after this.
199
+ if (stored.refreshToken)
200
+ await revokeToken({ ...base, token: stored.refreshToken, hint: "refresh_token", accessToken: stored.accessToken }, fetchImpl);
201
+ await revokeToken({ ...base, token: stored.accessToken, hint: "access_token" }, fetchImpl);
202
+ }
203
+ /** Whether we hold anything for this server, without producing it. Drives whether the
204
+ * manage screen offers "Sign in" or "Sign out". */
205
+ export async function hasCredential(name, config) {
206
+ return (await readAuth(serverKey(name, config))) !== undefined;
207
+ }
208
+ /** The 401's scope, then the resource's, then the authorization server's own advertised
209
+ * list. The last one is a guess and is used only because some servers reject an
210
+ * authorize request carrying no scope at all. */
211
+ function scopeFor(challenge, resource, authServer) {
212
+ if (challenge.scope?.trim())
213
+ return challenge.scope.trim();
214
+ if (resource?.scopesSupported?.length)
215
+ return resource.scopesSupported.join(" ");
216
+ if (authServer.scopesSupported?.length)
217
+ return authServer.scopesSupported.join(" ");
218
+ return undefined;
219
+ }
220
+ /**
221
+ * Hand a URL to whatever the user's machine calls a browser.
222
+ *
223
+ * Detached and with its streams discarded, because this must not become a child whose
224
+ * exit we wait on or whose output lands in the middle of the UI. A failure here is
225
+ * swallowed on purpose: the URL was already reported through `onUrl`, so a machine with
226
+ * no default browser leaves the user able to copy it rather than staring at an error
227
+ * about a program they did not ask us to run.
228
+ */
229
+ function openInBrowser(url) {
230
+ try {
231
+ const child = process.platform === "win32"
232
+ ? spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore", windowsHide: true })
233
+ : process.platform === "darwin"
234
+ ? spawn("open", [url], { detached: true, stdio: "ignore" })
235
+ : spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
236
+ child.on("error", () => { });
237
+ child.unref();
238
+ }
239
+ catch {
240
+ // Reported through onUrl already.
241
+ }
242
+ }
243
+ //# sourceMappingURL=index.js.map