@lanonasis/oauth-client 1.2.1 → 1.2.2
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 +41 -6
- package/dist/browser-CUJNgghM.d.cts +246 -0
- package/dist/browser-CUJNgghM.d.ts +246 -0
- package/dist/browser.cjs +1312 -0
- package/dist/browser.d.cts +1 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.mjs +1286 -0
- package/dist/index.cjs +517 -107
- package/dist/index.d.cts +3 -194
- package/dist/index.d.ts +3 -194
- package/dist/index.mjs +514 -95
- package/package.json +12 -6
package/dist/index.mjs
CHANGED
|
@@ -5,12 +5,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
5
5
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
+
// src/flows/terminal-flow.ts
|
|
9
|
+
import fetch2 from "cross-fetch";
|
|
10
|
+
|
|
8
11
|
// src/flows/base-flow.ts
|
|
12
|
+
import fetch from "cross-fetch";
|
|
9
13
|
var BaseOAuthFlow = class {
|
|
10
14
|
constructor(config) {
|
|
11
15
|
this.clientId = config.clientId;
|
|
12
16
|
this.authBaseUrl = config.authBaseUrl || "https://auth.lanonasis.com";
|
|
13
|
-
this.scope = config.scope || "
|
|
17
|
+
this.scope = config.scope || "memories:read memories:write memories:delete profile";
|
|
14
18
|
}
|
|
15
19
|
async makeTokenRequest(body) {
|
|
16
20
|
const response = await fetch(`${this.authBaseUrl}/oauth/token`, {
|
|
@@ -26,11 +30,10 @@ var BaseOAuthFlow = class {
|
|
|
26
30
|
}
|
|
27
31
|
generateState() {
|
|
28
32
|
const array = new Uint8Array(32);
|
|
29
|
-
if (typeof
|
|
30
|
-
|
|
33
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
34
|
+
crypto.getRandomValues(array);
|
|
31
35
|
} else {
|
|
32
|
-
|
|
33
|
-
crypto.randomFillSync(array);
|
|
36
|
+
throw new Error("Secure random generation is not available");
|
|
34
37
|
}
|
|
35
38
|
return this.base64URLEncode(array);
|
|
36
39
|
}
|
|
@@ -40,7 +43,8 @@ var BaseOAuthFlow = class {
|
|
|
40
43
|
bytes.forEach((byte) => {
|
|
41
44
|
binary += String.fromCharCode(byte);
|
|
42
45
|
});
|
|
43
|
-
|
|
46
|
+
const base64 = typeof btoa !== "undefined" ? btoa(binary) : Buffer.from(bytes).toString("base64");
|
|
47
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
44
48
|
}
|
|
45
49
|
async refreshToken(refreshToken) {
|
|
46
50
|
return this.makeTokenRequest({
|
|
@@ -66,7 +70,6 @@ var BaseOAuthFlow = class {
|
|
|
66
70
|
};
|
|
67
71
|
|
|
68
72
|
// src/flows/terminal-flow.ts
|
|
69
|
-
import open from "open";
|
|
70
73
|
var TerminalOAuthFlow = class extends BaseOAuthFlow {
|
|
71
74
|
constructor(config) {
|
|
72
75
|
super({
|
|
@@ -89,7 +92,7 @@ var TerminalOAuthFlow = class extends BaseOAuthFlow {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
async requestDeviceCode() {
|
|
92
|
-
const response = await
|
|
95
|
+
const response = await fetch2(`${this.authBaseUrl}/oauth/device`, {
|
|
93
96
|
method: "POST",
|
|
94
97
|
headers: { "Content-Type": "application/json" },
|
|
95
98
|
body: JSON.stringify({
|
|
@@ -114,6 +117,7 @@ var TerminalOAuthFlow = class extends BaseOAuthFlow {
|
|
|
114
117
|
}
|
|
115
118
|
async openBrowser(url) {
|
|
116
119
|
try {
|
|
120
|
+
const { default: open } = await import("open");
|
|
117
121
|
await Promise.race([
|
|
118
122
|
this.waitForEnter(),
|
|
119
123
|
new Promise((resolve) => setTimeout(resolve, 2e3))
|
|
@@ -162,7 +166,7 @@ var TerminalOAuthFlow = class extends BaseOAuthFlow {
|
|
|
162
166
|
throw new Error("Authorization timeout - please try again");
|
|
163
167
|
}
|
|
164
168
|
async checkDeviceCode(deviceCode) {
|
|
165
|
-
const response = await
|
|
169
|
+
const response = await fetch2(`${this.authBaseUrl}/oauth/token`, {
|
|
166
170
|
method: "POST",
|
|
167
171
|
headers: { "Content-Type": "application/json" },
|
|
168
172
|
body: JSON.stringify({
|
|
@@ -203,25 +207,22 @@ var DesktopOAuthFlow = class extends BaseOAuthFlow {
|
|
|
203
207
|
}
|
|
204
208
|
generateCodeVerifier() {
|
|
205
209
|
const array = new Uint8Array(32);
|
|
206
|
-
if (typeof
|
|
207
|
-
|
|
210
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
211
|
+
crypto.getRandomValues(array);
|
|
208
212
|
} else {
|
|
209
|
-
|
|
210
|
-
crypto.randomFillSync(array);
|
|
213
|
+
throw new Error("Secure random generation is not available in this environment");
|
|
211
214
|
}
|
|
212
215
|
return this.base64URLEncode(array);
|
|
213
216
|
}
|
|
214
217
|
async generateCodeChallenge(verifier) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const hash = await window.crypto.subtle.digest("SHA-256", data);
|
|
219
|
-
return this.base64URLEncode(hash);
|
|
220
|
-
} else {
|
|
221
|
-
const crypto = __require("crypto");
|
|
222
|
-
const hash = crypto.createHash("sha256").update(verifier).digest();
|
|
223
|
-
return this.base64URLEncode(hash);
|
|
218
|
+
const subtle = typeof crypto !== "undefined" ? crypto.subtle : void 0;
|
|
219
|
+
if (!subtle) {
|
|
220
|
+
throw new Error("Web Crypto is required to generate PKCE code challenge");
|
|
224
221
|
}
|
|
222
|
+
const encoder = new TextEncoder();
|
|
223
|
+
const data = encoder.encode(verifier);
|
|
224
|
+
const hash = await subtle.digest("SHA-256", data);
|
|
225
|
+
return this.base64URLEncode(hash);
|
|
225
226
|
}
|
|
226
227
|
buildAuthorizationUrl(codeChallenge, state) {
|
|
227
228
|
const params = new URLSearchParams({
|
|
@@ -406,6 +407,9 @@ var TokenStorage = class {
|
|
|
406
407
|
}
|
|
407
408
|
}
|
|
408
409
|
isTokenExpired(tokens) {
|
|
410
|
+
if (tokens.token_type === "api-key" || tokens.expires_in === 0) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
409
413
|
if (!tokens.expires_in) return false;
|
|
410
414
|
if (!tokens.issued_at) {
|
|
411
415
|
console.warn("Token missing issued_at timestamp, treating as expired");
|
|
@@ -420,13 +424,13 @@ var TokenStorage = class {
|
|
|
420
424
|
const fs = __require("fs").promises;
|
|
421
425
|
const path = __require("path");
|
|
422
426
|
const os = __require("os");
|
|
423
|
-
const
|
|
427
|
+
const crypto2 = __require("crypto");
|
|
424
428
|
const configDir = path.join(os.homedir(), ".lanonasis");
|
|
425
429
|
const tokenFile = path.join(configDir, "mcp-tokens.enc");
|
|
426
430
|
await fs.mkdir(configDir, { recursive: true });
|
|
427
431
|
const key = this.getFileEncryptionKey();
|
|
428
|
-
const iv =
|
|
429
|
-
const cipher =
|
|
432
|
+
const iv = crypto2.randomBytes(16);
|
|
433
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
|
|
430
434
|
let encrypted = cipher.update(tokenString, "utf8", "hex");
|
|
431
435
|
encrypted += cipher.final("hex");
|
|
432
436
|
const authTag = cipher.getAuthTag().toString("hex");
|
|
@@ -438,7 +442,7 @@ var TokenStorage = class {
|
|
|
438
442
|
const fs = __require("fs").promises;
|
|
439
443
|
const path = __require("path");
|
|
440
444
|
const os = __require("os");
|
|
441
|
-
const
|
|
445
|
+
const crypto2 = __require("crypto");
|
|
442
446
|
const tokenFile = path.join(os.homedir(), ".lanonasis", "mcp-tokens.enc");
|
|
443
447
|
try {
|
|
444
448
|
const data = await fs.readFile(tokenFile, "utf8");
|
|
@@ -448,7 +452,7 @@ var TokenStorage = class {
|
|
|
448
452
|
const [ivHex, authTagHex, encrypted] = parts;
|
|
449
453
|
const iv = Buffer.from(ivHex, "hex");
|
|
450
454
|
const authTag = Buffer.from(authTagHex, "hex");
|
|
451
|
-
const decipher =
|
|
455
|
+
const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
|
|
452
456
|
decipher.setAuthTag(authTag);
|
|
453
457
|
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
454
458
|
decrypted += decipher.final("utf8");
|
|
@@ -457,7 +461,7 @@ var TokenStorage = class {
|
|
|
457
461
|
if (parts.length === 2) {
|
|
458
462
|
const [ivHex, encrypted] = parts;
|
|
459
463
|
const iv = Buffer.from(ivHex, "hex");
|
|
460
|
-
const decipher =
|
|
464
|
+
const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
|
|
461
465
|
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
462
466
|
decrypted += decipher.final("utf8");
|
|
463
467
|
return decrypted;
|
|
@@ -479,17 +483,17 @@ var TokenStorage = class {
|
|
|
479
483
|
}
|
|
480
484
|
}
|
|
481
485
|
getFileEncryptionKey() {
|
|
482
|
-
const
|
|
486
|
+
const crypto2 = __require("crypto");
|
|
483
487
|
const os = __require("os");
|
|
484
488
|
const machineId = os.hostname() + os.userInfo().username;
|
|
485
489
|
const salt = "lanonasis-mcp-oauth-2024";
|
|
486
|
-
return
|
|
490
|
+
return crypto2.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
|
|
487
491
|
}
|
|
488
492
|
async encrypt(text) {
|
|
489
493
|
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
490
494
|
const encoder2 = new TextEncoder();
|
|
491
495
|
const data2 = encoder2.encode(text);
|
|
492
|
-
return
|
|
496
|
+
return this.base64Encode(data2);
|
|
493
497
|
}
|
|
494
498
|
const encoder = new TextEncoder();
|
|
495
499
|
const data = encoder.encode(text);
|
|
@@ -522,23 +526,15 @@ var TokenStorage = class {
|
|
|
522
526
|
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
523
527
|
combined.set(iv, 0);
|
|
524
528
|
combined.set(new Uint8Array(encrypted), iv.length);
|
|
525
|
-
return
|
|
529
|
+
return this.base64Encode(combined);
|
|
526
530
|
}
|
|
527
531
|
async decrypt(encrypted) {
|
|
528
532
|
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
529
|
-
const
|
|
530
|
-
const bytes2 = new Uint8Array(binary2.length);
|
|
531
|
-
for (let i = 0; i < binary2.length; i++) {
|
|
532
|
-
bytes2[i] = binary2.charCodeAt(i);
|
|
533
|
-
}
|
|
533
|
+
const bytes2 = this.base64Decode(encrypted);
|
|
534
534
|
const decoder2 = new TextDecoder();
|
|
535
535
|
return decoder2.decode(bytes2);
|
|
536
536
|
}
|
|
537
|
-
const
|
|
538
|
-
const bytes = new Uint8Array(binary.length);
|
|
539
|
-
for (let i = 0; i < binary.length; i++) {
|
|
540
|
-
bytes[i] = binary.charCodeAt(i);
|
|
541
|
-
}
|
|
537
|
+
const bytes = this.base64Decode(encrypted);
|
|
542
538
|
const iv = bytes.slice(0, 12);
|
|
543
539
|
const data = bytes.slice(12);
|
|
544
540
|
const encoder = new TextEncoder();
|
|
@@ -579,6 +575,33 @@ var TokenStorage = class {
|
|
|
579
575
|
isMobile() {
|
|
580
576
|
return typeof window !== "undefined" && window.SecureStorage !== void 0;
|
|
581
577
|
}
|
|
578
|
+
base64Encode(bytes) {
|
|
579
|
+
if (typeof btoa !== "undefined") {
|
|
580
|
+
let binary = "";
|
|
581
|
+
bytes.forEach((b) => {
|
|
582
|
+
binary += String.fromCharCode(b);
|
|
583
|
+
});
|
|
584
|
+
return btoa(binary);
|
|
585
|
+
}
|
|
586
|
+
if (typeof Buffer !== "undefined") {
|
|
587
|
+
return Buffer.from(bytes).toString("base64");
|
|
588
|
+
}
|
|
589
|
+
throw new Error("No base64 encoder available");
|
|
590
|
+
}
|
|
591
|
+
base64Decode(value) {
|
|
592
|
+
if (typeof atob !== "undefined") {
|
|
593
|
+
const binary = atob(value);
|
|
594
|
+
const bytes = new Uint8Array(binary.length);
|
|
595
|
+
for (let i = 0; i < binary.length; i++) {
|
|
596
|
+
bytes[i] = binary.charCodeAt(i);
|
|
597
|
+
}
|
|
598
|
+
return bytes;
|
|
599
|
+
}
|
|
600
|
+
if (typeof Buffer !== "undefined") {
|
|
601
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
602
|
+
}
|
|
603
|
+
throw new Error("No base64 decoder available");
|
|
604
|
+
}
|
|
582
605
|
async getWebEncryptionKey() {
|
|
583
606
|
const existing = typeof localStorage !== "undefined" ? localStorage.getItem(this.webEncryptionKeyStorage) : null;
|
|
584
607
|
if (existing) {
|
|
@@ -590,7 +613,8 @@ var TokenStorage = class {
|
|
|
590
613
|
window.crypto.getRandomValues(buf);
|
|
591
614
|
raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
592
615
|
} else {
|
|
593
|
-
|
|
616
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "node";
|
|
617
|
+
raw = `${ua}-${Math.random().toString(36).slice(2)}-${Date.now()}`;
|
|
594
618
|
}
|
|
595
619
|
if (typeof localStorage !== "undefined") {
|
|
596
620
|
localStorage.setItem(this.webEncryptionKeyStorage, raw);
|
|
@@ -802,13 +826,13 @@ var ApiKeyStorage = class {
|
|
|
802
826
|
const fs = __require("fs").promises;
|
|
803
827
|
const path = __require("path");
|
|
804
828
|
const os = __require("os");
|
|
805
|
-
const
|
|
829
|
+
const crypto2 = __require("crypto");
|
|
806
830
|
const configDir = path.join(os.homedir(), ".lanonasis");
|
|
807
831
|
const keyFile = path.join(configDir, "api-key.enc");
|
|
808
832
|
await fs.mkdir(configDir, { recursive: true, mode: 448 });
|
|
809
833
|
const key = this.getFileEncryptionKey();
|
|
810
|
-
const iv =
|
|
811
|
-
const cipher =
|
|
834
|
+
const iv = crypto2.randomBytes(16);
|
|
835
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
|
|
812
836
|
let encrypted = cipher.update(keyString, "utf8", "hex");
|
|
813
837
|
encrypted += cipher.final("hex");
|
|
814
838
|
const authTag = cipher.getAuthTag();
|
|
@@ -820,7 +844,7 @@ var ApiKeyStorage = class {
|
|
|
820
844
|
const fs = __require("fs").promises;
|
|
821
845
|
const path = __require("path");
|
|
822
846
|
const os = __require("os");
|
|
823
|
-
const
|
|
847
|
+
const crypto2 = __require("crypto");
|
|
824
848
|
const keyFile = path.join(os.homedir(), ".lanonasis", "api-key.enc");
|
|
825
849
|
try {
|
|
826
850
|
const data = await fs.readFile(keyFile, "utf8");
|
|
@@ -831,7 +855,7 @@ var ApiKeyStorage = class {
|
|
|
831
855
|
const key = this.getFileEncryptionKey();
|
|
832
856
|
const iv = Buffer.from(ivHex, "hex");
|
|
833
857
|
const authTag = Buffer.from(authTagHex, "hex");
|
|
834
|
-
const decipher =
|
|
858
|
+
const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
|
|
835
859
|
decipher.setAuthTag(authTag);
|
|
836
860
|
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
837
861
|
decrypted += decipher.final("utf8");
|
|
@@ -875,18 +899,18 @@ var ApiKeyStorage = class {
|
|
|
875
899
|
}
|
|
876
900
|
}
|
|
877
901
|
getFileEncryptionKey() {
|
|
878
|
-
const
|
|
902
|
+
const crypto2 = __require("crypto");
|
|
879
903
|
const os = __require("os");
|
|
880
904
|
const machineId = os.hostname() + os.userInfo().username;
|
|
881
905
|
const salt = "lanonasis-mcp-api-key-2024";
|
|
882
|
-
return
|
|
906
|
+
return crypto2.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
|
|
883
907
|
}
|
|
884
908
|
// ==================== Web Encryption ====================
|
|
885
909
|
async encrypt(text) {
|
|
886
910
|
if (typeof window === "undefined" || !window.crypto || !window.crypto.subtle) {
|
|
887
911
|
const encoder = new TextEncoder();
|
|
888
912
|
const data = encoder.encode(text);
|
|
889
|
-
return
|
|
913
|
+
return this.base64Encode(data);
|
|
890
914
|
}
|
|
891
915
|
try {
|
|
892
916
|
const encoder = new TextEncoder();
|
|
@@ -925,16 +949,12 @@ var ApiKeyStorage = class {
|
|
|
925
949
|
console.error("Web encryption failed:", error);
|
|
926
950
|
const encoder = new TextEncoder();
|
|
927
951
|
const data = encoder.encode(text);
|
|
928
|
-
return
|
|
952
|
+
return this.base64Encode(data);
|
|
929
953
|
}
|
|
930
954
|
}
|
|
931
955
|
async decrypt(encrypted) {
|
|
932
956
|
if (typeof window === "undefined" || !window.crypto || !window.crypto.subtle) {
|
|
933
|
-
const
|
|
934
|
-
const bytes = new Uint8Array(binary.length);
|
|
935
|
-
for (let i = 0; i < binary.length; i++) {
|
|
936
|
-
bytes[i] = binary.charCodeAt(i);
|
|
937
|
-
}
|
|
957
|
+
const bytes = this.base64Decode(encrypted);
|
|
938
958
|
const decoder = new TextDecoder();
|
|
939
959
|
return decoder.decode(bytes);
|
|
940
960
|
}
|
|
@@ -976,11 +996,7 @@ var ApiKeyStorage = class {
|
|
|
976
996
|
return decoder.decode(decrypted);
|
|
977
997
|
} catch (error) {
|
|
978
998
|
console.error("Web decryption failed:", error);
|
|
979
|
-
const
|
|
980
|
-
const bytes = new Uint8Array(binary.length);
|
|
981
|
-
for (let i = 0; i < binary.length; i++) {
|
|
982
|
-
bytes[i] = binary.charCodeAt(i);
|
|
983
|
-
}
|
|
999
|
+
const bytes = this.base64Decode(encrypted);
|
|
984
1000
|
const decoder = new TextDecoder();
|
|
985
1001
|
return decoder.decode(bytes);
|
|
986
1002
|
}
|
|
@@ -996,7 +1012,8 @@ var ApiKeyStorage = class {
|
|
|
996
1012
|
window.crypto.getRandomValues(buf);
|
|
997
1013
|
raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
998
1014
|
} else {
|
|
999
|
-
|
|
1015
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "node";
|
|
1016
|
+
raw = `${ua}-${Math.random().toString(36).slice(2)}-${Date.now()}`;
|
|
1000
1017
|
}
|
|
1001
1018
|
if (typeof localStorage !== "undefined") {
|
|
1002
1019
|
localStorage.setItem(this.webEncryptionKeyStorage, raw);
|
|
@@ -1013,6 +1030,33 @@ var ApiKeyStorage = class {
|
|
|
1013
1030
|
isMobile() {
|
|
1014
1031
|
return typeof window !== "undefined" && window.SecureStorage !== void 0;
|
|
1015
1032
|
}
|
|
1033
|
+
base64Encode(bytes) {
|
|
1034
|
+
if (typeof btoa !== "undefined") {
|
|
1035
|
+
let binary = "";
|
|
1036
|
+
bytes.forEach((b) => {
|
|
1037
|
+
binary += String.fromCharCode(b);
|
|
1038
|
+
});
|
|
1039
|
+
return btoa(binary);
|
|
1040
|
+
}
|
|
1041
|
+
if (typeof Buffer !== "undefined") {
|
|
1042
|
+
return Buffer.from(bytes).toString("base64");
|
|
1043
|
+
}
|
|
1044
|
+
throw new Error("No base64 encoder available");
|
|
1045
|
+
}
|
|
1046
|
+
base64Decode(value) {
|
|
1047
|
+
if (typeof atob !== "undefined") {
|
|
1048
|
+
const binary = atob(value);
|
|
1049
|
+
const bytes = new Uint8Array(binary.length);
|
|
1050
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1051
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1052
|
+
}
|
|
1053
|
+
return bytes;
|
|
1054
|
+
}
|
|
1055
|
+
if (typeof Buffer !== "undefined") {
|
|
1056
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
1057
|
+
}
|
|
1058
|
+
throw new Error("No base64 decoder available");
|
|
1059
|
+
}
|
|
1016
1060
|
/**
|
|
1017
1061
|
* Normalize API keys to a SHA-256 hex digest.
|
|
1018
1062
|
* Accepts pre-hashed input and lowercases it to prevent double hashing.
|
|
@@ -1037,9 +1081,338 @@ var ApiKeyStorage = class {
|
|
|
1037
1081
|
}
|
|
1038
1082
|
};
|
|
1039
1083
|
|
|
1084
|
+
// src/storage/token-storage-web.ts
|
|
1085
|
+
var TokenStorageWeb = class {
|
|
1086
|
+
constructor() {
|
|
1087
|
+
this.storageKey = "lanonasis_mcp_tokens";
|
|
1088
|
+
this.webEncryptionKeyStorage = "lanonasis_web_token_enc_key";
|
|
1089
|
+
}
|
|
1090
|
+
async store(tokens) {
|
|
1091
|
+
const tokensWithTimestamp = {
|
|
1092
|
+
...tokens,
|
|
1093
|
+
issued_at: Date.now()
|
|
1094
|
+
};
|
|
1095
|
+
const tokenString = JSON.stringify(tokensWithTimestamp);
|
|
1096
|
+
const encrypted = await this.encrypt(tokenString);
|
|
1097
|
+
localStorage.setItem(this.storageKey, encrypted);
|
|
1098
|
+
}
|
|
1099
|
+
async retrieve() {
|
|
1100
|
+
const encrypted = localStorage.getItem(this.storageKey);
|
|
1101
|
+
if (!encrypted) return null;
|
|
1102
|
+
try {
|
|
1103
|
+
const tokenString = await this.decrypt(encrypted);
|
|
1104
|
+
return JSON.parse(tokenString);
|
|
1105
|
+
} catch {
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
async clear() {
|
|
1110
|
+
localStorage.removeItem(this.storageKey);
|
|
1111
|
+
}
|
|
1112
|
+
isTokenExpired(tokens) {
|
|
1113
|
+
if (tokens.token_type === "api-key" || tokens.expires_in === 0) return false;
|
|
1114
|
+
if (!tokens.expires_in) return false;
|
|
1115
|
+
if (!tokens.issued_at) return true;
|
|
1116
|
+
const expiresAt = tokens.issued_at + tokens.expires_in * 1e3;
|
|
1117
|
+
return expiresAt - Date.now() < 3e5;
|
|
1118
|
+
}
|
|
1119
|
+
async encrypt(text) {
|
|
1120
|
+
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
1121
|
+
const encoder2 = new TextEncoder();
|
|
1122
|
+
return this.base64Encode(encoder2.encode(text));
|
|
1123
|
+
}
|
|
1124
|
+
const encoder = new TextEncoder();
|
|
1125
|
+
const data = encoder.encode(text);
|
|
1126
|
+
const passphrase = await this.getWebEncryptionKey();
|
|
1127
|
+
const keyMaterial = await window.crypto.subtle.importKey(
|
|
1128
|
+
"raw",
|
|
1129
|
+
encoder.encode(passphrase),
|
|
1130
|
+
"PBKDF2",
|
|
1131
|
+
false,
|
|
1132
|
+
["deriveBits", "deriveKey"]
|
|
1133
|
+
);
|
|
1134
|
+
const key = await window.crypto.subtle.deriveKey(
|
|
1135
|
+
{
|
|
1136
|
+
name: "PBKDF2",
|
|
1137
|
+
salt: encoder.encode("lanonasis-token-salt"),
|
|
1138
|
+
iterations: 1e5,
|
|
1139
|
+
hash: "SHA-256"
|
|
1140
|
+
},
|
|
1141
|
+
keyMaterial,
|
|
1142
|
+
{ name: "AES-GCM", length: 256 },
|
|
1143
|
+
true,
|
|
1144
|
+
["encrypt", "decrypt"]
|
|
1145
|
+
);
|
|
1146
|
+
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
|
1147
|
+
const encrypted = await window.crypto.subtle.encrypt(
|
|
1148
|
+
{ name: "AES-GCM", iv },
|
|
1149
|
+
key,
|
|
1150
|
+
data
|
|
1151
|
+
);
|
|
1152
|
+
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
1153
|
+
combined.set(iv, 0);
|
|
1154
|
+
combined.set(new Uint8Array(encrypted), iv.length);
|
|
1155
|
+
return this.base64Encode(combined);
|
|
1156
|
+
}
|
|
1157
|
+
async decrypt(encrypted) {
|
|
1158
|
+
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
1159
|
+
const decoder2 = new TextDecoder();
|
|
1160
|
+
return decoder2.decode(this.base64Decode(encrypted));
|
|
1161
|
+
}
|
|
1162
|
+
const bytes = this.base64Decode(encrypted);
|
|
1163
|
+
const iv = bytes.slice(0, 12);
|
|
1164
|
+
const data = bytes.slice(12);
|
|
1165
|
+
const encoder = new TextEncoder();
|
|
1166
|
+
const decoder = new TextDecoder();
|
|
1167
|
+
const passphrase = await this.getWebEncryptionKey();
|
|
1168
|
+
const keyMaterial = await window.crypto.subtle.importKey(
|
|
1169
|
+
"raw",
|
|
1170
|
+
encoder.encode(passphrase),
|
|
1171
|
+
"PBKDF2",
|
|
1172
|
+
false,
|
|
1173
|
+
["deriveBits", "deriveKey"]
|
|
1174
|
+
);
|
|
1175
|
+
const key = await window.crypto.subtle.deriveKey(
|
|
1176
|
+
{
|
|
1177
|
+
name: "PBKDF2",
|
|
1178
|
+
salt: encoder.encode("lanonasis-token-salt"),
|
|
1179
|
+
iterations: 1e5,
|
|
1180
|
+
hash: "SHA-256"
|
|
1181
|
+
},
|
|
1182
|
+
keyMaterial,
|
|
1183
|
+
{ name: "AES-GCM", length: 256 },
|
|
1184
|
+
true,
|
|
1185
|
+
["encrypt", "decrypt"]
|
|
1186
|
+
);
|
|
1187
|
+
const decrypted = await window.crypto.subtle.decrypt(
|
|
1188
|
+
{ name: "AES-GCM", iv },
|
|
1189
|
+
key,
|
|
1190
|
+
data
|
|
1191
|
+
);
|
|
1192
|
+
return decoder.decode(decrypted);
|
|
1193
|
+
}
|
|
1194
|
+
async getWebEncryptionKey() {
|
|
1195
|
+
const existing = localStorage.getItem(this.webEncryptionKeyStorage);
|
|
1196
|
+
if (existing) return existing;
|
|
1197
|
+
const buf = new Uint8Array(32);
|
|
1198
|
+
if (typeof window !== "undefined" && window.crypto?.getRandomValues) {
|
|
1199
|
+
window.crypto.getRandomValues(buf);
|
|
1200
|
+
}
|
|
1201
|
+
const raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1202
|
+
localStorage.setItem(this.webEncryptionKeyStorage, raw);
|
|
1203
|
+
return raw;
|
|
1204
|
+
}
|
|
1205
|
+
base64Encode(bytes) {
|
|
1206
|
+
let binary = "";
|
|
1207
|
+
bytes.forEach((b) => {
|
|
1208
|
+
binary += String.fromCharCode(b);
|
|
1209
|
+
});
|
|
1210
|
+
return btoa(binary);
|
|
1211
|
+
}
|
|
1212
|
+
base64Decode(value) {
|
|
1213
|
+
const binary = atob(value);
|
|
1214
|
+
const bytes = new Uint8Array(binary.length);
|
|
1215
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1216
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1217
|
+
}
|
|
1218
|
+
return bytes;
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
// src/storage/api-key-storage-web.ts
|
|
1223
|
+
var ApiKeyStorageWeb = class {
|
|
1224
|
+
constructor() {
|
|
1225
|
+
this.storageKey = "lanonasis_api_key";
|
|
1226
|
+
this.webEncryptionKeyStorage = "lanonasis_web_enc_key";
|
|
1227
|
+
}
|
|
1228
|
+
async store(data) {
|
|
1229
|
+
const payload = JSON.stringify({
|
|
1230
|
+
...data,
|
|
1231
|
+
createdAt: data.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
1232
|
+
});
|
|
1233
|
+
const encrypted = await this.encrypt(payload);
|
|
1234
|
+
localStorage.setItem(this.storageKey, encrypted);
|
|
1235
|
+
}
|
|
1236
|
+
async retrieve() {
|
|
1237
|
+
const encrypted = localStorage.getItem(this.storageKey);
|
|
1238
|
+
if (!encrypted) return null;
|
|
1239
|
+
try {
|
|
1240
|
+
const decrypted = await this.decrypt(encrypted);
|
|
1241
|
+
return JSON.parse(decrypted);
|
|
1242
|
+
} catch {
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
async clear() {
|
|
1247
|
+
localStorage.removeItem(this.storageKey);
|
|
1248
|
+
}
|
|
1249
|
+
async encrypt(text) {
|
|
1250
|
+
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
1251
|
+
const encoder2 = new TextEncoder();
|
|
1252
|
+
return this.base64Encode(encoder2.encode(text));
|
|
1253
|
+
}
|
|
1254
|
+
const encoder = new TextEncoder();
|
|
1255
|
+
const data = encoder.encode(text);
|
|
1256
|
+
const passphrase = await this.getWebEncryptionKey();
|
|
1257
|
+
const keyMaterial = await window.crypto.subtle.importKey(
|
|
1258
|
+
"raw",
|
|
1259
|
+
encoder.encode(passphrase),
|
|
1260
|
+
"PBKDF2",
|
|
1261
|
+
false,
|
|
1262
|
+
["deriveBits", "deriveKey"]
|
|
1263
|
+
);
|
|
1264
|
+
const key = await window.crypto.subtle.deriveKey(
|
|
1265
|
+
{
|
|
1266
|
+
name: "PBKDF2",
|
|
1267
|
+
salt: encoder.encode("lanonasis-api-key-salt"),
|
|
1268
|
+
iterations: 1e5,
|
|
1269
|
+
hash: "SHA-256"
|
|
1270
|
+
},
|
|
1271
|
+
keyMaterial,
|
|
1272
|
+
{ name: "AES-GCM", length: 256 },
|
|
1273
|
+
true,
|
|
1274
|
+
["encrypt", "decrypt"]
|
|
1275
|
+
);
|
|
1276
|
+
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
|
1277
|
+
const encrypted = await window.crypto.subtle.encrypt(
|
|
1278
|
+
{ name: "AES-GCM", iv },
|
|
1279
|
+
key,
|
|
1280
|
+
data
|
|
1281
|
+
);
|
|
1282
|
+
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
1283
|
+
combined.set(iv, 0);
|
|
1284
|
+
combined.set(new Uint8Array(encrypted), iv.length);
|
|
1285
|
+
return this.base64Encode(combined);
|
|
1286
|
+
}
|
|
1287
|
+
async decrypt(encrypted) {
|
|
1288
|
+
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
1289
|
+
const decoder2 = new TextDecoder();
|
|
1290
|
+
return decoder2.decode(this.base64Decode(encrypted));
|
|
1291
|
+
}
|
|
1292
|
+
const bytes = this.base64Decode(encrypted);
|
|
1293
|
+
const iv = bytes.slice(0, 12);
|
|
1294
|
+
const data = bytes.slice(12);
|
|
1295
|
+
const encoder = new TextEncoder();
|
|
1296
|
+
const decoder = new TextDecoder();
|
|
1297
|
+
const passphrase = await this.getWebEncryptionKey();
|
|
1298
|
+
const keyMaterial = await window.crypto.subtle.importKey(
|
|
1299
|
+
"raw",
|
|
1300
|
+
encoder.encode(passphrase),
|
|
1301
|
+
"PBKDF2",
|
|
1302
|
+
false,
|
|
1303
|
+
["deriveBits", "deriveKey"]
|
|
1304
|
+
);
|
|
1305
|
+
const key = await window.crypto.subtle.deriveKey(
|
|
1306
|
+
{
|
|
1307
|
+
name: "PBKDF2",
|
|
1308
|
+
salt: encoder.encode("lanonasis-api-key-salt"),
|
|
1309
|
+
iterations: 1e5,
|
|
1310
|
+
hash: "SHA-256"
|
|
1311
|
+
},
|
|
1312
|
+
keyMaterial,
|
|
1313
|
+
{ name: "AES-GCM", length: 256 },
|
|
1314
|
+
true,
|
|
1315
|
+
["encrypt", "decrypt"]
|
|
1316
|
+
);
|
|
1317
|
+
const decrypted = await window.crypto.subtle.decrypt(
|
|
1318
|
+
{ name: "AES-GCM", iv },
|
|
1319
|
+
key,
|
|
1320
|
+
data
|
|
1321
|
+
);
|
|
1322
|
+
return decoder.decode(decrypted);
|
|
1323
|
+
}
|
|
1324
|
+
async getWebEncryptionKey() {
|
|
1325
|
+
const existing = localStorage.getItem(this.webEncryptionKeyStorage);
|
|
1326
|
+
if (existing) return existing;
|
|
1327
|
+
const buf = new Uint8Array(32);
|
|
1328
|
+
if (typeof window !== "undefined" && window.crypto?.getRandomValues) {
|
|
1329
|
+
window.crypto.getRandomValues(buf);
|
|
1330
|
+
}
|
|
1331
|
+
const raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1332
|
+
localStorage.setItem(this.webEncryptionKeyStorage, raw);
|
|
1333
|
+
return raw;
|
|
1334
|
+
}
|
|
1335
|
+
base64Encode(bytes) {
|
|
1336
|
+
let binary = "";
|
|
1337
|
+
bytes.forEach((b) => {
|
|
1338
|
+
binary += String.fromCharCode(b);
|
|
1339
|
+
});
|
|
1340
|
+
return btoa(binary);
|
|
1341
|
+
}
|
|
1342
|
+
base64Decode(value) {
|
|
1343
|
+
const binary = atob(value);
|
|
1344
|
+
const bytes = new Uint8Array(binary.length);
|
|
1345
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1346
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1347
|
+
}
|
|
1348
|
+
return bytes;
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1352
|
+
// src/client/mcp-client.ts
|
|
1353
|
+
import fetch4 from "cross-fetch";
|
|
1354
|
+
|
|
1355
|
+
// src/flows/apikey-flow.ts
|
|
1356
|
+
import fetch3 from "cross-fetch";
|
|
1357
|
+
var APIKeyFlow = class extends BaseOAuthFlow {
|
|
1358
|
+
constructor(apiKey, authBaseUrl = "https://mcp.lanonasis.com") {
|
|
1359
|
+
super({
|
|
1360
|
+
clientId: "api-key-client",
|
|
1361
|
+
authBaseUrl
|
|
1362
|
+
});
|
|
1363
|
+
this.apiKey = apiKey;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* "Authenticate" by returning the API key as a virtual token
|
|
1367
|
+
* The API key will be used directly in request headers
|
|
1368
|
+
*/
|
|
1369
|
+
async authenticate() {
|
|
1370
|
+
if (!this.apiKey || !this.apiKey.startsWith("lano_") && !this.apiKey.startsWith("vx_")) {
|
|
1371
|
+
throw new Error(
|
|
1372
|
+
'Invalid API key format. Must start with "lano_" or "vx_". Please regenerate your API key from the dashboard.'
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
if (this.apiKey.startsWith("vx_")) {
|
|
1376
|
+
console.warn(
|
|
1377
|
+
'\u26A0\uFE0F DEPRECATION WARNING: API keys with "vx_" prefix are deprecated and will stop working soon. Please regenerate your API key from the dashboard to get a "lano_" prefixed key. Support for "vx_" keys will be removed in a future version.'
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
access_token: this.apiKey,
|
|
1382
|
+
token_type: "api-key",
|
|
1383
|
+
expires_in: 0,
|
|
1384
|
+
// API keys don't expire
|
|
1385
|
+
issued_at: Date.now()
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* API keys don't need refresh
|
|
1390
|
+
*/
|
|
1391
|
+
async refreshToken(refreshToken) {
|
|
1392
|
+
throw new Error("API keys do not support token refresh");
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Optional: Validate API key by making a test request
|
|
1396
|
+
*/
|
|
1397
|
+
async validateAPIKey() {
|
|
1398
|
+
try {
|
|
1399
|
+
const response = await fetch3(`${this.config.authBaseUrl}/api/v1/health`, {
|
|
1400
|
+
headers: {
|
|
1401
|
+
"x-api-key": this.apiKey
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
return response.ok;
|
|
1405
|
+
} catch (error) {
|
|
1406
|
+
console.error("API key validation failed:", error);
|
|
1407
|
+
return false;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1040
1412
|
// src/client/mcp-client.ts
|
|
1041
1413
|
var MCPClient = class {
|
|
1042
1414
|
constructor(config = {}) {
|
|
1415
|
+
// ← NEW: Track auth mode
|
|
1043
1416
|
this.ws = null;
|
|
1044
1417
|
this.eventSource = null;
|
|
1045
1418
|
this.accessToken = null;
|
|
@@ -1049,31 +1422,47 @@ var MCPClient = class {
|
|
|
1049
1422
|
autoRefresh: true,
|
|
1050
1423
|
...config
|
|
1051
1424
|
};
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1425
|
+
const defaultStorage = typeof window !== "undefined" ? new TokenStorageWeb() : new TokenStorage();
|
|
1426
|
+
this.tokenStorage = config.tokenStorage || defaultStorage;
|
|
1427
|
+
this.authMode = config.apiKey ? "apikey" : "oauth";
|
|
1428
|
+
if (this.authMode === "apikey") {
|
|
1429
|
+
this.authFlow = new APIKeyFlow(
|
|
1430
|
+
config.apiKey,
|
|
1431
|
+
config.authBaseUrl || "https://mcp.lanonasis.com"
|
|
1432
|
+
);
|
|
1055
1433
|
} else {
|
|
1056
|
-
this.
|
|
1434
|
+
if (this.isTerminal()) {
|
|
1435
|
+
this.authFlow = new TerminalOAuthFlow(config);
|
|
1436
|
+
} else {
|
|
1437
|
+
this.authFlow = new DesktopOAuthFlow(config);
|
|
1438
|
+
}
|
|
1057
1439
|
}
|
|
1058
1440
|
}
|
|
1059
1441
|
async connect() {
|
|
1060
1442
|
try {
|
|
1061
1443
|
let tokens = await this.tokenStorage.retrieve();
|
|
1062
|
-
if (
|
|
1063
|
-
if (tokens
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1444
|
+
if (this.authMode === "apikey") {
|
|
1445
|
+
if (!tokens) {
|
|
1446
|
+
tokens = await this.authenticate();
|
|
1447
|
+
}
|
|
1448
|
+
this.accessToken = tokens.access_token;
|
|
1449
|
+
} else {
|
|
1450
|
+
if (!tokens || this.tokenStorage.isTokenExpired(tokens)) {
|
|
1451
|
+
if (tokens?.refresh_token) {
|
|
1452
|
+
try {
|
|
1453
|
+
tokens = await this.authFlow.refreshToken(tokens.refresh_token);
|
|
1454
|
+
await this.tokenStorage.store(tokens);
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
tokens = await this.authenticate();
|
|
1457
|
+
}
|
|
1458
|
+
} else {
|
|
1068
1459
|
tokens = await this.authenticate();
|
|
1069
1460
|
}
|
|
1070
|
-
} else {
|
|
1071
|
-
tokens = await this.authenticate();
|
|
1072
1461
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1462
|
+
this.accessToken = tokens.access_token;
|
|
1463
|
+
if (this.config.autoRefresh && tokens.expires_in) {
|
|
1464
|
+
this.scheduleTokenRefresh(tokens);
|
|
1465
|
+
}
|
|
1077
1466
|
}
|
|
1078
1467
|
await this.establishConnection();
|
|
1079
1468
|
} catch (error) {
|
|
@@ -1093,6 +1482,10 @@ var MCPClient = class {
|
|
|
1093
1482
|
if (!tokens) {
|
|
1094
1483
|
throw new Error("Not authenticated");
|
|
1095
1484
|
}
|
|
1485
|
+
if (this.authMode === "apikey") {
|
|
1486
|
+
this.accessToken = tokens.access_token;
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1096
1489
|
if (this.tokenStorage.isTokenExpired(tokens)) {
|
|
1097
1490
|
if (tokens.refresh_token) {
|
|
1098
1491
|
try {
|
|
@@ -1149,11 +1542,19 @@ var MCPClient = class {
|
|
|
1149
1542
|
this.ws = new WebSocket(wsUrl.toString());
|
|
1150
1543
|
} else {
|
|
1151
1544
|
const { default: WS } = await import("ws");
|
|
1152
|
-
this.
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1545
|
+
if (this.authMode === "apikey") {
|
|
1546
|
+
this.ws = new WS(wsUrl.toString(), {
|
|
1547
|
+
headers: {
|
|
1548
|
+
"x-api-key": this.accessToken
|
|
1549
|
+
}
|
|
1550
|
+
});
|
|
1551
|
+
} else {
|
|
1552
|
+
this.ws = new WS(wsUrl.toString(), {
|
|
1553
|
+
headers: {
|
|
1554
|
+
"Authorization": `Bearer ${this.accessToken}`
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1157
1558
|
}
|
|
1158
1559
|
return new Promise((resolve, reject) => {
|
|
1159
1560
|
if (!this.ws) {
|
|
@@ -1187,11 +1588,19 @@ var MCPClient = class {
|
|
|
1187
1588
|
} else {
|
|
1188
1589
|
const EventSourceModule = await import("eventsource");
|
|
1189
1590
|
const ES = EventSourceModule.default || EventSourceModule;
|
|
1190
|
-
this.
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1591
|
+
if (this.authMode === "apikey") {
|
|
1592
|
+
this.eventSource = new ES(sseUrl.toString(), {
|
|
1593
|
+
headers: {
|
|
1594
|
+
"x-api-key": this.accessToken
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
} else {
|
|
1598
|
+
this.eventSource = new ES(sseUrl.toString(), {
|
|
1599
|
+
headers: {
|
|
1600
|
+
"Authorization": `Bearer ${this.accessToken}`
|
|
1601
|
+
}
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1195
1604
|
}
|
|
1196
1605
|
this.eventSource.onopen = () => {
|
|
1197
1606
|
console.log("MCP SSE connected");
|
|
@@ -1221,12 +1630,17 @@ var MCPClient = class {
|
|
|
1221
1630
|
if (!this.accessToken) {
|
|
1222
1631
|
throw new Error("Not authenticated");
|
|
1223
1632
|
}
|
|
1224
|
-
const
|
|
1633
|
+
const headers = {
|
|
1634
|
+
"Content-Type": "application/json"
|
|
1635
|
+
};
|
|
1636
|
+
if (this.authMode === "apikey") {
|
|
1637
|
+
headers["x-api-key"] = this.accessToken;
|
|
1638
|
+
} else {
|
|
1639
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
1640
|
+
}
|
|
1641
|
+
const response = await fetch4(`${this.config.mcpEndpoint}/api`, {
|
|
1225
1642
|
method: "POST",
|
|
1226
|
-
headers
|
|
1227
|
-
"Authorization": `Bearer ${this.accessToken}`,
|
|
1228
|
-
"Content-Type": "application/json"
|
|
1229
|
-
},
|
|
1643
|
+
headers,
|
|
1230
1644
|
body: JSON.stringify({
|
|
1231
1645
|
jsonrpc: "2.0",
|
|
1232
1646
|
id: this.generateId(),
|
|
@@ -1235,6 +1649,9 @@ var MCPClient = class {
|
|
|
1235
1649
|
})
|
|
1236
1650
|
});
|
|
1237
1651
|
if (response.status === 401) {
|
|
1652
|
+
if (this.authMode === "apikey") {
|
|
1653
|
+
throw new Error("Invalid API key - please check your credentials");
|
|
1654
|
+
}
|
|
1238
1655
|
const tokens = await this.tokenStorage.retrieve();
|
|
1239
1656
|
if (tokens?.refresh_token) {
|
|
1240
1657
|
const newTokens = await this.authFlow.refreshToken(tokens.refresh_token);
|
|
@@ -1322,9 +1739,11 @@ var MCPClient = class {
|
|
|
1322
1739
|
};
|
|
1323
1740
|
export {
|
|
1324
1741
|
ApiKeyStorage,
|
|
1742
|
+
ApiKeyStorageWeb,
|
|
1325
1743
|
BaseOAuthFlow,
|
|
1326
1744
|
DesktopOAuthFlow,
|
|
1327
1745
|
MCPClient,
|
|
1328
1746
|
TerminalOAuthFlow,
|
|
1329
|
-
TokenStorage
|
|
1747
|
+
TokenStorage,
|
|
1748
|
+
TokenStorageWeb
|
|
1330
1749
|
};
|