oauth-callback 1.2.4 → 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.
- package/README.md +33 -19
- package/dist/auth/browser-auth.d.ts +3 -1
- package/dist/auth/browser-auth.d.ts.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -720
- package/dist/mcp-types.d.ts +3 -2
- package/dist/mcp-types.d.ts.map +1 -1
- package/dist/mcp.js +67 -720
- package/dist/server.d.ts.map +1 -1
- package/dist/types.d.ts +14 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -11
- package/src/auth/browser-auth.ts +56 -116
- package/src/index.ts +13 -28
- package/src/mcp-types.ts +4 -4
- package/src/server.ts +25 -31
- package/src/types.ts +14 -5
package/dist/index.js
CHANGED
|
@@ -26,604 +26,6 @@ var __export = (target, all) => {
|
|
|
26
26
|
};
|
|
27
27
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
28
28
|
|
|
29
|
-
// node_modules/open/index.js
|
|
30
|
-
import process7 from "node:process";
|
|
31
|
-
import path from "node:path";
|
|
32
|
-
import { fileURLToPath } from "node:url";
|
|
33
|
-
import childProcess3 from "node:child_process";
|
|
34
|
-
import fs5, { constants as fsConstants2 } from "node:fs/promises";
|
|
35
|
-
|
|
36
|
-
// node_modules/wsl-utils/index.js
|
|
37
|
-
import { promisify as promisify2 } from "node:util";
|
|
38
|
-
import childProcess2 from "node:child_process";
|
|
39
|
-
import fs4, { constants as fsConstants } from "node:fs/promises";
|
|
40
|
-
|
|
41
|
-
// node_modules/is-wsl/index.js
|
|
42
|
-
import process from "node:process";
|
|
43
|
-
import os from "node:os";
|
|
44
|
-
import fs3 from "node:fs";
|
|
45
|
-
|
|
46
|
-
// node_modules/is-inside-container/index.js
|
|
47
|
-
import fs2 from "node:fs";
|
|
48
|
-
|
|
49
|
-
// node_modules/is-docker/index.js
|
|
50
|
-
import fs from "node:fs";
|
|
51
|
-
var isDockerCached;
|
|
52
|
-
function hasDockerEnv() {
|
|
53
|
-
try {
|
|
54
|
-
fs.statSync("/.dockerenv");
|
|
55
|
-
return true;
|
|
56
|
-
} catch {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function hasDockerCGroup() {
|
|
61
|
-
try {
|
|
62
|
-
return fs.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
63
|
-
} catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function isDocker() {
|
|
68
|
-
if (isDockerCached === undefined) {
|
|
69
|
-
isDockerCached = hasDockerEnv() || hasDockerCGroup();
|
|
70
|
-
}
|
|
71
|
-
return isDockerCached;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// node_modules/is-inside-container/index.js
|
|
75
|
-
var cachedResult;
|
|
76
|
-
var hasContainerEnv = () => {
|
|
77
|
-
try {
|
|
78
|
-
fs2.statSync("/run/.containerenv");
|
|
79
|
-
return true;
|
|
80
|
-
} catch {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
function isInsideContainer() {
|
|
85
|
-
if (cachedResult === undefined) {
|
|
86
|
-
cachedResult = hasContainerEnv() || isDocker();
|
|
87
|
-
}
|
|
88
|
-
return cachedResult;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// node_modules/is-wsl/index.js
|
|
92
|
-
var isWsl = () => {
|
|
93
|
-
if (process.platform !== "linux") {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
if (os.release().toLowerCase().includes("microsoft")) {
|
|
97
|
-
if (isInsideContainer()) {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
try {
|
|
103
|
-
return fs3.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
|
|
104
|
-
} catch {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
var is_wsl_default = process.env.__IS_WSL_TEST__ ? isWsl : isWsl();
|
|
109
|
-
|
|
110
|
-
// node_modules/powershell-utils/index.js
|
|
111
|
-
import process2 from "node:process";
|
|
112
|
-
import { Buffer } from "node:buffer";
|
|
113
|
-
import { promisify } from "node:util";
|
|
114
|
-
import childProcess from "node:child_process";
|
|
115
|
-
var execFile = promisify(childProcess.execFile);
|
|
116
|
-
var powerShellPath = () => `${process2.env.SYSTEMROOT || process2.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
117
|
-
var executePowerShell = async (command, options = {}) => {
|
|
118
|
-
const {
|
|
119
|
-
powerShellPath: psPath,
|
|
120
|
-
...execFileOptions
|
|
121
|
-
} = options;
|
|
122
|
-
const encodedCommand = executePowerShell.encodeCommand(command);
|
|
123
|
-
return execFile(psPath ?? powerShellPath(), [
|
|
124
|
-
...executePowerShell.argumentsPrefix,
|
|
125
|
-
encodedCommand
|
|
126
|
-
], {
|
|
127
|
-
encoding: "utf8",
|
|
128
|
-
...execFileOptions
|
|
129
|
-
});
|
|
130
|
-
};
|
|
131
|
-
executePowerShell.argumentsPrefix = [
|
|
132
|
-
"-NoProfile",
|
|
133
|
-
"-NonInteractive",
|
|
134
|
-
"-ExecutionPolicy",
|
|
135
|
-
"Bypass",
|
|
136
|
-
"-EncodedCommand"
|
|
137
|
-
];
|
|
138
|
-
executePowerShell.encodeCommand = (command) => Buffer.from(command, "utf16le").toString("base64");
|
|
139
|
-
executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
|
|
140
|
-
|
|
141
|
-
// node_modules/wsl-utils/utilities.js
|
|
142
|
-
function parseMountPointFromConfig(content) {
|
|
143
|
-
for (const line of content.split(`
|
|
144
|
-
`)) {
|
|
145
|
-
if (/^\s*#/.test(line)) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
|
|
149
|
-
if (!match) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// node_modules/wsl-utils/index.js
|
|
157
|
-
var execFile2 = promisify2(childProcess2.execFile);
|
|
158
|
-
var wslDrivesMountPoint = (() => {
|
|
159
|
-
const defaultMountPoint = "/mnt/";
|
|
160
|
-
let mountPoint;
|
|
161
|
-
return async function() {
|
|
162
|
-
if (mountPoint) {
|
|
163
|
-
return mountPoint;
|
|
164
|
-
}
|
|
165
|
-
const configFilePath = "/etc/wsl.conf";
|
|
166
|
-
let isConfigFileExists = false;
|
|
167
|
-
try {
|
|
168
|
-
await fs4.access(configFilePath, fsConstants.F_OK);
|
|
169
|
-
isConfigFileExists = true;
|
|
170
|
-
} catch {}
|
|
171
|
-
if (!isConfigFileExists) {
|
|
172
|
-
return defaultMountPoint;
|
|
173
|
-
}
|
|
174
|
-
const configContent = await fs4.readFile(configFilePath, { encoding: "utf8" });
|
|
175
|
-
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
176
|
-
if (parsedMountPoint === undefined) {
|
|
177
|
-
return defaultMountPoint;
|
|
178
|
-
}
|
|
179
|
-
mountPoint = parsedMountPoint;
|
|
180
|
-
mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
|
|
181
|
-
return mountPoint;
|
|
182
|
-
};
|
|
183
|
-
})();
|
|
184
|
-
var powerShellPathFromWsl = async () => {
|
|
185
|
-
const mountPoint = await wslDrivesMountPoint();
|
|
186
|
-
return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
|
|
187
|
-
};
|
|
188
|
-
var powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
|
|
189
|
-
var canAccessPowerShellPromise;
|
|
190
|
-
var canAccessPowerShell = async () => {
|
|
191
|
-
canAccessPowerShellPromise ??= (async () => {
|
|
192
|
-
try {
|
|
193
|
-
const psPath = await powerShellPath2();
|
|
194
|
-
await fs4.access(psPath, fsConstants.X_OK);
|
|
195
|
-
return true;
|
|
196
|
-
} catch {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
})();
|
|
200
|
-
return canAccessPowerShellPromise;
|
|
201
|
-
};
|
|
202
|
-
var wslDefaultBrowser = async () => {
|
|
203
|
-
const psPath = await powerShellPath2();
|
|
204
|
-
const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
|
|
205
|
-
const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
|
|
206
|
-
return stdout.trim();
|
|
207
|
-
};
|
|
208
|
-
var convertWslPathToWindows = async (path) => {
|
|
209
|
-
if (/^[a-z]+:\/\//i.test(path)) {
|
|
210
|
-
return path;
|
|
211
|
-
}
|
|
212
|
-
try {
|
|
213
|
-
const { stdout } = await execFile2("wslpath", ["-aw", path], { encoding: "utf8" });
|
|
214
|
-
return stdout.trim();
|
|
215
|
-
} catch {
|
|
216
|
-
return path;
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
// node_modules/define-lazy-prop/index.js
|
|
221
|
-
function defineLazyProperty(object, propertyName, valueGetter) {
|
|
222
|
-
const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
|
|
223
|
-
Object.defineProperty(object, propertyName, {
|
|
224
|
-
configurable: true,
|
|
225
|
-
enumerable: true,
|
|
226
|
-
get() {
|
|
227
|
-
const result = valueGetter();
|
|
228
|
-
define(result);
|
|
229
|
-
return result;
|
|
230
|
-
},
|
|
231
|
-
set(value) {
|
|
232
|
-
define(value);
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
return object;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// node_modules/default-browser/index.js
|
|
239
|
-
import { promisify as promisify6 } from "node:util";
|
|
240
|
-
import process5 from "node:process";
|
|
241
|
-
import { execFile as execFile6 } from "node:child_process";
|
|
242
|
-
|
|
243
|
-
// node_modules/default-browser-id/index.js
|
|
244
|
-
import { promisify as promisify3 } from "node:util";
|
|
245
|
-
import process3 from "node:process";
|
|
246
|
-
import { execFile as execFile3 } from "node:child_process";
|
|
247
|
-
var execFileAsync = promisify3(execFile3);
|
|
248
|
-
async function defaultBrowserId() {
|
|
249
|
-
if (process3.platform !== "darwin") {
|
|
250
|
-
throw new Error("macOS only");
|
|
251
|
-
}
|
|
252
|
-
const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
|
|
253
|
-
const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
|
|
254
|
-
return match?.groups.id ?? "com.apple.Safari";
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// node_modules/run-applescript/index.js
|
|
258
|
-
import process4 from "node:process";
|
|
259
|
-
import { promisify as promisify4 } from "node:util";
|
|
260
|
-
import { execFile as execFile4, execFileSync } from "node:child_process";
|
|
261
|
-
var execFileAsync2 = promisify4(execFile4);
|
|
262
|
-
async function runAppleScript(script, { humanReadableOutput = true } = {}) {
|
|
263
|
-
if (process4.platform !== "darwin") {
|
|
264
|
-
throw new Error("macOS only");
|
|
265
|
-
}
|
|
266
|
-
const outputArguments = humanReadableOutput ? [] : ["-ss"];
|
|
267
|
-
const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments]);
|
|
268
|
-
return stdout.trim();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// node_modules/bundle-name/index.js
|
|
272
|
-
async function bundleName(bundleId) {
|
|
273
|
-
return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
|
|
274
|
-
tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// node_modules/default-browser/windows.js
|
|
278
|
-
import { promisify as promisify5 } from "node:util";
|
|
279
|
-
import { execFile as execFile5 } from "node:child_process";
|
|
280
|
-
var execFileAsync3 = promisify5(execFile5);
|
|
281
|
-
var windowsBrowserProgIds = {
|
|
282
|
-
MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
|
|
283
|
-
MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
|
|
284
|
-
MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
|
|
285
|
-
AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
|
|
286
|
-
ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
|
|
287
|
-
ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
|
|
288
|
-
ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
|
|
289
|
-
ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
|
|
290
|
-
BraveHTML: { name: "Brave", id: "com.brave.Browser" },
|
|
291
|
-
BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
|
|
292
|
-
BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
|
|
293
|
-
BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
|
|
294
|
-
FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
|
|
295
|
-
OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
|
|
296
|
-
VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
|
|
297
|
-
"IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
|
|
298
|
-
};
|
|
299
|
-
var _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
|
|
300
|
-
|
|
301
|
-
class UnknownBrowserError extends Error {
|
|
302
|
-
}
|
|
303
|
-
async function defaultBrowser(_execFileAsync = execFileAsync3) {
|
|
304
|
-
const { stdout } = await _execFileAsync("reg", [
|
|
305
|
-
"QUERY",
|
|
306
|
-
" HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
|
|
307
|
-
"/v",
|
|
308
|
-
"ProgId"
|
|
309
|
-
]);
|
|
310
|
-
const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
|
|
311
|
-
if (!match) {
|
|
312
|
-
throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
|
|
313
|
-
}
|
|
314
|
-
const { id } = match.groups;
|
|
315
|
-
const browser = windowsBrowserProgIds[id];
|
|
316
|
-
if (!browser) {
|
|
317
|
-
throw new UnknownBrowserError(`Unknown browser ID: ${id}`);
|
|
318
|
-
}
|
|
319
|
-
return browser;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// node_modules/default-browser/index.js
|
|
323
|
-
var execFileAsync4 = promisify6(execFile6);
|
|
324
|
-
var titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
|
|
325
|
-
async function defaultBrowser2() {
|
|
326
|
-
if (process5.platform === "darwin") {
|
|
327
|
-
const id = await defaultBrowserId();
|
|
328
|
-
const name = await bundleName(id);
|
|
329
|
-
return { name, id };
|
|
330
|
-
}
|
|
331
|
-
if (process5.platform === "linux") {
|
|
332
|
-
const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
|
|
333
|
-
const id = stdout.trim();
|
|
334
|
-
const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
|
|
335
|
-
return { name, id };
|
|
336
|
-
}
|
|
337
|
-
if (process5.platform === "win32") {
|
|
338
|
-
return defaultBrowser();
|
|
339
|
-
}
|
|
340
|
-
throw new Error("Only macOS, Linux, and Windows are supported");
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// node_modules/is-in-ssh/index.js
|
|
344
|
-
import process6 from "node:process";
|
|
345
|
-
var isInSsh = Boolean(process6.env.SSH_CONNECTION || process6.env.SSH_CLIENT || process6.env.SSH_TTY);
|
|
346
|
-
var is_in_ssh_default = isInSsh;
|
|
347
|
-
|
|
348
|
-
// node_modules/open/index.js
|
|
349
|
-
var fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
350
|
-
var __dirname2 = import.meta.url ? path.dirname(fileURLToPath(import.meta.url)) : "";
|
|
351
|
-
var localXdgOpenPath = path.join(__dirname2, "xdg-open");
|
|
352
|
-
var { platform, arch } = process7;
|
|
353
|
-
var tryEachApp = async (apps, opener) => {
|
|
354
|
-
if (apps.length === 0) {
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
const errors = [];
|
|
358
|
-
for (const app of apps) {
|
|
359
|
-
try {
|
|
360
|
-
return await opener(app);
|
|
361
|
-
} catch (error) {
|
|
362
|
-
errors.push(error);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
throw new AggregateError(errors, "Failed to open in all supported apps");
|
|
366
|
-
};
|
|
367
|
-
var baseOpen = async (options) => {
|
|
368
|
-
options = {
|
|
369
|
-
wait: false,
|
|
370
|
-
background: false,
|
|
371
|
-
newInstance: false,
|
|
372
|
-
allowNonzeroExitCode: false,
|
|
373
|
-
...options
|
|
374
|
-
};
|
|
375
|
-
const isFallbackAttempt = options[fallbackAttemptSymbol] === true;
|
|
376
|
-
delete options[fallbackAttemptSymbol];
|
|
377
|
-
if (Array.isArray(options.app)) {
|
|
378
|
-
return tryEachApp(options.app, (singleApp) => baseOpen({
|
|
379
|
-
...options,
|
|
380
|
-
app: singleApp,
|
|
381
|
-
[fallbackAttemptSymbol]: true
|
|
382
|
-
}));
|
|
383
|
-
}
|
|
384
|
-
let { name: app, arguments: appArguments = [] } = options.app ?? {};
|
|
385
|
-
appArguments = [...appArguments];
|
|
386
|
-
if (Array.isArray(app)) {
|
|
387
|
-
return tryEachApp(app, (appName) => baseOpen({
|
|
388
|
-
...options,
|
|
389
|
-
app: {
|
|
390
|
-
name: appName,
|
|
391
|
-
arguments: appArguments
|
|
392
|
-
},
|
|
393
|
-
[fallbackAttemptSymbol]: true
|
|
394
|
-
}));
|
|
395
|
-
}
|
|
396
|
-
if (app === "browser" || app === "browserPrivate") {
|
|
397
|
-
const ids = {
|
|
398
|
-
"com.google.chrome": "chrome",
|
|
399
|
-
"google-chrome.desktop": "chrome",
|
|
400
|
-
"com.brave.browser": "brave",
|
|
401
|
-
"org.mozilla.firefox": "firefox",
|
|
402
|
-
"firefox.desktop": "firefox",
|
|
403
|
-
"com.microsoft.msedge": "edge",
|
|
404
|
-
"com.microsoft.edge": "edge",
|
|
405
|
-
"com.microsoft.edgemac": "edge",
|
|
406
|
-
"microsoft-edge.desktop": "edge",
|
|
407
|
-
"com.apple.safari": "safari"
|
|
408
|
-
};
|
|
409
|
-
const flags = {
|
|
410
|
-
chrome: "--incognito",
|
|
411
|
-
brave: "--incognito",
|
|
412
|
-
firefox: "--private-window",
|
|
413
|
-
edge: "--inPrivate"
|
|
414
|
-
};
|
|
415
|
-
let browser;
|
|
416
|
-
if (is_wsl_default) {
|
|
417
|
-
const progId = await wslDefaultBrowser();
|
|
418
|
-
const browserInfo = _windowsBrowserProgIdMap.get(progId);
|
|
419
|
-
browser = browserInfo ?? {};
|
|
420
|
-
} else {
|
|
421
|
-
browser = await defaultBrowser2();
|
|
422
|
-
}
|
|
423
|
-
if (browser.id in ids) {
|
|
424
|
-
const browserName = ids[browser.id.toLowerCase()];
|
|
425
|
-
if (app === "browserPrivate") {
|
|
426
|
-
if (browserName === "safari") {
|
|
427
|
-
throw new Error("Safari doesn't support opening in private mode via command line");
|
|
428
|
-
}
|
|
429
|
-
appArguments.push(flags[browserName]);
|
|
430
|
-
}
|
|
431
|
-
return baseOpen({
|
|
432
|
-
...options,
|
|
433
|
-
app: {
|
|
434
|
-
name: apps[browserName],
|
|
435
|
-
arguments: appArguments
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
throw new Error(`${browser.name} is not supported as a default browser`);
|
|
440
|
-
}
|
|
441
|
-
let command;
|
|
442
|
-
const cliArguments = [];
|
|
443
|
-
const childProcessOptions = {};
|
|
444
|
-
let shouldUseWindowsInWsl = false;
|
|
445
|
-
if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
|
|
446
|
-
shouldUseWindowsInWsl = await canAccessPowerShell();
|
|
447
|
-
}
|
|
448
|
-
if (platform === "darwin") {
|
|
449
|
-
command = "open";
|
|
450
|
-
if (options.wait) {
|
|
451
|
-
cliArguments.push("--wait-apps");
|
|
452
|
-
}
|
|
453
|
-
if (options.background) {
|
|
454
|
-
cliArguments.push("--background");
|
|
455
|
-
}
|
|
456
|
-
if (options.newInstance) {
|
|
457
|
-
cliArguments.push("--new");
|
|
458
|
-
}
|
|
459
|
-
if (app) {
|
|
460
|
-
cliArguments.push("-a", app);
|
|
461
|
-
}
|
|
462
|
-
} else if (platform === "win32" || shouldUseWindowsInWsl) {
|
|
463
|
-
command = await powerShellPath2();
|
|
464
|
-
cliArguments.push(...executePowerShell.argumentsPrefix);
|
|
465
|
-
if (!is_wsl_default) {
|
|
466
|
-
childProcessOptions.windowsVerbatimArguments = true;
|
|
467
|
-
}
|
|
468
|
-
if (is_wsl_default && options.target) {
|
|
469
|
-
options.target = await convertWslPathToWindows(options.target);
|
|
470
|
-
}
|
|
471
|
-
const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
|
|
472
|
-
if (options.wait) {
|
|
473
|
-
encodedArguments.push("-Wait");
|
|
474
|
-
}
|
|
475
|
-
if (app) {
|
|
476
|
-
encodedArguments.push(executePowerShell.escapeArgument(app));
|
|
477
|
-
if (options.target) {
|
|
478
|
-
appArguments.push(options.target);
|
|
479
|
-
}
|
|
480
|
-
} else if (options.target) {
|
|
481
|
-
encodedArguments.push(executePowerShell.escapeArgument(options.target));
|
|
482
|
-
}
|
|
483
|
-
if (appArguments.length > 0) {
|
|
484
|
-
appArguments = appArguments.map((argument) => executePowerShell.escapeArgument(argument));
|
|
485
|
-
encodedArguments.push("-ArgumentList", appArguments.join(","));
|
|
486
|
-
}
|
|
487
|
-
options.target = executePowerShell.encodeCommand(encodedArguments.join(" "));
|
|
488
|
-
if (!options.wait) {
|
|
489
|
-
childProcessOptions.stdio = "ignore";
|
|
490
|
-
}
|
|
491
|
-
} else {
|
|
492
|
-
if (app) {
|
|
493
|
-
command = app;
|
|
494
|
-
} else {
|
|
495
|
-
const isBundled = !__dirname2 || __dirname2 === "/";
|
|
496
|
-
let exeLocalXdgOpen = false;
|
|
497
|
-
try {
|
|
498
|
-
await fs5.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
499
|
-
exeLocalXdgOpen = true;
|
|
500
|
-
} catch {}
|
|
501
|
-
const useSystemXdgOpen = process7.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
|
|
502
|
-
command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
|
|
503
|
-
}
|
|
504
|
-
if (appArguments.length > 0) {
|
|
505
|
-
cliArguments.push(...appArguments);
|
|
506
|
-
}
|
|
507
|
-
if (!options.wait) {
|
|
508
|
-
childProcessOptions.stdio = "ignore";
|
|
509
|
-
childProcessOptions.detached = true;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
if (platform === "darwin" && appArguments.length > 0) {
|
|
513
|
-
cliArguments.push("--args", ...appArguments);
|
|
514
|
-
}
|
|
515
|
-
if (options.target) {
|
|
516
|
-
cliArguments.push(options.target);
|
|
517
|
-
}
|
|
518
|
-
const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
|
|
519
|
-
if (options.wait) {
|
|
520
|
-
return new Promise((resolve, reject) => {
|
|
521
|
-
subprocess.once("error", reject);
|
|
522
|
-
subprocess.once("close", (exitCode) => {
|
|
523
|
-
if (!options.allowNonzeroExitCode && exitCode !== 0) {
|
|
524
|
-
reject(new Error(`Exited with code ${exitCode}`));
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
resolve(subprocess);
|
|
528
|
-
});
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
if (isFallbackAttempt) {
|
|
532
|
-
return new Promise((resolve, reject) => {
|
|
533
|
-
subprocess.once("error", reject);
|
|
534
|
-
subprocess.once("spawn", () => {
|
|
535
|
-
subprocess.once("close", (exitCode) => {
|
|
536
|
-
subprocess.off("error", reject);
|
|
537
|
-
if (exitCode !== 0) {
|
|
538
|
-
reject(new Error(`Exited with code ${exitCode}`));
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
subprocess.unref();
|
|
542
|
-
resolve(subprocess);
|
|
543
|
-
});
|
|
544
|
-
});
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
subprocess.unref();
|
|
548
|
-
return new Promise((resolve, reject) => {
|
|
549
|
-
subprocess.once("error", reject);
|
|
550
|
-
subprocess.once("spawn", () => {
|
|
551
|
-
subprocess.off("error", reject);
|
|
552
|
-
resolve(subprocess);
|
|
553
|
-
});
|
|
554
|
-
});
|
|
555
|
-
};
|
|
556
|
-
var open = (target, options) => {
|
|
557
|
-
if (typeof target !== "string") {
|
|
558
|
-
throw new TypeError("Expected a `target`");
|
|
559
|
-
}
|
|
560
|
-
return baseOpen({
|
|
561
|
-
...options,
|
|
562
|
-
target
|
|
563
|
-
});
|
|
564
|
-
};
|
|
565
|
-
function detectArchBinary(binary) {
|
|
566
|
-
if (typeof binary === "string" || Array.isArray(binary)) {
|
|
567
|
-
return binary;
|
|
568
|
-
}
|
|
569
|
-
const { [arch]: archBinary } = binary;
|
|
570
|
-
if (!archBinary) {
|
|
571
|
-
throw new Error(`${arch} is not supported`);
|
|
572
|
-
}
|
|
573
|
-
return archBinary;
|
|
574
|
-
}
|
|
575
|
-
function detectPlatformBinary({ [platform]: platformBinary }, { wsl } = {}) {
|
|
576
|
-
if (wsl && is_wsl_default) {
|
|
577
|
-
return detectArchBinary(wsl);
|
|
578
|
-
}
|
|
579
|
-
if (!platformBinary) {
|
|
580
|
-
throw new Error(`${platform} is not supported`);
|
|
581
|
-
}
|
|
582
|
-
return detectArchBinary(platformBinary);
|
|
583
|
-
}
|
|
584
|
-
var apps = {
|
|
585
|
-
browser: "browser",
|
|
586
|
-
browserPrivate: "browserPrivate"
|
|
587
|
-
};
|
|
588
|
-
defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
|
|
589
|
-
darwin: "google chrome",
|
|
590
|
-
win32: "chrome",
|
|
591
|
-
linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
|
|
592
|
-
}, {
|
|
593
|
-
wsl: {
|
|
594
|
-
ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
|
|
595
|
-
x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
|
|
596
|
-
}
|
|
597
|
-
}));
|
|
598
|
-
defineLazyProperty(apps, "brave", () => detectPlatformBinary({
|
|
599
|
-
darwin: "brave browser",
|
|
600
|
-
win32: "brave",
|
|
601
|
-
linux: ["brave-browser", "brave"]
|
|
602
|
-
}, {
|
|
603
|
-
wsl: {
|
|
604
|
-
ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
|
|
605
|
-
x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
|
|
606
|
-
}
|
|
607
|
-
}));
|
|
608
|
-
defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
|
|
609
|
-
darwin: "firefox",
|
|
610
|
-
win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
|
|
611
|
-
linux: "firefox"
|
|
612
|
-
}, {
|
|
613
|
-
wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
|
|
614
|
-
}));
|
|
615
|
-
defineLazyProperty(apps, "edge", () => detectPlatformBinary({
|
|
616
|
-
darwin: "microsoft edge",
|
|
617
|
-
win32: "msedge",
|
|
618
|
-
linux: ["microsoft-edge", "microsoft-edge-dev"]
|
|
619
|
-
}, {
|
|
620
|
-
wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
|
|
621
|
-
}));
|
|
622
|
-
defineLazyProperty(apps, "safari", () => detectPlatformBinary({
|
|
623
|
-
darwin: "Safari"
|
|
624
|
-
}));
|
|
625
|
-
var open_default = open;
|
|
626
|
-
|
|
627
29
|
// src/errors.ts
|
|
628
30
|
class OAuthError extends Error {
|
|
629
31
|
error;
|
|
@@ -638,6 +40,13 @@ class OAuthError extends Error {
|
|
|
638
40
|
}
|
|
639
41
|
}
|
|
640
42
|
|
|
43
|
+
class TimeoutError extends Error {
|
|
44
|
+
constructor(message = "OAuth callback timed out") {
|
|
45
|
+
super(message);
|
|
46
|
+
this.name = "TimeoutError";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
641
50
|
// src/templates.ts
|
|
642
51
|
var successTemplate = `<!doctype html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Authorization Successful</title><style>:root{--system-font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;--mono-font:ui-monospace,SFMono-Regular,"SF Mono",Consolas,"Liberation Mono",Menlo,monospace}@media (prefers-color-scheme:dark){:root{color-scheme:dark}}.minimal-body{margin:0;padding:0;min-height:100vh;display:flex;align-items:center;justify-content:center;font-family:var(--system-font);background:linear-gradient(180deg,#fafafa 0%,#f0f0f0 100%);color:#1a1a1a;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.dark .minimal-body{background:linear-gradient(180deg,#0a0a0a 0%,#1a1a1a 100%);color:#fafafa}.minimal-card{background:white;border-radius:16px;padding:48px 40px 40px;max-width:420px;width:90%;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 6px 16px rgba(0,0,0,0.08);animation:fadeUp 0.4s ease-out}.dark .minimal-card{background:#1f1f1f;box-shadow:0 1px 3px rgba(0,0,0,0.2),0 8px 24px rgba(0,0,0,0.4)}.minimal-title{font-size:24px;font-weight:600;margin:0 0 8px;letter-spacing:-0.02em;line-height:1.2}.minimal-subtitle{font-size:15px;color:#666;margin:0 0 32px;line-height:1.5}.dark .minimal-subtitle{color:#999}.success-icon-container{width:56px;height:56px;margin:0 auto 24px}.checkmark{width:56px;height:56px}.checkmark-circle{stroke:#10b981;stroke-width:2;stroke-miterlimit:10;stroke-dasharray:157;stroke-dashoffset:157;fill:none}.checkmark-check{stroke:#10b981;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:48;stroke-dashoffset:48;fill:none}.checkmark.animate .checkmark-circle{animation:stroke 0.6s cubic-bezier(0.65,0,0.45,1) forwards}.checkmark.animate .checkmark-check{animation:stroke 0.3s cubic-bezier(0.65,0,0.45,1) 0.4s forwards}@keyframes stroke{100%{stroke-dashoffset:0}}.error-icon-container{width:56px;height:56px;margin:0 auto 24px;color:#ef4444}.error-icon-container.animate svg{animation:errorPulse 0.5s ease-out,shake 0.4s ease-out 0.3s}.error-icon-container.animate svg circle{stroke-dasharray:63;stroke-dashoffset:63;animation:errorStroke 0.5s cubic-bezier(0.65,0,0.45,1) forwards}.error-icon-container.animate svg path{stroke-dasharray:20;stroke-dashoffset:20;animation:errorStroke 0.4s cubic-bezier(0.65,0,0.45,1) 0.3s forwards}@keyframes errorPulse{0%{transform:scale(0);opacity:0}50%{transform:scale(1.1)}100%{transform:scale(1);opacity:1}}@keyframes errorStroke{to{stroke-dashoffset:0}}@keyframes shake{0%,100%{transform:translateX(0) scale(1)}25%{transform:translateX(-4px) scale(1)}75%{transform:translateX(4px) scale(1)}}.countdown-container{margin-top:32px;padding-top:24px;border-top:1px solid #e5e5e5}.dark .countdown-container{border-top-color:#333}.countdown-label{font-size:13px;color:#666;margin-bottom:12px;font-weight:500;text-transform:uppercase;letter-spacing:0.05em}.dark .countdown-label{color:#999}#countdown-text{font-family:var(--mono-font);font-weight:600;color:#1a1a1a}.dark #countdown-text{color:#fafafa}.progress-track{width:100%;height:3px;background:#e5e5e5;border-radius:3px;overflow:hidden}.dark .progress-track{background:#333}.progress-bar{height:100%;background:linear-gradient(90deg,#10b981 0%,#059669 100%);border-radius:3px;transition:width 100ms linear;width:0}.minimal-actions{display:flex;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid #e5e5e5}.dark .minimal-actions{border-top-color:#333}.minimal-button{flex:1;padding:10px 20px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all 0.2s ease;font-family:var(--system-font);letter-spacing:-0.01em}.minimal-button:active{transform:scale(0.98)}.minimal-button.primary{background:#1a1a1a;color:white}.minimal-button.primary:hover{background:#000}.dark .minimal-button.primary{background:#fafafa;color:#1a1a1a}.dark .minimal-button.primary:hover{background:#fff}.minimal-button.secondary{background:#f5f5f5;color:#666}.minimal-button.secondary:hover{background:#e8e8e8}.dark .minimal-button.secondary{background:#2a2a2a;color:#999}.dark .minimal-button.secondary:hover{background:#333}.minimal-help{font-size:12px;color:#999;margin-top:16px;line-height:1.5}.dark .minimal-help{color:#666}.minimal-error-details{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:12px 16px;margin:20px 0;text-align:left;font-size:13px;line-height:1.6}.dark .minimal-error-details{background:rgba(239,68,68,0.1);border-color:rgba(239,68,68,0.3)}.minimal-error-details strong{color:#dc2626;display:block;margin-bottom:4px;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:0.05em}.dark .minimal-error-details strong{color:#ef4444}.minimal-error-details code{font-family:var(--mono-font);background:rgba(0,0,0,0.05);padding:2px 6px;border-radius:4px;font-size:12px}.dark .minimal-error-details code{background:rgba(255,255,255,0.1)}@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){*{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important}}.minimal-button:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}@media (prefers-contrast:high){.minimal-card{border:2px solid currentColor}.minimal-button{border:1px solid currentColor}}</style> <script> if (window.matchMedia("(prefers-color-scheme: dark)").matches) { document.documentElement.classList.add("dark"); } window.addEventListener("DOMContentLoaded", () => { const checkmark = document.getElementById("checkmark"); if (checkmark) { setTimeout(() => checkmark.classList.add("animate"), 100); } });</script> </head><body class="minimal-body"><div class="minimal-card"><div class="success-icon-container"><svg id="checkmark" class="checkmark" viewBox="0 0 52 52"><circle class="checkmark-circle" cx="26" cy="26" r="25" fill="none" /><path class="checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" /></svg></div><h1 class="minimal-title">Authorization Successful</h1><p class="minimal-subtitle">You can now return to your application</p></div></body></html>`;
|
|
643
52
|
var errorTemplate = `<!doctype html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>{{ERROR_TITLE}}</title><style>:root{--system-font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;--mono-font:ui-monospace,SFMono-Regular,"SF Mono",Consolas,"Liberation Mono",Menlo,monospace}@media (prefers-color-scheme:dark){:root{color-scheme:dark}}.minimal-body{margin:0;padding:0;min-height:100vh;display:flex;align-items:center;justify-content:center;font-family:var(--system-font);background:linear-gradient(180deg,#fafafa 0%,#f0f0f0 100%);color:#1a1a1a;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.dark .minimal-body{background:linear-gradient(180deg,#0a0a0a 0%,#1a1a1a 100%);color:#fafafa}.minimal-card{background:white;border-radius:16px;padding:48px 40px 40px;max-width:420px;width:90%;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 6px 16px rgba(0,0,0,0.08);animation:fadeUp 0.4s ease-out}.dark .minimal-card{background:#1f1f1f;box-shadow:0 1px 3px rgba(0,0,0,0.2),0 8px 24px rgba(0,0,0,0.4)}.minimal-title{font-size:24px;font-weight:600;margin:0 0 8px;letter-spacing:-0.02em;line-height:1.2}.minimal-subtitle{font-size:15px;color:#666;margin:0 0 32px;line-height:1.5}.dark .minimal-subtitle{color:#999}.success-icon-container{width:56px;height:56px;margin:0 auto 24px}.checkmark{width:56px;height:56px}.checkmark-circle{stroke:#10b981;stroke-width:2;stroke-miterlimit:10;stroke-dasharray:157;stroke-dashoffset:157;fill:none}.checkmark-check{stroke:#10b981;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:48;stroke-dashoffset:48;fill:none}.checkmark.animate .checkmark-circle{animation:stroke 0.6s cubic-bezier(0.65,0,0.45,1) forwards}.checkmark.animate .checkmark-check{animation:stroke 0.3s cubic-bezier(0.65,0,0.45,1) 0.4s forwards}@keyframes stroke{100%{stroke-dashoffset:0}}.error-icon-container{width:56px;height:56px;margin:0 auto 24px;color:#ef4444}.error-icon-container.animate svg{animation:errorPulse 0.5s ease-out,shake 0.4s ease-out 0.3s}.error-icon-container.animate svg circle{stroke-dasharray:63;stroke-dashoffset:63;animation:errorStroke 0.5s cubic-bezier(0.65,0,0.45,1) forwards}.error-icon-container.animate svg path{stroke-dasharray:20;stroke-dashoffset:20;animation:errorStroke 0.4s cubic-bezier(0.65,0,0.45,1) 0.3s forwards}@keyframes errorPulse{0%{transform:scale(0);opacity:0}50%{transform:scale(1.1)}100%{transform:scale(1);opacity:1}}@keyframes errorStroke{to{stroke-dashoffset:0}}@keyframes shake{0%,100%{transform:translateX(0) scale(1)}25%{transform:translateX(-4px) scale(1)}75%{transform:translateX(4px) scale(1)}}.countdown-container{margin-top:32px;padding-top:24px;border-top:1px solid #e5e5e5}.dark .countdown-container{border-top-color:#333}.countdown-label{font-size:13px;color:#666;margin-bottom:12px;font-weight:500;text-transform:uppercase;letter-spacing:0.05em}.dark .countdown-label{color:#999}#countdown-text{font-family:var(--mono-font);font-weight:600;color:#1a1a1a}.dark #countdown-text{color:#fafafa}.progress-track{width:100%;height:3px;background:#e5e5e5;border-radius:3px;overflow:hidden}.dark .progress-track{background:#333}.progress-bar{height:100%;background:linear-gradient(90deg,#10b981 0%,#059669 100%);border-radius:3px;transition:width 100ms linear;width:0}.minimal-actions{display:flex;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid #e5e5e5}.dark .minimal-actions{border-top-color:#333}.minimal-button{flex:1;padding:10px 20px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all 0.2s ease;font-family:var(--system-font);letter-spacing:-0.01em}.minimal-button:active{transform:scale(0.98)}.minimal-button.primary{background:#1a1a1a;color:white}.minimal-button.primary:hover{background:#000}.dark .minimal-button.primary{background:#fafafa;color:#1a1a1a}.dark .minimal-button.primary:hover{background:#fff}.minimal-button.secondary{background:#f5f5f5;color:#666}.minimal-button.secondary:hover{background:#e8e8e8}.dark .minimal-button.secondary{background:#2a2a2a;color:#999}.dark .minimal-button.secondary:hover{background:#333}.minimal-help{font-size:12px;color:#999;margin-top:16px;line-height:1.5}.dark .minimal-help{color:#666}.minimal-error-details{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:12px 16px;margin:20px 0;text-align:left;font-size:13px;line-height:1.6}.dark .minimal-error-details{background:rgba(239,68,68,0.1);border-color:rgba(239,68,68,0.3)}.minimal-error-details strong{color:#dc2626;display:block;margin-bottom:4px;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:0.05em}.dark .minimal-error-details strong{color:#ef4444}.minimal-error-details code{font-family:var(--mono-font);background:rgba(0,0,0,0.05);padding:2px 6px;border-radius:4px;font-size:12px}.dark .minimal-error-details code{background:rgba(255,255,255,0.1)}@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){*{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important}}.minimal-button:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}@media (prefers-contrast:high){.minimal-card{border:2px solid currentColor}.minimal-button{border:1px solid currentColor}}</style> <script> if (window.matchMedia("(prefers-color-scheme: dark)").matches) { document.documentElement.classList.add("dark"); } window.addEventListener("DOMContentLoaded", () => { const errorIcon = document.getElementById("error-icon"); if (errorIcon) { setTimeout(() => errorIcon.classList.add("animate"), 100); } }); document.addEventListener("keydown", (e) => { if (e.key === "Escape") { window.close(); } else if (e.key === "Enter") { window.location.reload(); } });</script> </head><body class="minimal-body"><div class="minimal-card"><div class="error-icon-container" id="error-icon">{{ERROR_SVG_ICON}}</div><h1 class="minimal-title">{{ERROR_TITLE}}</h1><p class="minimal-subtitle">{{ERROR_MESSAGE}}</p> {{ERROR_DETAILS}} <div class="minimal-actions"><button onclick="window.location.reload()" class="minimal-button primary" > Try Again </button><button onclick="window.close()" class="minimal-button secondary"> Close </button></div><p class="minimal-help">{{HELP_TEXT}}</p></div></body></html>`;
|
|
@@ -718,6 +127,7 @@ class BaseCallbackServer {
|
|
|
718
127
|
successHtml;
|
|
719
128
|
errorHtml;
|
|
720
129
|
onRequest;
|
|
130
|
+
callbackReceived = false;
|
|
721
131
|
abortHandler;
|
|
722
132
|
signal;
|
|
723
133
|
setup(options) {
|
|
@@ -743,27 +153,34 @@ class BaseCallbackServer {
|
|
|
743
153
|
for (const [key, value] of url.searchParams)
|
|
744
154
|
params[key] = value;
|
|
745
155
|
listener.resolve(params);
|
|
156
|
+
this.callbackReceived = true;
|
|
746
157
|
return new Response(generateCallbackHTML(params, this.successHtml, this.errorHtml), {
|
|
747
158
|
status: 200,
|
|
748
159
|
headers: { "Content-Type": "text/html" }
|
|
749
160
|
});
|
|
750
161
|
}
|
|
751
|
-
async waitForCallback(
|
|
752
|
-
if (
|
|
753
|
-
|
|
162
|
+
async waitForCallback(path, timeout) {
|
|
163
|
+
if (!path)
|
|
164
|
+
throw new Error("Callback path is required");
|
|
165
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
166
|
+
if (this.callbackListeners.has(normalizedPath))
|
|
167
|
+
return Promise.reject(new Error(`A listener for the path "${normalizedPath}" is already active.`));
|
|
168
|
+
let timeoutId;
|
|
754
169
|
try {
|
|
755
170
|
return await Promise.race([
|
|
756
171
|
new Promise((resolve, reject) => {
|
|
757
|
-
this.callbackListeners.set(
|
|
172
|
+
this.callbackListeners.set(normalizedPath, { resolve, reject });
|
|
758
173
|
}),
|
|
759
174
|
new Promise((_, reject) => {
|
|
760
|
-
setTimeout(() => {
|
|
761
|
-
reject(new
|
|
175
|
+
timeoutId = setTimeout(() => {
|
|
176
|
+
reject(new TimeoutError(`OAuth callback timeout after ${timeout}ms waiting for ${normalizedPath}`));
|
|
762
177
|
}, timeout);
|
|
763
178
|
})
|
|
764
179
|
]);
|
|
765
180
|
} finally {
|
|
766
|
-
|
|
181
|
+
if (timeoutId)
|
|
182
|
+
clearTimeout(timeoutId);
|
|
183
|
+
this.callbackListeners.delete(normalizedPath);
|
|
767
184
|
}
|
|
768
185
|
}
|
|
769
186
|
async stop() {
|
|
@@ -792,13 +209,10 @@ class BunCallbackServer extends BaseCallbackServer {
|
|
|
792
209
|
async stopServer() {
|
|
793
210
|
if (!this.server)
|
|
794
211
|
return;
|
|
795
|
-
if (this.
|
|
796
|
-
|
|
797
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
798
|
-
}
|
|
799
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
212
|
+
if (this.callbackReceived) {
|
|
213
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
800
214
|
}
|
|
801
|
-
this.server.stop();
|
|
215
|
+
this.server.stop(true);
|
|
802
216
|
this.server = undefined;
|
|
803
217
|
}
|
|
804
218
|
}
|
|
@@ -809,7 +223,6 @@ class DenoCallbackServer extends BaseCallbackServer {
|
|
|
809
223
|
this.setup(options);
|
|
810
224
|
const { port, hostname = "localhost" } = options;
|
|
811
225
|
this.abortController = new AbortController;
|
|
812
|
-
options.signal?.addEventListener("abort", () => this.abortController?.abort());
|
|
813
226
|
Deno.serve({ port, hostname, signal: this.abortController.signal }, (request) => this.handleRequest(request));
|
|
814
227
|
}
|
|
815
228
|
async stopServer() {
|
|
@@ -831,16 +244,15 @@ class NodeCallbackServer extends BaseCallbackServer {
|
|
|
831
244
|
try {
|
|
832
245
|
const request = this.nodeToWebRequest(req, port, hostname);
|
|
833
246
|
const response = this.handleRequest(request);
|
|
247
|
+
res.shouldKeepAlive = false;
|
|
834
248
|
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
835
249
|
const body = await response.text();
|
|
836
250
|
res.end(body);
|
|
837
|
-
} catch
|
|
251
|
+
} catch {
|
|
838
252
|
res.writeHead(500);
|
|
839
253
|
res.end("Internal Server Error");
|
|
840
254
|
}
|
|
841
255
|
});
|
|
842
|
-
if (options.signal)
|
|
843
|
-
options.signal.addEventListener("abort", () => this.server?.close());
|
|
844
256
|
this.server.listen(port, hostname, () => resolve());
|
|
845
257
|
this.server.on("error", reject);
|
|
846
258
|
});
|
|
@@ -848,7 +260,6 @@ class NodeCallbackServer extends BaseCallbackServer {
|
|
|
848
260
|
async stopServer() {
|
|
849
261
|
if (!this.server)
|
|
850
262
|
return;
|
|
851
|
-
this.server.closeAllConnections();
|
|
852
263
|
return new Promise((resolve) => {
|
|
853
264
|
this.server?.close(() => {
|
|
854
265
|
this.server = undefined;
|
|
@@ -898,17 +309,17 @@ function inMemoryStore() {
|
|
|
898
309
|
};
|
|
899
310
|
}
|
|
900
311
|
// src/storage/file.ts
|
|
901
|
-
import * as
|
|
902
|
-
import * as
|
|
903
|
-
import * as
|
|
312
|
+
import * as fs from "node:fs/promises";
|
|
313
|
+
import * as path from "node:path";
|
|
314
|
+
import * as os from "node:os";
|
|
904
315
|
function fileStore(filepath) {
|
|
905
|
-
const file = filepath ??
|
|
316
|
+
const file = filepath ?? path.join(os.homedir(), ".mcp", "tokens.json");
|
|
906
317
|
async function ensureDir() {
|
|
907
|
-
await
|
|
318
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
908
319
|
}
|
|
909
320
|
async function readStore() {
|
|
910
321
|
try {
|
|
911
|
-
const data = await
|
|
322
|
+
const data = await fs.readFile(file, "utf-8");
|
|
912
323
|
return JSON.parse(data);
|
|
913
324
|
} catch {
|
|
914
325
|
return {};
|
|
@@ -916,7 +327,7 @@ function fileStore(filepath) {
|
|
|
916
327
|
}
|
|
917
328
|
async function writeStore(data) {
|
|
918
329
|
await ensureDir();
|
|
919
|
-
await
|
|
330
|
+
await fs.writeFile(file, JSON.stringify(data, null, 2), "utf-8");
|
|
920
331
|
}
|
|
921
332
|
return {
|
|
922
333
|
async get(key) {
|
|
@@ -968,8 +379,7 @@ class BrowserOAuthProvider {
|
|
|
968
379
|
_hostname;
|
|
969
380
|
_callbackPath;
|
|
970
381
|
_authTimeout;
|
|
971
|
-
|
|
972
|
-
_openBrowser;
|
|
382
|
+
_launch;
|
|
973
383
|
_clientId;
|
|
974
384
|
_clientSecret;
|
|
975
385
|
_scope;
|
|
@@ -985,7 +395,6 @@ class BrowserOAuthProvider {
|
|
|
985
395
|
_tokensLoaded = false;
|
|
986
396
|
_loadingTokens;
|
|
987
397
|
_authInProgress;
|
|
988
|
-
_refreshInProgress;
|
|
989
398
|
constructor(options = {}) {
|
|
990
399
|
this._store = options.store ?? inMemoryStore();
|
|
991
400
|
this._storeKey = options.storeKey ?? "mcp-tokens";
|
|
@@ -993,8 +402,7 @@ class BrowserOAuthProvider {
|
|
|
993
402
|
this._hostname = options.hostname ?? "localhost";
|
|
994
403
|
this._callbackPath = options.callbackPath ?? "/callback";
|
|
995
404
|
this._authTimeout = options.authTimeout ?? 300000;
|
|
996
|
-
this.
|
|
997
|
-
this._openBrowser = options.openBrowser ?? true;
|
|
405
|
+
this._launch = options.launch;
|
|
998
406
|
this._clientId = options.clientId;
|
|
999
407
|
this._clientSecret = options.clientSecret;
|
|
1000
408
|
this._scope = options.scope;
|
|
@@ -1024,7 +432,7 @@ class BrowserOAuthProvider {
|
|
|
1024
432
|
}
|
|
1025
433
|
if (this._isOAuthStore(this._store)) {
|
|
1026
434
|
const clientInfo = await this._store.getClient(this._storeKey);
|
|
1027
|
-
if (clientInfo) {
|
|
435
|
+
if (clientInfo?.clientId) {
|
|
1028
436
|
this._clientInfo = {
|
|
1029
437
|
client_id: clientInfo.clientId,
|
|
1030
438
|
client_secret: clientInfo.clientSecret,
|
|
@@ -1034,18 +442,17 @@ class BrowserOAuthProvider {
|
|
|
1034
442
|
};
|
|
1035
443
|
}
|
|
1036
444
|
const session = await this._store.getSession(this._storeKey);
|
|
1037
|
-
if (session) {
|
|
445
|
+
if (session?.codeVerifier) {
|
|
1038
446
|
this._codeVerifier = session.codeVerifier;
|
|
1039
447
|
}
|
|
1040
448
|
}
|
|
1041
449
|
this._tokensLoaded = true;
|
|
1042
|
-
} catch
|
|
1043
|
-
console.warn("Failed to load stored data:", error);
|
|
450
|
+
} catch {
|
|
1044
451
|
this._tokensLoaded = true;
|
|
1045
452
|
}
|
|
1046
453
|
}
|
|
1047
454
|
_isOAuthStore(store) {
|
|
1048
|
-
return typeof store.getClient === "function";
|
|
455
|
+
return typeof store.getClient === "function" && typeof store.setClient === "function" && typeof store.getSession === "function";
|
|
1049
456
|
}
|
|
1050
457
|
get redirectUrl() {
|
|
1051
458
|
return `http://${this._hostname}:${this._port}${this._callbackPath}`;
|
|
@@ -1098,19 +505,8 @@ class BrowserOAuthProvider {
|
|
|
1098
505
|
return;
|
|
1099
506
|
}
|
|
1100
507
|
const stored = await this._store.get(this._storeKey);
|
|
1101
|
-
if (stored?.expiresAt) {
|
|
1102
|
-
|
|
1103
|
-
if (this._tokens.refresh_token) {
|
|
1104
|
-
try {
|
|
1105
|
-
await this._refreshTokens();
|
|
1106
|
-
return this._tokens;
|
|
1107
|
-
} catch (error) {
|
|
1108
|
-
console.warn("Token refresh failed:", error);
|
|
1109
|
-
return;
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
508
|
+
if (stored?.expiresAt && Date.now() >= stored.expiresAt - 60000) {
|
|
509
|
+
return;
|
|
1114
510
|
}
|
|
1115
511
|
return this._tokens;
|
|
1116
512
|
}
|
|
@@ -1138,42 +534,25 @@ class BrowserOAuthProvider {
|
|
|
1138
534
|
}
|
|
1139
535
|
}
|
|
1140
536
|
async _doAuthorization(authorizationUrl) {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
this._pendingAuthCode =
|
|
1157
|
-
this._pendingAuthState =
|
|
1158
|
-
setTimeout(() => {
|
|
1159
|
-
if (this._pendingAuthCode === result.code) {
|
|
1160
|
-
this._pendingAuthCode = undefined;
|
|
1161
|
-
this._pendingAuthState = undefined;
|
|
1162
|
-
}
|
|
1163
|
-
}, this._authTimeout);
|
|
1164
|
-
return;
|
|
1165
|
-
} catch (error) {
|
|
1166
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1167
|
-
if (error instanceof OAuthError) {
|
|
1168
|
-
throw error;
|
|
1169
|
-
}
|
|
1170
|
-
if (attempt < maxRetries) {
|
|
1171
|
-
console.warn(`Auth attempt ${attempt + 1} failed, retrying...`, error);
|
|
1172
|
-
await new Promise((resolve) => setTimeout(resolve, 1000 * (attempt + 1)));
|
|
1173
|
-
}
|
|
537
|
+
const result = await getAuthCode({
|
|
538
|
+
authorizationUrl: authorizationUrl.href,
|
|
539
|
+
port: this._port,
|
|
540
|
+
hostname: this._hostname,
|
|
541
|
+
callbackPath: this._callbackPath,
|
|
542
|
+
timeout: this._authTimeout,
|
|
543
|
+
launch: this._launch,
|
|
544
|
+
successHtml: this._successHtml,
|
|
545
|
+
errorHtml: this._errorHtml,
|
|
546
|
+
onRequest: this._onRequest
|
|
547
|
+
});
|
|
548
|
+
this._pendingAuthCode = result.code;
|
|
549
|
+
this._pendingAuthState = result.state;
|
|
550
|
+
setTimeout(() => {
|
|
551
|
+
if (this._pendingAuthCode === result.code) {
|
|
552
|
+
this._pendingAuthCode = undefined;
|
|
553
|
+
this._pendingAuthState = undefined;
|
|
1174
554
|
}
|
|
1175
|
-
}
|
|
1176
|
-
throw new Error(`OAuth authorization failed after ${maxRetries + 1} attempts: ${lastError?.message}`);
|
|
555
|
+
}, this._authTimeout);
|
|
1177
556
|
}
|
|
1178
557
|
async saveCodeVerifier(codeVerifier) {
|
|
1179
558
|
this._codeVerifier = codeVerifier;
|
|
@@ -1211,9 +590,7 @@ class BrowserOAuthProvider {
|
|
|
1211
590
|
case "client":
|
|
1212
591
|
this._clientInfo = undefined;
|
|
1213
592
|
if (this._isOAuthStore(this._store)) {
|
|
1214
|
-
await this._store.setClient(this._storeKey, {
|
|
1215
|
-
clientId: ""
|
|
1216
|
-
});
|
|
593
|
+
await this._store.setClient(this._storeKey, { clientId: "" });
|
|
1217
594
|
}
|
|
1218
595
|
break;
|
|
1219
596
|
case "tokens":
|
|
@@ -1244,28 +621,6 @@ class BrowserOAuthProvider {
|
|
|
1244
621
|
}
|
|
1245
622
|
return;
|
|
1246
623
|
}
|
|
1247
|
-
async _refreshTokens() {
|
|
1248
|
-
if (this._refreshInProgress) {
|
|
1249
|
-
await this._refreshInProgress;
|
|
1250
|
-
return;
|
|
1251
|
-
}
|
|
1252
|
-
this._refreshInProgress = this._doRefreshTokens();
|
|
1253
|
-
try {
|
|
1254
|
-
await this._refreshInProgress;
|
|
1255
|
-
} finally {
|
|
1256
|
-
this._refreshInProgress = undefined;
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
async _doRefreshTokens() {
|
|
1260
|
-
if (!this._tokens?.refresh_token) {
|
|
1261
|
-
throw new Error("No refresh token available");
|
|
1262
|
-
}
|
|
1263
|
-
const clientInfo = await this.clientInformation();
|
|
1264
|
-
if (!clientInfo?.client_id) {
|
|
1265
|
-
throw new Error("No client information available for refresh");
|
|
1266
|
-
}
|
|
1267
|
-
throw new Error("Token refresh not yet implemented - requires token endpoint URL");
|
|
1268
|
-
}
|
|
1269
624
|
}
|
|
1270
625
|
// src/index.ts
|
|
1271
626
|
async function getAuthCode(input) {
|
|
@@ -1274,13 +629,13 @@ async function getAuthCode(input) {
|
|
|
1274
629
|
authorizationUrl,
|
|
1275
630
|
port = 3000,
|
|
1276
631
|
hostname = "localhost",
|
|
1277
|
-
openBrowser = true,
|
|
1278
632
|
timeout = 30000,
|
|
1279
633
|
callbackPath = "/callback",
|
|
1280
634
|
successHtml,
|
|
1281
635
|
errorHtml,
|
|
1282
636
|
signal,
|
|
1283
|
-
onRequest
|
|
637
|
+
onRequest,
|
|
638
|
+
launch
|
|
1284
639
|
} = options;
|
|
1285
640
|
const server = createCallbackServer();
|
|
1286
641
|
try {
|
|
@@ -1292,18 +647,8 @@ async function getAuthCode(input) {
|
|
|
1292
647
|
signal,
|
|
1293
648
|
onRequest
|
|
1294
649
|
});
|
|
1295
|
-
if (
|
|
1296
|
-
|
|
1297
|
-
} else {
|
|
1298
|
-
fetch(authorizationUrl).then(async (response) => {
|
|
1299
|
-
if (response.status === 302 || response.status === 301) {
|
|
1300
|
-
const location = response.headers.get("Location");
|
|
1301
|
-
if (location) {
|
|
1302
|
-
await fetch(location);
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
}).catch(() => {});
|
|
1306
|
-
}
|
|
650
|
+
if (launch)
|
|
651
|
+
Promise.resolve(launch(authorizationUrl)).catch(() => {});
|
|
1307
652
|
const result = await server.waitForCallback(callbackPath, timeout);
|
|
1308
653
|
if (result.error) {
|
|
1309
654
|
throw new OAuthError(result.error, result.error_description, result.error_uri);
|
|
@@ -1318,5 +663,6 @@ export {
|
|
|
1318
663
|
inMemoryStore,
|
|
1319
664
|
getAuthCode,
|
|
1320
665
|
fileStore,
|
|
666
|
+
TimeoutError,
|
|
1321
667
|
OAuthError
|
|
1322
668
|
};
|