@memoraone/mcp 0.1.54 → 0.1.56
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/dist/cli.cjs +8 -4
- package/dist/daemon.cjs +7 -3
- package/dist/gateway.cjs +531 -230
- package/dist/index.cjs +7 -3
- package/dist/pluginPairing.cjs +7 -3
- package/package.json +1 -1
package/dist/gateway.cjs
CHANGED
|
@@ -35,7 +35,7 @@ __export(gateway_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(gateway_exports);
|
|
36
36
|
var http = __toESM(require("http"), 1);
|
|
37
37
|
var fs38 = __toESM(require("fs"), 1);
|
|
38
|
-
var
|
|
38
|
+
var path52 = __toESM(require("path"), 1);
|
|
39
39
|
|
|
40
40
|
// src/socketPaths.ts
|
|
41
41
|
var os = __toESM(require("os"), 1);
|
|
@@ -245,6 +245,101 @@ function buildGatewayMcpUrl(workspaceRoot, env2 = process.env) {
|
|
|
245
245
|
return `${getGatewayBaseUrl(env2)}${GATEWAY_MCP_PATH}?${GATEWAY_WS_QUERY}=${encodeURIComponent(root)}`;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
// src/config.ts
|
|
249
|
+
var process2 = __toESM(require("process"), 1);
|
|
250
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
251
|
+
var path4 = __toESM(require("path"), 1);
|
|
252
|
+
var dotenv = __toESM(require("dotenv"), 1);
|
|
253
|
+
var import_v4 = require("zod/v4");
|
|
254
|
+
|
|
255
|
+
// src/configUtils.ts
|
|
256
|
+
var DEFAULT_API_URL = "https://api.memoraone.com";
|
|
257
|
+
var DEV_API_URL = "http://localhost:3001";
|
|
258
|
+
function resolveApiUrl(env2) {
|
|
259
|
+
const explicitUrl = env2.MEMORAONE_API_URL?.trim();
|
|
260
|
+
if (explicitUrl) {
|
|
261
|
+
return explicitUrl;
|
|
262
|
+
}
|
|
263
|
+
const aliasUrl = env2.MEMORA_API_URL?.trim();
|
|
264
|
+
if (aliasUrl) {
|
|
265
|
+
return aliasUrl;
|
|
266
|
+
}
|
|
267
|
+
if (env2.MEMORAONE_DEV_MODE === "1") {
|
|
268
|
+
return DEV_API_URL;
|
|
269
|
+
}
|
|
270
|
+
return DEFAULT_API_URL;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/config.ts
|
|
274
|
+
var dotenvPath = path4.resolve(process2.cwd(), ".env");
|
|
275
|
+
if (fs2.existsSync(dotenvPath)) {
|
|
276
|
+
try {
|
|
277
|
+
dotenv.config({ path: dotenvPath });
|
|
278
|
+
} catch (err) {
|
|
279
|
+
process2.stderr.write("[memoraone-mcp] Failed to load .env: " + String(err) + "\n");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
var EnvSchema = import_v4.z.object({
|
|
283
|
+
MEMORAONE_API_URL: import_v4.z.string().url().optional(),
|
|
284
|
+
MEMORAONE_API_KEY: import_v4.z.string().min(1).optional(),
|
|
285
|
+
MEMORAONE_DEV_MODE: import_v4.z.string().min(1).optional(),
|
|
286
|
+
MEMORAONE_AGENT_NAME: import_v4.z.string().min(1).optional(),
|
|
287
|
+
MEMORAONE_AGENT_TYPE: import_v4.z.string().min(1).optional(),
|
|
288
|
+
MEMORAONE_SOURCE: import_v4.z.string().min(1).optional(),
|
|
289
|
+
MEMORAONE_IDE_TYPE: import_v4.z.enum(IDE_TYPES).optional(),
|
|
290
|
+
MEMORAONE_WORKLOG: import_v4.z.string().min(1).optional(),
|
|
291
|
+
MEMORAONE_HEARTBEAT: import_v4.z.string().min(1).optional(),
|
|
292
|
+
MEMORAONE_HEARTBEAT_INTERVAL_MS: import_v4.z.string().min(1).optional()
|
|
293
|
+
});
|
|
294
|
+
var requiredEnvVars = [];
|
|
295
|
+
var missingEnvVars = requiredEnvVars.filter((key) => {
|
|
296
|
+
const value = process2.env[key];
|
|
297
|
+
return value === void 0 || value.trim() === "";
|
|
298
|
+
});
|
|
299
|
+
if (missingEnvVars.length > 0) {
|
|
300
|
+
for (const key of missingEnvVars) {
|
|
301
|
+
process2.stderr.write(`Missing ${key}
|
|
302
|
+
`);
|
|
303
|
+
}
|
|
304
|
+
process2.exit(1);
|
|
305
|
+
}
|
|
306
|
+
var parsed = EnvSchema.safeParse(process2.env);
|
|
307
|
+
var resolvedApiUrl = resolveApiUrl(process2.env);
|
|
308
|
+
if (!parsed.success) {
|
|
309
|
+
const formatted = parsed.error.format();
|
|
310
|
+
process2.stderr.write(
|
|
311
|
+
"[memoraone-mcp] Invalid environment variables " + JSON.stringify(formatted) + "\n"
|
|
312
|
+
);
|
|
313
|
+
throw new Error("Config validation failed");
|
|
314
|
+
}
|
|
315
|
+
var parseBooleanFlag = (value, defaultValue) => {
|
|
316
|
+
if (value === void 0) {
|
|
317
|
+
return defaultValue;
|
|
318
|
+
}
|
|
319
|
+
const normalized = value.trim().toLowerCase();
|
|
320
|
+
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
return defaultValue;
|
|
327
|
+
};
|
|
328
|
+
var config2 = {
|
|
329
|
+
apiUrl: resolvedApiUrl.replace(/\/+$/, ""),
|
|
330
|
+
apiKey: parsed.data.MEMORAONE_API_KEY,
|
|
331
|
+
agentName: parsed.data.MEMORAONE_AGENT_NAME ?? "cursor",
|
|
332
|
+
agentType: parsed.data.MEMORAONE_AGENT_TYPE ?? "agent",
|
|
333
|
+
source: parsed.data.MEMORAONE_SOURCE ?? "cursor",
|
|
334
|
+
ideType: parsed.data.MEMORAONE_IDE_TYPE,
|
|
335
|
+
devMode: parseBooleanFlag(parsed.data.MEMORAONE_DEV_MODE, false),
|
|
336
|
+
worklogEnabled: parseBooleanFlag(parsed.data.MEMORAONE_WORKLOG, true),
|
|
337
|
+
heartbeatEnabled: parseBooleanFlag(parsed.data.MEMORAONE_HEARTBEAT, true),
|
|
338
|
+
// Cadence is owned by LOCAL_MCP_HEARTBEAT_INTERVAL_MS in heartbeat.ts (1_000).
|
|
339
|
+
// Env override is accepted for forward-compat but the timer path ignores it.
|
|
340
|
+
heartbeatIntervalMs: Number.parseInt(parsed.data.MEMORAONE_HEARTBEAT_INTERVAL_MS ?? "1000", 10)
|
|
341
|
+
};
|
|
342
|
+
|
|
248
343
|
// src/gatewayOAuth.ts
|
|
249
344
|
var import_node_crypto28 = require("crypto");
|
|
250
345
|
|
|
@@ -268,14 +363,40 @@ var GATEWAY_OAUTH_ALLOWED_REDIRECT_URIS = [
|
|
|
268
363
|
function getGatewayIssuerUrl(env2 = process.env) {
|
|
269
364
|
return getGatewayBaseUrl(env2);
|
|
270
365
|
}
|
|
366
|
+
function getGatewayAuthorizationServerIssuerUrl(env2 = process.env) {
|
|
367
|
+
const raw = env2.MEMORAONE_OAUTH_ISSUER_URL?.trim();
|
|
368
|
+
if (!raw) {
|
|
369
|
+
return getGatewayIssuerUrl(env2);
|
|
370
|
+
}
|
|
371
|
+
let parsed2;
|
|
372
|
+
try {
|
|
373
|
+
parsed2 = new URL(raw);
|
|
374
|
+
} catch {
|
|
375
|
+
throw new Error(
|
|
376
|
+
`[memoraone-mcp] Invalid MEMORAONE_OAUTH_ISSUER_URL=${JSON.stringify(raw)} (expected absolute http: or https: URL)`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
if (parsed2.protocol !== "http:" && parsed2.protocol !== "https:") {
|
|
380
|
+
throw new Error(
|
|
381
|
+
`[memoraone-mcp] Invalid MEMORAONE_OAUTH_ISSUER_URL=${JSON.stringify(raw)} (expected absolute http: or https: URL)`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
return raw.replace(/\/+$/, "");
|
|
385
|
+
}
|
|
271
386
|
function getGatewayResourceBaseUrl(env2 = process.env) {
|
|
272
387
|
return `${getGatewayBaseUrl(env2)}${GATEWAY_MCP_PATH}`;
|
|
273
388
|
}
|
|
274
|
-
function getGatewayProtectedResourceMetadataUrl(env2 = process.env) {
|
|
275
|
-
|
|
389
|
+
function getGatewayProtectedResourceMetadataUrl(env2 = process.env, workspaceRoot) {
|
|
390
|
+
const metadataUrl = `${getGatewayBaseUrl(env2)}${GATEWAY_OAUTH_PRM_PATH}`;
|
|
391
|
+
if (workspaceRoot === void 0) {
|
|
392
|
+
return metadataUrl;
|
|
393
|
+
}
|
|
394
|
+
const mcpUrl = buildGatewayMcpUrl(workspaceRoot, env2);
|
|
395
|
+
const queryIndex = mcpUrl.indexOf("?");
|
|
396
|
+
return queryIndex >= 0 ? `${metadataUrl}${mcpUrl.slice(queryIndex)}` : metadataUrl;
|
|
276
397
|
}
|
|
277
|
-
function buildGatewayMcpWwwAuthenticate(env2 = process.env) {
|
|
278
|
-
const metadataUrl = getGatewayProtectedResourceMetadataUrl(env2);
|
|
398
|
+
function buildGatewayMcpWwwAuthenticate(env2 = process.env, workspaceRoot) {
|
|
399
|
+
const metadataUrl = getGatewayProtectedResourceMetadataUrl(env2, workspaceRoot);
|
|
279
400
|
return `Bearer resource_metadata="${metadataUrl}"`;
|
|
280
401
|
}
|
|
281
402
|
function isAllowedOAuthRedirectUri(redirectUri) {
|
|
@@ -391,47 +512,47 @@ var GatewayOAuthCodeStore = class {
|
|
|
391
512
|
};
|
|
392
513
|
|
|
393
514
|
// src/gatewayWorkspace.ts
|
|
394
|
-
var
|
|
395
|
-
var
|
|
515
|
+
var fs7 = __toESM(require("fs/promises"), 1);
|
|
516
|
+
var path12 = __toESM(require("path"), 1);
|
|
396
517
|
|
|
397
518
|
// src/localState/resolveLocalBinding.ts
|
|
398
|
-
var
|
|
519
|
+
var path11 = __toESM(require("path"), 1);
|
|
399
520
|
|
|
400
521
|
// src/localState/bindingStore.ts
|
|
401
|
-
var
|
|
402
|
-
var
|
|
522
|
+
var fs5 = __toESM(require("fs/promises"), 1);
|
|
523
|
+
var path7 = __toESM(require("path"), 1);
|
|
403
524
|
|
|
404
525
|
// src/localState/atomicFs.ts
|
|
405
|
-
var
|
|
406
|
-
var
|
|
526
|
+
var fs3 = __toESM(require("fs/promises"), 1);
|
|
527
|
+
var path5 = __toESM(require("path"), 1);
|
|
407
528
|
var import_node_crypto2 = require("crypto");
|
|
408
529
|
var STATE_DIR_MODE = 448;
|
|
409
530
|
var STATE_FILE_MODE = 384;
|
|
410
531
|
async function ensurePrivateDir(dirPath) {
|
|
411
|
-
await
|
|
532
|
+
await fs3.mkdir(dirPath, { recursive: true, mode: STATE_DIR_MODE });
|
|
412
533
|
try {
|
|
413
|
-
await
|
|
534
|
+
await fs3.chmod(dirPath, STATE_DIR_MODE);
|
|
414
535
|
} catch {
|
|
415
536
|
}
|
|
416
537
|
}
|
|
417
538
|
async function writeFileAtomic(filePath, content, options = {}) {
|
|
418
539
|
const mode = options.mode ?? STATE_FILE_MODE;
|
|
419
|
-
const dir =
|
|
540
|
+
const dir = path5.dirname(filePath);
|
|
420
541
|
await ensurePrivateDir(dir);
|
|
421
|
-
const tmpPath =
|
|
542
|
+
const tmpPath = path5.join(
|
|
422
543
|
dir,
|
|
423
|
-
`.${
|
|
544
|
+
`.${path5.basename(filePath)}.${process.pid}.${(0, import_node_crypto2.randomBytes)(8).toString("hex")}.tmp`
|
|
424
545
|
);
|
|
425
546
|
try {
|
|
426
|
-
await
|
|
427
|
-
await
|
|
547
|
+
await fs3.writeFile(tmpPath, content, { encoding: "utf8", mode });
|
|
548
|
+
await fs3.rename(tmpPath, filePath);
|
|
428
549
|
try {
|
|
429
|
-
await
|
|
550
|
+
await fs3.chmod(filePath, mode);
|
|
430
551
|
} catch {
|
|
431
552
|
}
|
|
432
553
|
} catch (err) {
|
|
433
554
|
try {
|
|
434
|
-
await
|
|
555
|
+
await fs3.unlink(tmpPath);
|
|
435
556
|
} catch {
|
|
436
557
|
}
|
|
437
558
|
throw err;
|
|
@@ -439,7 +560,7 @@ async function writeFileAtomic(filePath, content, options = {}) {
|
|
|
439
560
|
}
|
|
440
561
|
async function readJsonFile(filePath) {
|
|
441
562
|
try {
|
|
442
|
-
const raw = await
|
|
563
|
+
const raw = await fs3.readFile(filePath, "utf8");
|
|
443
564
|
return JSON.parse(raw);
|
|
444
565
|
} catch (err) {
|
|
445
566
|
if (err?.code === "ENOENT") {
|
|
@@ -566,32 +687,32 @@ function parseBindingRecord(raw) {
|
|
|
566
687
|
}
|
|
567
688
|
|
|
568
689
|
// src/localState/localLocks.ts
|
|
569
|
-
var
|
|
690
|
+
var fs4 = __toESM(require("fs/promises"), 1);
|
|
570
691
|
|
|
571
692
|
// src/localState/statePaths.ts
|
|
572
693
|
var os2 = __toESM(require("os"), 1);
|
|
573
|
-
var
|
|
694
|
+
var path6 = __toESM(require("path"), 1);
|
|
574
695
|
var MEMORAONE_STATE_DIRNAME = ".memoraone";
|
|
575
696
|
function getMemoraoneStateDir(homeDir = os2.homedir()) {
|
|
576
|
-
return
|
|
697
|
+
return path6.join(homeDir, MEMORAONE_STATE_DIRNAME);
|
|
577
698
|
}
|
|
578
699
|
function getPathIndexPath(homeDir = os2.homedir()) {
|
|
579
|
-
return
|
|
700
|
+
return path6.join(getMemoraoneStateDir(homeDir), "path-index.json");
|
|
580
701
|
}
|
|
581
702
|
function getBindingsDir(homeDir = os2.homedir()) {
|
|
582
|
-
return
|
|
703
|
+
return path6.join(getMemoraoneStateDir(homeDir), "bindings");
|
|
583
704
|
}
|
|
584
705
|
function getBindingFilePath(repositoryBindingId, homeDir = os2.homedir()) {
|
|
585
|
-
return
|
|
706
|
+
return path6.join(getBindingsDir(homeDir), `${repositoryBindingId}.json`);
|
|
586
707
|
}
|
|
587
708
|
function getLocksDir(homeDir = os2.homedir()) {
|
|
588
|
-
return
|
|
709
|
+
return path6.join(getMemoraoneStateDir(homeDir), "locks");
|
|
589
710
|
}
|
|
590
711
|
function getLockPath(lockName, homeDir = os2.homedir()) {
|
|
591
|
-
return
|
|
712
|
+
return path6.join(getLocksDir(homeDir), `${lockName}.lock`);
|
|
592
713
|
}
|
|
593
714
|
function getGatewayTicketsPath(homeDir = os2.homedir()) {
|
|
594
|
-
return
|
|
715
|
+
return path6.join(getMemoraoneStateDir(homeDir), "gateway-tickets.json");
|
|
595
716
|
}
|
|
596
717
|
|
|
597
718
|
// src/localState/localLocks.ts
|
|
@@ -606,16 +727,16 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
606
727
|
while (retries <= maxRetries) {
|
|
607
728
|
try {
|
|
608
729
|
try {
|
|
609
|
-
const stat5 = await
|
|
730
|
+
const stat5 = await fs4.stat(lockPath);
|
|
610
731
|
if (Date.now() - stat5.mtimeMs > maxLockAgeMs) {
|
|
611
|
-
await
|
|
732
|
+
await fs4.unlink(lockPath);
|
|
612
733
|
}
|
|
613
734
|
} catch (err) {
|
|
614
735
|
if (err?.code !== "ENOENT") {
|
|
615
736
|
throw err;
|
|
616
737
|
}
|
|
617
738
|
}
|
|
618
|
-
const fd = await
|
|
739
|
+
const fd = await fs4.open(lockPath, "wx");
|
|
619
740
|
await fd.writeFile(
|
|
620
741
|
JSON.stringify({
|
|
621
742
|
pid: process.pid,
|
|
@@ -626,7 +747,7 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
626
747
|
await fd.close();
|
|
627
748
|
return async () => {
|
|
628
749
|
try {
|
|
629
|
-
await
|
|
750
|
+
await fs4.unlink(lockPath);
|
|
630
751
|
} catch (err) {
|
|
631
752
|
if (err?.code !== "ENOENT") {
|
|
632
753
|
}
|
|
@@ -640,7 +761,7 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
640
761
|
`[memoraone-mcp] Failed to acquire lock ${lockName} after ${maxRetries} retries`
|
|
641
762
|
);
|
|
642
763
|
}
|
|
643
|
-
await new Promise((
|
|
764
|
+
await new Promise((resolve47) => setTimeout(resolve47, retryDelayMs));
|
|
644
765
|
continue;
|
|
645
766
|
}
|
|
646
767
|
throw err;
|
|
@@ -692,7 +813,7 @@ async function listBindingRecords(homeDir) {
|
|
|
692
813
|
const dir = await ensureBindingsDir(homeDir);
|
|
693
814
|
let names;
|
|
694
815
|
try {
|
|
695
|
-
names = await
|
|
816
|
+
names = await fs5.readdir(dir);
|
|
696
817
|
} catch (err) {
|
|
697
818
|
if (err?.code === "ENOENT") return [];
|
|
698
819
|
throw err;
|
|
@@ -710,9 +831,9 @@ async function listBindingRecords(homeDir) {
|
|
|
710
831
|
return out;
|
|
711
832
|
}
|
|
712
833
|
async function findBindingRecordByWorkspaceRoot(workspaceRoot, homeDir) {
|
|
713
|
-
const resolved =
|
|
834
|
+
const resolved = path7.resolve(workspaceRoot);
|
|
714
835
|
const records = await listBindingRecords(homeDir);
|
|
715
|
-
return records.find((r) =>
|
|
836
|
+
return records.find((r) => path7.resolve(r.workspaceRoot) === resolved) ?? null;
|
|
716
837
|
}
|
|
717
838
|
|
|
718
839
|
// src/localState/installationCredentials.ts
|
|
@@ -857,6 +978,7 @@ function parsePayload(raw, repositoryBindingId) {
|
|
|
857
978
|
optionalString("refreshTokenExpiresAt");
|
|
858
979
|
optionalString("clientRedeemKey");
|
|
859
980
|
optionalString("clientRefreshKey");
|
|
981
|
+
optionalString("supabaseUserId");
|
|
860
982
|
return payload;
|
|
861
983
|
}
|
|
862
984
|
async function readInstallationCredentials(repositoryBindingId, options = {}) {
|
|
@@ -939,12 +1061,12 @@ function hasUsableAccessToken(payload) {
|
|
|
939
1061
|
}
|
|
940
1062
|
|
|
941
1063
|
// src/localState/pathIndex.ts
|
|
942
|
-
var
|
|
1064
|
+
var path9 = __toESM(require("path"), 1);
|
|
943
1065
|
|
|
944
1066
|
// src/localState/rootFilesystemIdentity.ts
|
|
945
|
-
var
|
|
1067
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
946
1068
|
var os3 = __toESM(require("os"), 1);
|
|
947
|
-
var
|
|
1069
|
+
var path8 = __toESM(require("path"), 1);
|
|
948
1070
|
var import_node_child_process = require("child_process");
|
|
949
1071
|
var import_node_util = require("util");
|
|
950
1072
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
@@ -969,13 +1091,13 @@ async function readDarwinDeviceId() {
|
|
|
969
1091
|
}
|
|
970
1092
|
async function readLinuxDeviceId() {
|
|
971
1093
|
try {
|
|
972
|
-
const content = await
|
|
1094
|
+
const content = await fs6.readFile("/etc/machine-id", "utf8");
|
|
973
1095
|
const id = content.trim();
|
|
974
1096
|
if (id) return id;
|
|
975
1097
|
} catch {
|
|
976
1098
|
}
|
|
977
1099
|
try {
|
|
978
|
-
const content = await
|
|
1100
|
+
const content = await fs6.readFile("/var/lib/dbus/machine-id", "utf8");
|
|
979
1101
|
const id = content.trim();
|
|
980
1102
|
if (id) return id;
|
|
981
1103
|
} catch {
|
|
@@ -1008,10 +1130,10 @@ async function resolveDeviceId(platform2 = os3.platform()) {
|
|
|
1008
1130
|
throw new Error(`[memoraone-mcp] Unsupported platform for device ID: ${platform2}`);
|
|
1009
1131
|
}
|
|
1010
1132
|
async function captureRootFilesystemIdentity(rootPath, deps = {}) {
|
|
1011
|
-
const resolved =
|
|
1133
|
+
const resolved = path8.resolve(rootPath);
|
|
1012
1134
|
const platform2 = deps.platform ?? os3.platform();
|
|
1013
1135
|
const statRoot = deps.statRoot ?? (async (p) => {
|
|
1014
|
-
const st2 = await
|
|
1136
|
+
const st2 = await fs6.stat(p);
|
|
1015
1137
|
return {
|
|
1016
1138
|
ino: st2.ino,
|
|
1017
1139
|
dev: st2.dev,
|
|
@@ -1095,7 +1217,7 @@ async function savePathIndex(index, homeDir) {
|
|
|
1095
1217
|
await writeJsonAtomic(getPathIndexPath(homeDir), next);
|
|
1096
1218
|
}
|
|
1097
1219
|
function lookupPathIndex(index, workspaceRoot, identity) {
|
|
1098
|
-
const resolved =
|
|
1220
|
+
const resolved = path9.resolve(workspaceRoot);
|
|
1099
1221
|
const byPathId = index.byPath[resolved];
|
|
1100
1222
|
if (byPathId) {
|
|
1101
1223
|
return { kind: "path", repositoryBindingId: assertRepositoryBindingId(byPathId) };
|
|
@@ -1121,14 +1243,14 @@ function lookupPathIndex(index, workspaceRoot, identity) {
|
|
|
1121
1243
|
}
|
|
1122
1244
|
async function upsertPathIndexEntry(options) {
|
|
1123
1245
|
const repositoryBindingId = assertRepositoryBindingId(options.repositoryBindingId);
|
|
1124
|
-
const resolved =
|
|
1246
|
+
const resolved = path9.resolve(options.workspaceRoot);
|
|
1125
1247
|
const identityKey = filesystemIdentityKey(options.identity);
|
|
1126
1248
|
return withLocalLock(
|
|
1127
1249
|
"path-index",
|
|
1128
1250
|
async () => {
|
|
1129
1251
|
const index = await loadPathIndex(options.homeDir);
|
|
1130
|
-
if (options.previousPath &&
|
|
1131
|
-
delete index.byPath[
|
|
1252
|
+
if (options.previousPath && path9.resolve(options.previousPath) !== resolved) {
|
|
1253
|
+
delete index.byPath[path9.resolve(options.previousPath)];
|
|
1132
1254
|
}
|
|
1133
1255
|
for (const [p, id] of Object.entries(index.byPath)) {
|
|
1134
1256
|
if (id === repositoryBindingId && p !== resolved) {
|
|
@@ -1153,20 +1275,20 @@ function identityMatchesStored(stored, current) {
|
|
|
1153
1275
|
}
|
|
1154
1276
|
|
|
1155
1277
|
// src/localState/localConnectClient.ts
|
|
1156
|
-
var
|
|
1278
|
+
var path10 = __toESM(require("path"), 1);
|
|
1157
1279
|
|
|
1158
1280
|
// src/client/memoraClient.ts
|
|
1159
1281
|
var crypto2 = __toESM(require("crypto"), 1);
|
|
1160
1282
|
var PROJECT_ID_HEADER = "x-project-id";
|
|
1161
1283
|
var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1162
|
-
var
|
|
1284
|
+
var parseBooleanFlag2 = (value) => {
|
|
1163
1285
|
if (!value) {
|
|
1164
1286
|
return false;
|
|
1165
1287
|
}
|
|
1166
1288
|
const normalized = value.trim().toLowerCase();
|
|
1167
1289
|
return ["1", "true", "yes", "on"].includes(normalized);
|
|
1168
1290
|
};
|
|
1169
|
-
var debugEnabled =
|
|
1291
|
+
var debugEnabled = parseBooleanFlag2(process.env.MEMORAONE_DEV_MODE);
|
|
1170
1292
|
async function requestJson(url, method, headers, body) {
|
|
1171
1293
|
const res = await fetch(url, {
|
|
1172
1294
|
method,
|
|
@@ -1264,9 +1386,9 @@ var MemoraClient = class {
|
|
|
1264
1386
|
...options?.headers ?? {}
|
|
1265
1387
|
};
|
|
1266
1388
|
}
|
|
1267
|
-
async perform(method,
|
|
1389
|
+
async perform(method, path53, body, options, retried = false) {
|
|
1268
1390
|
const nonce = crypto2.randomBytes(8).toString("hex");
|
|
1269
|
-
const url = `${this.baseUrl}${
|
|
1391
|
+
const url = `${this.baseUrl}${path53.startsWith("/") ? path53 : `/${path53}`}`;
|
|
1270
1392
|
this.resolveProjectId();
|
|
1271
1393
|
console.error(
|
|
1272
1394
|
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=${method} url=${url}`
|
|
@@ -1293,7 +1415,7 @@ var MemoraClient = class {
|
|
|
1293
1415
|
if (res.status === 401 && !options?.skipAuthRefresh && !retried) {
|
|
1294
1416
|
try {
|
|
1295
1417
|
await this.refreshAccessToken();
|
|
1296
|
-
return this.perform(method,
|
|
1418
|
+
return this.perform(method, path53, body, options, true);
|
|
1297
1419
|
} catch (err) {
|
|
1298
1420
|
if (err instanceof ReconnectRequiredError) {
|
|
1299
1421
|
await this.markReconnectRequired();
|
|
@@ -1313,22 +1435,22 @@ var MemoraClient = class {
|
|
|
1313
1435
|
}
|
|
1314
1436
|
return res.text ? JSON.parse(res.text) : null;
|
|
1315
1437
|
}
|
|
1316
|
-
async post(
|
|
1317
|
-
console.error(`[memoraone-mcp][info] MemoraClient.post ENTER path=${
|
|
1318
|
-
const result = await this.perform("POST",
|
|
1319
|
-
console.error(`[memoraone-mcp][info] MemoraClient.post EXIT path=${
|
|
1438
|
+
async post(path53, body, options) {
|
|
1439
|
+
console.error(`[memoraone-mcp][info] MemoraClient.post ENTER path=${path53}`);
|
|
1440
|
+
const result = await this.perform("POST", path53, body, options);
|
|
1441
|
+
console.error(`[memoraone-mcp][info] MemoraClient.post EXIT path=${path53}`);
|
|
1320
1442
|
return result;
|
|
1321
1443
|
}
|
|
1322
|
-
async get(
|
|
1323
|
-
return this.perform("GET",
|
|
1444
|
+
async get(path53, options) {
|
|
1445
|
+
return this.perform("GET", path53, void 0, options);
|
|
1324
1446
|
}
|
|
1325
1447
|
};
|
|
1326
1448
|
var memoraClient_default = MemoraClient;
|
|
1327
1449
|
|
|
1328
1450
|
// src/localState/localConnectClient.ts
|
|
1329
|
-
async function requestJson2(baseUrl, method,
|
|
1451
|
+
async function requestJson2(baseUrl, method, path53, options = {}) {
|
|
1330
1452
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1331
|
-
const url = `${baseUrl.replace(/\/+$/, "")}${
|
|
1453
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${path53.startsWith("/") ? path53 : `/${path53}`}`;
|
|
1332
1454
|
const res = await fetchImpl(url, {
|
|
1333
1455
|
method,
|
|
1334
1456
|
headers: {
|
|
@@ -1365,7 +1487,7 @@ function normalizePluginPairingSource(source) {
|
|
|
1365
1487
|
return source;
|
|
1366
1488
|
}
|
|
1367
1489
|
function pairingRepositoryName(workspaceRoot) {
|
|
1368
|
-
const repositoryName =
|
|
1490
|
+
const repositoryName = path10.basename(path10.resolve(workspaceRoot)).trim();
|
|
1369
1491
|
if (repositoryName.length === 0 || repositoryName.length > 256 || repositoryName.includes("/") || repositoryName.includes("\\")) {
|
|
1370
1492
|
throw new Error("[memoraone-mcp] Workspace basename is not safe pairing metadata");
|
|
1371
1493
|
}
|
|
@@ -1460,7 +1582,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
1460
1582
|
const installationPublicId = data?.installation_public_id;
|
|
1461
1583
|
const projectId = data?.project_id;
|
|
1462
1584
|
const repositoryBindingId = data?.repository_binding_id;
|
|
1463
|
-
|
|
1585
|
+
const supabaseUserId = data?.supabase_user_id;
|
|
1586
|
+
if (typeof accessToken !== "string" || typeof refreshToken !== "string" || typeof installationPublicId !== "string" || typeof projectId !== "string" || typeof repositoryBindingId !== "string" || typeof supabaseUserId !== "string" || supabaseUserId.length === 0) {
|
|
1464
1587
|
throw new Error("[memoraone-mcp] Invalid redeem response");
|
|
1465
1588
|
}
|
|
1466
1589
|
let sourceId = null;
|
|
@@ -1478,7 +1601,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
1478
1601
|
project_id: projectId,
|
|
1479
1602
|
repository_binding_id: repositoryBindingId,
|
|
1480
1603
|
source_id: sourceId,
|
|
1481
|
-
recovered: Boolean(data.recovered)
|
|
1604
|
+
recovered: Boolean(data.recovered),
|
|
1605
|
+
supabase_user_id: supabaseUserId
|
|
1482
1606
|
};
|
|
1483
1607
|
}
|
|
1484
1608
|
async function refreshLocalMcpToken(apiUrl, body, options = {}) {
|
|
@@ -1500,6 +1624,33 @@ async function refreshLocalMcpToken(apiUrl, body, options = {}) {
|
|
|
1500
1624
|
repository_binding_id: typeof data.repository_binding_id === "string" ? data.repository_binding_id : void 0
|
|
1501
1625
|
};
|
|
1502
1626
|
}
|
|
1627
|
+
var AUTH_IDENTITY_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
1628
|
+
async function fetchAuthIdentity(apiUrl, bearerToken, options = {}) {
|
|
1629
|
+
const res = await requestJson2(apiUrl, "GET", "/auth/identity", {
|
|
1630
|
+
headers: { Authorization: `Bearer ${bearerToken}` },
|
|
1631
|
+
fetchImpl: options.fetchImpl
|
|
1632
|
+
});
|
|
1633
|
+
if (res.status !== 200) {
|
|
1634
|
+
throw new Error("[memoraone-mcp] Invalid auth identity response");
|
|
1635
|
+
}
|
|
1636
|
+
const data = res.json;
|
|
1637
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
1638
|
+
throw new Error("[memoraone-mcp] Invalid auth identity response");
|
|
1639
|
+
}
|
|
1640
|
+
const obj = data;
|
|
1641
|
+
const supabaseUserIdRaw = obj.supabase_user_id;
|
|
1642
|
+
if (obj.authenticated !== true || typeof supabaseUserIdRaw !== "string") {
|
|
1643
|
+
throw new Error("[memoraone-mcp] Invalid auth identity response");
|
|
1644
|
+
}
|
|
1645
|
+
const supabaseUserId = supabaseUserIdRaw.trim();
|
|
1646
|
+
if (!AUTH_IDENTITY_UUID_RE.test(supabaseUserId)) {
|
|
1647
|
+
throw new Error("[memoraone-mcp] Invalid auth identity response");
|
|
1648
|
+
}
|
|
1649
|
+
return {
|
|
1650
|
+
authenticated: true,
|
|
1651
|
+
supabase_user_id: supabaseUserId
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1503
1654
|
|
|
1504
1655
|
// src/localState/tokenRefreshCoordinator.ts
|
|
1505
1656
|
var ReconnectRequiredError = class extends Error {
|
|
@@ -1564,14 +1715,14 @@ async function refreshInstallationAccessToken(options) {
|
|
|
1564
1715
|
|
|
1565
1716
|
// src/localState/resolveLocalBinding.ts
|
|
1566
1717
|
async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
|
|
1567
|
-
const resolved =
|
|
1718
|
+
const resolved = path11.resolve(workspaceRoot);
|
|
1568
1719
|
const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
|
|
1569
1720
|
const index = await loadPathIndex(options.homeDir);
|
|
1570
1721
|
const lookup = lookupPathIndex(index, resolved, identity);
|
|
1571
1722
|
if (lookup.kind === "path") {
|
|
1572
1723
|
const record = await readBindingRecord(lookup.repositoryBindingId, options.homeDir);
|
|
1573
1724
|
if (record && identityMatchesStored(record.filesystemIdentity, identity)) {
|
|
1574
|
-
if (
|
|
1725
|
+
if (path11.resolve(record.workspaceRoot) !== resolved) {
|
|
1575
1726
|
const updated = {
|
|
1576
1727
|
...record,
|
|
1577
1728
|
workspaceRoot: resolved,
|
|
@@ -1630,7 +1781,7 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
|
|
|
1630
1781
|
};
|
|
1631
1782
|
}
|
|
1632
1783
|
async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
|
|
1633
|
-
const resolved =
|
|
1784
|
+
const resolved = path11.resolve(workspaceRoot);
|
|
1634
1785
|
const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
|
|
1635
1786
|
const index = await loadPathIndex(options.homeDir);
|
|
1636
1787
|
const lookup = lookupPathIndex(index, resolved, identity);
|
|
@@ -1641,7 +1792,7 @@ async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
|
|
|
1641
1792
|
if (!record) {
|
|
1642
1793
|
return { status: "unbound", workspaceRoot: resolved };
|
|
1643
1794
|
}
|
|
1644
|
-
if (
|
|
1795
|
+
if (path11.resolve(record.workspaceRoot) !== resolved) {
|
|
1645
1796
|
return { status: "unbound", workspaceRoot: resolved };
|
|
1646
1797
|
}
|
|
1647
1798
|
if (!identityMatchesStored(record.filesystemIdentity, identity)) {
|
|
@@ -1654,7 +1805,7 @@ async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
|
|
|
1654
1805
|
};
|
|
1655
1806
|
}
|
|
1656
1807
|
async function resolveLocalBinding(workspaceRoot, options = {}) {
|
|
1657
|
-
const resolved =
|
|
1808
|
+
const resolved = path11.resolve(workspaceRoot);
|
|
1658
1809
|
const { repositoryBindingId } = await ensureRepositoryBindingForRoot(
|
|
1659
1810
|
resolved,
|
|
1660
1811
|
{ ...options, createIfMissing: false }
|
|
@@ -1740,12 +1891,12 @@ async function parseGatewayWorkspaceRoot(wsRaw) {
|
|
|
1740
1891
|
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
|
|
1741
1892
|
return fail("malformed_ws", "ws must be an absolute filesystem path");
|
|
1742
1893
|
}
|
|
1743
|
-
if (!
|
|
1894
|
+
if (!path12.isAbsolute(trimmed)) {
|
|
1744
1895
|
return fail("non_absolute", "ws must be an absolute workspace root");
|
|
1745
1896
|
}
|
|
1746
|
-
const workspaceRoot =
|
|
1897
|
+
const workspaceRoot = path12.resolve(trimmed);
|
|
1747
1898
|
try {
|
|
1748
|
-
const st = await
|
|
1899
|
+
const st = await fs7.stat(workspaceRoot);
|
|
1749
1900
|
if (!st.isDirectory()) {
|
|
1750
1901
|
return fail("not_directory", "ws must point to an existing directory");
|
|
1751
1902
|
}
|
|
@@ -1768,7 +1919,7 @@ async function parseGatewayWorkspaceUrl(input2) {
|
|
|
1768
1919
|
|
|
1769
1920
|
// src/localState/gatewayTicketStore.ts
|
|
1770
1921
|
var import_node_crypto5 = require("crypto");
|
|
1771
|
-
var
|
|
1922
|
+
var path13 = __toESM(require("path"), 1);
|
|
1772
1923
|
var GATEWAY_TICKETS_VERSION = 1;
|
|
1773
1924
|
var GATEWAY_TICKETS_LOCK_NAME = "gateway-tickets";
|
|
1774
1925
|
var MGT_PREFIX = "mgt_";
|
|
@@ -1862,7 +2013,7 @@ async function resolveGatewayTicketScope(input2, options = {}) {
|
|
|
1862
2013
|
if (!("workspaceRoot" in input2) || input2.workspaceRoot == null) {
|
|
1863
2014
|
return fail2("invalid_workspace", "workspaceRoot or canonicalUrl is required");
|
|
1864
2015
|
}
|
|
1865
|
-
const parsed2 = await parseGatewayWorkspaceRoot(
|
|
2016
|
+
const parsed2 = await parseGatewayWorkspaceRoot(path13.resolve(input2.workspaceRoot));
|
|
1866
2017
|
if (!parsed2.ok) {
|
|
1867
2018
|
return fail2("invalid_workspace", parsed2.error);
|
|
1868
2019
|
}
|
|
@@ -1959,101 +2110,6 @@ async function revokeGatewaySetupTicket(ticket, options = {}) {
|
|
|
1959
2110
|
// src/pluginPairing.ts
|
|
1960
2111
|
var path47 = __toESM(require("path"), 1);
|
|
1961
2112
|
|
|
1962
|
-
// src/config.ts
|
|
1963
|
-
var process2 = __toESM(require("process"), 1);
|
|
1964
|
-
var fs7 = __toESM(require("fs"), 1);
|
|
1965
|
-
var path13 = __toESM(require("path"), 1);
|
|
1966
|
-
var dotenv = __toESM(require("dotenv"), 1);
|
|
1967
|
-
var import_v4 = require("zod/v4");
|
|
1968
|
-
|
|
1969
|
-
// src/configUtils.ts
|
|
1970
|
-
var DEFAULT_API_URL = "https://api.memoraone.com";
|
|
1971
|
-
var DEV_API_URL = "http://localhost:3001";
|
|
1972
|
-
function resolveApiUrl(env2) {
|
|
1973
|
-
const explicitUrl = env2.MEMORAONE_API_URL?.trim();
|
|
1974
|
-
if (explicitUrl) {
|
|
1975
|
-
return explicitUrl;
|
|
1976
|
-
}
|
|
1977
|
-
const aliasUrl = env2.MEMORA_API_URL?.trim();
|
|
1978
|
-
if (aliasUrl) {
|
|
1979
|
-
return aliasUrl;
|
|
1980
|
-
}
|
|
1981
|
-
if (env2.MEMORAONE_DEV_MODE === "1") {
|
|
1982
|
-
return DEV_API_URL;
|
|
1983
|
-
}
|
|
1984
|
-
return DEFAULT_API_URL;
|
|
1985
|
-
}
|
|
1986
|
-
|
|
1987
|
-
// src/config.ts
|
|
1988
|
-
var dotenvPath = path13.resolve(process2.cwd(), ".env");
|
|
1989
|
-
if (fs7.existsSync(dotenvPath)) {
|
|
1990
|
-
try {
|
|
1991
|
-
dotenv.config({ path: dotenvPath });
|
|
1992
|
-
} catch (err) {
|
|
1993
|
-
process2.stderr.write("[memoraone-mcp] Failed to load .env: " + String(err) + "\n");
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
var EnvSchema = import_v4.z.object({
|
|
1997
|
-
MEMORAONE_API_URL: import_v4.z.string().url().optional(),
|
|
1998
|
-
MEMORAONE_API_KEY: import_v4.z.string().min(1).optional(),
|
|
1999
|
-
MEMORAONE_DEV_MODE: import_v4.z.string().min(1).optional(),
|
|
2000
|
-
MEMORAONE_AGENT_NAME: import_v4.z.string().min(1).optional(),
|
|
2001
|
-
MEMORAONE_AGENT_TYPE: import_v4.z.string().min(1).optional(),
|
|
2002
|
-
MEMORAONE_SOURCE: import_v4.z.string().min(1).optional(),
|
|
2003
|
-
MEMORAONE_IDE_TYPE: import_v4.z.enum(IDE_TYPES).optional(),
|
|
2004
|
-
MEMORAONE_WORKLOG: import_v4.z.string().min(1).optional(),
|
|
2005
|
-
MEMORAONE_HEARTBEAT: import_v4.z.string().min(1).optional(),
|
|
2006
|
-
MEMORAONE_HEARTBEAT_INTERVAL_MS: import_v4.z.string().min(1).optional()
|
|
2007
|
-
});
|
|
2008
|
-
var requiredEnvVars = [];
|
|
2009
|
-
var missingEnvVars = requiredEnvVars.filter((key) => {
|
|
2010
|
-
const value = process2.env[key];
|
|
2011
|
-
return value === void 0 || value.trim() === "";
|
|
2012
|
-
});
|
|
2013
|
-
if (missingEnvVars.length > 0) {
|
|
2014
|
-
for (const key of missingEnvVars) {
|
|
2015
|
-
process2.stderr.write(`Missing ${key}
|
|
2016
|
-
`);
|
|
2017
|
-
}
|
|
2018
|
-
process2.exit(1);
|
|
2019
|
-
}
|
|
2020
|
-
var parsed = EnvSchema.safeParse(process2.env);
|
|
2021
|
-
var resolvedApiUrl = resolveApiUrl(process2.env);
|
|
2022
|
-
if (!parsed.success) {
|
|
2023
|
-
const formatted = parsed.error.format();
|
|
2024
|
-
process2.stderr.write(
|
|
2025
|
-
"[memoraone-mcp] Invalid environment variables " + JSON.stringify(formatted) + "\n"
|
|
2026
|
-
);
|
|
2027
|
-
throw new Error("Config validation failed");
|
|
2028
|
-
}
|
|
2029
|
-
var parseBooleanFlag2 = (value, defaultValue) => {
|
|
2030
|
-
if (value === void 0) {
|
|
2031
|
-
return defaultValue;
|
|
2032
|
-
}
|
|
2033
|
-
const normalized = value.trim().toLowerCase();
|
|
2034
|
-
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
2035
|
-
return true;
|
|
2036
|
-
}
|
|
2037
|
-
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
2038
|
-
return false;
|
|
2039
|
-
}
|
|
2040
|
-
return defaultValue;
|
|
2041
|
-
};
|
|
2042
|
-
var config2 = {
|
|
2043
|
-
apiUrl: resolvedApiUrl.replace(/\/+$/, ""),
|
|
2044
|
-
apiKey: parsed.data.MEMORAONE_API_KEY,
|
|
2045
|
-
agentName: parsed.data.MEMORAONE_AGENT_NAME ?? "cursor",
|
|
2046
|
-
agentType: parsed.data.MEMORAONE_AGENT_TYPE ?? "agent",
|
|
2047
|
-
source: parsed.data.MEMORAONE_SOURCE ?? "cursor",
|
|
2048
|
-
ideType: parsed.data.MEMORAONE_IDE_TYPE,
|
|
2049
|
-
devMode: parseBooleanFlag2(parsed.data.MEMORAONE_DEV_MODE, false),
|
|
2050
|
-
worklogEnabled: parseBooleanFlag2(parsed.data.MEMORAONE_WORKLOG, true),
|
|
2051
|
-
heartbeatEnabled: parseBooleanFlag2(parsed.data.MEMORAONE_HEARTBEAT, true),
|
|
2052
|
-
// Cadence is owned by LOCAL_MCP_HEARTBEAT_INTERVAL_MS in heartbeat.ts (1_000).
|
|
2053
|
-
// Env override is accepted for forward-compat but the timer path ignores it.
|
|
2054
|
-
heartbeatIntervalMs: Number.parseInt(parsed.data.MEMORAONE_HEARTBEAT_INTERVAL_MS ?? "1000", 10)
|
|
2055
|
-
};
|
|
2056
|
-
|
|
2057
2113
|
// src/localState/connectCommand.ts
|
|
2058
2114
|
var fs36 = __toESM(require("fs/promises"), 1);
|
|
2059
2115
|
var path46 = __toESM(require("path"), 1);
|
|
@@ -4109,7 +4165,7 @@ function formatOptionalJetBrainsHandshakeUnavailableDetail(detail) {
|
|
|
4109
4165
|
async function verifyJetBrainsMcpHandshake(options) {
|
|
4110
4166
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
4111
4167
|
const { server } = options;
|
|
4112
|
-
return new Promise((
|
|
4168
|
+
return new Promise((resolve47) => {
|
|
4113
4169
|
let settled = false;
|
|
4114
4170
|
const finish = (ok, detail, optionalUnavailable) => {
|
|
4115
4171
|
if (settled) return;
|
|
@@ -4119,7 +4175,7 @@ async function verifyJetBrainsMcpHandshake(options) {
|
|
|
4119
4175
|
child.kill();
|
|
4120
4176
|
} catch {
|
|
4121
4177
|
}
|
|
4122
|
-
|
|
4178
|
+
resolve47({ ok, detail, optionalUnavailable });
|
|
4123
4179
|
};
|
|
4124
4180
|
const child = (0, import_node_child_process4.spawn)(server.command, [...server.args], {
|
|
4125
4181
|
env: { ...process.env, ...server.env },
|
|
@@ -10663,7 +10719,8 @@ async function runConnectCommand(options) {
|
|
|
10663
10719
|
accessTokenExpiresAt: redeemed.access_token_expires_at ?? void 0,
|
|
10664
10720
|
refreshTokenExpiresAt: redeemed.refresh_token_expires_at ?? void 0,
|
|
10665
10721
|
installationPublicId: redeemed.installation_public_id,
|
|
10666
|
-
projectId: redeemed.project_id
|
|
10722
|
+
projectId: redeemed.project_id,
|
|
10723
|
+
supabaseUserId: redeemed.supabase_user_id
|
|
10667
10724
|
},
|
|
10668
10725
|
{ ...options.credentialOptions, clearKeys: ["clientRedeemKey"] }
|
|
10669
10726
|
);
|
|
@@ -11108,7 +11165,7 @@ function html(res, status, body) {
|
|
|
11108
11165
|
res.end(body);
|
|
11109
11166
|
}
|
|
11110
11167
|
function readUrlEncodedBody(req) {
|
|
11111
|
-
return new Promise((
|
|
11168
|
+
return new Promise((resolve47, reject) => {
|
|
11112
11169
|
const chunks = [];
|
|
11113
11170
|
let size = 0;
|
|
11114
11171
|
req.on("data", (chunk) => {
|
|
@@ -11121,13 +11178,13 @@ function readUrlEncodedBody(req) {
|
|
|
11121
11178
|
chunks.push(chunk);
|
|
11122
11179
|
});
|
|
11123
11180
|
req.on("end", () => {
|
|
11124
|
-
|
|
11181
|
+
resolve47(new URLSearchParams(Buffer.concat(chunks).toString("utf8")));
|
|
11125
11182
|
});
|
|
11126
11183
|
req.on("error", reject);
|
|
11127
11184
|
});
|
|
11128
11185
|
}
|
|
11129
11186
|
function readJsonBody(req) {
|
|
11130
|
-
return new Promise((
|
|
11187
|
+
return new Promise((resolve47, reject) => {
|
|
11131
11188
|
const chunks = [];
|
|
11132
11189
|
let size = 0;
|
|
11133
11190
|
req.on("data", (chunk) => {
|
|
@@ -11142,11 +11199,11 @@ function readJsonBody(req) {
|
|
|
11142
11199
|
req.on("end", () => {
|
|
11143
11200
|
const raw = Buffer.concat(chunks).toString("utf8");
|
|
11144
11201
|
if (!raw.trim()) {
|
|
11145
|
-
|
|
11202
|
+
resolve47({});
|
|
11146
11203
|
return;
|
|
11147
11204
|
}
|
|
11148
11205
|
try {
|
|
11149
|
-
|
|
11206
|
+
resolve47(JSON.parse(raw));
|
|
11150
11207
|
} catch {
|
|
11151
11208
|
reject(new Error("invalid json"));
|
|
11152
11209
|
}
|
|
@@ -11158,12 +11215,12 @@ function opaqueId(prefix) {
|
|
|
11158
11215
|
return `${prefix}${(0, import_node_crypto28.randomBytes)(24).toString("base64url")}`;
|
|
11159
11216
|
}
|
|
11160
11217
|
function delay(ms) {
|
|
11161
|
-
return new Promise((
|
|
11218
|
+
return new Promise((resolve47) => setTimeout(resolve47, ms));
|
|
11162
11219
|
}
|
|
11163
|
-
function buildProtectedResourceMetadata(env2 = process.env) {
|
|
11220
|
+
function buildProtectedResourceMetadata(env2 = process.env, workspaceRoot) {
|
|
11164
11221
|
return {
|
|
11165
|
-
resource: getGatewayResourceBaseUrl(env2),
|
|
11166
|
-
authorization_servers: [
|
|
11222
|
+
resource: workspaceRoot === void 0 ? getGatewayResourceBaseUrl(env2) : buildGatewayMcpUrl(workspaceRoot, env2),
|
|
11223
|
+
authorization_servers: [getGatewayAuthorizationServerIssuerUrl(env2)]
|
|
11167
11224
|
};
|
|
11168
11225
|
}
|
|
11169
11226
|
function buildAuthorizationServerMetadata(env2 = process.env) {
|
|
@@ -11287,11 +11344,38 @@ var GatewayOAuthSurface = class {
|
|
|
11287
11344
|
return;
|
|
11288
11345
|
}
|
|
11289
11346
|
}
|
|
11347
|
+
/**
|
|
11348
|
+
* RFC 9728 PRM. Generic probes (no `ws`) advertise the bare /mcp resource.
|
|
11349
|
+
* `?ws=` reuses gateway workspace identity; malformed/nonexistent fail closed.
|
|
11350
|
+
*/
|
|
11351
|
+
async handleProtectedResourceMetadata(res, url) {
|
|
11352
|
+
const wsOrError = getGatewayWsQueryParam(url);
|
|
11353
|
+
if (wsOrError !== null && typeof wsOrError === "object" && wsOrError.ok === false) {
|
|
11354
|
+
json(res, 404, {
|
|
11355
|
+
error: "invalid_request",
|
|
11356
|
+
error_description: "resource workspace is invalid"
|
|
11357
|
+
});
|
|
11358
|
+
return;
|
|
11359
|
+
}
|
|
11360
|
+
if (wsOrError === null) {
|
|
11361
|
+
json(res, 200, buildProtectedResourceMetadata(this.env));
|
|
11362
|
+
return;
|
|
11363
|
+
}
|
|
11364
|
+
const parsed2 = await parseGatewayWorkspaceRoot(wsOrError);
|
|
11365
|
+
if (!parsed2.ok) {
|
|
11366
|
+
json(res, 404, {
|
|
11367
|
+
error: "invalid_request",
|
|
11368
|
+
error_description: "resource workspace is invalid"
|
|
11369
|
+
});
|
|
11370
|
+
return;
|
|
11371
|
+
}
|
|
11372
|
+
json(res, 200, buildProtectedResourceMetadata(this.env, parsed2.workspaceRoot));
|
|
11373
|
+
}
|
|
11290
11374
|
async handleRequest(req, res, url) {
|
|
11291
11375
|
const { pathname } = url;
|
|
11292
11376
|
const method = req.method ?? "GET";
|
|
11293
11377
|
if (method === "GET" && (pathname === GATEWAY_OAUTH_PRM_PATH || pathname === GATEWAY_OAUTH_PRM_ROOT_PATH)) {
|
|
11294
|
-
|
|
11378
|
+
await this.handleProtectedResourceMetadata(res, url);
|
|
11295
11379
|
return true;
|
|
11296
11380
|
}
|
|
11297
11381
|
if (method === "GET" && pathname === GATEWAY_OAUTH_AS_METADATA_PATH) {
|
|
@@ -11769,11 +11853,11 @@ async function isRetryablePluginStartupBindingError(err, options = {}) {
|
|
|
11769
11853
|
return false;
|
|
11770
11854
|
}
|
|
11771
11855
|
function defaultSleep(ms) {
|
|
11772
|
-
return new Promise((
|
|
11773
|
-
setTimeout(
|
|
11856
|
+
return new Promise((resolve47) => {
|
|
11857
|
+
setTimeout(resolve47, ms);
|
|
11774
11858
|
});
|
|
11775
11859
|
}
|
|
11776
|
-
async function waitForPluginStartupBinding(
|
|
11860
|
+
async function waitForPluginStartupBinding(resolve47, options = {}) {
|
|
11777
11861
|
const now = options.now ?? Date.now;
|
|
11778
11862
|
const sleep = options.sleep ?? defaultSleep;
|
|
11779
11863
|
const timeoutMs = options.timeoutMs ?? PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS;
|
|
@@ -11783,7 +11867,7 @@ async function waitForPluginStartupBinding(resolve46, options = {}) {
|
|
|
11783
11867
|
let lastError;
|
|
11784
11868
|
while (true) {
|
|
11785
11869
|
try {
|
|
11786
|
-
const binding = await
|
|
11870
|
+
const binding = await resolve47();
|
|
11787
11871
|
if (waitingLogged) {
|
|
11788
11872
|
options.log?.(
|
|
11789
11873
|
`local MemoraOne binding available after Cursor plugin pairing (${now() - startedAt}ms)`
|
|
@@ -12263,9 +12347,9 @@ function buildPersonalContextPath(parsed2) {
|
|
|
12263
12347
|
}
|
|
12264
12348
|
async function handleGetPersonalContext(client, args) {
|
|
12265
12349
|
const parsed2 = getPersonalContextInputSchema.parse(args ?? {});
|
|
12266
|
-
const
|
|
12350
|
+
const path53 = buildPersonalContextPath(parsed2);
|
|
12267
12351
|
try {
|
|
12268
|
-
const result = await client.get(
|
|
12352
|
+
const result = await client.get(path53);
|
|
12269
12353
|
return { ok: true, result };
|
|
12270
12354
|
} catch (err) {
|
|
12271
12355
|
if (err instanceof MemoraOneHttpError) {
|
|
@@ -12737,8 +12821,8 @@ async function handleCreateConceptVersion(client, args) {
|
|
|
12737
12821
|
if (input2.tags !== void 0) body.tags = input2.tags;
|
|
12738
12822
|
if (input2.parent_id !== void 0) body.parent_id = input2.parent_id;
|
|
12739
12823
|
try {
|
|
12740
|
-
const
|
|
12741
|
-
return projectCreatedConceptVersionResponse(await client.post(
|
|
12824
|
+
const path53 = `/concepts/${encodeURIComponent(input2.id)}/versions`;
|
|
12825
|
+
return projectCreatedConceptVersionResponse(await client.post(path53, body));
|
|
12742
12826
|
} catch (error) {
|
|
12743
12827
|
rethrowToolRequestError("memora_create_concept_version", error);
|
|
12744
12828
|
}
|
|
@@ -12773,7 +12857,7 @@ function handleBindingStatus(binding, options = {}) {
|
|
|
12773
12857
|
// src/tools/handlers/finishSetup.ts
|
|
12774
12858
|
var DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
12775
12859
|
function delay2(ms) {
|
|
12776
|
-
return new Promise((
|
|
12860
|
+
return new Promise((resolve47) => setTimeout(resolve47, ms));
|
|
12777
12861
|
}
|
|
12778
12862
|
async function pollPairingSessionInBackground(session, pollIntervalMs, onReady) {
|
|
12779
12863
|
for (; ; ) {
|
|
@@ -13066,8 +13150,8 @@ function registerToolWithWorklog(server, runtime, sessionContext, toolName, desc
|
|
|
13066
13150
|
function createMemoraMcpServer(opts = {}) {
|
|
13067
13151
|
let bindingReadyResolve = null;
|
|
13068
13152
|
let bindingReadyReject = null;
|
|
13069
|
-
const bindingReady = new Promise((
|
|
13070
|
-
bindingReadyResolve =
|
|
13153
|
+
const bindingReady = new Promise((resolve47, reject) => {
|
|
13154
|
+
bindingReadyResolve = resolve47;
|
|
13071
13155
|
bindingReadyReject = reject;
|
|
13072
13156
|
});
|
|
13073
13157
|
const devMode = Boolean(config2.devMode);
|
|
@@ -13582,8 +13666,8 @@ function sendJsonRpcHttpError(res, status, message) {
|
|
|
13582
13666
|
});
|
|
13583
13667
|
res.end(body);
|
|
13584
13668
|
}
|
|
13585
|
-
function sendUnauthorizedChallenge(res, env2) {
|
|
13586
|
-
const challenge = buildGatewayMcpWwwAuthenticate(env2);
|
|
13669
|
+
function sendUnauthorizedChallenge(res, env2, workspaceRoot) {
|
|
13670
|
+
const challenge = buildGatewayMcpWwwAuthenticate(env2, workspaceRoot);
|
|
13587
13671
|
const body = "Unauthorized";
|
|
13588
13672
|
res.writeHead(401, {
|
|
13589
13673
|
"www-authenticate": challenge,
|
|
@@ -13737,7 +13821,7 @@ async function handleAuthenticatedGatewayMcpRequest(req, res, auth, options = {}
|
|
|
13737
13821
|
const binding = await loadGatewayHttpBinding(workspaceRoot, options);
|
|
13738
13822
|
if (!binding) {
|
|
13739
13823
|
await closeGatewayMcpSessionsForWorkspace(workspaceRoot);
|
|
13740
|
-
sendUnauthorizedChallenge(res, env2);
|
|
13824
|
+
sendUnauthorizedChallenge(res, env2, workspaceRoot);
|
|
13741
13825
|
return;
|
|
13742
13826
|
}
|
|
13743
13827
|
const sessionHeader = req.headers["mcp-session-id"];
|
|
@@ -13753,7 +13837,7 @@ async function handleAuthenticatedGatewayMcpRequest(req, res, auth, options = {}
|
|
|
13753
13837
|
return;
|
|
13754
13838
|
}
|
|
13755
13839
|
if (!await ensureBindingStillValid(existing, options)) {
|
|
13756
|
-
sendUnauthorizedChallenge(res, env2);
|
|
13840
|
+
sendUnauthorizedChallenge(res, env2, workspaceRoot);
|
|
13757
13841
|
return;
|
|
13758
13842
|
}
|
|
13759
13843
|
if (method === "POST") {
|
|
@@ -13810,8 +13894,8 @@ function extractBearerToken(authorizationHeader) {
|
|
|
13810
13894
|
const token = match[1];
|
|
13811
13895
|
return token || null;
|
|
13812
13896
|
}
|
|
13813
|
-
function sendUnauthorized(res, env2) {
|
|
13814
|
-
const challenge = buildGatewayMcpWwwAuthenticate(env2);
|
|
13897
|
+
function sendUnauthorized(res, env2, workspaceRoot) {
|
|
13898
|
+
const challenge = buildGatewayMcpWwwAuthenticate(env2, workspaceRoot);
|
|
13815
13899
|
const body = "Unauthorized";
|
|
13816
13900
|
res.writeHead(401, {
|
|
13817
13901
|
"www-authenticate": challenge,
|
|
@@ -13821,6 +13905,14 @@ function sendUnauthorized(res, env2) {
|
|
|
13821
13905
|
});
|
|
13822
13906
|
res.end(body);
|
|
13823
13907
|
}
|
|
13908
|
+
function sendPairingGate(res, body) {
|
|
13909
|
+
res.writeHead(403, {
|
|
13910
|
+
"content-type": "text/plain; charset=utf-8",
|
|
13911
|
+
"cache-control": "no-store",
|
|
13912
|
+
"content-length": Buffer.byteLength(body)
|
|
13913
|
+
});
|
|
13914
|
+
res.end(body);
|
|
13915
|
+
}
|
|
13824
13916
|
async function handleGatewayMcpAuthRequest(req, res, url, options = {}) {
|
|
13825
13917
|
if (url.pathname !== GATEWAY_MCP_PATH) return false;
|
|
13826
13918
|
const env2 = options.env ?? process.env;
|
|
@@ -13830,6 +13922,15 @@ async function handleGatewayMcpAuthRequest(req, res, url, options = {}) {
|
|
|
13830
13922
|
env: options.env ?? options.ticketOptions?.env ?? env2,
|
|
13831
13923
|
...options.ticketOptions
|
|
13832
13924
|
};
|
|
13925
|
+
const homeDir = options.homeDir ?? ticketOptions.homeDir;
|
|
13926
|
+
const identityDeps = options.identityDeps ?? ticketOptions.identityDeps;
|
|
13927
|
+
const transportOptions = {
|
|
13928
|
+
env: env2,
|
|
13929
|
+
homeDir,
|
|
13930
|
+
identityDeps,
|
|
13931
|
+
ticketOptions,
|
|
13932
|
+
createMemoraClient: options.createMemoraClient
|
|
13933
|
+
};
|
|
13833
13934
|
const parsed2 = await parseGatewayWorkspaceUrl(url);
|
|
13834
13935
|
if (!parsed2.ok) {
|
|
13835
13936
|
sendUnauthorized(res, env2);
|
|
@@ -13838,34 +13939,224 @@ async function handleGatewayMcpAuthRequest(req, res, url, options = {}) {
|
|
|
13838
13939
|
const canonicalUrl = buildGatewayMcpUrl(parsed2.workspaceRoot, env2);
|
|
13839
13940
|
const token = extractBearerToken(req.headers.authorization);
|
|
13840
13941
|
if (!token) {
|
|
13841
|
-
sendUnauthorized(res, env2);
|
|
13942
|
+
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
13842
13943
|
return true;
|
|
13843
13944
|
}
|
|
13844
|
-
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
13945
|
+
if (token.startsWith("mgt_")) {
|
|
13946
|
+
const verified = await verifyGatewaySetupTicket(
|
|
13947
|
+
token,
|
|
13948
|
+
{ canonicalUrl },
|
|
13949
|
+
ticketOptions
|
|
13950
|
+
);
|
|
13951
|
+
if (!verified.ok) {
|
|
13952
|
+
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
13953
|
+
return true;
|
|
13954
|
+
}
|
|
13955
|
+
await handleAuthenticatedGatewayMcpRequest(
|
|
13956
|
+
req,
|
|
13957
|
+
res,
|
|
13958
|
+
{
|
|
13959
|
+
workspaceRoot: verified.workspaceRoot,
|
|
13960
|
+
canonicalUrl: verified.canonicalUrl
|
|
13961
|
+
},
|
|
13962
|
+
transportOptions
|
|
13963
|
+
);
|
|
13851
13964
|
return true;
|
|
13852
13965
|
}
|
|
13853
|
-
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
canonicalUrl: verified.canonicalUrl
|
|
13859
|
-
},
|
|
13860
|
-
{
|
|
13861
|
-
env: env2,
|
|
13862
|
-
homeDir: options.homeDir ?? ticketOptions.homeDir,
|
|
13863
|
-
identityDeps: options.identityDeps ?? ticketOptions.identityDeps,
|
|
13864
|
-
ticketOptions,
|
|
13865
|
-
createMemoraClient: options.createMemoraClient
|
|
13966
|
+
try {
|
|
13967
|
+
const rawApiUrl = options.apiUrl;
|
|
13968
|
+
if (typeof rawApiUrl !== "string" || rawApiUrl.trim().length === 0) {
|
|
13969
|
+
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
13970
|
+
return true;
|
|
13866
13971
|
}
|
|
13867
|
-
|
|
13868
|
-
|
|
13972
|
+
const apiUrl = rawApiUrl.replace(/\/+$/, "");
|
|
13973
|
+
let binding = await lookupLocalBindingIdentity(parsed2.workspaceRoot, {
|
|
13974
|
+
homeDir,
|
|
13975
|
+
identityDeps
|
|
13976
|
+
});
|
|
13977
|
+
if (binding.status !== "resolved") {
|
|
13978
|
+
await fetchAuthIdentity(apiUrl, token, {
|
|
13979
|
+
fetchImpl: options.fetchImpl
|
|
13980
|
+
});
|
|
13981
|
+
const coordinator = options.unboundPairing;
|
|
13982
|
+
if (!coordinator) {
|
|
13983
|
+
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
13984
|
+
return true;
|
|
13985
|
+
}
|
|
13986
|
+
const pairing = await coordinator.ensure(parsed2.workspaceRoot);
|
|
13987
|
+
if (pairing.status === "pending") {
|
|
13988
|
+
sendPairingGate(res, "Pairing pending");
|
|
13989
|
+
return true;
|
|
13990
|
+
}
|
|
13991
|
+
if (pairing.status === "failed") {
|
|
13992
|
+
sendPairingGate(res, "Setup required");
|
|
13993
|
+
return true;
|
|
13994
|
+
}
|
|
13995
|
+
binding = await lookupLocalBindingIdentity(parsed2.workspaceRoot, {
|
|
13996
|
+
homeDir,
|
|
13997
|
+
identityDeps
|
|
13998
|
+
});
|
|
13999
|
+
if (binding.status !== "resolved") {
|
|
14000
|
+
sendPairingGate(res, "Setup required");
|
|
14001
|
+
return true;
|
|
14002
|
+
}
|
|
14003
|
+
}
|
|
14004
|
+
const creds = await readInstallationCredentials(binding.repositoryBindingId);
|
|
14005
|
+
const localOwnerId = creds?.supabaseUserId;
|
|
14006
|
+
if (typeof localOwnerId !== "string" || localOwnerId.length === 0) {
|
|
14007
|
+
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
14008
|
+
return true;
|
|
14009
|
+
}
|
|
14010
|
+
const identity = await fetchAuthIdentity(apiUrl, token, {
|
|
14011
|
+
fetchImpl: options.fetchImpl
|
|
14012
|
+
});
|
|
14013
|
+
if (identity.supabase_user_id !== localOwnerId) {
|
|
14014
|
+
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
14015
|
+
return true;
|
|
14016
|
+
}
|
|
14017
|
+
await handleAuthenticatedGatewayMcpRequest(
|
|
14018
|
+
req,
|
|
14019
|
+
res,
|
|
14020
|
+
{
|
|
14021
|
+
workspaceRoot: parsed2.workspaceRoot,
|
|
14022
|
+
canonicalUrl
|
|
14023
|
+
},
|
|
14024
|
+
transportOptions
|
|
14025
|
+
);
|
|
14026
|
+
return true;
|
|
14027
|
+
} catch {
|
|
14028
|
+
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
14029
|
+
return true;
|
|
14030
|
+
}
|
|
14031
|
+
}
|
|
14032
|
+
|
|
14033
|
+
// src/gatewayUnboundPairing.ts
|
|
14034
|
+
var path51 = __toESM(require("path"), 1);
|
|
14035
|
+
|
|
14036
|
+
// src/openUrlInBrowser.ts
|
|
14037
|
+
var import_node_child_process7 = require("child_process");
|
|
14038
|
+
var import_node_util6 = require("util");
|
|
14039
|
+
var execFileAsync6 = (0, import_node_util6.promisify)(import_node_child_process7.execFile);
|
|
14040
|
+
function assertHttpOrHttpsUrl(url) {
|
|
14041
|
+
let parsed2;
|
|
14042
|
+
try {
|
|
14043
|
+
parsed2 = new URL(url);
|
|
14044
|
+
} catch {
|
|
14045
|
+
throw new Error("URL must be an absolute http: or https: URL");
|
|
14046
|
+
}
|
|
14047
|
+
if (parsed2.protocol !== "http:" && parsed2.protocol !== "https:") {
|
|
14048
|
+
throw new Error("URL must be an absolute http: or https: URL");
|
|
14049
|
+
}
|
|
14050
|
+
}
|
|
14051
|
+
async function openUrlInBrowser(url, deps = {}) {
|
|
14052
|
+
assertHttpOrHttpsUrl(url);
|
|
14053
|
+
const platform2 = deps.platform ?? process.platform;
|
|
14054
|
+
const execFileImpl = deps.execFile ?? execFileAsync6;
|
|
14055
|
+
if (platform2 === "darwin") {
|
|
14056
|
+
await execFileImpl("open", [url]);
|
|
14057
|
+
return;
|
|
14058
|
+
}
|
|
14059
|
+
if (platform2 === "win32") {
|
|
14060
|
+
await execFileImpl("cmd", ["/c", "start", "", url]);
|
|
14061
|
+
return;
|
|
14062
|
+
}
|
|
14063
|
+
await execFileImpl("xdg-open", [url]);
|
|
14064
|
+
}
|
|
14065
|
+
|
|
14066
|
+
// src/gatewayUnboundPairing.ts
|
|
14067
|
+
var DEFAULT_POLL_INTERVAL_MS2 = 1e3;
|
|
14068
|
+
function delay3(ms) {
|
|
14069
|
+
return new Promise((resolve47) => {
|
|
14070
|
+
const timer = setTimeout(resolve47, ms);
|
|
14071
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
14072
|
+
});
|
|
14073
|
+
}
|
|
14074
|
+
function createGatewayUnboundPairingCoordinator(options = {}) {
|
|
14075
|
+
const latches = /* @__PURE__ */ new Map();
|
|
14076
|
+
const beginPairing = options.beginPairing ?? beginPluginRepositoryPairing;
|
|
14077
|
+
const openUrl = options.openUrl ?? ((url) => openUrlInBrowser(url));
|
|
14078
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
|
|
14079
|
+
const apiUrl = (options.apiUrl ?? config2.apiUrl).replace(/\/+$/, "");
|
|
14080
|
+
async function pollInBackground(key, session, entry) {
|
|
14081
|
+
for (; ; ) {
|
|
14082
|
+
let result;
|
|
14083
|
+
try {
|
|
14084
|
+
result = await session.poll();
|
|
14085
|
+
} catch {
|
|
14086
|
+
entry.status = "failed";
|
|
14087
|
+
return;
|
|
14088
|
+
}
|
|
14089
|
+
if (result.status === "pending") {
|
|
14090
|
+
await delay3(pollIntervalMs);
|
|
14091
|
+
continue;
|
|
14092
|
+
}
|
|
14093
|
+
if (result.status === "ready") {
|
|
14094
|
+
entry.status = "ready";
|
|
14095
|
+
latches.delete(key);
|
|
14096
|
+
return;
|
|
14097
|
+
}
|
|
14098
|
+
if (result.status === "backend_failure" && result.operation === "poll") {
|
|
14099
|
+
await delay3(pollIntervalMs);
|
|
14100
|
+
continue;
|
|
14101
|
+
}
|
|
14102
|
+
entry.status = "failed";
|
|
14103
|
+
return;
|
|
14104
|
+
}
|
|
14105
|
+
}
|
|
14106
|
+
async function startPairing(key, entry) {
|
|
14107
|
+
try {
|
|
14108
|
+
const session = await beginPairing({
|
|
14109
|
+
workspaceRoot: key,
|
|
14110
|
+
source: "cursor",
|
|
14111
|
+
apiUrl,
|
|
14112
|
+
fetchImpl: options.fetchImpl,
|
|
14113
|
+
connectOptions: {
|
|
14114
|
+
homeDir: options.homeDir,
|
|
14115
|
+
identityDeps: options.identityDeps,
|
|
14116
|
+
fetchImpl: options.fetchImpl
|
|
14117
|
+
}
|
|
14118
|
+
});
|
|
14119
|
+
const authorizationUrl = session.authorizationUrl;
|
|
14120
|
+
if (typeof authorizationUrl === "string" && authorizationUrl.length > 0) {
|
|
14121
|
+
try {
|
|
14122
|
+
await openUrl(authorizationUrl);
|
|
14123
|
+
} catch {
|
|
14124
|
+
entry.status = "failed";
|
|
14125
|
+
return;
|
|
14126
|
+
}
|
|
14127
|
+
} else {
|
|
14128
|
+
entry.status = "failed";
|
|
14129
|
+
return;
|
|
14130
|
+
}
|
|
14131
|
+
setImmediate(() => {
|
|
14132
|
+
void pollInBackground(key, session, entry);
|
|
14133
|
+
});
|
|
14134
|
+
} catch {
|
|
14135
|
+
entry.status = "failed";
|
|
14136
|
+
}
|
|
14137
|
+
}
|
|
14138
|
+
return {
|
|
14139
|
+
async ensure(workspaceRoot) {
|
|
14140
|
+
const key = path51.resolve(workspaceRoot);
|
|
14141
|
+
const existing = latches.get(key);
|
|
14142
|
+
if (existing) {
|
|
14143
|
+
await existing.startPromise;
|
|
14144
|
+
return { status: existing.status };
|
|
14145
|
+
}
|
|
14146
|
+
let resolveStart;
|
|
14147
|
+
const startPromise = new Promise((resolve47) => {
|
|
14148
|
+
resolveStart = resolve47;
|
|
14149
|
+
});
|
|
14150
|
+
const entry = { status: "pending", startPromise };
|
|
14151
|
+
latches.set(key, entry);
|
|
14152
|
+
try {
|
|
14153
|
+
await startPairing(key, entry);
|
|
14154
|
+
} finally {
|
|
14155
|
+
resolveStart();
|
|
14156
|
+
}
|
|
14157
|
+
return { status: entry.status };
|
|
14158
|
+
}
|
|
14159
|
+
};
|
|
13869
14160
|
}
|
|
13870
14161
|
|
|
13871
14162
|
// src/gateway.ts
|
|
@@ -13875,13 +14166,13 @@ var log3 = (msg) => {
|
|
|
13875
14166
|
};
|
|
13876
14167
|
function writePidFile(pid) {
|
|
13877
14168
|
const dir = ensureBaseDir();
|
|
13878
|
-
const pidPath =
|
|
14169
|
+
const pidPath = path52.join(dir, "gateway.pid");
|
|
13879
14170
|
fs38.writeFileSync(pidPath, `${pid}
|
|
13880
14171
|
`, { encoding: "utf8", mode: 384 });
|
|
13881
14172
|
return pidPath;
|
|
13882
14173
|
}
|
|
13883
14174
|
function removePidFile() {
|
|
13884
|
-
const pidPath =
|
|
14175
|
+
const pidPath = path52.join(getMcpBaseDir(), "gateway.pid");
|
|
13885
14176
|
try {
|
|
13886
14177
|
if (fs38.existsSync(pidPath)) {
|
|
13887
14178
|
fs38.unlinkSync(pidPath);
|
|
@@ -13902,6 +14193,13 @@ async function probeExistingGatewayHealthy(env2 = process.env) {
|
|
|
13902
14193
|
}
|
|
13903
14194
|
function createGatewayRequestHandler(options = {}) {
|
|
13904
14195
|
const oauth = options.oauth ?? createGatewayOAuthSurface(options);
|
|
14196
|
+
const apiUrl = options.apiUrl ?? config2.apiUrl;
|
|
14197
|
+
const unboundPairing = options.unboundPairing ?? createGatewayUnboundPairingCoordinator({
|
|
14198
|
+
apiUrl,
|
|
14199
|
+
fetchImpl: options.fetchImpl,
|
|
14200
|
+
homeDir: options.homeDir,
|
|
14201
|
+
identityDeps: options.identityDeps
|
|
14202
|
+
});
|
|
13905
14203
|
return (req, res) => {
|
|
13906
14204
|
void (async () => {
|
|
13907
14205
|
try {
|
|
@@ -13926,7 +14224,10 @@ function createGatewayRequestHandler(options = {}) {
|
|
|
13926
14224
|
homeDir: options.homeDir,
|
|
13927
14225
|
identityDeps: options.identityDeps,
|
|
13928
14226
|
ticketOptions: options.ticketOptions,
|
|
13929
|
-
createMemoraClient: options.createMemoraClient
|
|
14227
|
+
createMemoraClient: options.createMemoraClient,
|
|
14228
|
+
apiUrl,
|
|
14229
|
+
fetchImpl: options.fetchImpl,
|
|
14230
|
+
unboundPairing
|
|
13930
14231
|
});
|
|
13931
14232
|
if (mcpHandled) return;
|
|
13932
14233
|
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
@@ -13964,7 +14265,7 @@ async function runGateway(env2 = process.env, options = {}) {
|
|
|
13964
14265
|
process.on("SIGINT", () => shutdownNow("SIGINT"));
|
|
13965
14266
|
process.on("SIGTERM", () => shutdownNow("SIGTERM"));
|
|
13966
14267
|
process.on("exit", removePidFile);
|
|
13967
|
-
await new Promise((
|
|
14268
|
+
await new Promise((resolve47, reject) => {
|
|
13968
14269
|
server.once("error", async (err) => {
|
|
13969
14270
|
if (err.code === "EADDRINUSE") {
|
|
13970
14271
|
const healthy = await probeExistingGatewayHealthy(env2);
|
|
@@ -13991,7 +14292,7 @@ async function runGateway(env2 = process.env, options = {}) {
|
|
|
13991
14292
|
}
|
|
13992
14293
|
const pidPath = writePidFile(process.pid);
|
|
13993
14294
|
log3(`gateway started pid=${process.pid} listening on http://${host}:${port} pidFile=${pidPath}`);
|
|
13994
|
-
|
|
14295
|
+
resolve47();
|
|
13995
14296
|
});
|
|
13996
14297
|
});
|
|
13997
14298
|
}
|