@n24q02m/mcp-relay-core 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -0
- package/build/index.js.map +1 -1
- package/build/relay/browser.d.ts +15 -0
- package/build/relay/browser.d.ts.map +1 -0
- package/build/relay/browser.js +78 -0
- package/build/relay/browser.js.map +1 -0
- package/build/storage/index.d.ts +2 -0
- package/build/storage/index.d.ts.map +1 -1
- package/build/storage/index.js +2 -0
- package/build/storage/index.js.map +1 -1
- package/build/storage/mode.d.ts +23 -0
- package/build/storage/mode.d.ts.map +1 -0
- package/build/storage/mode.js +43 -0
- package/build/storage/mode.js.map +1 -0
- package/build/storage/session-lock.d.ts +19 -0
- package/build/storage/session-lock.d.ts.map +1 -0
- package/build/storage/session-lock.js +94 -0
- package/build/storage/session-lock.js.map +1 -0
- package/package.json +1 -1
package/build/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from './crypto/index.js';
|
|
2
|
+
export { tryOpenBrowser } from './relay/browser.js';
|
|
2
3
|
export { createSession, generatePassphrase, pollForResponses, pollForResult, type RelaySession, sendMessage } from './relay/client.js';
|
|
3
4
|
export type * from './schema/types.js';
|
|
4
5
|
export { deleteConfig, exportConfig, importConfig, listConfigs, readConfig, writeConfig } from './storage/config-file.js';
|
|
6
|
+
export { clearMode, getMode, type ServerMode, setLocalMode } from './storage/mode.js';
|
|
5
7
|
export * from './storage/resolver.js';
|
|
8
|
+
export { acquireSessionLock, releaseSessionLock, type SessionInfo, writeSessionLock } from './storage/session-lock.js';
|
|
6
9
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,KAAK,YAAY,EACjB,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAC1B,mBAAmB,mBAAmB,CAAA;AACtC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACZ,MAAM,0BAA0B,CAAA;AACjC,cAAc,uBAAuB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,KAAK,YAAY,EACjB,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAC1B,mBAAmB,mBAAmB,CAAA;AACtC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACZ,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,cAAc,uBAAuB,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,WAAW,EAChB,gBAAgB,EACjB,MAAM,2BAA2B,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export * from './crypto/index.js';
|
|
2
|
+
export { tryOpenBrowser } from './relay/browser.js';
|
|
2
3
|
export { createSession, generatePassphrase, pollForResponses, pollForResult, sendMessage } from './relay/client.js';
|
|
3
4
|
export { deleteConfig, exportConfig, importConfig, listConfigs, readConfig, writeConfig } from './storage/config-file.js';
|
|
5
|
+
export { clearMode, getMode, setLocalMode } from './storage/mode.js';
|
|
4
6
|
export * from './storage/resolver.js';
|
|
7
|
+
export { acquireSessionLock, releaseSessionLock, writeSessionLock } from './storage/session-lock.js';
|
|
5
8
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EAEb,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACZ,MAAM,0BAA0B,CAAA;AACjC,cAAc,uBAAuB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EAEb,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACZ,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAmB,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,cAAc,uBAAuB,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAElB,gBAAgB,EACjB,MAAM,2BAA2B,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform browser opening with WSL detection.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Try to open URL in default browser. Returns true if likely succeeded.
|
|
6
|
+
*
|
|
7
|
+
* Detection order:
|
|
8
|
+
* 1. win32: `start` command
|
|
9
|
+
* 2. darwin: `open` command
|
|
10
|
+
* 3. linux: check WSL then `xdg-open`
|
|
11
|
+
*
|
|
12
|
+
* Never throws. Returns false on failure.
|
|
13
|
+
*/
|
|
14
|
+
export declare function tryOpenBrowser(url: string): Promise<boolean>;
|
|
15
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/relay/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuCH;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgClE"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform browser opening with WSL detection.
|
|
3
|
+
*/
|
|
4
|
+
import { exec } from 'node:child_process';
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
const execAsync = promisify(exec);
|
|
8
|
+
async function isWsl() {
|
|
9
|
+
try {
|
|
10
|
+
const version = await readFile('/proc/version', 'utf-8');
|
|
11
|
+
const lower = version.toLowerCase();
|
|
12
|
+
return lower.includes('microsoft') || lower.includes('wsl');
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function openInWsl(url) {
|
|
19
|
+
// Try wslview first (from wslu package, commonly available)
|
|
20
|
+
try {
|
|
21
|
+
await execAsync(`wslview ${JSON.stringify(url)}`);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
/* fall through */
|
|
26
|
+
}
|
|
27
|
+
// Fallback to cmd.exe /c start
|
|
28
|
+
try {
|
|
29
|
+
const escapedUrl = url.replace(/&/g, '^&');
|
|
30
|
+
await execAsync(`cmd.exe /c start ${JSON.stringify(escapedUrl)}`);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* fall through */
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Try to open URL in default browser. Returns true if likely succeeded.
|
|
40
|
+
*
|
|
41
|
+
* Detection order:
|
|
42
|
+
* 1. win32: `start` command
|
|
43
|
+
* 2. darwin: `open` command
|
|
44
|
+
* 3. linux: check WSL then `xdg-open`
|
|
45
|
+
*
|
|
46
|
+
* Never throws. Returns false on failure.
|
|
47
|
+
*/
|
|
48
|
+
export async function tryOpenBrowser(url) {
|
|
49
|
+
try {
|
|
50
|
+
// Validate URL to prevent shell injection
|
|
51
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const platform = process.platform;
|
|
55
|
+
const quotedUrl = JSON.stringify(url);
|
|
56
|
+
if (platform === 'win32') {
|
|
57
|
+
await execAsync(`start "" ${quotedUrl}`);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (platform === 'darwin') {
|
|
61
|
+
await execAsync(`open ${quotedUrl}`);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
// linux
|
|
65
|
+
if (await isWsl()) {
|
|
66
|
+
const result = await openInWsl(url);
|
|
67
|
+
if (result)
|
|
68
|
+
return true;
|
|
69
|
+
// Fall through to xdg-open
|
|
70
|
+
}
|
|
71
|
+
await execAsync(`xdg-open ${quotedUrl}`);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/relay/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;AAEjC,KAAK,UAAU,KAAK;IAClB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;QACxD,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;QACnC,OAAO,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,GAAW;IAClC,4DAA4D;IAC5D,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACjD,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IAED,+BAA+B;IAC/B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC1C,MAAM,SAAS,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;QACjE,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,IAAI,CAAC;QACH,0CAA0C;QAC1C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,OAAO,KAAK,CAAA;QACd,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;QACjC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QAErC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,MAAM,SAAS,CAAC,YAAY,SAAS,EAAE,CAAC,CAAA;YACxC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,SAAS,CAAC,QAAQ,SAAS,EAAE,CAAC,CAAA;YACpC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,QAAQ;QACR,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAA;YACnC,IAAI,MAAM;gBAAE,OAAO,IAAI,CAAA;YACvB,2BAA2B;QAC7B,CAAC;QAED,MAAM,SAAS,CAAC,YAAY,SAAS,EAAE,CAAC,CAAA;QACxC,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
|
package/build/storage/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { deleteConfig, exportConfig, importConfig, listConfigs, readConfig, setConfigPath, writeConfig } from './config-file.js';
|
|
2
2
|
export { decryptData, deriveFileKey, derivePassphraseKey, encryptData } from './encryption.js';
|
|
3
3
|
export { getMachineId, getUsername } from './machine-id.js';
|
|
4
|
+
export { clearMode, getMode, type ServerMode, setLocalMode } from './mode.js';
|
|
4
5
|
export { type ConfigSource, type ResolvedConfig, resolveConfig } from './resolver.js';
|
|
6
|
+
export { acquireSessionLock, releaseSessionLock, type SessionInfo, setLockDir, writeSessionLock } from './session-lock.js';
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACZ,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACZ,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACrF,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,WAAW,EAChB,UAAU,EACV,gBAAgB,EACjB,MAAM,mBAAmB,CAAA"}
|
package/build/storage/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { deleteConfig, exportConfig, importConfig, listConfigs, readConfig, setConfigPath, writeConfig } from './config-file.js';
|
|
2
2
|
export { decryptData, deriveFileKey, derivePassphraseKey, encryptData } from './encryption.js';
|
|
3
3
|
export { getMachineId, getUsername } from './machine-id.js';
|
|
4
|
+
export { clearMode, getMode, setLocalMode } from './mode.js';
|
|
4
5
|
export { resolveConfig } from './resolver.js';
|
|
6
|
+
export { acquireSessionLock, releaseSessionLock, setLockDir, writeSessionLock } from './session-lock.js';
|
|
5
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACZ,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAA0C,aAAa,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACZ,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAmB,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAA0C,aAAa,EAAE,MAAM,eAAe,CAAA;AACrF,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAElB,UAAU,EACV,gBAAgB,EACjB,MAAM,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server mode management: local-only vs relay-configured.
|
|
3
|
+
*
|
|
4
|
+
* Persists user's choice to skip relay permanently by storing a mode marker
|
|
5
|
+
* in the existing config.enc encrypted storage.
|
|
6
|
+
*/
|
|
7
|
+
export type ServerMode = 'local' | 'configured' | null;
|
|
8
|
+
/**
|
|
9
|
+
* Mark server as local-only (user explicitly skipped relay).
|
|
10
|
+
* Writes {"_mode": "local"} to config.enc[serverName].
|
|
11
|
+
*/
|
|
12
|
+
export declare function setLocalMode(serverName: string): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Get server mode.
|
|
15
|
+
* Returns "local" if local mode set, "configured" if has other keys, null if empty.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getMode(serverName: string): Promise<ServerMode>;
|
|
18
|
+
/**
|
|
19
|
+
* Remove mode marker (allows relay to trigger again).
|
|
20
|
+
* Deletes the server's config entry entirely.
|
|
21
|
+
*/
|
|
22
|
+
export declare function clearMode(serverName: string): Promise<void>;
|
|
23
|
+
//# sourceMappingURL=mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../../src/storage/mode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,YAAY,GAAG,IAAI,CAAA;AAEtD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBrE;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server mode management: local-only vs relay-configured.
|
|
3
|
+
*
|
|
4
|
+
* Persists user's choice to skip relay permanently by storing a mode marker
|
|
5
|
+
* in the existing config.enc encrypted storage.
|
|
6
|
+
*/
|
|
7
|
+
import { deleteConfig, readConfig, writeConfig } from './config-file.js';
|
|
8
|
+
const MODE_KEY = '_mode';
|
|
9
|
+
const LOCAL_MODE_VALUE = 'local';
|
|
10
|
+
/**
|
|
11
|
+
* Mark server as local-only (user explicitly skipped relay).
|
|
12
|
+
* Writes {"_mode": "local"} to config.enc[serverName].
|
|
13
|
+
*/
|
|
14
|
+
export async function setLocalMode(serverName) {
|
|
15
|
+
await writeConfig(serverName, { [MODE_KEY]: LOCAL_MODE_VALUE });
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get server mode.
|
|
19
|
+
* Returns "local" if local mode set, "configured" if has other keys, null if empty.
|
|
20
|
+
*/
|
|
21
|
+
export async function getMode(serverName) {
|
|
22
|
+
const config = await readConfig(serverName);
|
|
23
|
+
if (config === null) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (config[MODE_KEY] === LOCAL_MODE_VALUE) {
|
|
27
|
+
return 'local';
|
|
28
|
+
}
|
|
29
|
+
// Has keys other than _mode -> configured
|
|
30
|
+
const nonModeKeys = Object.keys(config).filter((k) => k !== MODE_KEY);
|
|
31
|
+
if (nonModeKeys.length > 0) {
|
|
32
|
+
return 'configured';
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Remove mode marker (allows relay to trigger again).
|
|
38
|
+
* Deletes the server's config entry entirely.
|
|
39
|
+
*/
|
|
40
|
+
export async function clearMode(serverName) {
|
|
41
|
+
await deleteConfig(serverName);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode.js","sourceRoot":"","sources":["../../src/storage/mode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAExE,MAAM,QAAQ,GAAG,OAAO,CAAA;AACxB,MAAM,gBAAgB,GAAG,OAAO,CAAA;AAIhC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB;IACnD,MAAM,WAAW,CAAC,UAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAA;AACjE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,UAAkB;IAC9C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAA;IAC3C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,gBAAgB,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,0CAA0C;IAC1C,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAA;IACrE,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,UAAkB;IAChD,MAAM,YAAY,CAAC,UAAU,CAAC,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based session lock to prevent duplicate relay sessions.
|
|
3
|
+
*
|
|
4
|
+
* When multiple MCP server processes start simultaneously (common in Claude Code),
|
|
5
|
+
* this lock prevents each from creating a separate relay session (rate limit:
|
|
6
|
+
* max 10 sessions/IP/10min).
|
|
7
|
+
*
|
|
8
|
+
* Lock file location: <config_dir>/mcp/relay-session-<server>.lock
|
|
9
|
+
*/
|
|
10
|
+
export declare function setLockDir(path: string | null): void;
|
|
11
|
+
export interface SessionInfo {
|
|
12
|
+
sessionId: string;
|
|
13
|
+
relayUrl: string;
|
|
14
|
+
createdAt: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function acquireSessionLock(serverName: string, maxAgeMs?: number): Promise<SessionInfo | null>;
|
|
17
|
+
export declare function writeSessionLock(serverName: string, info: SessionInfo): Promise<void>;
|
|
18
|
+
export declare function releaseSessionLock(serverName: string): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=session-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lock.d.ts","sourceRoot":"","sources":["../../src/storage/session-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAeH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEpD;AAUD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB;AAQD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,QAAQ,SAAqB,GAC5B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA2C7B;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB3F;AAED,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS1E"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based session lock to prevent duplicate relay sessions.
|
|
3
|
+
*
|
|
4
|
+
* When multiple MCP server processes start simultaneously (common in Claude Code),
|
|
5
|
+
* this lock prevents each from creating a separate relay session (rate limit:
|
|
6
|
+
* max 10 sessions/IP/10min).
|
|
7
|
+
*
|
|
8
|
+
* Lock file location: <config_dir>/mcp/relay-session-<server>.lock
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import envPaths from 'env-paths';
|
|
14
|
+
const paths = envPaths('mcp', { suffix: '' });
|
|
15
|
+
const DEFAULT_LOCK_DIR = paths.config;
|
|
16
|
+
const DEFAULT_MAX_AGE_MS = 600_000;
|
|
17
|
+
// Allow overriding lock directory for testing
|
|
18
|
+
let lockDirOverride = null;
|
|
19
|
+
export function setLockDir(path) {
|
|
20
|
+
lockDirOverride = path;
|
|
21
|
+
}
|
|
22
|
+
function getLockDir() {
|
|
23
|
+
return lockDirOverride ?? DEFAULT_LOCK_DIR;
|
|
24
|
+
}
|
|
25
|
+
function lockPath(serverName) {
|
|
26
|
+
return join(getLockDir(), `relay-session-${serverName}.lock`);
|
|
27
|
+
}
|
|
28
|
+
export async function acquireSessionLock(serverName, maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
29
|
+
const path = lockPath(serverName);
|
|
30
|
+
try {
|
|
31
|
+
if (!existsSync(path)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const raw = await readFile(path, 'utf-8');
|
|
35
|
+
const data = JSON.parse(raw);
|
|
36
|
+
// Validate required fields
|
|
37
|
+
if (typeof data.session_id !== 'string' ||
|
|
38
|
+
typeof data.relay_url !== 'string' ||
|
|
39
|
+
typeof data.created_at !== 'number') {
|
|
40
|
+
await releaseSessionLock(serverName);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const info = {
|
|
44
|
+
sessionId: data.session_id,
|
|
45
|
+
relayUrl: data.relay_url,
|
|
46
|
+
createdAt: data.created_at
|
|
47
|
+
};
|
|
48
|
+
const age = Date.now() - info.createdAt;
|
|
49
|
+
if (age > maxAgeMs) {
|
|
50
|
+
// Expired lock: clean up and return null
|
|
51
|
+
await releaseSessionLock(serverName);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return info;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Corrupt or unreadable lock file: clean up and return null
|
|
58
|
+
try {
|
|
59
|
+
await releaseSessionLock(serverName);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* ignore cleanup errors */
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export async function writeSessionLock(serverName, info) {
|
|
68
|
+
const path = lockPath(serverName);
|
|
69
|
+
const dir = dirname(path);
|
|
70
|
+
if (!existsSync(dir)) {
|
|
71
|
+
await mkdir(dir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
const data = {
|
|
74
|
+
session_id: info.sessionId,
|
|
75
|
+
relay_url: info.relayUrl,
|
|
76
|
+
created_at: info.createdAt
|
|
77
|
+
};
|
|
78
|
+
// Write atomically via temp file + rename
|
|
79
|
+
const tmpPath = `${path}.tmp`;
|
|
80
|
+
await writeFile(tmpPath, JSON.stringify(data), 'utf-8');
|
|
81
|
+
await rename(tmpPath, path);
|
|
82
|
+
}
|
|
83
|
+
export async function releaseSessionLock(serverName) {
|
|
84
|
+
const path = lockPath(serverName);
|
|
85
|
+
try {
|
|
86
|
+
if (existsSync(path)) {
|
|
87
|
+
await unlink(path);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* ignore errors during cleanup */
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=session-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lock.js","sourceRoot":"","sources":["../../src/storage/session-lock.ts"],"names":[],"mappings":"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"}
|