@lanonasis/oauth-client 1.2.1 → 1.2.3
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/api-key-storage-web-DannE11B.d.cts +208 -0
- package/dist/api-key-storage-web-DannE11B.d.ts +208 -0
- package/dist/browser.cjs +793 -0
- package/dist/browser.d.cts +45 -0
- package/dist/browser.d.ts +45 -0
- package/dist/browser.mjs +767 -0
- package/dist/index.cjs +517 -107
- package/dist/index.d.cts +6 -159
- package/dist/index.d.ts +6 -159
- package/dist/index.mjs +514 -95
- package/package.json +13 -6
package/dist/index.cjs
CHANGED
|
@@ -31,23 +31,29 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ApiKeyStorage: () => ApiKeyStorage,
|
|
34
|
+
ApiKeyStorageWeb: () => ApiKeyStorageWeb,
|
|
34
35
|
BaseOAuthFlow: () => BaseOAuthFlow,
|
|
35
36
|
DesktopOAuthFlow: () => DesktopOAuthFlow,
|
|
36
37
|
MCPClient: () => MCPClient,
|
|
37
38
|
TerminalOAuthFlow: () => TerminalOAuthFlow,
|
|
38
|
-
TokenStorage: () => TokenStorage
|
|
39
|
+
TokenStorage: () => TokenStorage,
|
|
40
|
+
TokenStorageWeb: () => TokenStorageWeb
|
|
39
41
|
});
|
|
40
42
|
module.exports = __toCommonJS(index_exports);
|
|
41
43
|
|
|
44
|
+
// src/flows/terminal-flow.ts
|
|
45
|
+
var import_cross_fetch2 = __toESM(require("cross-fetch"), 1);
|
|
46
|
+
|
|
42
47
|
// src/flows/base-flow.ts
|
|
48
|
+
var import_cross_fetch = __toESM(require("cross-fetch"), 1);
|
|
43
49
|
var BaseOAuthFlow = class {
|
|
44
50
|
constructor(config) {
|
|
45
51
|
this.clientId = config.clientId;
|
|
46
52
|
this.authBaseUrl = config.authBaseUrl || "https://auth.lanonasis.com";
|
|
47
|
-
this.scope = config.scope || "
|
|
53
|
+
this.scope = config.scope || "memories:read memories:write memories:delete profile";
|
|
48
54
|
}
|
|
49
55
|
async makeTokenRequest(body) {
|
|
50
|
-
const response = await
|
|
56
|
+
const response = await (0, import_cross_fetch.default)(`${this.authBaseUrl}/oauth/token`, {
|
|
51
57
|
method: "POST",
|
|
52
58
|
headers: { "Content-Type": "application/json" },
|
|
53
59
|
body: JSON.stringify(body)
|
|
@@ -60,11 +66,10 @@ var BaseOAuthFlow = class {
|
|
|
60
66
|
}
|
|
61
67
|
generateState() {
|
|
62
68
|
const array = new Uint8Array(32);
|
|
63
|
-
if (typeof
|
|
64
|
-
|
|
69
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
70
|
+
crypto.getRandomValues(array);
|
|
65
71
|
} else {
|
|
66
|
-
|
|
67
|
-
crypto.randomFillSync(array);
|
|
72
|
+
throw new Error("Secure random generation is not available");
|
|
68
73
|
}
|
|
69
74
|
return this.base64URLEncode(array);
|
|
70
75
|
}
|
|
@@ -74,7 +79,8 @@ var BaseOAuthFlow = class {
|
|
|
74
79
|
bytes.forEach((byte) => {
|
|
75
80
|
binary += String.fromCharCode(byte);
|
|
76
81
|
});
|
|
77
|
-
|
|
82
|
+
const base64 = typeof btoa !== "undefined" ? btoa(binary) : Buffer.from(bytes).toString("base64");
|
|
83
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
78
84
|
}
|
|
79
85
|
async refreshToken(refreshToken) {
|
|
80
86
|
return this.makeTokenRequest({
|
|
@@ -84,7 +90,7 @@ var BaseOAuthFlow = class {
|
|
|
84
90
|
});
|
|
85
91
|
}
|
|
86
92
|
async revokeToken(token, tokenType = "access_token") {
|
|
87
|
-
const response = await
|
|
93
|
+
const response = await (0, import_cross_fetch.default)(`${this.authBaseUrl}/oauth/revoke`, {
|
|
88
94
|
method: "POST",
|
|
89
95
|
headers: { "Content-Type": "application/json" },
|
|
90
96
|
body: JSON.stringify({
|
|
@@ -100,7 +106,6 @@ var BaseOAuthFlow = class {
|
|
|
100
106
|
};
|
|
101
107
|
|
|
102
108
|
// src/flows/terminal-flow.ts
|
|
103
|
-
var import_open = __toESM(require("open"), 1);
|
|
104
109
|
var TerminalOAuthFlow = class extends BaseOAuthFlow {
|
|
105
110
|
constructor(config) {
|
|
106
111
|
super({
|
|
@@ -123,7 +128,7 @@ var TerminalOAuthFlow = class extends BaseOAuthFlow {
|
|
|
123
128
|
}
|
|
124
129
|
}
|
|
125
130
|
async requestDeviceCode() {
|
|
126
|
-
const response = await
|
|
131
|
+
const response = await (0, import_cross_fetch2.default)(`${this.authBaseUrl}/oauth/device`, {
|
|
127
132
|
method: "POST",
|
|
128
133
|
headers: { "Content-Type": "application/json" },
|
|
129
134
|
body: JSON.stringify({
|
|
@@ -148,12 +153,13 @@ var TerminalOAuthFlow = class extends BaseOAuthFlow {
|
|
|
148
153
|
}
|
|
149
154
|
async openBrowser(url) {
|
|
150
155
|
try {
|
|
156
|
+
const { default: open } = await import("open");
|
|
151
157
|
await Promise.race([
|
|
152
158
|
this.waitForEnter(),
|
|
153
159
|
new Promise((resolve) => setTimeout(resolve, 2e3))
|
|
154
160
|
]);
|
|
155
161
|
console.log("Opening browser...");
|
|
156
|
-
await (
|
|
162
|
+
await open(url);
|
|
157
163
|
} catch (error) {
|
|
158
164
|
console.log("Please open the URL manually in your browser.");
|
|
159
165
|
}
|
|
@@ -196,7 +202,7 @@ var TerminalOAuthFlow = class extends BaseOAuthFlow {
|
|
|
196
202
|
throw new Error("Authorization timeout - please try again");
|
|
197
203
|
}
|
|
198
204
|
async checkDeviceCode(deviceCode) {
|
|
199
|
-
const response = await
|
|
205
|
+
const response = await (0, import_cross_fetch2.default)(`${this.authBaseUrl}/oauth/token`, {
|
|
200
206
|
method: "POST",
|
|
201
207
|
headers: { "Content-Type": "application/json" },
|
|
202
208
|
body: JSON.stringify({
|
|
@@ -237,25 +243,22 @@ var DesktopOAuthFlow = class extends BaseOAuthFlow {
|
|
|
237
243
|
}
|
|
238
244
|
generateCodeVerifier() {
|
|
239
245
|
const array = new Uint8Array(32);
|
|
240
|
-
if (typeof
|
|
241
|
-
|
|
246
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
247
|
+
crypto.getRandomValues(array);
|
|
242
248
|
} else {
|
|
243
|
-
|
|
244
|
-
crypto.randomFillSync(array);
|
|
249
|
+
throw new Error("Secure random generation is not available in this environment");
|
|
245
250
|
}
|
|
246
251
|
return this.base64URLEncode(array);
|
|
247
252
|
}
|
|
248
253
|
async generateCodeChallenge(verifier) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const hash = await window.crypto.subtle.digest("SHA-256", data);
|
|
253
|
-
return this.base64URLEncode(hash);
|
|
254
|
-
} else {
|
|
255
|
-
const crypto = require("crypto");
|
|
256
|
-
const hash = crypto.createHash("sha256").update(verifier).digest();
|
|
257
|
-
return this.base64URLEncode(hash);
|
|
254
|
+
const subtle = typeof crypto !== "undefined" ? crypto.subtle : void 0;
|
|
255
|
+
if (!subtle) {
|
|
256
|
+
throw new Error("Web Crypto is required to generate PKCE code challenge");
|
|
258
257
|
}
|
|
258
|
+
const encoder = new TextEncoder();
|
|
259
|
+
const data = encoder.encode(verifier);
|
|
260
|
+
const hash = await subtle.digest("SHA-256", data);
|
|
261
|
+
return this.base64URLEncode(hash);
|
|
259
262
|
}
|
|
260
263
|
buildAuthorizationUrl(codeChallenge, state) {
|
|
261
264
|
const params = new URLSearchParams({
|
|
@@ -440,6 +443,9 @@ var TokenStorage = class {
|
|
|
440
443
|
}
|
|
441
444
|
}
|
|
442
445
|
isTokenExpired(tokens) {
|
|
446
|
+
if (tokens.token_type === "api-key" || tokens.expires_in === 0) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
443
449
|
if (!tokens.expires_in) return false;
|
|
444
450
|
if (!tokens.issued_at) {
|
|
445
451
|
console.warn("Token missing issued_at timestamp, treating as expired");
|
|
@@ -454,13 +460,13 @@ var TokenStorage = class {
|
|
|
454
460
|
const fs = require("fs").promises;
|
|
455
461
|
const path = require("path");
|
|
456
462
|
const os = require("os");
|
|
457
|
-
const
|
|
463
|
+
const crypto2 = require("crypto");
|
|
458
464
|
const configDir = path.join(os.homedir(), ".lanonasis");
|
|
459
465
|
const tokenFile = path.join(configDir, "mcp-tokens.enc");
|
|
460
466
|
await fs.mkdir(configDir, { recursive: true });
|
|
461
467
|
const key = this.getFileEncryptionKey();
|
|
462
|
-
const iv =
|
|
463
|
-
const cipher =
|
|
468
|
+
const iv = crypto2.randomBytes(16);
|
|
469
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
|
|
464
470
|
let encrypted = cipher.update(tokenString, "utf8", "hex");
|
|
465
471
|
encrypted += cipher.final("hex");
|
|
466
472
|
const authTag = cipher.getAuthTag().toString("hex");
|
|
@@ -472,7 +478,7 @@ var TokenStorage = class {
|
|
|
472
478
|
const fs = require("fs").promises;
|
|
473
479
|
const path = require("path");
|
|
474
480
|
const os = require("os");
|
|
475
|
-
const
|
|
481
|
+
const crypto2 = require("crypto");
|
|
476
482
|
const tokenFile = path.join(os.homedir(), ".lanonasis", "mcp-tokens.enc");
|
|
477
483
|
try {
|
|
478
484
|
const data = await fs.readFile(tokenFile, "utf8");
|
|
@@ -482,7 +488,7 @@ var TokenStorage = class {
|
|
|
482
488
|
const [ivHex, authTagHex, encrypted] = parts;
|
|
483
489
|
const iv = Buffer.from(ivHex, "hex");
|
|
484
490
|
const authTag = Buffer.from(authTagHex, "hex");
|
|
485
|
-
const decipher =
|
|
491
|
+
const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
|
|
486
492
|
decipher.setAuthTag(authTag);
|
|
487
493
|
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
488
494
|
decrypted += decipher.final("utf8");
|
|
@@ -491,7 +497,7 @@ var TokenStorage = class {
|
|
|
491
497
|
if (parts.length === 2) {
|
|
492
498
|
const [ivHex, encrypted] = parts;
|
|
493
499
|
const iv = Buffer.from(ivHex, "hex");
|
|
494
|
-
const decipher =
|
|
500
|
+
const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
|
|
495
501
|
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
496
502
|
decrypted += decipher.final("utf8");
|
|
497
503
|
return decrypted;
|
|
@@ -513,17 +519,17 @@ var TokenStorage = class {
|
|
|
513
519
|
}
|
|
514
520
|
}
|
|
515
521
|
getFileEncryptionKey() {
|
|
516
|
-
const
|
|
522
|
+
const crypto2 = require("crypto");
|
|
517
523
|
const os = require("os");
|
|
518
524
|
const machineId = os.hostname() + os.userInfo().username;
|
|
519
525
|
const salt = "lanonasis-mcp-oauth-2024";
|
|
520
|
-
return
|
|
526
|
+
return crypto2.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
|
|
521
527
|
}
|
|
522
528
|
async encrypt(text) {
|
|
523
529
|
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
524
530
|
const encoder2 = new TextEncoder();
|
|
525
531
|
const data2 = encoder2.encode(text);
|
|
526
|
-
return
|
|
532
|
+
return this.base64Encode(data2);
|
|
527
533
|
}
|
|
528
534
|
const encoder = new TextEncoder();
|
|
529
535
|
const data = encoder.encode(text);
|
|
@@ -556,23 +562,15 @@ var TokenStorage = class {
|
|
|
556
562
|
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
557
563
|
combined.set(iv, 0);
|
|
558
564
|
combined.set(new Uint8Array(encrypted), iv.length);
|
|
559
|
-
return
|
|
565
|
+
return this.base64Encode(combined);
|
|
560
566
|
}
|
|
561
567
|
async decrypt(encrypted) {
|
|
562
568
|
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
563
|
-
const
|
|
564
|
-
const bytes2 = new Uint8Array(binary2.length);
|
|
565
|
-
for (let i = 0; i < binary2.length; i++) {
|
|
566
|
-
bytes2[i] = binary2.charCodeAt(i);
|
|
567
|
-
}
|
|
569
|
+
const bytes2 = this.base64Decode(encrypted);
|
|
568
570
|
const decoder2 = new TextDecoder();
|
|
569
571
|
return decoder2.decode(bytes2);
|
|
570
572
|
}
|
|
571
|
-
const
|
|
572
|
-
const bytes = new Uint8Array(binary.length);
|
|
573
|
-
for (let i = 0; i < binary.length; i++) {
|
|
574
|
-
bytes[i] = binary.charCodeAt(i);
|
|
575
|
-
}
|
|
573
|
+
const bytes = this.base64Decode(encrypted);
|
|
576
574
|
const iv = bytes.slice(0, 12);
|
|
577
575
|
const data = bytes.slice(12);
|
|
578
576
|
const encoder = new TextEncoder();
|
|
@@ -613,6 +611,33 @@ var TokenStorage = class {
|
|
|
613
611
|
isMobile() {
|
|
614
612
|
return typeof window !== "undefined" && window.SecureStorage !== void 0;
|
|
615
613
|
}
|
|
614
|
+
base64Encode(bytes) {
|
|
615
|
+
if (typeof btoa !== "undefined") {
|
|
616
|
+
let binary = "";
|
|
617
|
+
bytes.forEach((b) => {
|
|
618
|
+
binary += String.fromCharCode(b);
|
|
619
|
+
});
|
|
620
|
+
return btoa(binary);
|
|
621
|
+
}
|
|
622
|
+
if (typeof Buffer !== "undefined") {
|
|
623
|
+
return Buffer.from(bytes).toString("base64");
|
|
624
|
+
}
|
|
625
|
+
throw new Error("No base64 encoder available");
|
|
626
|
+
}
|
|
627
|
+
base64Decode(value) {
|
|
628
|
+
if (typeof atob !== "undefined") {
|
|
629
|
+
const binary = atob(value);
|
|
630
|
+
const bytes = new Uint8Array(binary.length);
|
|
631
|
+
for (let i = 0; i < binary.length; i++) {
|
|
632
|
+
bytes[i] = binary.charCodeAt(i);
|
|
633
|
+
}
|
|
634
|
+
return bytes;
|
|
635
|
+
}
|
|
636
|
+
if (typeof Buffer !== "undefined") {
|
|
637
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
638
|
+
}
|
|
639
|
+
throw new Error("No base64 decoder available");
|
|
640
|
+
}
|
|
616
641
|
async getWebEncryptionKey() {
|
|
617
642
|
const existing = typeof localStorage !== "undefined" ? localStorage.getItem(this.webEncryptionKeyStorage) : null;
|
|
618
643
|
if (existing) {
|
|
@@ -624,7 +649,8 @@ var TokenStorage = class {
|
|
|
624
649
|
window.crypto.getRandomValues(buf);
|
|
625
650
|
raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
626
651
|
} else {
|
|
627
|
-
|
|
652
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "node";
|
|
653
|
+
raw = `${ua}-${Math.random().toString(36).slice(2)}-${Date.now()}`;
|
|
628
654
|
}
|
|
629
655
|
if (typeof localStorage !== "undefined") {
|
|
630
656
|
localStorage.setItem(this.webEncryptionKeyStorage, raw);
|
|
@@ -836,13 +862,13 @@ var ApiKeyStorage = class {
|
|
|
836
862
|
const fs = require("fs").promises;
|
|
837
863
|
const path = require("path");
|
|
838
864
|
const os = require("os");
|
|
839
|
-
const
|
|
865
|
+
const crypto2 = require("crypto");
|
|
840
866
|
const configDir = path.join(os.homedir(), ".lanonasis");
|
|
841
867
|
const keyFile = path.join(configDir, "api-key.enc");
|
|
842
868
|
await fs.mkdir(configDir, { recursive: true, mode: 448 });
|
|
843
869
|
const key = this.getFileEncryptionKey();
|
|
844
|
-
const iv =
|
|
845
|
-
const cipher =
|
|
870
|
+
const iv = crypto2.randomBytes(16);
|
|
871
|
+
const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
|
|
846
872
|
let encrypted = cipher.update(keyString, "utf8", "hex");
|
|
847
873
|
encrypted += cipher.final("hex");
|
|
848
874
|
const authTag = cipher.getAuthTag();
|
|
@@ -854,7 +880,7 @@ var ApiKeyStorage = class {
|
|
|
854
880
|
const fs = require("fs").promises;
|
|
855
881
|
const path = require("path");
|
|
856
882
|
const os = require("os");
|
|
857
|
-
const
|
|
883
|
+
const crypto2 = require("crypto");
|
|
858
884
|
const keyFile = path.join(os.homedir(), ".lanonasis", "api-key.enc");
|
|
859
885
|
try {
|
|
860
886
|
const data = await fs.readFile(keyFile, "utf8");
|
|
@@ -865,7 +891,7 @@ var ApiKeyStorage = class {
|
|
|
865
891
|
const key = this.getFileEncryptionKey();
|
|
866
892
|
const iv = Buffer.from(ivHex, "hex");
|
|
867
893
|
const authTag = Buffer.from(authTagHex, "hex");
|
|
868
|
-
const decipher =
|
|
894
|
+
const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
|
|
869
895
|
decipher.setAuthTag(authTag);
|
|
870
896
|
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
871
897
|
decrypted += decipher.final("utf8");
|
|
@@ -909,18 +935,18 @@ var ApiKeyStorage = class {
|
|
|
909
935
|
}
|
|
910
936
|
}
|
|
911
937
|
getFileEncryptionKey() {
|
|
912
|
-
const
|
|
938
|
+
const crypto2 = require("crypto");
|
|
913
939
|
const os = require("os");
|
|
914
940
|
const machineId = os.hostname() + os.userInfo().username;
|
|
915
941
|
const salt = "lanonasis-mcp-api-key-2024";
|
|
916
|
-
return
|
|
942
|
+
return crypto2.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
|
|
917
943
|
}
|
|
918
944
|
// ==================== Web Encryption ====================
|
|
919
945
|
async encrypt(text) {
|
|
920
946
|
if (typeof window === "undefined" || !window.crypto || !window.crypto.subtle) {
|
|
921
947
|
const encoder = new TextEncoder();
|
|
922
948
|
const data = encoder.encode(text);
|
|
923
|
-
return
|
|
949
|
+
return this.base64Encode(data);
|
|
924
950
|
}
|
|
925
951
|
try {
|
|
926
952
|
const encoder = new TextEncoder();
|
|
@@ -959,16 +985,12 @@ var ApiKeyStorage = class {
|
|
|
959
985
|
console.error("Web encryption failed:", error);
|
|
960
986
|
const encoder = new TextEncoder();
|
|
961
987
|
const data = encoder.encode(text);
|
|
962
|
-
return
|
|
988
|
+
return this.base64Encode(data);
|
|
963
989
|
}
|
|
964
990
|
}
|
|
965
991
|
async decrypt(encrypted) {
|
|
966
992
|
if (typeof window === "undefined" || !window.crypto || !window.crypto.subtle) {
|
|
967
|
-
const
|
|
968
|
-
const bytes = new Uint8Array(binary.length);
|
|
969
|
-
for (let i = 0; i < binary.length; i++) {
|
|
970
|
-
bytes[i] = binary.charCodeAt(i);
|
|
971
|
-
}
|
|
993
|
+
const bytes = this.base64Decode(encrypted);
|
|
972
994
|
const decoder = new TextDecoder();
|
|
973
995
|
return decoder.decode(bytes);
|
|
974
996
|
}
|
|
@@ -1010,11 +1032,7 @@ var ApiKeyStorage = class {
|
|
|
1010
1032
|
return decoder.decode(decrypted);
|
|
1011
1033
|
} catch (error) {
|
|
1012
1034
|
console.error("Web decryption failed:", error);
|
|
1013
|
-
const
|
|
1014
|
-
const bytes = new Uint8Array(binary.length);
|
|
1015
|
-
for (let i = 0; i < binary.length; i++) {
|
|
1016
|
-
bytes[i] = binary.charCodeAt(i);
|
|
1017
|
-
}
|
|
1035
|
+
const bytes = this.base64Decode(encrypted);
|
|
1018
1036
|
const decoder = new TextDecoder();
|
|
1019
1037
|
return decoder.decode(bytes);
|
|
1020
1038
|
}
|
|
@@ -1030,7 +1048,8 @@ var ApiKeyStorage = class {
|
|
|
1030
1048
|
window.crypto.getRandomValues(buf);
|
|
1031
1049
|
raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1032
1050
|
} else {
|
|
1033
|
-
|
|
1051
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "node";
|
|
1052
|
+
raw = `${ua}-${Math.random().toString(36).slice(2)}-${Date.now()}`;
|
|
1034
1053
|
}
|
|
1035
1054
|
if (typeof localStorage !== "undefined") {
|
|
1036
1055
|
localStorage.setItem(this.webEncryptionKeyStorage, raw);
|
|
@@ -1047,6 +1066,33 @@ var ApiKeyStorage = class {
|
|
|
1047
1066
|
isMobile() {
|
|
1048
1067
|
return typeof window !== "undefined" && window.SecureStorage !== void 0;
|
|
1049
1068
|
}
|
|
1069
|
+
base64Encode(bytes) {
|
|
1070
|
+
if (typeof btoa !== "undefined") {
|
|
1071
|
+
let binary = "";
|
|
1072
|
+
bytes.forEach((b) => {
|
|
1073
|
+
binary += String.fromCharCode(b);
|
|
1074
|
+
});
|
|
1075
|
+
return btoa(binary);
|
|
1076
|
+
}
|
|
1077
|
+
if (typeof Buffer !== "undefined") {
|
|
1078
|
+
return Buffer.from(bytes).toString("base64");
|
|
1079
|
+
}
|
|
1080
|
+
throw new Error("No base64 encoder available");
|
|
1081
|
+
}
|
|
1082
|
+
base64Decode(value) {
|
|
1083
|
+
if (typeof atob !== "undefined") {
|
|
1084
|
+
const binary = atob(value);
|
|
1085
|
+
const bytes = new Uint8Array(binary.length);
|
|
1086
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1087
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1088
|
+
}
|
|
1089
|
+
return bytes;
|
|
1090
|
+
}
|
|
1091
|
+
if (typeof Buffer !== "undefined") {
|
|
1092
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
1093
|
+
}
|
|
1094
|
+
throw new Error("No base64 decoder available");
|
|
1095
|
+
}
|
|
1050
1096
|
/**
|
|
1051
1097
|
* Normalize API keys to a SHA-256 hex digest.
|
|
1052
1098
|
* Accepts pre-hashed input and lowercases it to prevent double hashing.
|
|
@@ -1071,9 +1117,338 @@ var ApiKeyStorage = class {
|
|
|
1071
1117
|
}
|
|
1072
1118
|
};
|
|
1073
1119
|
|
|
1120
|
+
// src/storage/token-storage-web.ts
|
|
1121
|
+
var TokenStorageWeb = class {
|
|
1122
|
+
constructor() {
|
|
1123
|
+
this.storageKey = "lanonasis_mcp_tokens";
|
|
1124
|
+
this.webEncryptionKeyStorage = "lanonasis_web_token_enc_key";
|
|
1125
|
+
}
|
|
1126
|
+
async store(tokens) {
|
|
1127
|
+
const tokensWithTimestamp = {
|
|
1128
|
+
...tokens,
|
|
1129
|
+
issued_at: Date.now()
|
|
1130
|
+
};
|
|
1131
|
+
const tokenString = JSON.stringify(tokensWithTimestamp);
|
|
1132
|
+
const encrypted = await this.encrypt(tokenString);
|
|
1133
|
+
localStorage.setItem(this.storageKey, encrypted);
|
|
1134
|
+
}
|
|
1135
|
+
async retrieve() {
|
|
1136
|
+
const encrypted = localStorage.getItem(this.storageKey);
|
|
1137
|
+
if (!encrypted) return null;
|
|
1138
|
+
try {
|
|
1139
|
+
const tokenString = await this.decrypt(encrypted);
|
|
1140
|
+
return JSON.parse(tokenString);
|
|
1141
|
+
} catch {
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
async clear() {
|
|
1146
|
+
localStorage.removeItem(this.storageKey);
|
|
1147
|
+
}
|
|
1148
|
+
isTokenExpired(tokens) {
|
|
1149
|
+
if (tokens.token_type === "api-key" || tokens.expires_in === 0) return false;
|
|
1150
|
+
if (!tokens.expires_in) return false;
|
|
1151
|
+
if (!tokens.issued_at) return true;
|
|
1152
|
+
const expiresAt = tokens.issued_at + tokens.expires_in * 1e3;
|
|
1153
|
+
return expiresAt - Date.now() < 3e5;
|
|
1154
|
+
}
|
|
1155
|
+
async encrypt(text) {
|
|
1156
|
+
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
1157
|
+
const encoder2 = new TextEncoder();
|
|
1158
|
+
return this.base64Encode(encoder2.encode(text));
|
|
1159
|
+
}
|
|
1160
|
+
const encoder = new TextEncoder();
|
|
1161
|
+
const data = encoder.encode(text);
|
|
1162
|
+
const passphrase = await this.getWebEncryptionKey();
|
|
1163
|
+
const keyMaterial = await window.crypto.subtle.importKey(
|
|
1164
|
+
"raw",
|
|
1165
|
+
encoder.encode(passphrase),
|
|
1166
|
+
"PBKDF2",
|
|
1167
|
+
false,
|
|
1168
|
+
["deriveBits", "deriveKey"]
|
|
1169
|
+
);
|
|
1170
|
+
const key = await window.crypto.subtle.deriveKey(
|
|
1171
|
+
{
|
|
1172
|
+
name: "PBKDF2",
|
|
1173
|
+
salt: encoder.encode("lanonasis-token-salt"),
|
|
1174
|
+
iterations: 1e5,
|
|
1175
|
+
hash: "SHA-256"
|
|
1176
|
+
},
|
|
1177
|
+
keyMaterial,
|
|
1178
|
+
{ name: "AES-GCM", length: 256 },
|
|
1179
|
+
true,
|
|
1180
|
+
["encrypt", "decrypt"]
|
|
1181
|
+
);
|
|
1182
|
+
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
|
1183
|
+
const encrypted = await window.crypto.subtle.encrypt(
|
|
1184
|
+
{ name: "AES-GCM", iv },
|
|
1185
|
+
key,
|
|
1186
|
+
data
|
|
1187
|
+
);
|
|
1188
|
+
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
1189
|
+
combined.set(iv, 0);
|
|
1190
|
+
combined.set(new Uint8Array(encrypted), iv.length);
|
|
1191
|
+
return this.base64Encode(combined);
|
|
1192
|
+
}
|
|
1193
|
+
async decrypt(encrypted) {
|
|
1194
|
+
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
1195
|
+
const decoder2 = new TextDecoder();
|
|
1196
|
+
return decoder2.decode(this.base64Decode(encrypted));
|
|
1197
|
+
}
|
|
1198
|
+
const bytes = this.base64Decode(encrypted);
|
|
1199
|
+
const iv = bytes.slice(0, 12);
|
|
1200
|
+
const data = bytes.slice(12);
|
|
1201
|
+
const encoder = new TextEncoder();
|
|
1202
|
+
const decoder = new TextDecoder();
|
|
1203
|
+
const passphrase = await this.getWebEncryptionKey();
|
|
1204
|
+
const keyMaterial = await window.crypto.subtle.importKey(
|
|
1205
|
+
"raw",
|
|
1206
|
+
encoder.encode(passphrase),
|
|
1207
|
+
"PBKDF2",
|
|
1208
|
+
false,
|
|
1209
|
+
["deriveBits", "deriveKey"]
|
|
1210
|
+
);
|
|
1211
|
+
const key = await window.crypto.subtle.deriveKey(
|
|
1212
|
+
{
|
|
1213
|
+
name: "PBKDF2",
|
|
1214
|
+
salt: encoder.encode("lanonasis-token-salt"),
|
|
1215
|
+
iterations: 1e5,
|
|
1216
|
+
hash: "SHA-256"
|
|
1217
|
+
},
|
|
1218
|
+
keyMaterial,
|
|
1219
|
+
{ name: "AES-GCM", length: 256 },
|
|
1220
|
+
true,
|
|
1221
|
+
["encrypt", "decrypt"]
|
|
1222
|
+
);
|
|
1223
|
+
const decrypted = await window.crypto.subtle.decrypt(
|
|
1224
|
+
{ name: "AES-GCM", iv },
|
|
1225
|
+
key,
|
|
1226
|
+
data
|
|
1227
|
+
);
|
|
1228
|
+
return decoder.decode(decrypted);
|
|
1229
|
+
}
|
|
1230
|
+
async getWebEncryptionKey() {
|
|
1231
|
+
const existing = localStorage.getItem(this.webEncryptionKeyStorage);
|
|
1232
|
+
if (existing) return existing;
|
|
1233
|
+
const buf = new Uint8Array(32);
|
|
1234
|
+
if (typeof window !== "undefined" && window.crypto?.getRandomValues) {
|
|
1235
|
+
window.crypto.getRandomValues(buf);
|
|
1236
|
+
}
|
|
1237
|
+
const raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1238
|
+
localStorage.setItem(this.webEncryptionKeyStorage, raw);
|
|
1239
|
+
return raw;
|
|
1240
|
+
}
|
|
1241
|
+
base64Encode(bytes) {
|
|
1242
|
+
let binary = "";
|
|
1243
|
+
bytes.forEach((b) => {
|
|
1244
|
+
binary += String.fromCharCode(b);
|
|
1245
|
+
});
|
|
1246
|
+
return btoa(binary);
|
|
1247
|
+
}
|
|
1248
|
+
base64Decode(value) {
|
|
1249
|
+
const binary = atob(value);
|
|
1250
|
+
const bytes = new Uint8Array(binary.length);
|
|
1251
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1252
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1253
|
+
}
|
|
1254
|
+
return bytes;
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
// src/storage/api-key-storage-web.ts
|
|
1259
|
+
var ApiKeyStorageWeb = class {
|
|
1260
|
+
constructor() {
|
|
1261
|
+
this.storageKey = "lanonasis_api_key";
|
|
1262
|
+
this.webEncryptionKeyStorage = "lanonasis_web_enc_key";
|
|
1263
|
+
}
|
|
1264
|
+
async store(data) {
|
|
1265
|
+
const payload = JSON.stringify({
|
|
1266
|
+
...data,
|
|
1267
|
+
createdAt: data.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
1268
|
+
});
|
|
1269
|
+
const encrypted = await this.encrypt(payload);
|
|
1270
|
+
localStorage.setItem(this.storageKey, encrypted);
|
|
1271
|
+
}
|
|
1272
|
+
async retrieve() {
|
|
1273
|
+
const encrypted = localStorage.getItem(this.storageKey);
|
|
1274
|
+
if (!encrypted) return null;
|
|
1275
|
+
try {
|
|
1276
|
+
const decrypted = await this.decrypt(encrypted);
|
|
1277
|
+
return JSON.parse(decrypted);
|
|
1278
|
+
} catch {
|
|
1279
|
+
return null;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
async clear() {
|
|
1283
|
+
localStorage.removeItem(this.storageKey);
|
|
1284
|
+
}
|
|
1285
|
+
async encrypt(text) {
|
|
1286
|
+
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
1287
|
+
const encoder2 = new TextEncoder();
|
|
1288
|
+
return this.base64Encode(encoder2.encode(text));
|
|
1289
|
+
}
|
|
1290
|
+
const encoder = new TextEncoder();
|
|
1291
|
+
const data = encoder.encode(text);
|
|
1292
|
+
const passphrase = await this.getWebEncryptionKey();
|
|
1293
|
+
const keyMaterial = await window.crypto.subtle.importKey(
|
|
1294
|
+
"raw",
|
|
1295
|
+
encoder.encode(passphrase),
|
|
1296
|
+
"PBKDF2",
|
|
1297
|
+
false,
|
|
1298
|
+
["deriveBits", "deriveKey"]
|
|
1299
|
+
);
|
|
1300
|
+
const key = await window.crypto.subtle.deriveKey(
|
|
1301
|
+
{
|
|
1302
|
+
name: "PBKDF2",
|
|
1303
|
+
salt: encoder.encode("lanonasis-api-key-salt"),
|
|
1304
|
+
iterations: 1e5,
|
|
1305
|
+
hash: "SHA-256"
|
|
1306
|
+
},
|
|
1307
|
+
keyMaterial,
|
|
1308
|
+
{ name: "AES-GCM", length: 256 },
|
|
1309
|
+
true,
|
|
1310
|
+
["encrypt", "decrypt"]
|
|
1311
|
+
);
|
|
1312
|
+
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
|
1313
|
+
const encrypted = await window.crypto.subtle.encrypt(
|
|
1314
|
+
{ name: "AES-GCM", iv },
|
|
1315
|
+
key,
|
|
1316
|
+
data
|
|
1317
|
+
);
|
|
1318
|
+
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
1319
|
+
combined.set(iv, 0);
|
|
1320
|
+
combined.set(new Uint8Array(encrypted), iv.length);
|
|
1321
|
+
return this.base64Encode(combined);
|
|
1322
|
+
}
|
|
1323
|
+
async decrypt(encrypted) {
|
|
1324
|
+
if (typeof window === "undefined" || !window.crypto?.subtle) {
|
|
1325
|
+
const decoder2 = new TextDecoder();
|
|
1326
|
+
return decoder2.decode(this.base64Decode(encrypted));
|
|
1327
|
+
}
|
|
1328
|
+
const bytes = this.base64Decode(encrypted);
|
|
1329
|
+
const iv = bytes.slice(0, 12);
|
|
1330
|
+
const data = bytes.slice(12);
|
|
1331
|
+
const encoder = new TextEncoder();
|
|
1332
|
+
const decoder = new TextDecoder();
|
|
1333
|
+
const passphrase = await this.getWebEncryptionKey();
|
|
1334
|
+
const keyMaterial = await window.crypto.subtle.importKey(
|
|
1335
|
+
"raw",
|
|
1336
|
+
encoder.encode(passphrase),
|
|
1337
|
+
"PBKDF2",
|
|
1338
|
+
false,
|
|
1339
|
+
["deriveBits", "deriveKey"]
|
|
1340
|
+
);
|
|
1341
|
+
const key = await window.crypto.subtle.deriveKey(
|
|
1342
|
+
{
|
|
1343
|
+
name: "PBKDF2",
|
|
1344
|
+
salt: encoder.encode("lanonasis-api-key-salt"),
|
|
1345
|
+
iterations: 1e5,
|
|
1346
|
+
hash: "SHA-256"
|
|
1347
|
+
},
|
|
1348
|
+
keyMaterial,
|
|
1349
|
+
{ name: "AES-GCM", length: 256 },
|
|
1350
|
+
true,
|
|
1351
|
+
["encrypt", "decrypt"]
|
|
1352
|
+
);
|
|
1353
|
+
const decrypted = await window.crypto.subtle.decrypt(
|
|
1354
|
+
{ name: "AES-GCM", iv },
|
|
1355
|
+
key,
|
|
1356
|
+
data
|
|
1357
|
+
);
|
|
1358
|
+
return decoder.decode(decrypted);
|
|
1359
|
+
}
|
|
1360
|
+
async getWebEncryptionKey() {
|
|
1361
|
+
const existing = localStorage.getItem(this.webEncryptionKeyStorage);
|
|
1362
|
+
if (existing) return existing;
|
|
1363
|
+
const buf = new Uint8Array(32);
|
|
1364
|
+
if (typeof window !== "undefined" && window.crypto?.getRandomValues) {
|
|
1365
|
+
window.crypto.getRandomValues(buf);
|
|
1366
|
+
}
|
|
1367
|
+
const raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1368
|
+
localStorage.setItem(this.webEncryptionKeyStorage, raw);
|
|
1369
|
+
return raw;
|
|
1370
|
+
}
|
|
1371
|
+
base64Encode(bytes) {
|
|
1372
|
+
let binary = "";
|
|
1373
|
+
bytes.forEach((b) => {
|
|
1374
|
+
binary += String.fromCharCode(b);
|
|
1375
|
+
});
|
|
1376
|
+
return btoa(binary);
|
|
1377
|
+
}
|
|
1378
|
+
base64Decode(value) {
|
|
1379
|
+
const binary = atob(value);
|
|
1380
|
+
const bytes = new Uint8Array(binary.length);
|
|
1381
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1382
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1383
|
+
}
|
|
1384
|
+
return bytes;
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
// src/client/mcp-client.ts
|
|
1389
|
+
var import_cross_fetch4 = __toESM(require("cross-fetch"), 1);
|
|
1390
|
+
|
|
1391
|
+
// src/flows/apikey-flow.ts
|
|
1392
|
+
var import_cross_fetch3 = __toESM(require("cross-fetch"), 1);
|
|
1393
|
+
var APIKeyFlow = class extends BaseOAuthFlow {
|
|
1394
|
+
constructor(apiKey, authBaseUrl = "https://mcp.lanonasis.com") {
|
|
1395
|
+
super({
|
|
1396
|
+
clientId: "api-key-client",
|
|
1397
|
+
authBaseUrl
|
|
1398
|
+
});
|
|
1399
|
+
this.apiKey = apiKey;
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* "Authenticate" by returning the API key as a virtual token
|
|
1403
|
+
* The API key will be used directly in request headers
|
|
1404
|
+
*/
|
|
1405
|
+
async authenticate() {
|
|
1406
|
+
if (!this.apiKey || !this.apiKey.startsWith("lano_") && !this.apiKey.startsWith("vx_")) {
|
|
1407
|
+
throw new Error(
|
|
1408
|
+
'Invalid API key format. Must start with "lano_" or "vx_". Please regenerate your API key from the dashboard.'
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
if (this.apiKey.startsWith("vx_")) {
|
|
1412
|
+
console.warn(
|
|
1413
|
+
'\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.'
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
access_token: this.apiKey,
|
|
1418
|
+
token_type: "api-key",
|
|
1419
|
+
expires_in: 0,
|
|
1420
|
+
// API keys don't expire
|
|
1421
|
+
issued_at: Date.now()
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* API keys don't need refresh
|
|
1426
|
+
*/
|
|
1427
|
+
async refreshToken(refreshToken) {
|
|
1428
|
+
throw new Error("API keys do not support token refresh");
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Optional: Validate API key by making a test request
|
|
1432
|
+
*/
|
|
1433
|
+
async validateAPIKey() {
|
|
1434
|
+
try {
|
|
1435
|
+
const response = await (0, import_cross_fetch3.default)(`${this.config.authBaseUrl}/api/v1/health`, {
|
|
1436
|
+
headers: {
|
|
1437
|
+
"x-api-key": this.apiKey
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
return response.ok;
|
|
1441
|
+
} catch (error) {
|
|
1442
|
+
console.error("API key validation failed:", error);
|
|
1443
|
+
return false;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
|
|
1074
1448
|
// src/client/mcp-client.ts
|
|
1075
1449
|
var MCPClient = class {
|
|
1076
1450
|
constructor(config = {}) {
|
|
1451
|
+
// ← NEW: Track auth mode
|
|
1077
1452
|
this.ws = null;
|
|
1078
1453
|
this.eventSource = null;
|
|
1079
1454
|
this.accessToken = null;
|
|
@@ -1083,31 +1458,47 @@ var MCPClient = class {
|
|
|
1083
1458
|
autoRefresh: true,
|
|
1084
1459
|
...config
|
|
1085
1460
|
};
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1461
|
+
const defaultStorage = typeof window !== "undefined" ? new TokenStorageWeb() : new TokenStorage();
|
|
1462
|
+
this.tokenStorage = config.tokenStorage || defaultStorage;
|
|
1463
|
+
this.authMode = config.apiKey ? "apikey" : "oauth";
|
|
1464
|
+
if (this.authMode === "apikey") {
|
|
1465
|
+
this.authFlow = new APIKeyFlow(
|
|
1466
|
+
config.apiKey,
|
|
1467
|
+
config.authBaseUrl || "https://mcp.lanonasis.com"
|
|
1468
|
+
);
|
|
1089
1469
|
} else {
|
|
1090
|
-
this.
|
|
1470
|
+
if (this.isTerminal()) {
|
|
1471
|
+
this.authFlow = new TerminalOAuthFlow(config);
|
|
1472
|
+
} else {
|
|
1473
|
+
this.authFlow = new DesktopOAuthFlow(config);
|
|
1474
|
+
}
|
|
1091
1475
|
}
|
|
1092
1476
|
}
|
|
1093
1477
|
async connect() {
|
|
1094
1478
|
try {
|
|
1095
1479
|
let tokens = await this.tokenStorage.retrieve();
|
|
1096
|
-
if (
|
|
1097
|
-
if (tokens
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1480
|
+
if (this.authMode === "apikey") {
|
|
1481
|
+
if (!tokens) {
|
|
1482
|
+
tokens = await this.authenticate();
|
|
1483
|
+
}
|
|
1484
|
+
this.accessToken = tokens.access_token;
|
|
1485
|
+
} else {
|
|
1486
|
+
if (!tokens || this.tokenStorage.isTokenExpired(tokens)) {
|
|
1487
|
+
if (tokens?.refresh_token) {
|
|
1488
|
+
try {
|
|
1489
|
+
tokens = await this.authFlow.refreshToken(tokens.refresh_token);
|
|
1490
|
+
await this.tokenStorage.store(tokens);
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
tokens = await this.authenticate();
|
|
1493
|
+
}
|
|
1494
|
+
} else {
|
|
1102
1495
|
tokens = await this.authenticate();
|
|
1103
1496
|
}
|
|
1104
|
-
} else {
|
|
1105
|
-
tokens = await this.authenticate();
|
|
1106
1497
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1498
|
+
this.accessToken = tokens.access_token;
|
|
1499
|
+
if (this.config.autoRefresh && tokens.expires_in) {
|
|
1500
|
+
this.scheduleTokenRefresh(tokens);
|
|
1501
|
+
}
|
|
1111
1502
|
}
|
|
1112
1503
|
await this.establishConnection();
|
|
1113
1504
|
} catch (error) {
|
|
@@ -1127,6 +1518,10 @@ var MCPClient = class {
|
|
|
1127
1518
|
if (!tokens) {
|
|
1128
1519
|
throw new Error("Not authenticated");
|
|
1129
1520
|
}
|
|
1521
|
+
if (this.authMode === "apikey") {
|
|
1522
|
+
this.accessToken = tokens.access_token;
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1130
1525
|
if (this.tokenStorage.isTokenExpired(tokens)) {
|
|
1131
1526
|
if (tokens.refresh_token) {
|
|
1132
1527
|
try {
|
|
@@ -1183,11 +1578,19 @@ var MCPClient = class {
|
|
|
1183
1578
|
this.ws = new WebSocket(wsUrl.toString());
|
|
1184
1579
|
} else {
|
|
1185
1580
|
const { default: WS } = await import("ws");
|
|
1186
|
-
this.
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1581
|
+
if (this.authMode === "apikey") {
|
|
1582
|
+
this.ws = new WS(wsUrl.toString(), {
|
|
1583
|
+
headers: {
|
|
1584
|
+
"x-api-key": this.accessToken
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
} else {
|
|
1588
|
+
this.ws = new WS(wsUrl.toString(), {
|
|
1589
|
+
headers: {
|
|
1590
|
+
"Authorization": `Bearer ${this.accessToken}`
|
|
1591
|
+
}
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1191
1594
|
}
|
|
1192
1595
|
return new Promise((resolve, reject) => {
|
|
1193
1596
|
if (!this.ws) {
|
|
@@ -1221,11 +1624,19 @@ var MCPClient = class {
|
|
|
1221
1624
|
} else {
|
|
1222
1625
|
const EventSourceModule = await import("eventsource");
|
|
1223
1626
|
const ES = EventSourceModule.default || EventSourceModule;
|
|
1224
|
-
this.
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1627
|
+
if (this.authMode === "apikey") {
|
|
1628
|
+
this.eventSource = new ES(sseUrl.toString(), {
|
|
1629
|
+
headers: {
|
|
1630
|
+
"x-api-key": this.accessToken
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
} else {
|
|
1634
|
+
this.eventSource = new ES(sseUrl.toString(), {
|
|
1635
|
+
headers: {
|
|
1636
|
+
"Authorization": `Bearer ${this.accessToken}`
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1229
1640
|
}
|
|
1230
1641
|
this.eventSource.onopen = () => {
|
|
1231
1642
|
console.log("MCP SSE connected");
|
|
@@ -1255,12 +1666,17 @@ var MCPClient = class {
|
|
|
1255
1666
|
if (!this.accessToken) {
|
|
1256
1667
|
throw new Error("Not authenticated");
|
|
1257
1668
|
}
|
|
1258
|
-
const
|
|
1669
|
+
const headers = {
|
|
1670
|
+
"Content-Type": "application/json"
|
|
1671
|
+
};
|
|
1672
|
+
if (this.authMode === "apikey") {
|
|
1673
|
+
headers["x-api-key"] = this.accessToken;
|
|
1674
|
+
} else {
|
|
1675
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
1676
|
+
}
|
|
1677
|
+
const response = await (0, import_cross_fetch4.default)(`${this.config.mcpEndpoint}/api`, {
|
|
1259
1678
|
method: "POST",
|
|
1260
|
-
headers
|
|
1261
|
-
"Authorization": `Bearer ${this.accessToken}`,
|
|
1262
|
-
"Content-Type": "application/json"
|
|
1263
|
-
},
|
|
1679
|
+
headers,
|
|
1264
1680
|
body: JSON.stringify({
|
|
1265
1681
|
jsonrpc: "2.0",
|
|
1266
1682
|
id: this.generateId(),
|
|
@@ -1269,6 +1685,9 @@ var MCPClient = class {
|
|
|
1269
1685
|
})
|
|
1270
1686
|
});
|
|
1271
1687
|
if (response.status === 401) {
|
|
1688
|
+
if (this.authMode === "apikey") {
|
|
1689
|
+
throw new Error("Invalid API key - please check your credentials");
|
|
1690
|
+
}
|
|
1272
1691
|
const tokens = await this.tokenStorage.retrieve();
|
|
1273
1692
|
if (tokens?.refresh_token) {
|
|
1274
1693
|
const newTokens = await this.authFlow.refreshToken(tokens.refresh_token);
|
|
@@ -1354,12 +1773,3 @@ var MCPClient = class {
|
|
|
1354
1773
|
return this.request("memory/delete", { id });
|
|
1355
1774
|
}
|
|
1356
1775
|
};
|
|
1357
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1358
|
-
0 && (module.exports = {
|
|
1359
|
-
ApiKeyStorage,
|
|
1360
|
-
BaseOAuthFlow,
|
|
1361
|
-
DesktopOAuthFlow,
|
|
1362
|
-
MCPClient,
|
|
1363
|
-
TerminalOAuthFlow,
|
|
1364
|
-
TokenStorage
|
|
1365
|
-
});
|