@n24q02m/hull 0.0.0-stage → 0.4.0-beta.9

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.
Files changed (231) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +166 -2
  3. package/build/auth/credential-form.d.ts +163 -0
  4. package/build/auth/credential-form.d.ts.map +1 -0
  5. package/build/auth/credential-form.js +1869 -0
  6. package/build/auth/credential-form.js.map +1 -0
  7. package/build/auth/delegated-oauth-app.d.ts +90 -0
  8. package/build/auth/delegated-oauth-app.d.ts.map +1 -0
  9. package/build/auth/delegated-oauth-app.js +881 -0
  10. package/build/auth/delegated-oauth-app.js.map +1 -0
  11. package/build/auth/index.d.ts +12 -0
  12. package/build/auth/index.d.ts.map +1 -0
  13. package/build/auth/index.js +12 -0
  14. package/build/auth/index.js.map +1 -0
  15. package/build/auth/local-oauth-app.d.ts +152 -0
  16. package/build/auth/local-oauth-app.d.ts.map +1 -0
  17. package/build/auth/local-oauth-app.js +975 -0
  18. package/build/auth/local-oauth-app.js.map +1 -0
  19. package/build/auth/relay-login.d.ts +46 -0
  20. package/build/auth/relay-login.d.ts.map +1 -0
  21. package/build/auth/relay-login.js +197 -0
  22. package/build/auth/relay-login.js.map +1 -0
  23. package/build/auth/router.d.ts +18 -0
  24. package/build/auth/router.d.ts.map +1 -0
  25. package/build/auth/router.js +62 -0
  26. package/build/auth/router.js.map +1 -0
  27. package/build/auth/session-store.d.ts +49 -0
  28. package/build/auth/session-store.d.ts.map +1 -0
  29. package/build/auth/session-store.js +94 -0
  30. package/build/auth/session-store.js.map +1 -0
  31. package/build/auth/stable-sub.d.ts +27 -0
  32. package/build/auth/stable-sub.d.ts.map +1 -0
  33. package/build/auth/stable-sub.js +38 -0
  34. package/build/auth/stable-sub.js.map +1 -0
  35. package/build/auth/well-known.d.ts +4 -0
  36. package/build/auth/well-known.d.ts.map +1 -0
  37. package/build/auth/well-known.js +26 -0
  38. package/build/auth/well-known.js.map +1 -0
  39. package/build/cli/build-cli.d.ts +97 -0
  40. package/build/cli/build-cli.d.ts.map +1 -0
  41. package/build/cli/build-cli.js +309 -0
  42. package/build/cli/build-cli.js.map +1 -0
  43. package/build/cli/index.d.ts +2 -0
  44. package/build/cli/index.d.ts.map +1 -0
  45. package/build/cli/index.js +2 -0
  46. package/build/cli/index.js.map +1 -0
  47. package/build/crypto/aes.d.ts +8 -0
  48. package/build/crypto/aes.d.ts.map +1 -0
  49. package/build/crypto/aes.js +27 -0
  50. package/build/crypto/aes.js.map +1 -0
  51. package/build/crypto/ecdh.d.ts +5 -0
  52. package/build/crypto/ecdh.d.ts.map +1 -0
  53. package/build/crypto/ecdh.js +16 -0
  54. package/build/crypto/ecdh.js.map +1 -0
  55. package/build/crypto/index.d.ts +4 -0
  56. package/build/crypto/index.d.ts.map +1 -0
  57. package/build/crypto/index.js +4 -0
  58. package/build/crypto/index.js.map +1 -0
  59. package/build/crypto/kdf.d.ts +15 -0
  60. package/build/crypto/kdf.d.ts.map +1 -0
  61. package/build/crypto/kdf.js +26 -0
  62. package/build/crypto/kdf.js.map +1 -0
  63. package/build/index.d.ts +19 -0
  64. package/build/index.d.ts.map +1 -0
  65. package/build/index.js +28 -0
  66. package/build/index.js.map +1 -0
  67. package/build/lifecycle/lock.d.ts +68 -0
  68. package/build/lifecycle/lock.d.ts.map +1 -0
  69. package/build/lifecycle/lock.js +173 -0
  70. package/build/lifecycle/lock.js.map +1 -0
  71. package/build/oauth/cache.d.ts +22 -0
  72. package/build/oauth/cache.d.ts.map +1 -0
  73. package/build/oauth/cache.js +24 -0
  74. package/build/oauth/cache.js.map +1 -0
  75. package/build/oauth/index.d.ts +6 -0
  76. package/build/oauth/index.d.ts.map +1 -0
  77. package/build/oauth/index.js +6 -0
  78. package/build/oauth/index.js.map +1 -0
  79. package/build/oauth/jwt-issuer.d.ts +87 -0
  80. package/build/oauth/jwt-issuer.d.ts.map +1 -0
  81. package/build/oauth/jwt-issuer.js +205 -0
  82. package/build/oauth/jwt-issuer.js.map +1 -0
  83. package/build/oauth/pkce.d.ts +10 -0
  84. package/build/oauth/pkce.d.ts.map +1 -0
  85. package/build/oauth/pkce.js +34 -0
  86. package/build/oauth/pkce.js.map +1 -0
  87. package/build/oauth/provider.d.ts +52 -0
  88. package/build/oauth/provider.d.ts.map +1 -0
  89. package/build/oauth/provider.js +98 -0
  90. package/build/oauth/provider.js.map +1 -0
  91. package/build/oauth/user-store.d.ts +25 -0
  92. package/build/oauth/user-store.d.ts.map +1 -0
  93. package/build/oauth/user-store.js +85 -0
  94. package/build/oauth/user-store.js.map +1 -0
  95. package/build/relay/browser.d.ts +15 -0
  96. package/build/relay/browser.d.ts.map +1 -0
  97. package/build/relay/browser.js +121 -0
  98. package/build/relay/browser.js.map +1 -0
  99. package/build/relay/client.d.ts +36 -0
  100. package/build/relay/client.d.ts.map +1 -0
  101. package/build/relay/client.js +135 -0
  102. package/build/relay/client.js.map +1 -0
  103. package/build/relay/session.d.ts +12 -0
  104. package/build/relay/session.d.ts.map +1 -0
  105. package/build/relay/session.js +55 -0
  106. package/build/relay/session.js.map +1 -0
  107. package/build/relay/tool-helpers.d.ts +85 -0
  108. package/build/relay/tool-helpers.d.ts.map +1 -0
  109. package/build/relay/tool-helpers.js +103 -0
  110. package/build/relay/tool-helpers.js.map +1 -0
  111. package/build/relay/wordlist.d.ts +2 -0
  112. package/build/relay/wordlist.d.ts.map +1 -0
  113. package/build/relay/wordlist.js +7782 -0
  114. package/build/relay/wordlist.js.map +1 -0
  115. package/build/schema/types.d.ts +67 -0
  116. package/build/schema/types.d.ts.map +1 -0
  117. package/build/schema/types.js +2 -0
  118. package/build/schema/types.js.map +1 -0
  119. package/build/storage/atomic-write.d.ts +8 -0
  120. package/build/storage/atomic-write.d.ts.map +1 -0
  121. package/build/storage/atomic-write.js +26 -0
  122. package/build/storage/atomic-write.js.map +1 -0
  123. package/build/storage/backends.d.ts +74 -0
  124. package/build/storage/backends.d.ts.map +1 -0
  125. package/build/storage/backends.js +193 -0
  126. package/build/storage/backends.js.map +1 -0
  127. package/build/storage/config-file.d.ts +35 -0
  128. package/build/storage/config-file.d.ts.map +1 -0
  129. package/build/storage/config-file.js +259 -0
  130. package/build/storage/config-file.js.map +1 -0
  131. package/build/storage/credential-store.d.ts +71 -0
  132. package/build/storage/credential-store.d.ts.map +1 -0
  133. package/build/storage/credential-store.js +91 -0
  134. package/build/storage/credential-store.js.map +1 -0
  135. package/build/storage/data-dir.d.ts +58 -0
  136. package/build/storage/data-dir.d.ts.map +1 -0
  137. package/build/storage/data-dir.js +79 -0
  138. package/build/storage/data-dir.js.map +1 -0
  139. package/build/storage/encryption.d.ts +9 -0
  140. package/build/storage/encryption.d.ts.map +1 -0
  141. package/build/storage/encryption.js +27 -0
  142. package/build/storage/encryption.js.map +1 -0
  143. package/build/storage/home-dir.d.ts +11 -0
  144. package/build/storage/home-dir.d.ts.map +1 -0
  145. package/build/storage/home-dir.js +18 -0
  146. package/build/storage/home-dir.js.map +1 -0
  147. package/build/storage/index.d.ts +12 -0
  148. package/build/storage/index.d.ts.map +1 -0
  149. package/build/storage/index.js +15 -0
  150. package/build/storage/index.js.map +1 -0
  151. package/build/storage/machine-id.d.ts +4 -0
  152. package/build/storage/machine-id.d.ts.map +1 -0
  153. package/build/storage/machine-id.js +73 -0
  154. package/build/storage/machine-id.js.map +1 -0
  155. package/build/storage/mode.d.ts +23 -0
  156. package/build/storage/mode.d.ts.map +1 -0
  157. package/build/storage/mode.js +46 -0
  158. package/build/storage/mode.js.map +1 -0
  159. package/build/storage/per-plugin-store.d.ts +65 -0
  160. package/build/storage/per-plugin-store.d.ts.map +1 -0
  161. package/build/storage/per-plugin-store.js +194 -0
  162. package/build/storage/per-plugin-store.js.map +1 -0
  163. package/build/storage/resolver.d.ts +7 -0
  164. package/build/storage/resolver.d.ts.map +1 -0
  165. package/build/storage/resolver.js +38 -0
  166. package/build/storage/resolver.js.map +1 -0
  167. package/build/storage/session-lock.d.ts +19 -0
  168. package/build/storage/session-lock.d.ts.map +1 -0
  169. package/build/storage/session-lock.js +105 -0
  170. package/build/storage/session-lock.js.map +1 -0
  171. package/build/token-auth/authenticator.d.ts +70 -0
  172. package/build/token-auth/authenticator.d.ts.map +1 -0
  173. package/build/token-auth/authenticator.js +146 -0
  174. package/build/token-auth/authenticator.js.map +1 -0
  175. package/build/token-auth/cli.d.ts +38 -0
  176. package/build/token-auth/cli.d.ts.map +1 -0
  177. package/build/token-auth/cli.js +192 -0
  178. package/build/token-auth/cli.js.map +1 -0
  179. package/build/token-auth/context.d.ts +30 -0
  180. package/build/token-auth/context.d.ts.map +1 -0
  181. package/build/token-auth/context.js +32 -0
  182. package/build/token-auth/context.js.map +1 -0
  183. package/build/token-auth/http-guard.d.ts +26 -0
  184. package/build/token-auth/http-guard.d.ts.map +1 -0
  185. package/build/token-auth/http-guard.js +51 -0
  186. package/build/token-auth/http-guard.js.map +1 -0
  187. package/build/token-auth/index.d.ts +14 -0
  188. package/build/token-auth/index.d.ts.map +1 -0
  189. package/build/token-auth/index.js +14 -0
  190. package/build/token-auth/index.js.map +1 -0
  191. package/build/token-auth/limiter.d.ts +20 -0
  192. package/build/token-auth/limiter.d.ts.map +1 -0
  193. package/build/token-auth/limiter.js +41 -0
  194. package/build/token-auth/limiter.js.map +1 -0
  195. package/build/token-auth/loopback.d.ts +17 -0
  196. package/build/token-auth/loopback.d.ts.map +1 -0
  197. package/build/token-auth/loopback.js +62 -0
  198. package/build/token-auth/loopback.js.map +1 -0
  199. package/build/token-auth/py-compat.d.ts +24 -0
  200. package/build/token-auth/py-compat.d.ts.map +1 -0
  201. package/build/token-auth/py-compat.js +76 -0
  202. package/build/token-auth/py-compat.js.map +1 -0
  203. package/build/token-auth/tokens.d.ts +36 -0
  204. package/build/token-auth/tokens.d.ts.map +1 -0
  205. package/build/token-auth/tokens.js +115 -0
  206. package/build/token-auth/tokens.js.map +1 -0
  207. package/build/token-auth/users.d.ts +62 -0
  208. package/build/token-auth/users.d.ts.map +1 -0
  209. package/build/token-auth/users.js +174 -0
  210. package/build/token-auth/users.js.map +1 -0
  211. package/build/transport/cache.d.ts +19 -0
  212. package/build/transport/cache.d.ts.map +1 -0
  213. package/build/transport/cache.js +76 -0
  214. package/build/transport/cache.js.map +1 -0
  215. package/build/transport/index.d.ts +5 -0
  216. package/build/transport/index.d.ts.map +1 -0
  217. package/build/transport/index.js +4 -0
  218. package/build/transport/index.js.map +1 -0
  219. package/build/transport/local-server.d.ts +173 -0
  220. package/build/transport/local-server.d.ts.map +1 -0
  221. package/build/transport/local-server.js +457 -0
  222. package/build/transport/local-server.js.map +1 -0
  223. package/build/transport/oauth-middleware.d.ts +45 -0
  224. package/build/transport/oauth-middleware.d.ts.map +1 -0
  225. package/build/transport/oauth-middleware.js +86 -0
  226. package/build/transport/oauth-middleware.js.map +1 -0
  227. package/build/transport/streamable-http.d.ts +54 -0
  228. package/build/transport/streamable-http.d.ts.map +1 -0
  229. package/build/transport/streamable-http.js +57 -0
  230. package/build/transport/streamable-http.js.map +1 -0
  231. package/package.json +107 -4
@@ -0,0 +1,259 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { chmod, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import envPaths from 'env-paths';
5
+ import { decryptData, deriveFileKey, derivePassphraseKey, encryptData, LEGACY_EXPORT_SALT, LEGACY_PBKDF2_ITERATIONS, LEGACY_SALT, PBKDF2_ITERATIONS } from './encryption.js';
6
+ import { getMachineId, getUsername } from './machine-id.js';
7
+ const paths = envPaths('mcp', { suffix: '' });
8
+ const DEFAULT_CONFIG_PATH = join(paths.config, 'config.enc');
9
+ const SALT_SIZE = 16;
10
+ function validateAuth(config) {
11
+ return Object.values(config).every((value) => typeof value === 'string');
12
+ }
13
+ function validateServer(server) {
14
+ if (!server || typeof server !== 'object')
15
+ return false;
16
+ return validateAuth(server);
17
+ }
18
+ function validateSchema(data) {
19
+ if (!data || typeof data !== 'object')
20
+ return false;
21
+ const d = data;
22
+ if (d.version !== 1)
23
+ return false;
24
+ if (!d.servers || typeof d.servers !== 'object')
25
+ return false;
26
+ const servers = d.servers;
27
+ for (const serverName in servers) {
28
+ if (typeof serverName !== 'string' || !validateServer(servers[serverName])) {
29
+ return false;
30
+ }
31
+ }
32
+ return true;
33
+ }
34
+ // Allow overriding config path for testing
35
+ let configPathOverride = null;
36
+ export function setConfigPath(path) {
37
+ configPathOverride = path;
38
+ }
39
+ function getConfigPath() {
40
+ return configPathOverride ?? DEFAULT_CONFIG_PATH;
41
+ }
42
+ const MAX_RETRIES = 3;
43
+ const BASE_DELAY_MS = 100;
44
+ async function withRetry(fn) {
45
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
46
+ try {
47
+ return await fn();
48
+ }
49
+ catch (err) {
50
+ const isLocked = err instanceof Error && 'code' in err && err.code === 'EBUSY';
51
+ if (!isLocked || attempt === MAX_RETRIES - 1)
52
+ throw err;
53
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS * 2 ** attempt));
54
+ }
55
+ }
56
+ throw new Error('Unreachable');
57
+ }
58
+ // Cache the derived file key to avoid expensive PBKDF2 iterations on every config read/write.
59
+ // This significantly speeds up successive configuration accesses within the same process.
60
+ let cachedKey = null;
61
+ let cachedSalt = null;
62
+ export function clearKeyCacheForTesting() {
63
+ cachedKey = null;
64
+ cachedSalt = null;
65
+ }
66
+ async function getKey(salt) {
67
+ if (cachedKey && cachedSalt && Buffer.from(cachedSalt).equals(salt))
68
+ return cachedKey;
69
+ const [machineId, username] = await Promise.all([getMachineId(), getUsername()]);
70
+ cachedKey = await deriveFileKey(machineId, username, salt);
71
+ cachedSalt = new Uint8Array(salt);
72
+ return cachedKey;
73
+ }
74
+ async function loadStore() {
75
+ const configPath = getConfigPath();
76
+ if (!existsSync(configPath)) {
77
+ return { version: 1, servers: {} };
78
+ }
79
+ const [machineId, username] = await Promise.all([getMachineId(), getUsername()]);
80
+ const data = await readFile(configPath);
81
+ // Try new format: [16-byte salt][iv][ciphertext]
82
+ if (data.length >= SALT_SIZE + 12) {
83
+ const salt = data.subarray(0, SALT_SIZE);
84
+ const payload = data.subarray(SALT_SIZE);
85
+ try {
86
+ const key = await getKey(salt);
87
+ const json = await decryptData(key, payload);
88
+ const store = JSON.parse(json);
89
+ if (!validateSchema(store)) {
90
+ throw new Error('Invalid config schema');
91
+ }
92
+ return store;
93
+ }
94
+ catch (_err) {
95
+ // Fall through to legacy check
96
+ }
97
+ }
98
+ // Try legacy format: [iv][ciphertext] using LEGACY_SALT
99
+ try {
100
+ const legacyKey = await deriveFileKey(machineId, username, LEGACY_SALT, PBKDF2_ITERATIONS);
101
+ const json = await decryptData(legacyKey, data);
102
+ const store = JSON.parse(json);
103
+ if (!validateSchema(store)) {
104
+ throw new Error('Invalid config schema');
105
+ }
106
+ // Auto-migrate to current iterations AND new salted format
107
+ await saveStore(store);
108
+ return store;
109
+ }
110
+ catch (err) {
111
+ try {
112
+ const legacyKey = await deriveFileKey(machineId, username, LEGACY_SALT, LEGACY_PBKDF2_ITERATIONS);
113
+ const json = await decryptData(legacyKey, data);
114
+ const store = JSON.parse(json);
115
+ if (!validateSchema(store)) {
116
+ throw new Error('Invalid config schema');
117
+ }
118
+ // Auto-migrate
119
+ await saveStore(store);
120
+ return store;
121
+ }
122
+ catch {
123
+ throw err;
124
+ }
125
+ }
126
+ }
127
+ async function saveStore(store) {
128
+ const configPath = getConfigPath();
129
+ const dir = dirname(configPath);
130
+ if (!existsSync(dir)) {
131
+ await mkdir(dir, { recursive: true, mode: 0o700 });
132
+ if (process.platform !== 'win32') {
133
+ await chmod(dir, 0o700);
134
+ }
135
+ }
136
+ let salt = cachedSalt;
137
+ if (!salt) {
138
+ salt = crypto.getRandomValues(new Uint8Array(SALT_SIZE));
139
+ }
140
+ const key = await getKey(salt);
141
+ const encrypted = await encryptData(key, JSON.stringify(store));
142
+ const finalData = Buffer.concat([Buffer.from(salt), encrypted]);
143
+ await withRetry(() => writeFile(configPath, finalData, { mode: 0o600 }));
144
+ }
145
+ /**
146
+ * Schedule a process exit so an MCP client can respawn the server and pick up
147
+ * fresh credentials from disk. Only relevant for stdio-mode servers whose
148
+ * client supervises restart. HTTP-mode servers update credentials in-process
149
+ * and MUST NOT call this — an exit mid-OAuth-device-code flow kills the user's
150
+ * verification window. Skipped under vitest (NODE_ENV=test) and when
151
+ * MCP_NO_RELOAD is set.
152
+ */
153
+ export function scheduleReloadExit() {
154
+ if (process.env.NODE_ENV !== 'test' && !process.env.MCP_NO_RELOAD) {
155
+ setTimeout(() => process.exit(0), 1000).unref();
156
+ }
157
+ }
158
+ export async function readConfig(serverName) {
159
+ const store = await loadStore();
160
+ return store.servers[serverName] ?? null;
161
+ }
162
+ export async function writeConfig(serverName, config) {
163
+ const store = await loadStore();
164
+ store.servers[serverName] = config;
165
+ await saveStore(store);
166
+ }
167
+ export async function deleteConfig(serverName) {
168
+ const store = await loadStore();
169
+ delete store.servers[serverName];
170
+ const configPath = getConfigPath();
171
+ if (Object.keys(store.servers).length === 0) {
172
+ if (existsSync(configPath)) {
173
+ await unlink(configPath);
174
+ }
175
+ }
176
+ else {
177
+ await saveStore(store);
178
+ }
179
+ }
180
+ /**
181
+ * Metadata flag set by `markSetupComplete` after a successful relay-form
182
+ * submission (POST /authorize). `runHttpServer`/`isSchemaComplete` read this
183
+ * to distinguish "user has submitted the form" from "config.enc has values
184
+ * written by a peer-share or partial bootstrap path". Lives alongside the
185
+ * user's normal credential keys in the same per-server config dict.
186
+ */
187
+ export const SETUP_COMPLETE_KEY = '_setup_complete';
188
+ /**
189
+ * Set the `_setup_complete` flag in `serverName`'s config. Idempotent.
190
+ * Creates a new entry with just the flag if no prior config exists (useful
191
+ * for all-optional schemas where the user may submit an empty form).
192
+ *
193
+ * Strict equality semantics in `isSchemaComplete` rely on this writing the
194
+ * literal string `"true"`.
195
+ */
196
+ export async function markSetupComplete(serverName) {
197
+ const existing = (await readConfig(serverName)) ?? {};
198
+ existing[SETUP_COMPLETE_KEY] = 'true';
199
+ await writeConfig(serverName, existing);
200
+ }
201
+ export async function listConfigs() {
202
+ const store = await loadStore();
203
+ return Object.keys(store.servers);
204
+ }
205
+ export async function exportConfig(passphrase) {
206
+ const store = await loadStore();
207
+ const salt = crypto.getRandomValues(new Uint8Array(SALT_SIZE));
208
+ const key = await derivePassphraseKey(passphrase, salt);
209
+ const encrypted = await encryptData(key, JSON.stringify(store));
210
+ return Buffer.concat([Buffer.from(salt), encrypted]);
211
+ }
212
+ export async function importConfig(passphrase, data) {
213
+ let json;
214
+ // Try new format: [16-byte salt][iv][ciphertext]
215
+ if (data.length >= SALT_SIZE + 12) {
216
+ const salt = data.subarray(0, SALT_SIZE);
217
+ const payload = data.subarray(SALT_SIZE);
218
+ try {
219
+ const key = await derivePassphraseKey(passphrase, salt, PBKDF2_ITERATIONS);
220
+ json = await decryptData(key, payload);
221
+ }
222
+ catch (_err) {
223
+ try {
224
+ const legacyKey = await derivePassphraseKey(passphrase, salt, LEGACY_PBKDF2_ITERATIONS);
225
+ json = await decryptData(legacyKey, payload);
226
+ }
227
+ catch {
228
+ // Fall through to legacy format check
229
+ }
230
+ }
231
+ }
232
+ if (!json) {
233
+ // Try legacy format: [iv][ciphertext] using LEGACY_EXPORT_SALT
234
+ try {
235
+ const key = await derivePassphraseKey(passphrase, LEGACY_EXPORT_SALT, PBKDF2_ITERATIONS);
236
+ json = await decryptData(key, data);
237
+ }
238
+ catch (err) {
239
+ try {
240
+ const legacyKey = await derivePassphraseKey(passphrase, LEGACY_EXPORT_SALT, LEGACY_PBKDF2_ITERATIONS);
241
+ json = await decryptData(legacyKey, data);
242
+ }
243
+ catch {
244
+ throw err;
245
+ }
246
+ }
247
+ }
248
+ const imported = JSON.parse(json);
249
+ if (!validateSchema(imported)) {
250
+ throw new Error('Invalid config schema in imported data');
251
+ }
252
+ const store = await loadStore();
253
+ // Merge imported servers into local config
254
+ for (const [name, config] of Object.entries(imported.servers)) {
255
+ store.servers[name] = config;
256
+ }
257
+ await saveStore(store);
258
+ }
259
+ //# sourceMappingURL=config-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.js","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Unified single-user credential store router.
3
+ *
4
+ * Single-user MCP credentials historically lived in the shared, machine-bound
5
+ * `config.enc` (`storage/config-file.ts`), keyed by the console name. But the
6
+ * CLI built-ins (`buildCli` config/doctor) and the whole Python stack read and
7
+ * write via `PerPluginStore`, keyed by the plugin slug -- so the two never saw
8
+ * each other's data: a TS server would save credentials the CLI then reported
9
+ * as "not configured".
10
+ *
11
+ * This module unifies single-user storage onto `PerPluginStore` (matching the
12
+ * wet-mcp Python `credential_state.py` cutover) while keeping a READ fallback
13
+ * to the legacy `config.enc`, so credentials already on disk keep working with
14
+ * ZERO re-auth. On the next WRITE the credentials migrate to the per-plugin
15
+ * store; DELETE clears BOTH stores.
16
+ *
17
+ * WRITE -> PerPluginStore(pluginName).save(config)
18
+ * READ -> PerPluginStore(pluginName).load() ?? legacy readConfig(serverName)
19
+ * DELETE -> PerPluginStore(pluginName).clear() + legacy deleteConfig(serverName)
20
+ *
21
+ * `pluginName` defaults to `serverName` with a trailing `-mcp` stripped -- the
22
+ * same convention `buildCli` and core-py `build_cli` use -- so single-user
23
+ * credentials land under the SAME slug the CLI reads and the multi-user
24
+ * per-sub store keys on (`better-email`, `better-notion`, `wet`, ...). Pass it
25
+ * explicitly when the slug differs from that default (telegram saves under
26
+ * `"telegram"` though its console name is `"better-telegram-mcp"`).
27
+ *
28
+ * Multi-user HTTP storage (per-JWT-sub) is unaffected: it uses
29
+ * `PerPluginStore(pluginName, sub)`, a distinct on-disk key from the
30
+ * single-user `PerPluginStore(pluginName)` this module manages.
31
+ */
32
+ /**
33
+ * Derive the credential-storage plugin slug from a console/server name by
34
+ * stripping a trailing `-mcp`. Shared with `buildCli` so the CLI reads exactly
35
+ * the slug single-user writes land under.
36
+ */
37
+ export declare function pluginNameForServer(serverName: string): string;
38
+ /**
39
+ * Read a server's single-user credential config.
40
+ *
41
+ * Prefers the per-plugin store; falls back to the legacy shared `config.enc`
42
+ * so credentials written by the pre-unification path stay readable with no
43
+ * re-auth. Returns `null` when neither store has an entry.
44
+ */
45
+ export declare function readStoredConfig(serverName: string, pluginName?: string): Promise<Record<string, string> | null>;
46
+ /**
47
+ * Write a server's single-user credential config to the per-plugin store.
48
+ *
49
+ * Full replace, mirroring the legacy `writeConfig` contract and the wet-mcp
50
+ * Python `save()` call: callers that need to preserve prior keys read first and
51
+ * merge (see `markSetupComplete`). The write also serves as the migration
52
+ * trigger -- an existing legacy `config.enc` entry is superseded because
53
+ * `readStoredConfig` prefers the per-plugin store from here on.
54
+ */
55
+ export declare function writeStoredConfig(serverName: string, config: Record<string, string>, pluginName?: string): Promise<void>;
56
+ /**
57
+ * Delete a server's single-user credential config from BOTH stores.
58
+ *
59
+ * Clears the per-plugin store and the legacy `config.enc` entry so a reset
60
+ * leaves nothing behind in either backend.
61
+ */
62
+ export declare function deleteStoredConfig(serverName: string, pluginName?: string): Promise<void>;
63
+ /**
64
+ * Set the persistent `_setup_complete` flag in a server's single-user config,
65
+ * preserving any existing keys. Reads through the unified path (so a legacy
66
+ * entry is migrated forward) and writes the merged result to the per-plugin
67
+ * store. Creates an entry with just the flag when nothing exists (useful for
68
+ * all-optional schemas where the user may submit an empty form).
69
+ */
70
+ export declare function markSetupComplete(serverName: string, pluginName?: string): Promise<void>;
71
+ //# sourceMappingURL=credential-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-store.d.ts","sourceRoot":"","sources":["../../src/storage/credential-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AASH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAwC,GACnD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAMxC;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,UAAU,GAAE,MAAwC,GACnD,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAwC,GACnD,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAwC,GACnD,OAAO,CAAC,IAAI,CAAC,CAIf"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Unified single-user credential store router.
3
+ *
4
+ * Single-user MCP credentials historically lived in the shared, machine-bound
5
+ * `config.enc` (`storage/config-file.ts`), keyed by the console name. But the
6
+ * CLI built-ins (`buildCli` config/doctor) and the whole Python stack read and
7
+ * write via `PerPluginStore`, keyed by the plugin slug -- so the two never saw
8
+ * each other's data: a TS server would save credentials the CLI then reported
9
+ * as "not configured".
10
+ *
11
+ * This module unifies single-user storage onto `PerPluginStore` (matching the
12
+ * wet-mcp Python `credential_state.py` cutover) while keeping a READ fallback
13
+ * to the legacy `config.enc`, so credentials already on disk keep working with
14
+ * ZERO re-auth. On the next WRITE the credentials migrate to the per-plugin
15
+ * store; DELETE clears BOTH stores.
16
+ *
17
+ * WRITE -> PerPluginStore(pluginName).save(config)
18
+ * READ -> PerPluginStore(pluginName).load() ?? legacy readConfig(serverName)
19
+ * DELETE -> PerPluginStore(pluginName).clear() + legacy deleteConfig(serverName)
20
+ *
21
+ * `pluginName` defaults to `serverName` with a trailing `-mcp` stripped -- the
22
+ * same convention `buildCli` and core-py `build_cli` use -- so single-user
23
+ * credentials land under the SAME slug the CLI reads and the multi-user
24
+ * per-sub store keys on (`better-email`, `better-notion`, `wet`, ...). Pass it
25
+ * explicitly when the slug differs from that default (telegram saves under
26
+ * `"telegram"` though its console name is `"better-telegram-mcp"`).
27
+ *
28
+ * Multi-user HTTP storage (per-JWT-sub) is unaffected: it uses
29
+ * `PerPluginStore(pluginName, sub)`, a distinct on-disk key from the
30
+ * single-user `PerPluginStore(pluginName)` this module manages.
31
+ */
32
+ import { deleteConfig as deleteLegacyConfig, readConfig as readLegacyConfig, SETUP_COMPLETE_KEY } from './config-file.js';
33
+ import { PerPluginStore } from './per-plugin-store.js';
34
+ /**
35
+ * Derive the credential-storage plugin slug from a console/server name by
36
+ * stripping a trailing `-mcp`. Shared with `buildCli` so the CLI reads exactly
37
+ * the slug single-user writes land under.
38
+ */
39
+ export function pluginNameForServer(serverName) {
40
+ return serverName.replace(/-mcp$/, '');
41
+ }
42
+ /**
43
+ * Read a server's single-user credential config.
44
+ *
45
+ * Prefers the per-plugin store; falls back to the legacy shared `config.enc`
46
+ * so credentials written by the pre-unification path stay readable with no
47
+ * re-auth. Returns `null` when neither store has an entry.
48
+ */
49
+ export async function readStoredConfig(serverName, pluginName = pluginNameForServer(serverName)) {
50
+ const fromStore = await new PerPluginStore(pluginName).load();
51
+ // Single-user blobs are always string maps (written by writeStoredConfig /
52
+ // markSetupComplete below); the cast restates that invariant.
53
+ if (fromStore !== null)
54
+ return fromStore;
55
+ return readLegacyConfig(serverName);
56
+ }
57
+ /**
58
+ * Write a server's single-user credential config to the per-plugin store.
59
+ *
60
+ * Full replace, mirroring the legacy `writeConfig` contract and the wet-mcp
61
+ * Python `save()` call: callers that need to preserve prior keys read first and
62
+ * merge (see `markSetupComplete`). The write also serves as the migration
63
+ * trigger -- an existing legacy `config.enc` entry is superseded because
64
+ * `readStoredConfig` prefers the per-plugin store from here on.
65
+ */
66
+ export async function writeStoredConfig(serverName, config, pluginName = pluginNameForServer(serverName)) {
67
+ await new PerPluginStore(pluginName).save(config);
68
+ }
69
+ /**
70
+ * Delete a server's single-user credential config from BOTH stores.
71
+ *
72
+ * Clears the per-plugin store and the legacy `config.enc` entry so a reset
73
+ * leaves nothing behind in either backend.
74
+ */
75
+ export async function deleteStoredConfig(serverName, pluginName = pluginNameForServer(serverName)) {
76
+ await new PerPluginStore(pluginName).clear();
77
+ await deleteLegacyConfig(serverName);
78
+ }
79
+ /**
80
+ * Set the persistent `_setup_complete` flag in a server's single-user config,
81
+ * preserving any existing keys. Reads through the unified path (so a legacy
82
+ * entry is migrated forward) and writes the merged result to the per-plugin
83
+ * store. Creates an entry with just the flag when nothing exists (useful for
84
+ * all-optional schemas where the user may submit an empty form).
85
+ */
86
+ export async function markSetupComplete(serverName, pluginName = pluginNameForServer(serverName)) {
87
+ const existing = (await readStoredConfig(serverName, pluginName)) ?? {};
88
+ existing[SETUP_COMPLETE_KEY] = 'true';
89
+ await writeStoredConfig(serverName, existing, pluginName);
90
+ }
91
+ //# sourceMappingURL=credential-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../../src/storage/credential-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,YAAY,IAAI,kBAAkB,EAClC,UAAU,IAAI,gBAAgB,EAC9B,kBAAkB,EACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,OAAO,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;AACxC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,UAAkB,EAClB,UAAU,GAAW,mBAAmB,CAAC,UAAU,CAAC;IAEpD,MAAM,SAAS,GAAG,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7D,2EAA2E;IAC3E,8DAA8D;IAC9D,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,SAAmC,CAAA;IAClE,OAAO,gBAAgB,CAAC,UAAU,CAAC,CAAA;AACrC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAkB,EAClB,MAA8B,EAC9B,UAAU,GAAW,mBAAmB,CAAC,UAAU,CAAC;IAEpD,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AACnD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,UAAkB,EAClB,UAAU,GAAW,mBAAmB,CAAC,UAAU,CAAC;IAEpD,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,CAAA;IAC5C,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAA;AACtC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAkB,EAClB,UAAU,GAAW,mBAAmB,CAAC,UAAU,CAAC;IAEpD,MAAM,QAAQ,GAAG,CAAC,MAAM,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAA;IACvE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAA;IACrC,MAAM,iBAAiB,CAAC,UAAU,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAA;AAC3D,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * On-disk data directory layout for per-plugin credential storage.
3
+ *
4
+ * Current layout (written by every save):
5
+ *
6
+ * ~/.<plugin>/config.json single-user blob
7
+ * ~/.<plugin>/subs/<sub>/config.json multi-user (per-sub) blob
8
+ * ~/.<plugin>/.secret single-user machine key (0600)
9
+ *
10
+ * Earlier releases stored the same files under two longer names. They stay
11
+ * READABLE so credentials already on disk keep working with zero re-auth, in
12
+ * this order (newest naming first):
13
+ *
14
+ * 1. ~/.<plugin>/ current
15
+ * 2. ~/.<plugin>-mcp/ legacy, keyed by `<plugin>`
16
+ * 3. ~/.better-<plugin>-mcp/ legacy, keyed by `better-<plugin>`
17
+ *
18
+ * "Keyed by" is the plugin name the legacy writer used for key derivation: the
19
+ * single-user machine key is that directory's own `.secret`, and the multi-user
20
+ * scrypt salt is `<keyPlugin>:<sub>`. A `better-<x>-mcp` directory was written
21
+ * by a server whose plugin slug was `better-<x>`, so its salt carries the
22
+ * `better-` prefix too.
23
+ */
24
+ /** One place a credential blob may live on disk. */
25
+ export interface DataDirLocation {
26
+ /** Absolute directory path (resolved against the current home dir). */
27
+ readonly dir: string;
28
+ /** Plugin name the writer used for key derivation in this directory. */
29
+ readonly keyPlugin: string;
30
+ /** True for the read-only fallback layouts; false for the current one. */
31
+ readonly legacy: boolean;
32
+ }
33
+ /**
34
+ * Validate a plugin name for use as a single path component. Underscore,
35
+ * hyphen and dot are allowed; separators and `..` are not.
36
+ */
37
+ export declare function assertValidPluginName(pluginName: string): void;
38
+ /**
39
+ * Validate a multi-user sub for use as a single path component. The OAuth AS
40
+ * mints sub = token_urlsafe(), whose base64url alphabet includes `_` and `-`,
41
+ * so both must pass; `/` and `..` stay rejected.
42
+ */
43
+ export declare function assertValidSub(sub: string): void;
44
+ /** Directory name (relative to home) of the current layout: `.<plugin>`. */
45
+ export declare function dataDirName(pluginName: string): string;
46
+ /** Absolute path of the current data directory: `~/.<plugin>`. */
47
+ export declare function dataDir(pluginName: string): string;
48
+ /**
49
+ * Every location a plugin's credentials may live in, in read-precedence order:
50
+ * the current `~/.<plugin>/` first, then `~/.<plugin>-mcp/`, then
51
+ * `~/.better-<plugin>-mcp/`.
52
+ */
53
+ export declare function dataDirCandidates(pluginName: string): DataDirLocation[];
54
+ /** Only the legacy (read-only fallback) locations, in read-precedence order. */
55
+ export declare function legacyDataDirs(pluginName: string): DataDirLocation[];
56
+ /** Path of the credential blob inside a given data directory. */
57
+ export declare function blobPathIn(dir: string, sub: string | null): string;
58
+ //# sourceMappingURL=data-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-dir.d.ts","sourceRoot":"","sources":["../../src/storage/data-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAMD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAI9D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAIhD;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,EAAE,CAQvE;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,EAAE,CAEpE;AAED,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAElE"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * On-disk data directory layout for per-plugin credential storage.
3
+ *
4
+ * Current layout (written by every save):
5
+ *
6
+ * ~/.<plugin>/config.json single-user blob
7
+ * ~/.<plugin>/subs/<sub>/config.json multi-user (per-sub) blob
8
+ * ~/.<plugin>/.secret single-user machine key (0600)
9
+ *
10
+ * Earlier releases stored the same files under two longer names. They stay
11
+ * READABLE so credentials already on disk keep working with zero re-auth, in
12
+ * this order (newest naming first):
13
+ *
14
+ * 1. ~/.<plugin>/ current
15
+ * 2. ~/.<plugin>-mcp/ legacy, keyed by `<plugin>`
16
+ * 3. ~/.better-<plugin>-mcp/ legacy, keyed by `better-<plugin>`
17
+ *
18
+ * "Keyed by" is the plugin name the legacy writer used for key derivation: the
19
+ * single-user machine key is that directory's own `.secret`, and the multi-user
20
+ * scrypt salt is `<keyPlugin>:<sub>`. A `better-<x>-mcp` directory was written
21
+ * by a server whose plugin slug was `better-<x>`, so its salt carries the
22
+ * `better-` prefix too.
23
+ */
24
+ import { join } from 'node:path';
25
+ import { getHomeDir } from './home-dir.js';
26
+ // Not global: a /g flag makes .test() stateful across calls (lastIndex), which
27
+ // could let a second input bypass validation.
28
+ const UNSAFE = /[^a-zA-Z0-9._-]/;
29
+ /**
30
+ * Validate a plugin name for use as a single path component. Underscore,
31
+ * hyphen and dot are allowed; separators and `..` are not.
32
+ */
33
+ export function assertValidPluginName(pluginName) {
34
+ if (!pluginName || UNSAFE.test(pluginName) || pluginName.includes('..')) {
35
+ throw new Error('Invalid pluginName');
36
+ }
37
+ }
38
+ /**
39
+ * Validate a multi-user sub for use as a single path component. The OAuth AS
40
+ * mints sub = token_urlsafe(), whose base64url alphabet includes `_` and `-`,
41
+ * so both must pass; `/` and `..` stay rejected.
42
+ */
43
+ export function assertValidSub(sub) {
44
+ if (!sub || UNSAFE.test(sub) || sub.includes('..')) {
45
+ throw new Error('Invalid sub');
46
+ }
47
+ }
48
+ /** Directory name (relative to home) of the current layout: `.<plugin>`. */
49
+ export function dataDirName(pluginName) {
50
+ return `.${pluginName}`;
51
+ }
52
+ /** Absolute path of the current data directory: `~/.<plugin>`. */
53
+ export function dataDir(pluginName) {
54
+ assertValidPluginName(pluginName);
55
+ return join(getHomeDir(), dataDirName(pluginName));
56
+ }
57
+ /**
58
+ * Every location a plugin's credentials may live in, in read-precedence order:
59
+ * the current `~/.<plugin>/` first, then `~/.<plugin>-mcp/`, then
60
+ * `~/.better-<plugin>-mcp/`.
61
+ */
62
+ export function dataDirCandidates(pluginName) {
63
+ assertValidPluginName(pluginName);
64
+ const home = getHomeDir();
65
+ return [
66
+ { dir: join(home, dataDirName(pluginName)), keyPlugin: pluginName, legacy: false },
67
+ { dir: join(home, `.${pluginName}-mcp`), keyPlugin: pluginName, legacy: true },
68
+ { dir: join(home, `.better-${pluginName}-mcp`), keyPlugin: `better-${pluginName}`, legacy: true }
69
+ ];
70
+ }
71
+ /** Only the legacy (read-only fallback) locations, in read-precedence order. */
72
+ export function legacyDataDirs(pluginName) {
73
+ return dataDirCandidates(pluginName).filter((loc) => loc.legacy);
74
+ }
75
+ /** Path of the credential blob inside a given data directory. */
76
+ export function blobPathIn(dir, sub) {
77
+ return sub ? join(dir, 'subs', sub, 'config.json') : join(dir, 'config.json');
78
+ }
79
+ //# sourceMappingURL=data-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-dir.js","sourceRoot":"","sources":["../../src/storage/data-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAY1C,+EAA+E;AAC/E,8CAA8C;AAC9C,MAAM,MAAM,GAAG,iBAAiB,CAAA;AAEhC;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAAkB;IACtD,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;IACvC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAA;IAChC,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,IAAI,UAAU,EAAE,CAAA;AACzB,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,OAAO,CAAC,UAAkB;IACxC,qBAAqB,CAAC,UAAU,CAAC,CAAA;IACjC,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAA;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB;IAClD,qBAAqB,CAAC,UAAU,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAA;IACzB,OAAO;QACL,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE;QAClF,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,UAAU,MAAM,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE;QAC9E,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,UAAU,MAAM,CAAC,EAAE,SAAS,EAAE,UAAU,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;KAClG,CAAA;AACH,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,OAAO,iBAAiB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AAClE,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,GAAkB;IACxD,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;AAC/E,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare const LEGACY_SALT: Uint8Array<ArrayBuffer>;
2
+ export declare const LEGACY_EXPORT_SALT: Uint8Array<ArrayBuffer>;
3
+ export declare const PBKDF2_ITERATIONS = 600000;
4
+ export declare const LEGACY_PBKDF2_ITERATIONS = 100000;
5
+ export declare function deriveFileKey(machineId: string, username: string, salt: Uint8Array, iterations?: number): Promise<CryptoKey>;
6
+ export declare function derivePassphraseKey(passphrase: string, salt: Uint8Array, iterations?: number): Promise<CryptoKey>;
7
+ export declare function encryptData(key: CryptoKey, plaintext: string): Promise<Buffer>;
8
+ export declare function decryptData(key: CryptoKey, data: Buffer): Promise<string>;
9
+ //# sourceMappingURL=encryption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/storage/encryption.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,yBAAqC,CAAA;AAC7D,eAAO,MAAM,kBAAkB,yBAAqC,CAAA;AACpE,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,wBAAwB,SAAU,CAAA;AAE/C,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,UAAU,EAChB,UAAU,GAAE,MAA0B,GACrC,OAAO,CAAC,SAAS,CAAC,CAepB;AAED,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,UAAU,EAChB,UAAU,GAAE,MAA0B,GACrC,OAAO,CAAC,SAAS,CAAC,CASpB;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKpF;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS/E"}
@@ -0,0 +1,27 @@
1
+ const encoder = new TextEncoder();
2
+ const decoder = new TextDecoder();
3
+ export const LEGACY_SALT = encoder.encode('mcp-relay-config');
4
+ export const LEGACY_EXPORT_SALT = encoder.encode('mcp-relay-export');
5
+ export const PBKDF2_ITERATIONS = 600_000;
6
+ export const LEGACY_PBKDF2_ITERATIONS = 100_000;
7
+ export async function deriveFileKey(machineId, username, salt, iterations = PBKDF2_ITERATIONS) {
8
+ const keyMaterial = await crypto.subtle.importKey('raw', encoder.encode(`${machineId}:${username}`), 'PBKDF2', false, ['deriveKey']);
9
+ return crypto.subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-256', salt: salt, iterations }, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
10
+ }
11
+ export async function derivePassphraseKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
12
+ const keyMaterial = await crypto.subtle.importKey('raw', encoder.encode(passphrase), 'PBKDF2', false, ['deriveKey']);
13
+ return crypto.subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-256', salt: salt, iterations }, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
14
+ }
15
+ export async function encryptData(key, plaintext) {
16
+ const iv = crypto.getRandomValues(new Uint8Array(12));
17
+ const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoder.encode(plaintext));
18
+ // Format: [12-byte IV][ciphertext+tag]
19
+ return Buffer.concat([iv, Buffer.from(encrypted)]);
20
+ }
21
+ export async function decryptData(key, data) {
22
+ const iv = data.subarray(0, 12);
23
+ const ciphertext = data.subarray(12);
24
+ const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(iv) }, key, new Uint8Array(ciphertext));
25
+ return decoder.decode(decrypted);
26
+ }
27
+ //# sourceMappingURL=encryption.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/storage/encryption.ts"],"names":[],"mappings":"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"}