@gizmodata/hardcharger-mcp 0.0.0 → 2.0.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.
@@ -0,0 +1,460 @@
1
+ // Read-only classification and result-limit wrapping for SQL text.
2
+ //
3
+ // This is defense in depth, not the security boundary: the Hardcharger
4
+ // user's database-level role/privileges are the real boundary (see the
5
+ // README). The classifier is deliberately conservative — anything it
6
+ // cannot confidently classify as a read is rejected when writes are
7
+ // disabled.
8
+ /** Leading keywords that are always reads and can be wrapped in a subquery. */
9
+ const WRAPPABLE_READ_KEYWORDS = new Set([
10
+ "SELECT",
11
+ "FROM", // DuckDB FROM-first syntax
12
+ "VALUES",
13
+ "SHOW",
14
+ "DESCRIBE",
15
+ "DESC",
16
+ "SUMMARIZE",
17
+ "TABLE", // `TABLE t` is shorthand for SELECT * FROM t
18
+ ]);
19
+ /** Leading keywords that are reads but cannot be used as a subquery. */
20
+ // USE only changes the session's search path (no data access), so it is
21
+ // allowed in read-only mode.
22
+ const UNWRAPPABLE_READ_KEYWORDS = new Set(["EXPLAIN", "PRAGMA", "USE"]);
23
+ /** Everything here is a write / side-effecting statement. */
24
+ const WRITE_KEYWORDS = new Set([
25
+ "INSERT",
26
+ "UPDATE",
27
+ "DELETE",
28
+ "MERGE",
29
+ "UPSERT",
30
+ "REPLACE",
31
+ "TRUNCATE",
32
+ "CREATE",
33
+ "DROP",
34
+ "ALTER",
35
+ "RENAME",
36
+ "COPY",
37
+ "EXPORT",
38
+ "IMPORT",
39
+ "ATTACH",
40
+ "DETACH",
41
+ "INSTALL",
42
+ "FORCE", // FORCE INSTALL / FORCE CHECKPOINT
43
+ "LOAD",
44
+ "SET",
45
+ "RESET",
46
+ "CALL",
47
+ "BEGIN",
48
+ "START",
49
+ "COMMIT",
50
+ "ROLLBACK",
51
+ "ABORT",
52
+ "END",
53
+ "CHECKPOINT",
54
+ "VACUUM",
55
+ "ANALYZE",
56
+ "GRANT",
57
+ "REVOKE",
58
+ "KILL",
59
+ "COMMENT",
60
+ "PIVOT",
61
+ "UNPIVOT",
62
+ ]);
63
+ // PIVOT/UNPIVOT are reads in DuckDB, but they are rewritten by the
64
+ // binder into multiple internal statements and cannot be prepared with
65
+ // parameters or reliably wrapped; treat them as unclassifiable rather
66
+ // than as writes.
67
+ WRITE_KEYWORDS.delete("PIVOT");
68
+ WRITE_KEYWORDS.delete("UNPIVOT");
69
+ /**
70
+ * Removes SQL comments (`-- ...`, `/* ... *\/` with nesting) while
71
+ * preserving string literals, quoted identifiers and dollar-quoted
72
+ * strings. Comments are replaced by a single space so offsets stay
73
+ * meaningful for error messages.
74
+ */
75
+ export function stripComments(sql) {
76
+ let out = "";
77
+ let i = 0;
78
+ const n = sql.length;
79
+ while (i < n) {
80
+ const ch = sql[i];
81
+ const next = sql[i + 1];
82
+ if (ch === "'") {
83
+ // single-quoted string with '' escapes
84
+ let j = i + 1;
85
+ while (j < n) {
86
+ if (sql[j] === "'") {
87
+ if (sql[j + 1] === "'") {
88
+ j += 2;
89
+ continue;
90
+ }
91
+ break;
92
+ }
93
+ j++;
94
+ }
95
+ out += sql.slice(i, j + 1);
96
+ i = j + 1;
97
+ continue;
98
+ }
99
+ if (ch === '"') {
100
+ let j = i + 1;
101
+ while (j < n) {
102
+ if (sql[j] === '"') {
103
+ if (sql[j + 1] === '"') {
104
+ j += 2;
105
+ continue;
106
+ }
107
+ break;
108
+ }
109
+ j++;
110
+ }
111
+ out += sql.slice(i, j + 1);
112
+ i = j + 1;
113
+ continue;
114
+ }
115
+ if (ch === "$") {
116
+ // dollar-quoted string: $tag$ ... $tag$
117
+ const m = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(sql.slice(i));
118
+ if (m) {
119
+ const delim = m[0];
120
+ const end = sql.indexOf(delim, i + delim.length);
121
+ const stop = end === -1 ? n : end + delim.length;
122
+ out += sql.slice(i, stop);
123
+ i = stop;
124
+ continue;
125
+ }
126
+ }
127
+ if (ch === "-" && next === "-") {
128
+ let j = i + 2;
129
+ while (j < n && sql[j] !== "\n")
130
+ j++;
131
+ out += " ";
132
+ i = j;
133
+ continue;
134
+ }
135
+ if (ch === "/" && next === "*") {
136
+ let depth = 1;
137
+ let j = i + 2;
138
+ while (j < n && depth > 0) {
139
+ if (sql[j] === "/" && sql[j + 1] === "*") {
140
+ depth++;
141
+ j += 2;
142
+ }
143
+ else if (sql[j] === "*" && sql[j + 1] === "/") {
144
+ depth--;
145
+ j += 2;
146
+ }
147
+ else {
148
+ j++;
149
+ }
150
+ }
151
+ out += " ";
152
+ i = j;
153
+ continue;
154
+ }
155
+ out += ch;
156
+ i++;
157
+ }
158
+ return out;
159
+ }
160
+ /** Tokenizes comment-stripped SQL into words, punctuation and literals. */
161
+ function tokenize(sql) {
162
+ const tokens = [];
163
+ let i = 0;
164
+ const n = sql.length;
165
+ while (i < n) {
166
+ const ch = sql[i];
167
+ if (/\s/.test(ch)) {
168
+ i++;
169
+ continue;
170
+ }
171
+ if (ch === "'") {
172
+ let j = i + 1;
173
+ while (j < n) {
174
+ if (sql[j] === "'") {
175
+ if (sql[j + 1] === "'") {
176
+ j += 2;
177
+ continue;
178
+ }
179
+ break;
180
+ }
181
+ j++;
182
+ }
183
+ tokens.push({ text: sql.slice(i, j + 1), kind: "string", start: i });
184
+ i = j + 1;
185
+ continue;
186
+ }
187
+ if (ch === '"') {
188
+ let j = i + 1;
189
+ while (j < n) {
190
+ if (sql[j] === '"') {
191
+ if (sql[j + 1] === '"') {
192
+ j += 2;
193
+ continue;
194
+ }
195
+ break;
196
+ }
197
+ j++;
198
+ }
199
+ tokens.push({ text: sql.slice(i, j + 1), kind: "quoted", start: i });
200
+ i = j + 1;
201
+ continue;
202
+ }
203
+ if (ch === "$") {
204
+ const m = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(sql.slice(i));
205
+ if (m) {
206
+ const delim = m[0];
207
+ const end = sql.indexOf(delim, i + delim.length);
208
+ const stop = end === -1 ? n : end + delim.length;
209
+ tokens.push({ text: sql.slice(i, stop), kind: "string", start: i });
210
+ i = stop;
211
+ continue;
212
+ }
213
+ }
214
+ const word = /^[A-Za-z_][A-Za-z0-9_$]*/.exec(sql.slice(i));
215
+ if (word) {
216
+ tokens.push({ text: word[0].toUpperCase(), kind: "word", start: i });
217
+ i += word[0].length;
218
+ continue;
219
+ }
220
+ tokens.push({ text: ch, kind: "punct", start: i });
221
+ i++;
222
+ }
223
+ return tokens;
224
+ }
225
+ /**
226
+ * Number of parameters a statement expects: `?` placeholders, or the highest
227
+ * `$n` index, counted outside strings, quoted identifiers and comments.
228
+ */
229
+ export function countPlaceholders(sql) {
230
+ const tokens = tokenize(stripComments(sql));
231
+ let question = 0;
232
+ let dollarMax = 0;
233
+ for (let i = 0; i < tokens.length; i++) {
234
+ const t = tokens[i];
235
+ if (t.kind !== "punct")
236
+ continue;
237
+ if (t.text === "?") {
238
+ question++;
239
+ continue;
240
+ }
241
+ if (t.text === "$") {
242
+ // Digits are tokenized one per punct token; take the contiguous run after `$`.
243
+ let digits = "";
244
+ let pos = t.start + 1;
245
+ for (let j = i + 1; j < tokens.length && tokens[j].kind === "punct" && /\d/.test(tokens[j].text) && tokens[j].start === pos; j++) {
246
+ digits += tokens[j].text;
247
+ pos++;
248
+ }
249
+ if (digits)
250
+ dollarMax = Math.max(dollarMax, Number(digits));
251
+ }
252
+ }
253
+ return Math.max(question, dollarMax);
254
+ }
255
+ /**
256
+ * Splits SQL into top-level statements on semicolons that are outside
257
+ * strings, quoted identifiers and comments. Empty statements (e.g. from a
258
+ * trailing semicolon) are dropped.
259
+ */
260
+ export function splitStatements(sql) {
261
+ const stripped = stripComments(sql);
262
+ const tokens = tokenize(stripped);
263
+ const statements = [];
264
+ let start = 0;
265
+ for (const t of tokens) {
266
+ if (t.kind === "punct" && t.text === ";") {
267
+ const piece = stripped.slice(start, t.start).trim();
268
+ if (piece)
269
+ statements.push(piece);
270
+ start = t.start + 1;
271
+ }
272
+ }
273
+ const tail = stripped.slice(start).trim();
274
+ if (tail)
275
+ statements.push(tail);
276
+ return statements;
277
+ }
278
+ /**
279
+ * Returns the statement with comments removed and any trailing
280
+ * semicolons stripped, ready to be wrapped in a subquery.
281
+ */
282
+ export function normalizeStatement(sql) {
283
+ return stripComments(sql).trim().replace(/;+\s*$/u, "").trim();
284
+ }
285
+ /** Index of the token that closes the parenthesis opened at `open`. */
286
+ function matchParen(tokens, open) {
287
+ let depth = 0;
288
+ for (let i = open; i < tokens.length; i++) {
289
+ const t = tokens[i];
290
+ if (t.kind !== "punct")
291
+ continue;
292
+ if (t.text === "(")
293
+ depth++;
294
+ else if (t.text === ")") {
295
+ depth--;
296
+ if (depth === 0)
297
+ return i;
298
+ }
299
+ }
300
+ return -1;
301
+ }
302
+ /**
303
+ * Given tokens starting at a `WITH`, returns the index of the first token
304
+ * of the main statement that follows the CTE list, or -1 if the CTE list
305
+ * cannot be parsed.
306
+ */
307
+ function skipCteList(tokens, withIndex) {
308
+ let i = withIndex + 1;
309
+ if (tokens[i]?.text === "RECURSIVE")
310
+ i++;
311
+ for (;;) {
312
+ // cte name
313
+ const name = tokens[i];
314
+ if (!name || (name.kind !== "word" && name.kind !== "quoted"))
315
+ return -1;
316
+ i++;
317
+ // optional column list
318
+ if (tokens[i]?.kind === "punct" && tokens[i].text === "(") {
319
+ const close = matchParen(tokens, i);
320
+ if (close === -1)
321
+ return -1;
322
+ i = close + 1;
323
+ }
324
+ if (tokens[i]?.text !== "AS")
325
+ return -1;
326
+ i++;
327
+ if (tokens[i]?.text === "NOT")
328
+ i++;
329
+ if (tokens[i]?.text === "MATERIALIZED")
330
+ i++;
331
+ if (!(tokens[i]?.kind === "punct" && tokens[i].text === "("))
332
+ return -1;
333
+ const close = matchParen(tokens, i);
334
+ if (close === -1)
335
+ return -1;
336
+ i = close + 1;
337
+ if (tokens[i]?.kind === "punct" && tokens[i].text === ",") {
338
+ i++;
339
+ continue;
340
+ }
341
+ return i < tokens.length ? i : -1;
342
+ }
343
+ }
344
+ /**
345
+ * Classifies a single SQL statement by its leading keyword, looking
346
+ * through leading parentheses and CTE lists to find the statement that
347
+ * actually executes.
348
+ */
349
+ export function classifyStatement(sql) {
350
+ const tokens = tokenize(stripComments(sql));
351
+ let i = 0;
352
+ let sawWith = false;
353
+ for (let guard = 0; guard < 64 && i < tokens.length; guard++) {
354
+ const t = tokens[i];
355
+ if (t.kind === "punct" && t.text === "(") {
356
+ // `(SELECT ...) UNION (SELECT ...)` — classify the inner statement.
357
+ i++;
358
+ continue;
359
+ }
360
+ if (t.kind !== "word") {
361
+ return { kind: "unknown", keyword: t.text, wrappable: false };
362
+ }
363
+ if (t.text === "WITH") {
364
+ sawWith = true;
365
+ const main = skipCteList(tokens, i);
366
+ if (main === -1) {
367
+ return { kind: "unknown", keyword: "WITH", wrappable: false };
368
+ }
369
+ i = main;
370
+ continue;
371
+ }
372
+ if (WRAPPABLE_READ_KEYWORDS.has(t.text)) {
373
+ return { kind: "read", keyword: sawWith ? "WITH" : t.text, wrappable: true };
374
+ }
375
+ if (UNWRAPPABLE_READ_KEYWORDS.has(t.text)) {
376
+ if (t.text === "PRAGMA") {
377
+ // `PRAGMA name` / `PRAGMA fn(args)` are reads; `PRAGMA x = y` mutates settings.
378
+ const hasAssignment = tokens
379
+ .slice(i + 1)
380
+ .some((tok) => tok.kind === "punct" && tok.text === "=");
381
+ if (hasAssignment) {
382
+ return { kind: "write", keyword: "PRAGMA", wrappable: false };
383
+ }
384
+ }
385
+ if (t.text === "EXPLAIN") {
386
+ // EXPLAIN of a write is a read (it never executes), but keep it
387
+ // conservative: classify the explained statement instead.
388
+ let j = i + 1;
389
+ if (tokens[j]?.text === "ANALYZE")
390
+ j++;
391
+ if (tokens[j]?.kind === "punct" && tokens[j].text === "(") {
392
+ // EXPLAIN (FORMAT ...) options
393
+ const close = matchParen(tokens, j);
394
+ if (close !== -1)
395
+ j = close + 1;
396
+ }
397
+ const inner = classifyStatement(stripComments(sql).slice(tokens[j]?.start ?? sql.length));
398
+ return { kind: inner.kind, keyword: "EXPLAIN", wrappable: false };
399
+ }
400
+ return { kind: "read", keyword: t.text, wrappable: false };
401
+ }
402
+ if (WRITE_KEYWORDS.has(t.text)) {
403
+ return { kind: "write", keyword: t.text, wrappable: false };
404
+ }
405
+ return { kind: "unknown", keyword: t.text, wrappable: false };
406
+ }
407
+ return { kind: "unknown", keyword: "", wrappable: false };
408
+ }
409
+ export class SqlGuardError extends Error {
410
+ constructor(message) {
411
+ super(message);
412
+ this.name = "SqlGuardError";
413
+ }
414
+ }
415
+ /**
416
+ * Validates that `sql` is exactly one statement and, when writes are
417
+ * disabled, that it is a read. Throws {@link SqlGuardError} otherwise.
418
+ */
419
+ export function guardStatement(sql, allowWrites) {
420
+ if (typeof sql !== "string" || !sql.trim()) {
421
+ throw new SqlGuardError("SQL statement is empty.");
422
+ }
423
+ const statements = splitStatements(sql);
424
+ if (statements.length === 0) {
425
+ throw new SqlGuardError("SQL statement is empty (only comments/semicolons).");
426
+ }
427
+ if (statements.length > 1) {
428
+ throw new SqlGuardError(`Only one SQL statement per call is allowed (found ${statements.length}). ` +
429
+ "Split the statements into separate calls.");
430
+ }
431
+ const single = normalizeStatement(statements[0]);
432
+ const classification = classifyStatement(single);
433
+ if (!allowWrites) {
434
+ if (classification.kind === "write") {
435
+ throw new SqlGuardError(`Statement type ${classification.keyword} is not allowed: this server is read-only ` +
436
+ "(HARDCHARGER_ALLOW_WRITES is not enabled). Only SELECT/WITH/FROM/VALUES/SHOW/" +
437
+ "DESCRIBE/SUMMARIZE/EXPLAIN/PRAGMA (read)/USE statements are permitted.");
438
+ }
439
+ if (classification.kind === "unknown") {
440
+ throw new SqlGuardError(`Unable to classify statement starting with "${classification.keyword || single.slice(0, 20)}" ` +
441
+ "as read-only; this server is read-only (HARDCHARGER_ALLOW_WRITES is not enabled). " +
442
+ "Rewrite it as a SELECT/WITH query.");
443
+ }
444
+ }
445
+ return { sql: single, classification };
446
+ }
447
+ /** Alias used by the query wrapper so it never collides with user aliases. */
448
+ export const WRAPPER_ALIAS = "hardcharger_mcp_q";
449
+ /**
450
+ * Wraps a read statement so the server enforces the row cap:
451
+ * `SELECT * FROM (<sql>) AS hardcharger_mcp_q LIMIT <limit>`.
452
+ * The caller must pass a normalized (comment-free, no trailing `;`) statement.
453
+ */
454
+ export function wrapWithLimit(sql, limit) {
455
+ if (!Number.isInteger(limit) || limit < 1) {
456
+ throw new SqlGuardError(`Row limit must be a positive integer (got ${limit}).`);
457
+ }
458
+ return `SELECT * FROM (\n${sql}\n) AS ${WRAPPER_ALIAS} LIMIT ${limit}`;
459
+ }
460
+ //# sourceMappingURL=sql-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-guard.js","sourceRoot":"","sources":["../src/sql-guard.ts"],"names":[],"mappings":"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"}
package/dist/sso.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { type BrowserSignInFlow, type BrowserSignInPoll, type BrowserSignInTarget } from "@gizmodata/hardcharger";
2
+ import type { HardchargerConnection } from "./connection.js";
3
+ /** The two Warp SQL calls a sign-in makes; replaceable in tests. */
4
+ export interface BrowserSignInCalls {
5
+ start(target: BrowserSignInTarget): Promise<BrowserSignInFlow>;
6
+ poll(target: BrowserSignInTarget, flow: BrowserSignInFlow): Promise<BrowserSignInPoll>;
7
+ }
8
+ export interface SsoOptions {
9
+ waitSeconds: number;
10
+ /** Overrides the server's poll interval (tests). */
11
+ pollIntervalMs?: number;
12
+ /** Test hook: replaces the browser opener. */
13
+ openUrl?: (url: string) => Promise<void>;
14
+ /** Test hook: replaces the Warp SQL browser sign-in calls. */
15
+ calls?: BrowserSignInCalls;
16
+ }
17
+ export interface SsoOutcome {
18
+ status: "complete" | "pending" | "unavailable" | "error";
19
+ message: string;
20
+ authUrl?: string;
21
+ }
22
+ /**
23
+ * Opens a URL in the user's browser without any extra dependency: the command in
24
+ * HARDCHARGER_BROWSER when set (run with the URL as its one argument, no shell; the
25
+ * Hardcharger CLI's variable of the same meaning), else the platform's default opener.
26
+ */
27
+ export declare function openInBrowser(url: string, env?: NodeJS.ProcessEnv): Promise<void>;
28
+ /** Runs (or resumes) the browser sign-in flow for a connection. */
29
+ export declare function runSsoLogin(connection: HardchargerConnection, options: SsoOptions): Promise<SsoOutcome>;
package/dist/sso.js ADDED
@@ -0,0 +1,145 @@
1
+ // Optional browser sign-in through the server's identity provider (enabled with
2
+ // HARDCHARGER_ENABLE_SSO=true): Warp SQL browser sign-in (§4.3, capability oauth_browser), on
3
+ // the server's own Warp SQL port, through @gizmodata/hardcharger:
4
+ //
5
+ // startBrowserSignIn POST {uri}/v1/auth/oauth/flows -> flow id, poll secret, authorize URL
6
+ // open the authorize URL in the user's browser
7
+ // pollBrowserSignIn POST {uri}/v1/auth/oauth/flows/{id} -> pending | complete (the server's
8
+ // own access token) | failed (a code)
9
+ //
10
+ // The access token lives in process memory only, and is used as is: when it expires, call
11
+ // login_sso again.
12
+ import { spawn } from "node:child_process";
13
+ import { HardchargerError, HardchargerErrorCode, pollBrowserSignIn, startBrowserSignIn, } from "@gizmodata/hardcharger";
14
+ const pendingSessions = new WeakMap();
15
+ const warpCalls = {
16
+ start: (target) => startBrowserSignIn(target),
17
+ poll: (target, flow) => pollBrowserSignIn({ ...target, flow }),
18
+ };
19
+ /**
20
+ * Opens a URL in the user's browser without any extra dependency: the command in
21
+ * HARDCHARGER_BROWSER when set (run with the URL as its one argument, no shell; the
22
+ * Hardcharger CLI's variable of the same meaning), else the platform's default opener.
23
+ */
24
+ export function openInBrowser(url, env = process.env) {
25
+ return new Promise((resolve, reject) => {
26
+ let cmd;
27
+ let args;
28
+ const browser = env.HARDCHARGER_BROWSER?.trim();
29
+ if (browser) {
30
+ cmd = browser;
31
+ args = [url];
32
+ }
33
+ else if (process.platform === "darwin") {
34
+ cmd = "open";
35
+ args = [url];
36
+ }
37
+ else if (process.platform === "win32") {
38
+ cmd = "rundll32";
39
+ args = ["url.dll,FileProtocolHandler", url];
40
+ }
41
+ else {
42
+ cmd = "xdg-open";
43
+ args = [url];
44
+ }
45
+ try {
46
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
47
+ child.on("error", reject);
48
+ child.on("spawn", () => {
49
+ child.unref();
50
+ resolve();
51
+ });
52
+ }
53
+ catch (err) {
54
+ reject(err);
55
+ }
56
+ });
57
+ }
58
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
59
+ function unavailable(uri, why) {
60
+ return {
61
+ status: "unavailable",
62
+ message: `The Hardcharger server at ${uri} offers no browser sign-in (${why}). It is offered only when the ` +
63
+ "server is started with --oauth-client-id (and its identity provider settings); otherwise configure " +
64
+ "a password, token or client certificate.",
65
+ };
66
+ }
67
+ const code = (err) => (err instanceof HardchargerError ? err.code : undefined);
68
+ /** A flow the server no longer knows, or one past its expiry: start a new one. */
69
+ function stale(session) {
70
+ const expires = Date.parse(session.flow.expiresAt);
71
+ const now = Date.now();
72
+ return now - session.startedAt > 10 * 60 * 1000 || (!Number.isNaN(expires) && now >= expires);
73
+ }
74
+ /** Runs (or resumes) the browser sign-in flow for a connection. */
75
+ export async function runSsoLogin(connection, options) {
76
+ const calls = options.calls ?? warpCalls;
77
+ const openUrl = options.openUrl ?? openInBrowser;
78
+ const target = connection.signInTarget();
79
+ const redact = (err) => connection.redact(err instanceof Error ? err.message : String(err));
80
+ let session = pendingSessions.get(connection);
81
+ if (!session || stale(session)) {
82
+ let flow;
83
+ try {
84
+ flow = await calls.start(target);
85
+ }
86
+ catch (err) {
87
+ const c = code(err);
88
+ if (c === HardchargerErrorCode.notImplemented || c === HardchargerErrorCode.notFound) {
89
+ return unavailable(connection.config.uri, `${c}: ${redact(err)}`);
90
+ }
91
+ if (c === HardchargerErrorCode.clientTransportError)
92
+ return unavailable(connection.config.uri, redact(err));
93
+ throw err;
94
+ }
95
+ session = { flow, startedAt: Date.now() };
96
+ pendingSessions.set(connection, session);
97
+ try {
98
+ await openUrl(flow.authorizeUrl);
99
+ }
100
+ catch {
101
+ // The URL is returned in the message so the user can open it manually.
102
+ }
103
+ }
104
+ const pollIntervalMs = options.pollIntervalMs ?? session.flow.pollIntervalS * 1000;
105
+ const deadline = Date.now() + options.waitSeconds * 1000;
106
+ while (Date.now() < deadline) {
107
+ let poll;
108
+ try {
109
+ poll = await calls.poll(target, session.flow);
110
+ }
111
+ catch (err) {
112
+ pendingSessions.delete(connection);
113
+ const c = code(err);
114
+ if (c === HardchargerErrorCode.flowNotFound) {
115
+ return { status: "error", message: "The sign-in expired or was already used. Call login_sso again." };
116
+ }
117
+ // A failed flow throws its code (access_denied, identity_provider_error,
118
+ // invalid_credentials, ...) with no HTTP status: report it with the code.
119
+ const why = redact(err).replace(/^browser sign-in failed: /iu, "");
120
+ return { status: "error", message: `Browser sign-in failed: ${c !== undefined && !why.includes(c) ? `${why} (${c})` : why}` };
121
+ }
122
+ if (poll.state === "complete") {
123
+ pendingSessions.delete(connection);
124
+ await connection.useCredentials({ accessToken: poll.accessToken });
125
+ try {
126
+ await connection.query("SELECT 1");
127
+ }
128
+ catch (err) {
129
+ return { status: "error", message: `Signed in, but connecting with the access token failed: ${redact(err)}` };
130
+ }
131
+ return {
132
+ status: "complete",
133
+ message: "Signed in with SSO. Subsequent queries use your Hardcharger access token (call login_sso again when it expires).",
134
+ };
135
+ }
136
+ await sleep(Math.max(0, Math.min(pollIntervalMs, deadline - Date.now())));
137
+ }
138
+ return {
139
+ status: "pending",
140
+ authUrl: session.flow.authorizeUrl,
141
+ message: "Waiting for you to finish signing in. A browser window should have opened; if not, open this URL: " +
142
+ `${session.flow.authorizeUrl}\nThen call login_sso again to complete the sign-in.`,
143
+ };
144
+ }
145
+ //# sourceMappingURL=sso.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso.js","sourceRoot":"","sources":["../src/sso.ts"],"names":[],"mappings":"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"}