@n24q02m/mcp-core 1.9.0 → 1.11.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/auth/credential-form.d.ts +48 -0
- package/build/auth/credential-form.d.ts.map +1 -1
- package/build/auth/credential-form.js +31 -0
- package/build/auth/credential-form.js.map +1 -1
- package/build/auth/local-oauth-app.d.ts +24 -1
- package/build/auth/local-oauth-app.d.ts.map +1 -1
- package/build/auth/local-oauth-app.js +133 -1
- package/build/auth/local-oauth-app.js.map +1 -1
- package/build/lifecycle/lock.d.ts +124 -0
- package/build/lifecycle/lock.d.ts.map +1 -0
- package/build/lifecycle/lock.js +318 -0
- package/build/lifecycle/lock.js.map +1 -0
- package/build/relay/session.d.ts +12 -0
- package/build/relay/session.d.ts.map +1 -0
- package/build/relay/session.js +55 -0
- package/build/relay/session.js.map +1 -0
- package/build/relay/tool-helpers.d.ts +56 -0
- package/build/relay/tool-helpers.d.ts.map +1 -0
- package/build/relay/tool-helpers.js +60 -0
- package/build/relay/tool-helpers.js.map +1 -0
- package/build/storage/config-file.d.ts +17 -0
- package/build/storage/config-file.d.ts.map +1 -1
- package/build/storage/config-file.js +22 -1
- package/build/storage/config-file.js.map +1 -1
- package/build/storage/session-lock.js +1 -1
- package/build/storage/session-lock.js.map +1 -1
- package/build/transport/cache.d.ts +19 -0
- package/build/transport/cache.d.ts.map +1 -0
- package/build/transport/cache.js +67 -0
- package/build/transport/cache.js.map +1 -0
- package/build/transport/local-server.d.ts.map +1 -1
- package/build/transport/local-server.js +47 -2
- package/build/transport/local-server.js.map +1 -1
- package/build/transport/smart-stdio.d.ts +64 -0
- package/build/transport/smart-stdio.d.ts.map +1 -1
- package/build/transport/smart-stdio.js +170 -0
- package/build/transport/smart-stdio.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock-file lifecycle helpers (D9 — hybrid TTL extension).
|
|
3
|
+
*
|
|
4
|
+
* The lock file written by `runLocalServer` may contain 4 lines (legacy
|
|
5
|
+
* v1) or 6 lines (D9 modern):
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* {pid}
|
|
9
|
+
* {port}
|
|
10
|
+
* {token}
|
|
11
|
+
* {spawnedAt_iso8601_utc}
|
|
12
|
+
* {credState} # "configured" | "unconfigured" (D9)
|
|
13
|
+
* {lastActivityAt_iso8601_utc} # bumped by daemon refresh loop (D9)
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Stale locks are detected when *any* of:
|
|
17
|
+
* - file has legacy 3-line format (no timestamp) → migrate by deletion
|
|
18
|
+
* - timestamp older than the applicable TTL
|
|
19
|
+
* - configured credState → 24h
|
|
20
|
+
* - unconfigured credState → 30 min
|
|
21
|
+
* - writing PID does not exist on this host
|
|
22
|
+
*
|
|
23
|
+
* Dead-PID locks are reaped immediately. Past-TTL idle setup daemons
|
|
24
|
+
* (unconfigured) are terminated; configured daemons past TTL get their
|
|
25
|
+
* lock unlinked but the process is left alone (parity with pre-D9 sweep
|
|
26
|
+
* behavior so a long-running daemon never gets SIGKILL'd by a sweep
|
|
27
|
+
* race).
|
|
28
|
+
*
|
|
29
|
+
* TypeScript port of core-py's `mcp_core.lifecycle.lock`. Behaviour and
|
|
30
|
+
* TTL constants kept identical for cross-language parity.
|
|
31
|
+
*/
|
|
32
|
+
import { existsSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
33
|
+
import { homedir } from 'node:os';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
// Self-import so `sweepStaleLocks` can resolve `lockDir`/`isAlive`/
|
|
36
|
+
// `terminateDaemon` through the module namespace at call time. This
|
|
37
|
+
// makes `vi.spyOn(lockModule, '<name>').mockReturnValue(...)` actually
|
|
38
|
+
// intercept the helper from inside the sweep loop — without this
|
|
39
|
+
// indirection, the closure-captured local binding ignores spies and
|
|
40
|
+
// test mocks become silent no-ops.
|
|
41
|
+
import * as self from './lock.js';
|
|
42
|
+
export const DEFAULT_LOCK_TTL_HOURS = 24;
|
|
43
|
+
/** D9 hybrid TTL constants — selected by `credState` in `sweepStaleLocks`. */
|
|
44
|
+
export const LIFECYCLE_TTL_CONFIGURED_MS = 24 * 3600 * 1000;
|
|
45
|
+
export const LIFECYCLE_TTL_UNCONFIGURED_MS = 30 * 60 * 1000;
|
|
46
|
+
export function locksDir(root) {
|
|
47
|
+
return root ?? join(homedir(), '.config', 'mcp', 'locks');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* No-arg lock-dir resolver — D9 sweep tests `vi.spyOn(lockModule, 'lockDir')`
|
|
51
|
+
* to redirect into a temp directory without threading a `root` arg.
|
|
52
|
+
*/
|
|
53
|
+
export function lockDir() {
|
|
54
|
+
return locksDir();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Serialize `LockMetadata` to the 6-line on-disk format. Trailing
|
|
58
|
+
* newline included so writes always end in `\n` and the parser's
|
|
59
|
+
* `lines.length` checks behave deterministically.
|
|
60
|
+
*/
|
|
61
|
+
export function serializeLock(meta) {
|
|
62
|
+
return [
|
|
63
|
+
String(meta.pid),
|
|
64
|
+
String(meta.port),
|
|
65
|
+
meta.token,
|
|
66
|
+
meta.spawnedAt.toISOString(),
|
|
67
|
+
meta.credState,
|
|
68
|
+
meta.lastActivityAt.toISOString(),
|
|
69
|
+
''
|
|
70
|
+
].join('\n');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parse the textual lock payload into `LockMetadata`. Accepts 4-line
|
|
74
|
+
* legacy, 5-line transitional, and 6-line D9 modern formats. Throws on
|
|
75
|
+
* fewer than 4 lines or unparseable timestamp / pid / port.
|
|
76
|
+
*/
|
|
77
|
+
export function parseLock(raw) {
|
|
78
|
+
const lines = raw.replace(/\n+$/, '').split('\n');
|
|
79
|
+
if (lines.length < 4)
|
|
80
|
+
throw new Error(`lock file too few lines: ${lines.length}`);
|
|
81
|
+
const pid = Number(lines[0]);
|
|
82
|
+
const port = Number(lines[1]);
|
|
83
|
+
if (!Number.isInteger(pid) || !Number.isInteger(port)) {
|
|
84
|
+
throw new Error(`lock file has non-integer pid/port`);
|
|
85
|
+
}
|
|
86
|
+
const token = lines[2];
|
|
87
|
+
const spawnedAt = new Date(lines[3]);
|
|
88
|
+
if (Number.isNaN(spawnedAt.getTime())) {
|
|
89
|
+
throw new Error(`lock file has unparseable spawnedAt`);
|
|
90
|
+
}
|
|
91
|
+
let credState = 'configured';
|
|
92
|
+
let lastActivityAt = spawnedAt;
|
|
93
|
+
if (lines.length >= 6) {
|
|
94
|
+
credState = lines[4];
|
|
95
|
+
lastActivityAt = new Date(lines[5]);
|
|
96
|
+
if (Number.isNaN(lastActivityAt.getTime()))
|
|
97
|
+
lastActivityAt = spawnedAt;
|
|
98
|
+
}
|
|
99
|
+
else if (lines.length === 5) {
|
|
100
|
+
credState = lines[4];
|
|
101
|
+
}
|
|
102
|
+
return { pid, port, token, spawnedAt, credState, lastActivityAt, createdAt: spawnedAt };
|
|
103
|
+
}
|
|
104
|
+
export function parseLockMetadata(path) {
|
|
105
|
+
if (!existsSync(path))
|
|
106
|
+
return null;
|
|
107
|
+
let content;
|
|
108
|
+
try {
|
|
109
|
+
content = readFileSync(path, { encoding: 'utf-8' });
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const lines = content.trim().split('\n');
|
|
115
|
+
// Pre-D9 callers expect null for legacy 3-line format. The new
|
|
116
|
+
// `parseLock` instead throws — adapter logic here keeps both
|
|
117
|
+
// contracts: legacy / corrupt -> null; valid 4/5/6-line -> populated.
|
|
118
|
+
if (lines.length < 4)
|
|
119
|
+
return null;
|
|
120
|
+
try {
|
|
121
|
+
return parseLock(content);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function isLockExpired(path, ttlHours = DEFAULT_LOCK_TTL_HOURS) {
|
|
128
|
+
const md = parseLockMetadata(path);
|
|
129
|
+
if (md === null)
|
|
130
|
+
return true;
|
|
131
|
+
const ageMs = Date.now() - md.spawnedAt.getTime();
|
|
132
|
+
return ageMs > ttlHours * 3600 * 1000;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Cross-platform PID liveness check via `process.kill(pid, 0)`. Best-effort.
|
|
136
|
+
* Returns false on EPERM (different-user processes) so sweep does not steal
|
|
137
|
+
* locks owned by other users. Pre-D9 entry point — kept for backward compat.
|
|
138
|
+
*/
|
|
139
|
+
export function isPidAlive(pid) {
|
|
140
|
+
if (pid <= 0)
|
|
141
|
+
return false;
|
|
142
|
+
try {
|
|
143
|
+
process.kill(pid, 0);
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
const code = err?.code;
|
|
148
|
+
if (code === 'EPERM')
|
|
149
|
+
return true; // exists but we lack permission
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Liveness check on a parsed `LockMetadata`. D9 sweep tests
|
|
155
|
+
* `vi.spyOn(lockModule, 'isAlive')` to control the alive/dead branch
|
|
156
|
+
* without needing a real PID. Wraps `isPidAlive` so the underlying
|
|
157
|
+
* cross-platform logic stays in one place.
|
|
158
|
+
*/
|
|
159
|
+
export function isAlive(meta) {
|
|
160
|
+
return isPidAlive(meta.pid);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Kill the lock owner. D9 sweep tests `vi.spyOn(lockModule, 'terminateDaemon')`
|
|
164
|
+
* to assert the unconfigured-TTL branch invokes termination, without
|
|
165
|
+
* killing real PIDs. Used by `sweepStaleLocks` when an unconfigured
|
|
166
|
+
* daemon's idle TTL elapses.
|
|
167
|
+
*/
|
|
168
|
+
export function terminateDaemon(pid) {
|
|
169
|
+
if (pid <= 0)
|
|
170
|
+
return;
|
|
171
|
+
try {
|
|
172
|
+
process.kill(pid, 'SIGKILL');
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
/* ignore */
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Refresh `spawnedAt` of an existing lock file. Silent no-op on legacy /
|
|
180
|
+
* malformed files so callers don't need try/except.
|
|
181
|
+
*
|
|
182
|
+
* Padded to 512 bytes so on-disk size stays stable while a Windows
|
|
183
|
+
* byte-range lock is held past the metadata region (parity with core-py).
|
|
184
|
+
* Preserves the on-disk line count: a 4-line legacy lock stays 4-line
|
|
185
|
+
* after refresh; a 6-line modern lock stays 6-line (with both `spawnedAt`
|
|
186
|
+
* and `lastActivityAt` bumped).
|
|
187
|
+
*/
|
|
188
|
+
export function refreshLockTimestamp(path) {
|
|
189
|
+
const md = parseLockMetadata(path);
|
|
190
|
+
if (md === null)
|
|
191
|
+
return;
|
|
192
|
+
let raw;
|
|
193
|
+
try {
|
|
194
|
+
raw = readFileSync(path, { encoding: 'utf-8' });
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const lineCount = raw.trim().split('\n').length;
|
|
200
|
+
const now = new Date().toISOString();
|
|
201
|
+
let payload;
|
|
202
|
+
if (lineCount >= 6) {
|
|
203
|
+
payload = `${md.pid}\n${md.port}\n${md.token}\n${now}\n${md.credState}\n${now}\n`;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
payload = `${md.pid}\n${md.port}\n${md.token}\n${now}\n`;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
writeFileSync(path, payload.padEnd(512, ' '), { encoding: 'utf-8' });
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Best-effort
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Remove stale lock files for `serverName` (D9 hybrid TTL). Returns count removed.
|
|
217
|
+
*
|
|
218
|
+
* Called by `runLocalServer` at daemon startup before writing its own lock,
|
|
219
|
+
* preventing pile-up of dozens of `<server>-<port>.lock` files when daemons
|
|
220
|
+
* exit abnormally (Windows OOM, taskkill, signal).
|
|
221
|
+
*
|
|
222
|
+
* Backward-compat: the legacy signature `(serverName, ttlHours, root)` is
|
|
223
|
+
* preserved for pre-D9 callers. New D9 callers pass just `serverName` and
|
|
224
|
+
* rely on the hybrid TTL plus the `lockDir()` lookup (mockable in tests).
|
|
225
|
+
*/
|
|
226
|
+
export function sweepStaleLocks(serverName, ttlHours, root) {
|
|
227
|
+
const dir = root !== undefined ? root : self.lockDir();
|
|
228
|
+
if (!existsSync(dir))
|
|
229
|
+
return 0;
|
|
230
|
+
let removed = 0;
|
|
231
|
+
let entries;
|
|
232
|
+
try {
|
|
233
|
+
entries = readdirSync(dir);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
const prefix = `${serverName}-`;
|
|
239
|
+
const suffix = '.lock';
|
|
240
|
+
const now = Date.now();
|
|
241
|
+
const configuredTtlMs = (ttlHours ?? DEFAULT_LOCK_TTL_HOURS) * 3600 * 1000;
|
|
242
|
+
for (const entry of entries) {
|
|
243
|
+
if (!entry.startsWith(prefix) || !entry.endsWith(suffix))
|
|
244
|
+
continue;
|
|
245
|
+
const full = join(dir, entry);
|
|
246
|
+
let raw;
|
|
247
|
+
try {
|
|
248
|
+
raw = readFileSync(full, { encoding: 'utf-8' });
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
try {
|
|
252
|
+
unlinkSync(full);
|
|
253
|
+
removed += 1;
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
/* ignore */
|
|
257
|
+
}
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
let meta;
|
|
261
|
+
try {
|
|
262
|
+
meta = parseLock(raw);
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
try {
|
|
266
|
+
unlinkSync(full);
|
|
267
|
+
removed += 1;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
/* ignore */
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (!self.isAlive(meta)) {
|
|
275
|
+
try {
|
|
276
|
+
unlinkSync(full);
|
|
277
|
+
removed += 1;
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
/* ignore */
|
|
281
|
+
}
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
const ttlMs = meta.credState === 'configured' ? configuredTtlMs : LIFECYCLE_TTL_UNCONFIGURED_MS;
|
|
285
|
+
const lastActivityMs = meta.lastActivityAt.getTime();
|
|
286
|
+
if (now - lastActivityMs > ttlMs) {
|
|
287
|
+
// Only terminate idle setup daemons aggressively; preserve pre-D9
|
|
288
|
+
// behavior of unlink-only for configured daemons past TTL.
|
|
289
|
+
if (meta.credState === 'unconfigured') {
|
|
290
|
+
self.terminateDaemon(meta.pid);
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
unlinkSync(full);
|
|
294
|
+
removed += 1;
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
/* ignore */
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return removed;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Write the 4-line lock payload (pid, port, token, ISO timestamp) padded
|
|
305
|
+
* to 512 bytes. Used by `runLocalServer` after the OS file lock is
|
|
306
|
+
* acquired. Continues to write 4-line for backward compat — D9 sweep
|
|
307
|
+
* treats 4-line as `credState='configured'` so behavior is equivalent.
|
|
308
|
+
*
|
|
309
|
+
* Returns the absolute path to the lock file.
|
|
310
|
+
*/
|
|
311
|
+
export function writeLockFile(serverName, port, token, root) {
|
|
312
|
+
const dir = locksDir(root);
|
|
313
|
+
const path = join(dir, `${serverName}-${port}.lock`);
|
|
314
|
+
const payload = `${process.pid}\n${port}\n${token}\n${new Date().toISOString()}\n`;
|
|
315
|
+
writeFileSync(path, payload.padEnd(512, ' '), { encoding: 'utf-8', mode: 0o600 });
|
|
316
|
+
return path;
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/lifecycle/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ActiveFormSession {
|
|
2
|
+
token: string;
|
|
3
|
+
clientId: string;
|
|
4
|
+
startedAt: Date;
|
|
5
|
+
expiresAt: Date;
|
|
6
|
+
}
|
|
7
|
+
export declare function claimSession(clientId: string): ActiveFormSession;
|
|
8
|
+
export declare function releaseSession(): void;
|
|
9
|
+
export declare function isSessionActive(): boolean;
|
|
10
|
+
export declare function validateSessionToken(token: string): boolean;
|
|
11
|
+
export declare function getActiveSession(): ActiveFormSession | null;
|
|
12
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/relay/session.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,EAAE,IAAI,CAAA;CAChB;AAQD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAYhE;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAU3D;AAED,wBAAgB,gBAAgB,IAAI,iBAAiB,GAAG,IAAI,CAO3D"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
const SESSION_TTL_MS = 30 * 60 * 1000;
|
|
3
|
+
const TOKEN_BYTES = 32;
|
|
4
|
+
let state = null;
|
|
5
|
+
function now() {
|
|
6
|
+
return new Date();
|
|
7
|
+
}
|
|
8
|
+
export function claimSession(clientId) {
|
|
9
|
+
if (state !== null && state.expiresAt > now()) {
|
|
10
|
+
return state;
|
|
11
|
+
}
|
|
12
|
+
const startedAt = now();
|
|
13
|
+
state = {
|
|
14
|
+
token: randomBytes(TOKEN_BYTES).toString('base64url'),
|
|
15
|
+
clientId,
|
|
16
|
+
startedAt,
|
|
17
|
+
expiresAt: new Date(startedAt.getTime() + SESSION_TTL_MS)
|
|
18
|
+
};
|
|
19
|
+
return state;
|
|
20
|
+
}
|
|
21
|
+
export function releaseSession() {
|
|
22
|
+
state = null;
|
|
23
|
+
}
|
|
24
|
+
export function isSessionActive() {
|
|
25
|
+
if (state === null)
|
|
26
|
+
return false;
|
|
27
|
+
if (state.expiresAt <= now()) {
|
|
28
|
+
state = null;
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
export function validateSessionToken(token) {
|
|
34
|
+
if (state === null)
|
|
35
|
+
return false;
|
|
36
|
+
if (state.expiresAt <= now()) {
|
|
37
|
+
state = null;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const a = Buffer.from(state.token);
|
|
41
|
+
const b = Buffer.from(token);
|
|
42
|
+
if (a.length !== b.length)
|
|
43
|
+
return false;
|
|
44
|
+
return timingSafeEqual(a, b);
|
|
45
|
+
}
|
|
46
|
+
export function getActiveSession() {
|
|
47
|
+
if (state === null)
|
|
48
|
+
return null;
|
|
49
|
+
if (state.expiresAt <= now()) {
|
|
50
|
+
state = null;
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return state;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/relay/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE1D,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AACrC,MAAM,WAAW,GAAG,EAAE,CAAA;AAStB,IAAI,KAAK,GAA6B,IAAI,CAAA;AAE1C,SAAS,GAAG;IACV,OAAO,IAAI,IAAI,EAAE,CAAA;AACnB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,EAAE,CAAA;IACvB,KAAK,GAAG;QACN,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QACrD,QAAQ;QACR,SAAS;QACT,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC;KAC1D,CAAA;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,KAAK,GAAG,IAAI,CAAA;AACd,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5B,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACvC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAC9B,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC/B,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to register `config__open_relay` MCP tool — D6 (Task 1.8).
|
|
3
|
+
*
|
|
4
|
+
* Each consumer server calls `registerOpenRelayTool(mcp, SERVER_NAME, SCHEMA)`
|
|
5
|
+
* to get the standard tool registered into its MCP server instance. The tool,
|
|
6
|
+
* when invoked by the LLM, returns the relay form URL and attempts to open
|
|
7
|
+
* the user's browser. If the daemon is dead it auto-respawns; if a relay
|
|
8
|
+
* session is already active in another Claude Code window, the handler
|
|
9
|
+
* returns `session_active` status without auto-opening a second browser tab.
|
|
10
|
+
*/
|
|
11
|
+
export interface OpenRelayResult {
|
|
12
|
+
url: string;
|
|
13
|
+
browserOpened: boolean;
|
|
14
|
+
status: 'configured' | 'unconfigured' | 'expired' | 'session_active';
|
|
15
|
+
}
|
|
16
|
+
export interface OpenRelaySchema {
|
|
17
|
+
server: string;
|
|
18
|
+
fields: Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
label?: string;
|
|
21
|
+
required?: boolean;
|
|
22
|
+
secret?: boolean;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}>;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build the `open_relay` handler closure. Separated out for testability —
|
|
29
|
+
* tests can call `buildOpenRelayHandler('demo', SCHEMA)` directly without
|
|
30
|
+
* needing a real MCP server instance.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildOpenRelayHandler(serverName: string, _schema: OpenRelaySchema | unknown): () => Promise<OpenRelayResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Minimal shape we require from the MCP server instance to register the
|
|
35
|
+
* tool. Both `@modelcontextprotocol/sdk`'s `McpServer.tool` and the
|
|
36
|
+
* FastMCP-style helpers used in consumer servers conform to this surface.
|
|
37
|
+
*/
|
|
38
|
+
export interface ToolRegistrar {
|
|
39
|
+
tool: (config: {
|
|
40
|
+
name: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
}, handler: () => Promise<OpenRelayResult>) => void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Register `config__open_relay` on the given MCP server instance.
|
|
46
|
+
*
|
|
47
|
+
* Consumer servers add this single line in their tool registry after the
|
|
48
|
+
* other `config__*` registrations:
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { registerOpenRelayTool } from '@n24q02m/mcp-core'
|
|
52
|
+
* registerOpenRelayTool(mcp, SERVER_NAME, RELAY_SCHEMA)
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function registerOpenRelayTool(mcp: ToolRegistrar, serverName: string, schema: OpenRelaySchema | unknown): void;
|
|
56
|
+
//# sourceMappingURL=tool-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,OAAO,CAAA;IACtB,MAAM,EAAE,YAAY,GAAG,cAAc,GAAG,SAAS,GAAG,gBAAgB,CAAA;CACrE;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,MAAM,CAAC,EAAE,OAAO,CAAA;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,CAAC,CAAA;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAeD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,GAAG,OAAO,GACjC,MAAM,OAAO,CAAC,eAAe,CAAC,CAYhC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;CACxG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,GAAG,IAAI,CASrH"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to register `config__open_relay` MCP tool — D6 (Task 1.8).
|
|
3
|
+
*
|
|
4
|
+
* Each consumer server calls `registerOpenRelayTool(mcp, SERVER_NAME, SCHEMA)`
|
|
5
|
+
* to get the standard tool registered into its MCP server instance. The tool,
|
|
6
|
+
* when invoked by the LLM, returns the relay form URL and attempts to open
|
|
7
|
+
* the user's browser. If the daemon is dead it auto-respawns; if a relay
|
|
8
|
+
* session is already active in another Claude Code window, the handler
|
|
9
|
+
* returns `session_active` status without auto-opening a second browser tab.
|
|
10
|
+
*/
|
|
11
|
+
import { daemonCredState, daemonIsAlive, daemonRelayUrl, daemonRespawn } from '../transport/smart-stdio.js';
|
|
12
|
+
import { tryOpenBrowser } from './browser.js';
|
|
13
|
+
import { isSessionActive } from './session.js';
|
|
14
|
+
/**
|
|
15
|
+
* Per-server session probe.
|
|
16
|
+
*
|
|
17
|
+
* The current session module tracks one global active form session at a
|
|
18
|
+
* time (Task 1.5), so the server name is currently ignored. Kept as a
|
|
19
|
+
* one-arg helper to give vi.spyOn hooks a single, server-aware seam that
|
|
20
|
+
* future per-server session tracking can implement without refactoring
|
|
21
|
+
* callers.
|
|
22
|
+
*/
|
|
23
|
+
function isSessionActiveForServer(_serverName) {
|
|
24
|
+
return isSessionActive();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build the `open_relay` handler closure. Separated out for testability —
|
|
28
|
+
* tests can call `buildOpenRelayHandler('demo', SCHEMA)` directly without
|
|
29
|
+
* needing a real MCP server instance.
|
|
30
|
+
*/
|
|
31
|
+
export function buildOpenRelayHandler(serverName, _schema) {
|
|
32
|
+
return async function openRelay() {
|
|
33
|
+
const url = daemonIsAlive(serverName) ? daemonRelayUrl(serverName) : daemonRespawn(serverName);
|
|
34
|
+
const credState = daemonCredState(serverName);
|
|
35
|
+
if (isSessionActiveForServer(serverName)) {
|
|
36
|
+
return { url, browserOpened: false, status: 'session_active' };
|
|
37
|
+
}
|
|
38
|
+
const opened = await tryOpenBrowser(url);
|
|
39
|
+
return { url, browserOpened: !!opened, status: credState };
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Register `config__open_relay` on the given MCP server instance.
|
|
44
|
+
*
|
|
45
|
+
* Consumer servers add this single line in their tool registry after the
|
|
46
|
+
* other `config__*` registrations:
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { registerOpenRelayTool } from '@n24q02m/mcp-core'
|
|
50
|
+
* registerOpenRelayTool(mcp, SERVER_NAME, RELAY_SCHEMA)
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function registerOpenRelayTool(mcp, serverName, schema) {
|
|
54
|
+
const handler = buildOpenRelayHandler(serverName, schema);
|
|
55
|
+
mcp.tool({
|
|
56
|
+
name: 'config__open_relay',
|
|
57
|
+
description: `Open the relay configuration form for ${serverName} in the user's browser.`
|
|
58
|
+
}, handler);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=tool-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAoB9C;;;;;;;;GAQG;AACH,SAAS,wBAAwB,CAAC,WAAmB;IACnD,OAAO,eAAe,EAAE,CAAA;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAkB,EAClB,OAAkC;IAElC,OAAO,KAAK,UAAU,SAAS;QAC7B,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;QAC9F,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,CAA8B,CAAA;QAE1E,IAAI,wBAAwB,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;QAChE,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAA;QACxC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;IAC5D,CAAC,CAAA;AACH,CAAC;AAWD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAkB,EAAE,UAAkB,EAAE,MAAiC;IAC7G,MAAM,OAAO,GAAG,qBAAqB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;IACzD,GAAG,CAAC,IAAI,CACN;QACE,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,yCAAyC,UAAU,yBAAyB;KAC1F,EACD,OAAO,CACR,CAAA;AACH,CAAC"}
|
|
@@ -12,6 +12,23 @@ export declare function scheduleReloadExit(): void;
|
|
|
12
12
|
export declare function readConfig(serverName: string): Promise<Record<string, string> | null>;
|
|
13
13
|
export declare function writeConfig(serverName: string, config: Record<string, string>): Promise<void>;
|
|
14
14
|
export declare function deleteConfig(serverName: string): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Metadata flag set by `markSetupComplete` after a successful relay-form
|
|
17
|
+
* submission (POST /authorize). `runLocalServer`/`isSchemaComplete` read this
|
|
18
|
+
* to distinguish "user has submitted the form" from "config.enc has values
|
|
19
|
+
* written by a peer-share or partial bootstrap path". Lives alongside the
|
|
20
|
+
* user's normal credential keys in the same per-server config dict.
|
|
21
|
+
*/
|
|
22
|
+
export declare const SETUP_COMPLETE_KEY = "_setup_complete";
|
|
23
|
+
/**
|
|
24
|
+
* Set the `_setup_complete` flag in `serverName`'s config. Idempotent.
|
|
25
|
+
* Creates a new entry with just the flag if no prior config exists (useful
|
|
26
|
+
* for all-optional schemas where the user may submit an empty form).
|
|
27
|
+
*
|
|
28
|
+
* Strict equality semantics in `isSchemaComplete` rely on this writing the
|
|
29
|
+
* literal string `"true"`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function markSetupComplete(serverName: string): Promise<void>;
|
|
15
32
|
export declare function listConfigs(): Promise<string[]>;
|
|
16
33
|
export declare function exportConfig(passphrase: string): Promise<Buffer>;
|
|
17
34
|
export declare function importConfig(passphrase: string, data: Buffer): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"AAkDA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEvD;AA0BD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAsDD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAG3F;AAED,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAInG;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYpE;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAGrD;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAItE;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBlF"}
|
|
1
|
+
{"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"AAkDA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEvD;AA0BD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAsDD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAG3F;AAED,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAInG;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYpE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,oBAAoB,CAAA;AAEnD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAGrD;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAItE;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBlF"}
|
|
@@ -108,7 +108,7 @@ async function saveStore(store) {
|
|
|
108
108
|
}
|
|
109
109
|
const key = await getKey();
|
|
110
110
|
const encrypted = await encryptData(key, JSON.stringify(store));
|
|
111
|
-
await withRetry(() => writeFile(configPath, encrypted));
|
|
111
|
+
await withRetry(() => writeFile(configPath, encrypted, { mode: 0o600 }));
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
114
|
* Schedule a process exit so an MCP client can respawn the server and pick up
|
|
@@ -145,6 +145,27 @@ export async function deleteConfig(serverName) {
|
|
|
145
145
|
await saveStore(store);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Metadata flag set by `markSetupComplete` after a successful relay-form
|
|
150
|
+
* submission (POST /authorize). `runLocalServer`/`isSchemaComplete` read this
|
|
151
|
+
* to distinguish "user has submitted the form" from "config.enc has values
|
|
152
|
+
* written by a peer-share or partial bootstrap path". Lives alongside the
|
|
153
|
+
* user's normal credential keys in the same per-server config dict.
|
|
154
|
+
*/
|
|
155
|
+
export const SETUP_COMPLETE_KEY = '_setup_complete';
|
|
156
|
+
/**
|
|
157
|
+
* Set the `_setup_complete` flag in `serverName`'s config. Idempotent.
|
|
158
|
+
* Creates a new entry with just the flag if no prior config exists (useful
|
|
159
|
+
* for all-optional schemas where the user may submit an empty form).
|
|
160
|
+
*
|
|
161
|
+
* Strict equality semantics in `isSchemaComplete` rely on this writing the
|
|
162
|
+
* literal string `"true"`.
|
|
163
|
+
*/
|
|
164
|
+
export async function markSetupComplete(serverName) {
|
|
165
|
+
const existing = (await readConfig(serverName)) ?? {};
|
|
166
|
+
existing[SETUP_COMPLETE_KEY] = 'true';
|
|
167
|
+
await writeConfig(serverName, existing);
|
|
168
|
+
}
|
|
148
169
|
export async function listConfigs() {
|
|
149
170
|
const store = await loadStore();
|
|
150
171
|
return Object.keys(store.servers);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-file.js","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config-file.js","sourceRoot":"","sources":["../../src/storage/config-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -77,7 +77,7 @@ export async function writeSessionLock(serverName, info) {
|
|
|
77
77
|
};
|
|
78
78
|
// Write atomically via temp file + rename
|
|
79
79
|
const tmpPath = `${path}.tmp`;
|
|
80
|
-
await writeFile(tmpPath, JSON.stringify(data), 'utf-8');
|
|
80
|
+
await writeFile(tmpPath, JSON.stringify(data), { encoding: 'utf-8', mode: 0o600 });
|
|
81
81
|
await rename(tmpPath, path);
|
|
82
82
|
}
|
|
83
83
|
export async function releaseSessionLock(serverName) {
|