@lanonasis/oauth-client 1.2.0 → 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.
@@ -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 || "mcp:read mcp:write api_keys:manage";
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 window !== "undefined" && window.crypto) {
30
- window.crypto.getRandomValues(array);
33
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
34
+ crypto.getRandomValues(array);
31
35
  } else {
32
- const crypto = __require("crypto");
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
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
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 fetch(`${this.authBaseUrl}/oauth/device`, {
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 fetch(`${this.authBaseUrl}/oauth/token`, {
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 window !== "undefined" && window.crypto) {
207
- window.crypto.getRandomValues(array);
210
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
211
+ crypto.getRandomValues(array);
208
212
  } else {
209
- const crypto = __require("crypto");
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
- if (typeof window !== "undefined" && window.crypto?.subtle) {
216
- const encoder = new TextEncoder();
217
- const data = encoder.encode(verifier);
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({
@@ -423,13 +424,13 @@ var TokenStorage = class {
423
424
  const fs = __require("fs").promises;
424
425
  const path = __require("path");
425
426
  const os = __require("os");
426
- const crypto = __require("crypto");
427
+ const crypto2 = __require("crypto");
427
428
  const configDir = path.join(os.homedir(), ".lanonasis");
428
429
  const tokenFile = path.join(configDir, "mcp-tokens.enc");
429
430
  await fs.mkdir(configDir, { recursive: true });
430
431
  const key = this.getFileEncryptionKey();
431
- const iv = crypto.randomBytes(16);
432
- const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
432
+ const iv = crypto2.randomBytes(16);
433
+ const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
433
434
  let encrypted = cipher.update(tokenString, "utf8", "hex");
434
435
  encrypted += cipher.final("hex");
435
436
  const authTag = cipher.getAuthTag().toString("hex");
@@ -441,7 +442,7 @@ var TokenStorage = class {
441
442
  const fs = __require("fs").promises;
442
443
  const path = __require("path");
443
444
  const os = __require("os");
444
- const crypto = __require("crypto");
445
+ const crypto2 = __require("crypto");
445
446
  const tokenFile = path.join(os.homedir(), ".lanonasis", "mcp-tokens.enc");
446
447
  try {
447
448
  const data = await fs.readFile(tokenFile, "utf8");
@@ -451,7 +452,7 @@ var TokenStorage = class {
451
452
  const [ivHex, authTagHex, encrypted] = parts;
452
453
  const iv = Buffer.from(ivHex, "hex");
453
454
  const authTag = Buffer.from(authTagHex, "hex");
454
- const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
455
+ const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
455
456
  decipher.setAuthTag(authTag);
456
457
  let decrypted = decipher.update(encrypted, "hex", "utf8");
457
458
  decrypted += decipher.final("utf8");
@@ -460,7 +461,7 @@ var TokenStorage = class {
460
461
  if (parts.length === 2) {
461
462
  const [ivHex, encrypted] = parts;
462
463
  const iv = Buffer.from(ivHex, "hex");
463
- const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
464
+ const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
464
465
  let decrypted = decipher.update(encrypted, "hex", "utf8");
465
466
  decrypted += decipher.final("utf8");
466
467
  return decrypted;
@@ -482,17 +483,17 @@ var TokenStorage = class {
482
483
  }
483
484
  }
484
485
  getFileEncryptionKey() {
485
- const crypto = __require("crypto");
486
+ const crypto2 = __require("crypto");
486
487
  const os = __require("os");
487
488
  const machineId = os.hostname() + os.userInfo().username;
488
489
  const salt = "lanonasis-mcp-oauth-2024";
489
- return crypto.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
490
+ return crypto2.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
490
491
  }
491
492
  async encrypt(text) {
492
493
  if (typeof window === "undefined" || !window.crypto?.subtle) {
493
494
  const encoder2 = new TextEncoder();
494
495
  const data2 = encoder2.encode(text);
495
- return btoa(String.fromCharCode(...data2));
496
+ return this.base64Encode(data2);
496
497
  }
497
498
  const encoder = new TextEncoder();
498
499
  const data = encoder.encode(text);
@@ -525,23 +526,15 @@ var TokenStorage = class {
525
526
  const combined = new Uint8Array(iv.length + encrypted.byteLength);
526
527
  combined.set(iv, 0);
527
528
  combined.set(new Uint8Array(encrypted), iv.length);
528
- return btoa(String.fromCharCode(...combined));
529
+ return this.base64Encode(combined);
529
530
  }
530
531
  async decrypt(encrypted) {
531
532
  if (typeof window === "undefined" || !window.crypto?.subtle) {
532
- const binary2 = atob(encrypted);
533
- const bytes2 = new Uint8Array(binary2.length);
534
- for (let i = 0; i < binary2.length; i++) {
535
- bytes2[i] = binary2.charCodeAt(i);
536
- }
533
+ const bytes2 = this.base64Decode(encrypted);
537
534
  const decoder2 = new TextDecoder();
538
535
  return decoder2.decode(bytes2);
539
536
  }
540
- const binary = atob(encrypted);
541
- const bytes = new Uint8Array(binary.length);
542
- for (let i = 0; i < binary.length; i++) {
543
- bytes[i] = binary.charCodeAt(i);
544
- }
537
+ const bytes = this.base64Decode(encrypted);
545
538
  const iv = bytes.slice(0, 12);
546
539
  const data = bytes.slice(12);
547
540
  const encoder = new TextEncoder();
@@ -582,6 +575,33 @@ var TokenStorage = class {
582
575
  isMobile() {
583
576
  return typeof window !== "undefined" && window.SecureStorage !== void 0;
584
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
+ }
585
605
  async getWebEncryptionKey() {
586
606
  const existing = typeof localStorage !== "undefined" ? localStorage.getItem(this.webEncryptionKeyStorage) : null;
587
607
  if (existing) {
@@ -593,7 +613,8 @@ var TokenStorage = class {
593
613
  window.crypto.getRandomValues(buf);
594
614
  raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
595
615
  } else {
596
- raw = `${navigator.userAgent}-${Math.random().toString(36).slice(2)}-${Date.now()}`;
616
+ const ua = typeof navigator !== "undefined" ? navigator.userAgent : "node";
617
+ raw = `${ua}-${Math.random().toString(36).slice(2)}-${Date.now()}`;
597
618
  }
598
619
  if (typeof localStorage !== "undefined") {
599
620
  localStorage.setItem(this.webEncryptionKeyStorage, raw);
@@ -805,13 +826,13 @@ var ApiKeyStorage = class {
805
826
  const fs = __require("fs").promises;
806
827
  const path = __require("path");
807
828
  const os = __require("os");
808
- const crypto = __require("crypto");
829
+ const crypto2 = __require("crypto");
809
830
  const configDir = path.join(os.homedir(), ".lanonasis");
810
831
  const keyFile = path.join(configDir, "api-key.enc");
811
832
  await fs.mkdir(configDir, { recursive: true, mode: 448 });
812
833
  const key = this.getFileEncryptionKey();
813
- const iv = crypto.randomBytes(16);
814
- const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
834
+ const iv = crypto2.randomBytes(16);
835
+ const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
815
836
  let encrypted = cipher.update(keyString, "utf8", "hex");
816
837
  encrypted += cipher.final("hex");
817
838
  const authTag = cipher.getAuthTag();
@@ -823,7 +844,7 @@ var ApiKeyStorage = class {
823
844
  const fs = __require("fs").promises;
824
845
  const path = __require("path");
825
846
  const os = __require("os");
826
- const crypto = __require("crypto");
847
+ const crypto2 = __require("crypto");
827
848
  const keyFile = path.join(os.homedir(), ".lanonasis", "api-key.enc");
828
849
  try {
829
850
  const data = await fs.readFile(keyFile, "utf8");
@@ -834,7 +855,7 @@ var ApiKeyStorage = class {
834
855
  const key = this.getFileEncryptionKey();
835
856
  const iv = Buffer.from(ivHex, "hex");
836
857
  const authTag = Buffer.from(authTagHex, "hex");
837
- const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
858
+ const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
838
859
  decipher.setAuthTag(authTag);
839
860
  let decrypted = decipher.update(encrypted, "hex", "utf8");
840
861
  decrypted += decipher.final("utf8");
@@ -878,18 +899,18 @@ var ApiKeyStorage = class {
878
899
  }
879
900
  }
880
901
  getFileEncryptionKey() {
881
- const crypto = __require("crypto");
902
+ const crypto2 = __require("crypto");
882
903
  const os = __require("os");
883
904
  const machineId = os.hostname() + os.userInfo().username;
884
905
  const salt = "lanonasis-mcp-api-key-2024";
885
- return crypto.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
906
+ return crypto2.pbkdf2Sync(machineId, salt, 1e5, 32, "sha256");
886
907
  }
887
908
  // ==================== Web Encryption ====================
888
909
  async encrypt(text) {
889
910
  if (typeof window === "undefined" || !window.crypto || !window.crypto.subtle) {
890
911
  const encoder = new TextEncoder();
891
912
  const data = encoder.encode(text);
892
- return btoa(String.fromCharCode(...data));
913
+ return this.base64Encode(data);
893
914
  }
894
915
  try {
895
916
  const encoder = new TextEncoder();
@@ -928,16 +949,12 @@ var ApiKeyStorage = class {
928
949
  console.error("Web encryption failed:", error);
929
950
  const encoder = new TextEncoder();
930
951
  const data = encoder.encode(text);
931
- return btoa(String.fromCharCode(...data));
952
+ return this.base64Encode(data);
932
953
  }
933
954
  }
934
955
  async decrypt(encrypted) {
935
956
  if (typeof window === "undefined" || !window.crypto || !window.crypto.subtle) {
936
- const binary = atob(encrypted);
937
- const bytes = new Uint8Array(binary.length);
938
- for (let i = 0; i < binary.length; i++) {
939
- bytes[i] = binary.charCodeAt(i);
940
- }
957
+ const bytes = this.base64Decode(encrypted);
941
958
  const decoder = new TextDecoder();
942
959
  return decoder.decode(bytes);
943
960
  }
@@ -979,11 +996,7 @@ var ApiKeyStorage = class {
979
996
  return decoder.decode(decrypted);
980
997
  } catch (error) {
981
998
  console.error("Web decryption failed:", error);
982
- const binary = atob(encrypted);
983
- const bytes = new Uint8Array(binary.length);
984
- for (let i = 0; i < binary.length; i++) {
985
- bytes[i] = binary.charCodeAt(i);
986
- }
999
+ const bytes = this.base64Decode(encrypted);
987
1000
  const decoder = new TextDecoder();
988
1001
  return decoder.decode(bytes);
989
1002
  }
@@ -999,7 +1012,8 @@ var ApiKeyStorage = class {
999
1012
  window.crypto.getRandomValues(buf);
1000
1013
  raw = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
1001
1014
  } else {
1002
- raw = `${navigator.userAgent}-${Math.random().toString(36).slice(2)}-${Date.now()}`;
1015
+ const ua = typeof navigator !== "undefined" ? navigator.userAgent : "node";
1016
+ raw = `${ua}-${Math.random().toString(36).slice(2)}-${Date.now()}`;
1003
1017
  }
1004
1018
  if (typeof localStorage !== "undefined") {
1005
1019
  localStorage.setItem(this.webEncryptionKeyStorage, raw);
@@ -1016,6 +1030,33 @@ var ApiKeyStorage = class {
1016
1030
  isMobile() {
1017
1031
  return typeof window !== "undefined" && window.SecureStorage !== void 0;
1018
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
+ }
1019
1060
  /**
1020
1061
  * Normalize API keys to a SHA-256 hex digest.
1021
1062
  * Accepts pre-hashed input and lowercases it to prevent double hashing.
@@ -1040,7 +1081,279 @@ var ApiKeyStorage = class {
1040
1081
  }
1041
1082
  };
1042
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
+
1043
1355
  // src/flows/apikey-flow.ts
1356
+ import fetch3 from "cross-fetch";
1044
1357
  var APIKeyFlow = class extends BaseOAuthFlow {
1045
1358
  constructor(apiKey, authBaseUrl = "https://mcp.lanonasis.com") {
1046
1359
  super({
@@ -1083,7 +1396,7 @@ var APIKeyFlow = class extends BaseOAuthFlow {
1083
1396
  */
1084
1397
  async validateAPIKey() {
1085
1398
  try {
1086
- const response = await fetch(`${this.config.authBaseUrl}/api/v1/health`, {
1399
+ const response = await fetch3(`${this.config.authBaseUrl}/api/v1/health`, {
1087
1400
  headers: {
1088
1401
  "x-api-key": this.apiKey
1089
1402
  }
@@ -1109,7 +1422,8 @@ var MCPClient = class {
1109
1422
  autoRefresh: true,
1110
1423
  ...config
1111
1424
  };
1112
- this.tokenStorage = new TokenStorage();
1425
+ const defaultStorage = typeof window !== "undefined" ? new TokenStorageWeb() : new TokenStorage();
1426
+ this.tokenStorage = config.tokenStorage || defaultStorage;
1113
1427
  this.authMode = config.apiKey ? "apikey" : "oauth";
1114
1428
  if (this.authMode === "apikey") {
1115
1429
  this.authFlow = new APIKeyFlow(
@@ -1324,7 +1638,7 @@ var MCPClient = class {
1324
1638
  } else {
1325
1639
  headers["Authorization"] = `Bearer ${this.accessToken}`;
1326
1640
  }
1327
- const response = await fetch(`${this.config.mcpEndpoint}/api`, {
1641
+ const response = await fetch4(`${this.config.mcpEndpoint}/api`, {
1328
1642
  method: "POST",
1329
1643
  headers,
1330
1644
  body: JSON.stringify({
@@ -1425,9 +1739,11 @@ var MCPClient = class {
1425
1739
  };
1426
1740
  export {
1427
1741
  ApiKeyStorage,
1742
+ ApiKeyStorageWeb,
1428
1743
  BaseOAuthFlow,
1429
1744
  DesktopOAuthFlow,
1430
1745
  MCPClient,
1431
1746
  TerminalOAuthFlow,
1432
- TokenStorage
1747
+ TokenStorage,
1748
+ TokenStorageWeb
1433
1749
  };