@memoraone/mcp 0.1.55 → 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 +483 -215
- package/dist/index.cjs +7 -3
- package/dist/pluginPairing.cjs +7 -3
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -231,7 +231,7 @@ var require_package = __commonJS({
|
|
|
231
231
|
"package.json"(exports2, module2) {
|
|
232
232
|
module2.exports = {
|
|
233
233
|
name: "@memoraone/mcp",
|
|
234
|
-
version: "0.1.
|
|
234
|
+
version: "0.1.56",
|
|
235
235
|
type: "module",
|
|
236
236
|
main: "dist/index.cjs",
|
|
237
237
|
exports: {
|
|
@@ -769,6 +769,7 @@ function parsePayload(raw, repositoryBindingId) {
|
|
|
769
769
|
optionalString("refreshTokenExpiresAt");
|
|
770
770
|
optionalString("clientRedeemKey");
|
|
771
771
|
optionalString("clientRefreshKey");
|
|
772
|
+
optionalString("supabaseUserId");
|
|
772
773
|
return payload;
|
|
773
774
|
}
|
|
774
775
|
async function readInstallationCredentials(repositoryBindingId, options = {}) {
|
|
@@ -1226,7 +1227,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
1226
1227
|
const installationPublicId = data?.installation_public_id;
|
|
1227
1228
|
const projectId = data?.project_id;
|
|
1228
1229
|
const repositoryBindingId = data?.repository_binding_id;
|
|
1229
|
-
|
|
1230
|
+
const supabaseUserId = data?.supabase_user_id;
|
|
1231
|
+
if (typeof accessToken !== "string" || typeof refreshToken !== "string" || typeof installationPublicId !== "string" || typeof projectId !== "string" || typeof repositoryBindingId !== "string" || typeof supabaseUserId !== "string" || supabaseUserId.length === 0) {
|
|
1230
1232
|
throw new Error("[memoraone-mcp] Invalid redeem response");
|
|
1231
1233
|
}
|
|
1232
1234
|
let sourceId = null;
|
|
@@ -1244,7 +1246,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
1244
1246
|
project_id: projectId,
|
|
1245
1247
|
repository_binding_id: repositoryBindingId,
|
|
1246
1248
|
source_id: sourceId,
|
|
1247
|
-
recovered: Boolean(data.recovered)
|
|
1249
|
+
recovered: Boolean(data.recovered),
|
|
1250
|
+
supabase_user_id: supabaseUserId
|
|
1248
1251
|
};
|
|
1249
1252
|
}
|
|
1250
1253
|
var path8;
|
|
@@ -12339,7 +12342,8 @@ async function runConnectCommand(options) {
|
|
|
12339
12342
|
accessTokenExpiresAt: redeemed.access_token_expires_at ?? void 0,
|
|
12340
12343
|
refreshTokenExpiresAt: redeemed.refresh_token_expires_at ?? void 0,
|
|
12341
12344
|
installationPublicId: redeemed.installation_public_id,
|
|
12342
|
-
projectId: redeemed.project_id
|
|
12345
|
+
projectId: redeemed.project_id,
|
|
12346
|
+
supabaseUserId: redeemed.supabase_user_id
|
|
12343
12347
|
},
|
|
12344
12348
|
{ ...options.credentialOptions, clearKeys: ["clientRedeemKey"] }
|
|
12345
12349
|
);
|
package/dist/daemon.cjs
CHANGED
|
@@ -675,6 +675,7 @@ function parsePayload(raw, repositoryBindingId) {
|
|
|
675
675
|
optionalString("refreshTokenExpiresAt");
|
|
676
676
|
optionalString("clientRedeemKey");
|
|
677
677
|
optionalString("clientRefreshKey");
|
|
678
|
+
optionalString("supabaseUserId");
|
|
678
679
|
return payload;
|
|
679
680
|
}
|
|
680
681
|
async function readInstallationCredentials(repositoryBindingId, options = {}) {
|
|
@@ -1278,7 +1279,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
1278
1279
|
const installationPublicId = data?.installation_public_id;
|
|
1279
1280
|
const projectId = data?.project_id;
|
|
1280
1281
|
const repositoryBindingId = data?.repository_binding_id;
|
|
1281
|
-
|
|
1282
|
+
const supabaseUserId = data?.supabase_user_id;
|
|
1283
|
+
if (typeof accessToken !== "string" || typeof refreshToken !== "string" || typeof installationPublicId !== "string" || typeof projectId !== "string" || typeof repositoryBindingId !== "string" || typeof supabaseUserId !== "string" || supabaseUserId.length === 0) {
|
|
1282
1284
|
throw new Error("[memoraone-mcp] Invalid redeem response");
|
|
1283
1285
|
}
|
|
1284
1286
|
let sourceId = null;
|
|
@@ -1296,7 +1298,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
1296
1298
|
project_id: projectId,
|
|
1297
1299
|
repository_binding_id: repositoryBindingId,
|
|
1298
1300
|
source_id: sourceId,
|
|
1299
|
-
recovered: Boolean(data.recovered)
|
|
1301
|
+
recovered: Boolean(data.recovered),
|
|
1302
|
+
supabase_user_id: supabaseUserId
|
|
1300
1303
|
};
|
|
1301
1304
|
}
|
|
1302
1305
|
async function refreshLocalMcpToken(apiUrl, body, options = {}) {
|
|
@@ -11366,7 +11369,8 @@ async function runConnectCommand(options) {
|
|
|
11366
11369
|
accessTokenExpiresAt: redeemed.access_token_expires_at ?? void 0,
|
|
11367
11370
|
refreshTokenExpiresAt: redeemed.refresh_token_expires_at ?? void 0,
|
|
11368
11371
|
installationPublicId: redeemed.installation_public_id,
|
|
11369
|
-
projectId: redeemed.project_id
|
|
11372
|
+
projectId: redeemed.project_id,
|
|
11373
|
+
supabaseUserId: redeemed.supabase_user_id
|
|
11370
11374
|
},
|
|
11371
11375
|
{ ...options.credentialOptions, clearKeys: ["clientRedeemKey"] }
|
|
11372
11376
|
);
|
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,6 +363,26 @@ 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
|
}
|
|
@@ -397,47 +512,47 @@ var GatewayOAuthCodeStore = class {
|
|
|
397
512
|
};
|
|
398
513
|
|
|
399
514
|
// src/gatewayWorkspace.ts
|
|
400
|
-
var
|
|
401
|
-
var
|
|
515
|
+
var fs7 = __toESM(require("fs/promises"), 1);
|
|
516
|
+
var path12 = __toESM(require("path"), 1);
|
|
402
517
|
|
|
403
518
|
// src/localState/resolveLocalBinding.ts
|
|
404
|
-
var
|
|
519
|
+
var path11 = __toESM(require("path"), 1);
|
|
405
520
|
|
|
406
521
|
// src/localState/bindingStore.ts
|
|
407
|
-
var
|
|
408
|
-
var
|
|
522
|
+
var fs5 = __toESM(require("fs/promises"), 1);
|
|
523
|
+
var path7 = __toESM(require("path"), 1);
|
|
409
524
|
|
|
410
525
|
// src/localState/atomicFs.ts
|
|
411
|
-
var
|
|
412
|
-
var
|
|
526
|
+
var fs3 = __toESM(require("fs/promises"), 1);
|
|
527
|
+
var path5 = __toESM(require("path"), 1);
|
|
413
528
|
var import_node_crypto2 = require("crypto");
|
|
414
529
|
var STATE_DIR_MODE = 448;
|
|
415
530
|
var STATE_FILE_MODE = 384;
|
|
416
531
|
async function ensurePrivateDir(dirPath) {
|
|
417
|
-
await
|
|
532
|
+
await fs3.mkdir(dirPath, { recursive: true, mode: STATE_DIR_MODE });
|
|
418
533
|
try {
|
|
419
|
-
await
|
|
534
|
+
await fs3.chmod(dirPath, STATE_DIR_MODE);
|
|
420
535
|
} catch {
|
|
421
536
|
}
|
|
422
537
|
}
|
|
423
538
|
async function writeFileAtomic(filePath, content, options = {}) {
|
|
424
539
|
const mode = options.mode ?? STATE_FILE_MODE;
|
|
425
|
-
const dir =
|
|
540
|
+
const dir = path5.dirname(filePath);
|
|
426
541
|
await ensurePrivateDir(dir);
|
|
427
|
-
const tmpPath =
|
|
542
|
+
const tmpPath = path5.join(
|
|
428
543
|
dir,
|
|
429
|
-
`.${
|
|
544
|
+
`.${path5.basename(filePath)}.${process.pid}.${(0, import_node_crypto2.randomBytes)(8).toString("hex")}.tmp`
|
|
430
545
|
);
|
|
431
546
|
try {
|
|
432
|
-
await
|
|
433
|
-
await
|
|
547
|
+
await fs3.writeFile(tmpPath, content, { encoding: "utf8", mode });
|
|
548
|
+
await fs3.rename(tmpPath, filePath);
|
|
434
549
|
try {
|
|
435
|
-
await
|
|
550
|
+
await fs3.chmod(filePath, mode);
|
|
436
551
|
} catch {
|
|
437
552
|
}
|
|
438
553
|
} catch (err) {
|
|
439
554
|
try {
|
|
440
|
-
await
|
|
555
|
+
await fs3.unlink(tmpPath);
|
|
441
556
|
} catch {
|
|
442
557
|
}
|
|
443
558
|
throw err;
|
|
@@ -445,7 +560,7 @@ async function writeFileAtomic(filePath, content, options = {}) {
|
|
|
445
560
|
}
|
|
446
561
|
async function readJsonFile(filePath) {
|
|
447
562
|
try {
|
|
448
|
-
const raw = await
|
|
563
|
+
const raw = await fs3.readFile(filePath, "utf8");
|
|
449
564
|
return JSON.parse(raw);
|
|
450
565
|
} catch (err) {
|
|
451
566
|
if (err?.code === "ENOENT") {
|
|
@@ -572,32 +687,32 @@ function parseBindingRecord(raw) {
|
|
|
572
687
|
}
|
|
573
688
|
|
|
574
689
|
// src/localState/localLocks.ts
|
|
575
|
-
var
|
|
690
|
+
var fs4 = __toESM(require("fs/promises"), 1);
|
|
576
691
|
|
|
577
692
|
// src/localState/statePaths.ts
|
|
578
693
|
var os2 = __toESM(require("os"), 1);
|
|
579
|
-
var
|
|
694
|
+
var path6 = __toESM(require("path"), 1);
|
|
580
695
|
var MEMORAONE_STATE_DIRNAME = ".memoraone";
|
|
581
696
|
function getMemoraoneStateDir(homeDir = os2.homedir()) {
|
|
582
|
-
return
|
|
697
|
+
return path6.join(homeDir, MEMORAONE_STATE_DIRNAME);
|
|
583
698
|
}
|
|
584
699
|
function getPathIndexPath(homeDir = os2.homedir()) {
|
|
585
|
-
return
|
|
700
|
+
return path6.join(getMemoraoneStateDir(homeDir), "path-index.json");
|
|
586
701
|
}
|
|
587
702
|
function getBindingsDir(homeDir = os2.homedir()) {
|
|
588
|
-
return
|
|
703
|
+
return path6.join(getMemoraoneStateDir(homeDir), "bindings");
|
|
589
704
|
}
|
|
590
705
|
function getBindingFilePath(repositoryBindingId, homeDir = os2.homedir()) {
|
|
591
|
-
return
|
|
706
|
+
return path6.join(getBindingsDir(homeDir), `${repositoryBindingId}.json`);
|
|
592
707
|
}
|
|
593
708
|
function getLocksDir(homeDir = os2.homedir()) {
|
|
594
|
-
return
|
|
709
|
+
return path6.join(getMemoraoneStateDir(homeDir), "locks");
|
|
595
710
|
}
|
|
596
711
|
function getLockPath(lockName, homeDir = os2.homedir()) {
|
|
597
|
-
return
|
|
712
|
+
return path6.join(getLocksDir(homeDir), `${lockName}.lock`);
|
|
598
713
|
}
|
|
599
714
|
function getGatewayTicketsPath(homeDir = os2.homedir()) {
|
|
600
|
-
return
|
|
715
|
+
return path6.join(getMemoraoneStateDir(homeDir), "gateway-tickets.json");
|
|
601
716
|
}
|
|
602
717
|
|
|
603
718
|
// src/localState/localLocks.ts
|
|
@@ -612,16 +727,16 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
612
727
|
while (retries <= maxRetries) {
|
|
613
728
|
try {
|
|
614
729
|
try {
|
|
615
|
-
const stat5 = await
|
|
730
|
+
const stat5 = await fs4.stat(lockPath);
|
|
616
731
|
if (Date.now() - stat5.mtimeMs > maxLockAgeMs) {
|
|
617
|
-
await
|
|
732
|
+
await fs4.unlink(lockPath);
|
|
618
733
|
}
|
|
619
734
|
} catch (err) {
|
|
620
735
|
if (err?.code !== "ENOENT") {
|
|
621
736
|
throw err;
|
|
622
737
|
}
|
|
623
738
|
}
|
|
624
|
-
const fd = await
|
|
739
|
+
const fd = await fs4.open(lockPath, "wx");
|
|
625
740
|
await fd.writeFile(
|
|
626
741
|
JSON.stringify({
|
|
627
742
|
pid: process.pid,
|
|
@@ -632,7 +747,7 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
632
747
|
await fd.close();
|
|
633
748
|
return async () => {
|
|
634
749
|
try {
|
|
635
|
-
await
|
|
750
|
+
await fs4.unlink(lockPath);
|
|
636
751
|
} catch (err) {
|
|
637
752
|
if (err?.code !== "ENOENT") {
|
|
638
753
|
}
|
|
@@ -646,7 +761,7 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
646
761
|
`[memoraone-mcp] Failed to acquire lock ${lockName} after ${maxRetries} retries`
|
|
647
762
|
);
|
|
648
763
|
}
|
|
649
|
-
await new Promise((
|
|
764
|
+
await new Promise((resolve47) => setTimeout(resolve47, retryDelayMs));
|
|
650
765
|
continue;
|
|
651
766
|
}
|
|
652
767
|
throw err;
|
|
@@ -698,7 +813,7 @@ async function listBindingRecords(homeDir) {
|
|
|
698
813
|
const dir = await ensureBindingsDir(homeDir);
|
|
699
814
|
let names;
|
|
700
815
|
try {
|
|
701
|
-
names = await
|
|
816
|
+
names = await fs5.readdir(dir);
|
|
702
817
|
} catch (err) {
|
|
703
818
|
if (err?.code === "ENOENT") return [];
|
|
704
819
|
throw err;
|
|
@@ -716,9 +831,9 @@ async function listBindingRecords(homeDir) {
|
|
|
716
831
|
return out;
|
|
717
832
|
}
|
|
718
833
|
async function findBindingRecordByWorkspaceRoot(workspaceRoot, homeDir) {
|
|
719
|
-
const resolved =
|
|
834
|
+
const resolved = path7.resolve(workspaceRoot);
|
|
720
835
|
const records = await listBindingRecords(homeDir);
|
|
721
|
-
return records.find((r) =>
|
|
836
|
+
return records.find((r) => path7.resolve(r.workspaceRoot) === resolved) ?? null;
|
|
722
837
|
}
|
|
723
838
|
|
|
724
839
|
// src/localState/installationCredentials.ts
|
|
@@ -863,6 +978,7 @@ function parsePayload(raw, repositoryBindingId) {
|
|
|
863
978
|
optionalString("refreshTokenExpiresAt");
|
|
864
979
|
optionalString("clientRedeemKey");
|
|
865
980
|
optionalString("clientRefreshKey");
|
|
981
|
+
optionalString("supabaseUserId");
|
|
866
982
|
return payload;
|
|
867
983
|
}
|
|
868
984
|
async function readInstallationCredentials(repositoryBindingId, options = {}) {
|
|
@@ -945,12 +1061,12 @@ function hasUsableAccessToken(payload) {
|
|
|
945
1061
|
}
|
|
946
1062
|
|
|
947
1063
|
// src/localState/pathIndex.ts
|
|
948
|
-
var
|
|
1064
|
+
var path9 = __toESM(require("path"), 1);
|
|
949
1065
|
|
|
950
1066
|
// src/localState/rootFilesystemIdentity.ts
|
|
951
|
-
var
|
|
1067
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
952
1068
|
var os3 = __toESM(require("os"), 1);
|
|
953
|
-
var
|
|
1069
|
+
var path8 = __toESM(require("path"), 1);
|
|
954
1070
|
var import_node_child_process = require("child_process");
|
|
955
1071
|
var import_node_util = require("util");
|
|
956
1072
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
@@ -975,13 +1091,13 @@ async function readDarwinDeviceId() {
|
|
|
975
1091
|
}
|
|
976
1092
|
async function readLinuxDeviceId() {
|
|
977
1093
|
try {
|
|
978
|
-
const content = await
|
|
1094
|
+
const content = await fs6.readFile("/etc/machine-id", "utf8");
|
|
979
1095
|
const id = content.trim();
|
|
980
1096
|
if (id) return id;
|
|
981
1097
|
} catch {
|
|
982
1098
|
}
|
|
983
1099
|
try {
|
|
984
|
-
const content = await
|
|
1100
|
+
const content = await fs6.readFile("/var/lib/dbus/machine-id", "utf8");
|
|
985
1101
|
const id = content.trim();
|
|
986
1102
|
if (id) return id;
|
|
987
1103
|
} catch {
|
|
@@ -1014,10 +1130,10 @@ async function resolveDeviceId(platform2 = os3.platform()) {
|
|
|
1014
1130
|
throw new Error(`[memoraone-mcp] Unsupported platform for device ID: ${platform2}`);
|
|
1015
1131
|
}
|
|
1016
1132
|
async function captureRootFilesystemIdentity(rootPath, deps = {}) {
|
|
1017
|
-
const resolved =
|
|
1133
|
+
const resolved = path8.resolve(rootPath);
|
|
1018
1134
|
const platform2 = deps.platform ?? os3.platform();
|
|
1019
1135
|
const statRoot = deps.statRoot ?? (async (p) => {
|
|
1020
|
-
const st2 = await
|
|
1136
|
+
const st2 = await fs6.stat(p);
|
|
1021
1137
|
return {
|
|
1022
1138
|
ino: st2.ino,
|
|
1023
1139
|
dev: st2.dev,
|
|
@@ -1101,7 +1217,7 @@ async function savePathIndex(index, homeDir) {
|
|
|
1101
1217
|
await writeJsonAtomic(getPathIndexPath(homeDir), next);
|
|
1102
1218
|
}
|
|
1103
1219
|
function lookupPathIndex(index, workspaceRoot, identity) {
|
|
1104
|
-
const resolved =
|
|
1220
|
+
const resolved = path9.resolve(workspaceRoot);
|
|
1105
1221
|
const byPathId = index.byPath[resolved];
|
|
1106
1222
|
if (byPathId) {
|
|
1107
1223
|
return { kind: "path", repositoryBindingId: assertRepositoryBindingId(byPathId) };
|
|
@@ -1127,14 +1243,14 @@ function lookupPathIndex(index, workspaceRoot, identity) {
|
|
|
1127
1243
|
}
|
|
1128
1244
|
async function upsertPathIndexEntry(options) {
|
|
1129
1245
|
const repositoryBindingId = assertRepositoryBindingId(options.repositoryBindingId);
|
|
1130
|
-
const resolved =
|
|
1246
|
+
const resolved = path9.resolve(options.workspaceRoot);
|
|
1131
1247
|
const identityKey = filesystemIdentityKey(options.identity);
|
|
1132
1248
|
return withLocalLock(
|
|
1133
1249
|
"path-index",
|
|
1134
1250
|
async () => {
|
|
1135
1251
|
const index = await loadPathIndex(options.homeDir);
|
|
1136
|
-
if (options.previousPath &&
|
|
1137
|
-
delete index.byPath[
|
|
1252
|
+
if (options.previousPath && path9.resolve(options.previousPath) !== resolved) {
|
|
1253
|
+
delete index.byPath[path9.resolve(options.previousPath)];
|
|
1138
1254
|
}
|
|
1139
1255
|
for (const [p, id] of Object.entries(index.byPath)) {
|
|
1140
1256
|
if (id === repositoryBindingId && p !== resolved) {
|
|
@@ -1159,20 +1275,20 @@ function identityMatchesStored(stored, current) {
|
|
|
1159
1275
|
}
|
|
1160
1276
|
|
|
1161
1277
|
// src/localState/localConnectClient.ts
|
|
1162
|
-
var
|
|
1278
|
+
var path10 = __toESM(require("path"), 1);
|
|
1163
1279
|
|
|
1164
1280
|
// src/client/memoraClient.ts
|
|
1165
1281
|
var crypto2 = __toESM(require("crypto"), 1);
|
|
1166
1282
|
var PROJECT_ID_HEADER = "x-project-id";
|
|
1167
1283
|
var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1168
|
-
var
|
|
1284
|
+
var parseBooleanFlag2 = (value) => {
|
|
1169
1285
|
if (!value) {
|
|
1170
1286
|
return false;
|
|
1171
1287
|
}
|
|
1172
1288
|
const normalized = value.trim().toLowerCase();
|
|
1173
1289
|
return ["1", "true", "yes", "on"].includes(normalized);
|
|
1174
1290
|
};
|
|
1175
|
-
var debugEnabled =
|
|
1291
|
+
var debugEnabled = parseBooleanFlag2(process.env.MEMORAONE_DEV_MODE);
|
|
1176
1292
|
async function requestJson(url, method, headers, body) {
|
|
1177
1293
|
const res = await fetch(url, {
|
|
1178
1294
|
method,
|
|
@@ -1270,9 +1386,9 @@ var MemoraClient = class {
|
|
|
1270
1386
|
...options?.headers ?? {}
|
|
1271
1387
|
};
|
|
1272
1388
|
}
|
|
1273
|
-
async perform(method,
|
|
1389
|
+
async perform(method, path53, body, options, retried = false) {
|
|
1274
1390
|
const nonce = crypto2.randomBytes(8).toString("hex");
|
|
1275
|
-
const url = `${this.baseUrl}${
|
|
1391
|
+
const url = `${this.baseUrl}${path53.startsWith("/") ? path53 : `/${path53}`}`;
|
|
1276
1392
|
this.resolveProjectId();
|
|
1277
1393
|
console.error(
|
|
1278
1394
|
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=${method} url=${url}`
|
|
@@ -1299,7 +1415,7 @@ var MemoraClient = class {
|
|
|
1299
1415
|
if (res.status === 401 && !options?.skipAuthRefresh && !retried) {
|
|
1300
1416
|
try {
|
|
1301
1417
|
await this.refreshAccessToken();
|
|
1302
|
-
return this.perform(method,
|
|
1418
|
+
return this.perform(method, path53, body, options, true);
|
|
1303
1419
|
} catch (err) {
|
|
1304
1420
|
if (err instanceof ReconnectRequiredError) {
|
|
1305
1421
|
await this.markReconnectRequired();
|
|
@@ -1319,22 +1435,22 @@ var MemoraClient = class {
|
|
|
1319
1435
|
}
|
|
1320
1436
|
return res.text ? JSON.parse(res.text) : null;
|
|
1321
1437
|
}
|
|
1322
|
-
async post(
|
|
1323
|
-
console.error(`[memoraone-mcp][info] MemoraClient.post ENTER path=${
|
|
1324
|
-
const result = await this.perform("POST",
|
|
1325
|
-
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}`);
|
|
1326
1442
|
return result;
|
|
1327
1443
|
}
|
|
1328
|
-
async get(
|
|
1329
|
-
return this.perform("GET",
|
|
1444
|
+
async get(path53, options) {
|
|
1445
|
+
return this.perform("GET", path53, void 0, options);
|
|
1330
1446
|
}
|
|
1331
1447
|
};
|
|
1332
1448
|
var memoraClient_default = MemoraClient;
|
|
1333
1449
|
|
|
1334
1450
|
// src/localState/localConnectClient.ts
|
|
1335
|
-
async function requestJson2(baseUrl, method,
|
|
1451
|
+
async function requestJson2(baseUrl, method, path53, options = {}) {
|
|
1336
1452
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1337
|
-
const url = `${baseUrl.replace(/\/+$/, "")}${
|
|
1453
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${path53.startsWith("/") ? path53 : `/${path53}`}`;
|
|
1338
1454
|
const res = await fetchImpl(url, {
|
|
1339
1455
|
method,
|
|
1340
1456
|
headers: {
|
|
@@ -1371,7 +1487,7 @@ function normalizePluginPairingSource(source) {
|
|
|
1371
1487
|
return source;
|
|
1372
1488
|
}
|
|
1373
1489
|
function pairingRepositoryName(workspaceRoot) {
|
|
1374
|
-
const repositoryName =
|
|
1490
|
+
const repositoryName = path10.basename(path10.resolve(workspaceRoot)).trim();
|
|
1375
1491
|
if (repositoryName.length === 0 || repositoryName.length > 256 || repositoryName.includes("/") || repositoryName.includes("\\")) {
|
|
1376
1492
|
throw new Error("[memoraone-mcp] Workspace basename is not safe pairing metadata");
|
|
1377
1493
|
}
|
|
@@ -1466,7 +1582,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
1466
1582
|
const installationPublicId = data?.installation_public_id;
|
|
1467
1583
|
const projectId = data?.project_id;
|
|
1468
1584
|
const repositoryBindingId = data?.repository_binding_id;
|
|
1469
|
-
|
|
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) {
|
|
1470
1587
|
throw new Error("[memoraone-mcp] Invalid redeem response");
|
|
1471
1588
|
}
|
|
1472
1589
|
let sourceId = null;
|
|
@@ -1484,7 +1601,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
1484
1601
|
project_id: projectId,
|
|
1485
1602
|
repository_binding_id: repositoryBindingId,
|
|
1486
1603
|
source_id: sourceId,
|
|
1487
|
-
recovered: Boolean(data.recovered)
|
|
1604
|
+
recovered: Boolean(data.recovered),
|
|
1605
|
+
supabase_user_id: supabaseUserId
|
|
1488
1606
|
};
|
|
1489
1607
|
}
|
|
1490
1608
|
async function refreshLocalMcpToken(apiUrl, body, options = {}) {
|
|
@@ -1506,6 +1624,33 @@ async function refreshLocalMcpToken(apiUrl, body, options = {}) {
|
|
|
1506
1624
|
repository_binding_id: typeof data.repository_binding_id === "string" ? data.repository_binding_id : void 0
|
|
1507
1625
|
};
|
|
1508
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
|
+
}
|
|
1509
1654
|
|
|
1510
1655
|
// src/localState/tokenRefreshCoordinator.ts
|
|
1511
1656
|
var ReconnectRequiredError = class extends Error {
|
|
@@ -1570,14 +1715,14 @@ async function refreshInstallationAccessToken(options) {
|
|
|
1570
1715
|
|
|
1571
1716
|
// src/localState/resolveLocalBinding.ts
|
|
1572
1717
|
async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
|
|
1573
|
-
const resolved =
|
|
1718
|
+
const resolved = path11.resolve(workspaceRoot);
|
|
1574
1719
|
const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
|
|
1575
1720
|
const index = await loadPathIndex(options.homeDir);
|
|
1576
1721
|
const lookup = lookupPathIndex(index, resolved, identity);
|
|
1577
1722
|
if (lookup.kind === "path") {
|
|
1578
1723
|
const record = await readBindingRecord(lookup.repositoryBindingId, options.homeDir);
|
|
1579
1724
|
if (record && identityMatchesStored(record.filesystemIdentity, identity)) {
|
|
1580
|
-
if (
|
|
1725
|
+
if (path11.resolve(record.workspaceRoot) !== resolved) {
|
|
1581
1726
|
const updated = {
|
|
1582
1727
|
...record,
|
|
1583
1728
|
workspaceRoot: resolved,
|
|
@@ -1636,7 +1781,7 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
|
|
|
1636
1781
|
};
|
|
1637
1782
|
}
|
|
1638
1783
|
async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
|
|
1639
|
-
const resolved =
|
|
1784
|
+
const resolved = path11.resolve(workspaceRoot);
|
|
1640
1785
|
const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
|
|
1641
1786
|
const index = await loadPathIndex(options.homeDir);
|
|
1642
1787
|
const lookup = lookupPathIndex(index, resolved, identity);
|
|
@@ -1647,7 +1792,7 @@ async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
|
|
|
1647
1792
|
if (!record) {
|
|
1648
1793
|
return { status: "unbound", workspaceRoot: resolved };
|
|
1649
1794
|
}
|
|
1650
|
-
if (
|
|
1795
|
+
if (path11.resolve(record.workspaceRoot) !== resolved) {
|
|
1651
1796
|
return { status: "unbound", workspaceRoot: resolved };
|
|
1652
1797
|
}
|
|
1653
1798
|
if (!identityMatchesStored(record.filesystemIdentity, identity)) {
|
|
@@ -1660,7 +1805,7 @@ async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
|
|
|
1660
1805
|
};
|
|
1661
1806
|
}
|
|
1662
1807
|
async function resolveLocalBinding(workspaceRoot, options = {}) {
|
|
1663
|
-
const resolved =
|
|
1808
|
+
const resolved = path11.resolve(workspaceRoot);
|
|
1664
1809
|
const { repositoryBindingId } = await ensureRepositoryBindingForRoot(
|
|
1665
1810
|
resolved,
|
|
1666
1811
|
{ ...options, createIfMissing: false }
|
|
@@ -1746,12 +1891,12 @@ async function parseGatewayWorkspaceRoot(wsRaw) {
|
|
|
1746
1891
|
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
|
|
1747
1892
|
return fail("malformed_ws", "ws must be an absolute filesystem path");
|
|
1748
1893
|
}
|
|
1749
|
-
if (!
|
|
1894
|
+
if (!path12.isAbsolute(trimmed)) {
|
|
1750
1895
|
return fail("non_absolute", "ws must be an absolute workspace root");
|
|
1751
1896
|
}
|
|
1752
|
-
const workspaceRoot =
|
|
1897
|
+
const workspaceRoot = path12.resolve(trimmed);
|
|
1753
1898
|
try {
|
|
1754
|
-
const st = await
|
|
1899
|
+
const st = await fs7.stat(workspaceRoot);
|
|
1755
1900
|
if (!st.isDirectory()) {
|
|
1756
1901
|
return fail("not_directory", "ws must point to an existing directory");
|
|
1757
1902
|
}
|
|
@@ -1774,7 +1919,7 @@ async function parseGatewayWorkspaceUrl(input2) {
|
|
|
1774
1919
|
|
|
1775
1920
|
// src/localState/gatewayTicketStore.ts
|
|
1776
1921
|
var import_node_crypto5 = require("crypto");
|
|
1777
|
-
var
|
|
1922
|
+
var path13 = __toESM(require("path"), 1);
|
|
1778
1923
|
var GATEWAY_TICKETS_VERSION = 1;
|
|
1779
1924
|
var GATEWAY_TICKETS_LOCK_NAME = "gateway-tickets";
|
|
1780
1925
|
var MGT_PREFIX = "mgt_";
|
|
@@ -1868,7 +2013,7 @@ async function resolveGatewayTicketScope(input2, options = {}) {
|
|
|
1868
2013
|
if (!("workspaceRoot" in input2) || input2.workspaceRoot == null) {
|
|
1869
2014
|
return fail2("invalid_workspace", "workspaceRoot or canonicalUrl is required");
|
|
1870
2015
|
}
|
|
1871
|
-
const parsed2 = await parseGatewayWorkspaceRoot(
|
|
2016
|
+
const parsed2 = await parseGatewayWorkspaceRoot(path13.resolve(input2.workspaceRoot));
|
|
1872
2017
|
if (!parsed2.ok) {
|
|
1873
2018
|
return fail2("invalid_workspace", parsed2.error);
|
|
1874
2019
|
}
|
|
@@ -1965,101 +2110,6 @@ async function revokeGatewaySetupTicket(ticket, options = {}) {
|
|
|
1965
2110
|
// src/pluginPairing.ts
|
|
1966
2111
|
var path47 = __toESM(require("path"), 1);
|
|
1967
2112
|
|
|
1968
|
-
// src/config.ts
|
|
1969
|
-
var process2 = __toESM(require("process"), 1);
|
|
1970
|
-
var fs7 = __toESM(require("fs"), 1);
|
|
1971
|
-
var path13 = __toESM(require("path"), 1);
|
|
1972
|
-
var dotenv = __toESM(require("dotenv"), 1);
|
|
1973
|
-
var import_v4 = require("zod/v4");
|
|
1974
|
-
|
|
1975
|
-
// src/configUtils.ts
|
|
1976
|
-
var DEFAULT_API_URL = "https://api.memoraone.com";
|
|
1977
|
-
var DEV_API_URL = "http://localhost:3001";
|
|
1978
|
-
function resolveApiUrl(env2) {
|
|
1979
|
-
const explicitUrl = env2.MEMORAONE_API_URL?.trim();
|
|
1980
|
-
if (explicitUrl) {
|
|
1981
|
-
return explicitUrl;
|
|
1982
|
-
}
|
|
1983
|
-
const aliasUrl = env2.MEMORA_API_URL?.trim();
|
|
1984
|
-
if (aliasUrl) {
|
|
1985
|
-
return aliasUrl;
|
|
1986
|
-
}
|
|
1987
|
-
if (env2.MEMORAONE_DEV_MODE === "1") {
|
|
1988
|
-
return DEV_API_URL;
|
|
1989
|
-
}
|
|
1990
|
-
return DEFAULT_API_URL;
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
// src/config.ts
|
|
1994
|
-
var dotenvPath = path13.resolve(process2.cwd(), ".env");
|
|
1995
|
-
if (fs7.existsSync(dotenvPath)) {
|
|
1996
|
-
try {
|
|
1997
|
-
dotenv.config({ path: dotenvPath });
|
|
1998
|
-
} catch (err) {
|
|
1999
|
-
process2.stderr.write("[memoraone-mcp] Failed to load .env: " + String(err) + "\n");
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
var EnvSchema = import_v4.z.object({
|
|
2003
|
-
MEMORAONE_API_URL: import_v4.z.string().url().optional(),
|
|
2004
|
-
MEMORAONE_API_KEY: import_v4.z.string().min(1).optional(),
|
|
2005
|
-
MEMORAONE_DEV_MODE: import_v4.z.string().min(1).optional(),
|
|
2006
|
-
MEMORAONE_AGENT_NAME: import_v4.z.string().min(1).optional(),
|
|
2007
|
-
MEMORAONE_AGENT_TYPE: import_v4.z.string().min(1).optional(),
|
|
2008
|
-
MEMORAONE_SOURCE: import_v4.z.string().min(1).optional(),
|
|
2009
|
-
MEMORAONE_IDE_TYPE: import_v4.z.enum(IDE_TYPES).optional(),
|
|
2010
|
-
MEMORAONE_WORKLOG: import_v4.z.string().min(1).optional(),
|
|
2011
|
-
MEMORAONE_HEARTBEAT: import_v4.z.string().min(1).optional(),
|
|
2012
|
-
MEMORAONE_HEARTBEAT_INTERVAL_MS: import_v4.z.string().min(1).optional()
|
|
2013
|
-
});
|
|
2014
|
-
var requiredEnvVars = [];
|
|
2015
|
-
var missingEnvVars = requiredEnvVars.filter((key) => {
|
|
2016
|
-
const value = process2.env[key];
|
|
2017
|
-
return value === void 0 || value.trim() === "";
|
|
2018
|
-
});
|
|
2019
|
-
if (missingEnvVars.length > 0) {
|
|
2020
|
-
for (const key of missingEnvVars) {
|
|
2021
|
-
process2.stderr.write(`Missing ${key}
|
|
2022
|
-
`);
|
|
2023
|
-
}
|
|
2024
|
-
process2.exit(1);
|
|
2025
|
-
}
|
|
2026
|
-
var parsed = EnvSchema.safeParse(process2.env);
|
|
2027
|
-
var resolvedApiUrl = resolveApiUrl(process2.env);
|
|
2028
|
-
if (!parsed.success) {
|
|
2029
|
-
const formatted = parsed.error.format();
|
|
2030
|
-
process2.stderr.write(
|
|
2031
|
-
"[memoraone-mcp] Invalid environment variables " + JSON.stringify(formatted) + "\n"
|
|
2032
|
-
);
|
|
2033
|
-
throw new Error("Config validation failed");
|
|
2034
|
-
}
|
|
2035
|
-
var parseBooleanFlag2 = (value, defaultValue) => {
|
|
2036
|
-
if (value === void 0) {
|
|
2037
|
-
return defaultValue;
|
|
2038
|
-
}
|
|
2039
|
-
const normalized = value.trim().toLowerCase();
|
|
2040
|
-
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
2041
|
-
return true;
|
|
2042
|
-
}
|
|
2043
|
-
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
2044
|
-
return false;
|
|
2045
|
-
}
|
|
2046
|
-
return defaultValue;
|
|
2047
|
-
};
|
|
2048
|
-
var config2 = {
|
|
2049
|
-
apiUrl: resolvedApiUrl.replace(/\/+$/, ""),
|
|
2050
|
-
apiKey: parsed.data.MEMORAONE_API_KEY,
|
|
2051
|
-
agentName: parsed.data.MEMORAONE_AGENT_NAME ?? "cursor",
|
|
2052
|
-
agentType: parsed.data.MEMORAONE_AGENT_TYPE ?? "agent",
|
|
2053
|
-
source: parsed.data.MEMORAONE_SOURCE ?? "cursor",
|
|
2054
|
-
ideType: parsed.data.MEMORAONE_IDE_TYPE,
|
|
2055
|
-
devMode: parseBooleanFlag2(parsed.data.MEMORAONE_DEV_MODE, false),
|
|
2056
|
-
worklogEnabled: parseBooleanFlag2(parsed.data.MEMORAONE_WORKLOG, true),
|
|
2057
|
-
heartbeatEnabled: parseBooleanFlag2(parsed.data.MEMORAONE_HEARTBEAT, true),
|
|
2058
|
-
// Cadence is owned by LOCAL_MCP_HEARTBEAT_INTERVAL_MS in heartbeat.ts (1_000).
|
|
2059
|
-
// Env override is accepted for forward-compat but the timer path ignores it.
|
|
2060
|
-
heartbeatIntervalMs: Number.parseInt(parsed.data.MEMORAONE_HEARTBEAT_INTERVAL_MS ?? "1000", 10)
|
|
2061
|
-
};
|
|
2062
|
-
|
|
2063
2113
|
// src/localState/connectCommand.ts
|
|
2064
2114
|
var fs36 = __toESM(require("fs/promises"), 1);
|
|
2065
2115
|
var path46 = __toESM(require("path"), 1);
|
|
@@ -4115,7 +4165,7 @@ function formatOptionalJetBrainsHandshakeUnavailableDetail(detail) {
|
|
|
4115
4165
|
async function verifyJetBrainsMcpHandshake(options) {
|
|
4116
4166
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
4117
4167
|
const { server } = options;
|
|
4118
|
-
return new Promise((
|
|
4168
|
+
return new Promise((resolve47) => {
|
|
4119
4169
|
let settled = false;
|
|
4120
4170
|
const finish = (ok, detail, optionalUnavailable) => {
|
|
4121
4171
|
if (settled) return;
|
|
@@ -4125,7 +4175,7 @@ async function verifyJetBrainsMcpHandshake(options) {
|
|
|
4125
4175
|
child.kill();
|
|
4126
4176
|
} catch {
|
|
4127
4177
|
}
|
|
4128
|
-
|
|
4178
|
+
resolve47({ ok, detail, optionalUnavailable });
|
|
4129
4179
|
};
|
|
4130
4180
|
const child = (0, import_node_child_process4.spawn)(server.command, [...server.args], {
|
|
4131
4181
|
env: { ...process.env, ...server.env },
|
|
@@ -10669,7 +10719,8 @@ async function runConnectCommand(options) {
|
|
|
10669
10719
|
accessTokenExpiresAt: redeemed.access_token_expires_at ?? void 0,
|
|
10670
10720
|
refreshTokenExpiresAt: redeemed.refresh_token_expires_at ?? void 0,
|
|
10671
10721
|
installationPublicId: redeemed.installation_public_id,
|
|
10672
|
-
projectId: redeemed.project_id
|
|
10722
|
+
projectId: redeemed.project_id,
|
|
10723
|
+
supabaseUserId: redeemed.supabase_user_id
|
|
10673
10724
|
},
|
|
10674
10725
|
{ ...options.credentialOptions, clearKeys: ["clientRedeemKey"] }
|
|
10675
10726
|
);
|
|
@@ -11114,7 +11165,7 @@ function html(res, status, body) {
|
|
|
11114
11165
|
res.end(body);
|
|
11115
11166
|
}
|
|
11116
11167
|
function readUrlEncodedBody(req) {
|
|
11117
|
-
return new Promise((
|
|
11168
|
+
return new Promise((resolve47, reject) => {
|
|
11118
11169
|
const chunks = [];
|
|
11119
11170
|
let size = 0;
|
|
11120
11171
|
req.on("data", (chunk) => {
|
|
@@ -11127,13 +11178,13 @@ function readUrlEncodedBody(req) {
|
|
|
11127
11178
|
chunks.push(chunk);
|
|
11128
11179
|
});
|
|
11129
11180
|
req.on("end", () => {
|
|
11130
|
-
|
|
11181
|
+
resolve47(new URLSearchParams(Buffer.concat(chunks).toString("utf8")));
|
|
11131
11182
|
});
|
|
11132
11183
|
req.on("error", reject);
|
|
11133
11184
|
});
|
|
11134
11185
|
}
|
|
11135
11186
|
function readJsonBody(req) {
|
|
11136
|
-
return new Promise((
|
|
11187
|
+
return new Promise((resolve47, reject) => {
|
|
11137
11188
|
const chunks = [];
|
|
11138
11189
|
let size = 0;
|
|
11139
11190
|
req.on("data", (chunk) => {
|
|
@@ -11148,11 +11199,11 @@ function readJsonBody(req) {
|
|
|
11148
11199
|
req.on("end", () => {
|
|
11149
11200
|
const raw = Buffer.concat(chunks).toString("utf8");
|
|
11150
11201
|
if (!raw.trim()) {
|
|
11151
|
-
|
|
11202
|
+
resolve47({});
|
|
11152
11203
|
return;
|
|
11153
11204
|
}
|
|
11154
11205
|
try {
|
|
11155
|
-
|
|
11206
|
+
resolve47(JSON.parse(raw));
|
|
11156
11207
|
} catch {
|
|
11157
11208
|
reject(new Error("invalid json"));
|
|
11158
11209
|
}
|
|
@@ -11164,12 +11215,12 @@ function opaqueId(prefix) {
|
|
|
11164
11215
|
return `${prefix}${(0, import_node_crypto28.randomBytes)(24).toString("base64url")}`;
|
|
11165
11216
|
}
|
|
11166
11217
|
function delay(ms) {
|
|
11167
|
-
return new Promise((
|
|
11218
|
+
return new Promise((resolve47) => setTimeout(resolve47, ms));
|
|
11168
11219
|
}
|
|
11169
11220
|
function buildProtectedResourceMetadata(env2 = process.env, workspaceRoot) {
|
|
11170
11221
|
return {
|
|
11171
11222
|
resource: workspaceRoot === void 0 ? getGatewayResourceBaseUrl(env2) : buildGatewayMcpUrl(workspaceRoot, env2),
|
|
11172
|
-
authorization_servers: [
|
|
11223
|
+
authorization_servers: [getGatewayAuthorizationServerIssuerUrl(env2)]
|
|
11173
11224
|
};
|
|
11174
11225
|
}
|
|
11175
11226
|
function buildAuthorizationServerMetadata(env2 = process.env) {
|
|
@@ -11802,11 +11853,11 @@ async function isRetryablePluginStartupBindingError(err, options = {}) {
|
|
|
11802
11853
|
return false;
|
|
11803
11854
|
}
|
|
11804
11855
|
function defaultSleep(ms) {
|
|
11805
|
-
return new Promise((
|
|
11806
|
-
setTimeout(
|
|
11856
|
+
return new Promise((resolve47) => {
|
|
11857
|
+
setTimeout(resolve47, ms);
|
|
11807
11858
|
});
|
|
11808
11859
|
}
|
|
11809
|
-
async function waitForPluginStartupBinding(
|
|
11860
|
+
async function waitForPluginStartupBinding(resolve47, options = {}) {
|
|
11810
11861
|
const now = options.now ?? Date.now;
|
|
11811
11862
|
const sleep = options.sleep ?? defaultSleep;
|
|
11812
11863
|
const timeoutMs = options.timeoutMs ?? PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS;
|
|
@@ -11816,7 +11867,7 @@ async function waitForPluginStartupBinding(resolve46, options = {}) {
|
|
|
11816
11867
|
let lastError;
|
|
11817
11868
|
while (true) {
|
|
11818
11869
|
try {
|
|
11819
|
-
const binding = await
|
|
11870
|
+
const binding = await resolve47();
|
|
11820
11871
|
if (waitingLogged) {
|
|
11821
11872
|
options.log?.(
|
|
11822
11873
|
`local MemoraOne binding available after Cursor plugin pairing (${now() - startedAt}ms)`
|
|
@@ -12296,9 +12347,9 @@ function buildPersonalContextPath(parsed2) {
|
|
|
12296
12347
|
}
|
|
12297
12348
|
async function handleGetPersonalContext(client, args) {
|
|
12298
12349
|
const parsed2 = getPersonalContextInputSchema.parse(args ?? {});
|
|
12299
|
-
const
|
|
12350
|
+
const path53 = buildPersonalContextPath(parsed2);
|
|
12300
12351
|
try {
|
|
12301
|
-
const result = await client.get(
|
|
12352
|
+
const result = await client.get(path53);
|
|
12302
12353
|
return { ok: true, result };
|
|
12303
12354
|
} catch (err) {
|
|
12304
12355
|
if (err instanceof MemoraOneHttpError) {
|
|
@@ -12770,8 +12821,8 @@ async function handleCreateConceptVersion(client, args) {
|
|
|
12770
12821
|
if (input2.tags !== void 0) body.tags = input2.tags;
|
|
12771
12822
|
if (input2.parent_id !== void 0) body.parent_id = input2.parent_id;
|
|
12772
12823
|
try {
|
|
12773
|
-
const
|
|
12774
|
-
return projectCreatedConceptVersionResponse(await client.post(
|
|
12824
|
+
const path53 = `/concepts/${encodeURIComponent(input2.id)}/versions`;
|
|
12825
|
+
return projectCreatedConceptVersionResponse(await client.post(path53, body));
|
|
12775
12826
|
} catch (error) {
|
|
12776
12827
|
rethrowToolRequestError("memora_create_concept_version", error);
|
|
12777
12828
|
}
|
|
@@ -12806,7 +12857,7 @@ function handleBindingStatus(binding, options = {}) {
|
|
|
12806
12857
|
// src/tools/handlers/finishSetup.ts
|
|
12807
12858
|
var DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
12808
12859
|
function delay2(ms) {
|
|
12809
|
-
return new Promise((
|
|
12860
|
+
return new Promise((resolve47) => setTimeout(resolve47, ms));
|
|
12810
12861
|
}
|
|
12811
12862
|
async function pollPairingSessionInBackground(session, pollIntervalMs, onReady) {
|
|
12812
12863
|
for (; ; ) {
|
|
@@ -13099,8 +13150,8 @@ function registerToolWithWorklog(server, runtime, sessionContext, toolName, desc
|
|
|
13099
13150
|
function createMemoraMcpServer(opts = {}) {
|
|
13100
13151
|
let bindingReadyResolve = null;
|
|
13101
13152
|
let bindingReadyReject = null;
|
|
13102
|
-
const bindingReady = new Promise((
|
|
13103
|
-
bindingReadyResolve =
|
|
13153
|
+
const bindingReady = new Promise((resolve47, reject) => {
|
|
13154
|
+
bindingReadyResolve = resolve47;
|
|
13104
13155
|
bindingReadyReject = reject;
|
|
13105
13156
|
});
|
|
13106
13157
|
const devMode = Boolean(config2.devMode);
|
|
@@ -13854,6 +13905,14 @@ function sendUnauthorized(res, env2, workspaceRoot) {
|
|
|
13854
13905
|
});
|
|
13855
13906
|
res.end(body);
|
|
13856
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
|
+
}
|
|
13857
13916
|
async function handleGatewayMcpAuthRequest(req, res, url, options = {}) {
|
|
13858
13917
|
if (url.pathname !== GATEWAY_MCP_PATH) return false;
|
|
13859
13918
|
const env2 = options.env ?? process.env;
|
|
@@ -13863,6 +13922,15 @@ async function handleGatewayMcpAuthRequest(req, res, url, options = {}) {
|
|
|
13863
13922
|
env: options.env ?? options.ticketOptions?.env ?? env2,
|
|
13864
13923
|
...options.ticketOptions
|
|
13865
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
|
+
};
|
|
13866
13934
|
const parsed2 = await parseGatewayWorkspaceUrl(url);
|
|
13867
13935
|
if (!parsed2.ok) {
|
|
13868
13936
|
sendUnauthorized(res, env2);
|
|
@@ -13874,31 +13942,221 @@ async function handleGatewayMcpAuthRequest(req, res, url, options = {}) {
|
|
|
13874
13942
|
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
13875
13943
|
return true;
|
|
13876
13944
|
}
|
|
13877
|
-
|
|
13878
|
-
|
|
13879
|
-
|
|
13880
|
-
|
|
13881
|
-
|
|
13882
|
-
|
|
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
|
+
);
|
|
13964
|
+
return true;
|
|
13965
|
+
}
|
|
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;
|
|
13971
|
+
}
|
|
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 {
|
|
13883
14028
|
sendUnauthorized(res, env2, parsed2.workspaceRoot);
|
|
13884
14029
|
return true;
|
|
13885
14030
|
}
|
|
13886
|
-
|
|
13887
|
-
|
|
13888
|
-
|
|
13889
|
-
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
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;
|
|
13899
14104
|
}
|
|
13900
|
-
|
|
13901
|
-
|
|
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
|
+
};
|
|
13902
14160
|
}
|
|
13903
14161
|
|
|
13904
14162
|
// src/gateway.ts
|
|
@@ -13908,13 +14166,13 @@ var log3 = (msg) => {
|
|
|
13908
14166
|
};
|
|
13909
14167
|
function writePidFile(pid) {
|
|
13910
14168
|
const dir = ensureBaseDir();
|
|
13911
|
-
const pidPath =
|
|
14169
|
+
const pidPath = path52.join(dir, "gateway.pid");
|
|
13912
14170
|
fs38.writeFileSync(pidPath, `${pid}
|
|
13913
14171
|
`, { encoding: "utf8", mode: 384 });
|
|
13914
14172
|
return pidPath;
|
|
13915
14173
|
}
|
|
13916
14174
|
function removePidFile() {
|
|
13917
|
-
const pidPath =
|
|
14175
|
+
const pidPath = path52.join(getMcpBaseDir(), "gateway.pid");
|
|
13918
14176
|
try {
|
|
13919
14177
|
if (fs38.existsSync(pidPath)) {
|
|
13920
14178
|
fs38.unlinkSync(pidPath);
|
|
@@ -13935,6 +14193,13 @@ async function probeExistingGatewayHealthy(env2 = process.env) {
|
|
|
13935
14193
|
}
|
|
13936
14194
|
function createGatewayRequestHandler(options = {}) {
|
|
13937
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
|
+
});
|
|
13938
14203
|
return (req, res) => {
|
|
13939
14204
|
void (async () => {
|
|
13940
14205
|
try {
|
|
@@ -13959,7 +14224,10 @@ function createGatewayRequestHandler(options = {}) {
|
|
|
13959
14224
|
homeDir: options.homeDir,
|
|
13960
14225
|
identityDeps: options.identityDeps,
|
|
13961
14226
|
ticketOptions: options.ticketOptions,
|
|
13962
|
-
createMemoraClient: options.createMemoraClient
|
|
14227
|
+
createMemoraClient: options.createMemoraClient,
|
|
14228
|
+
apiUrl,
|
|
14229
|
+
fetchImpl: options.fetchImpl,
|
|
14230
|
+
unboundPairing
|
|
13963
14231
|
});
|
|
13964
14232
|
if (mcpHandled) return;
|
|
13965
14233
|
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
@@ -13997,7 +14265,7 @@ async function runGateway(env2 = process.env, options = {}) {
|
|
|
13997
14265
|
process.on("SIGINT", () => shutdownNow("SIGINT"));
|
|
13998
14266
|
process.on("SIGTERM", () => shutdownNow("SIGTERM"));
|
|
13999
14267
|
process.on("exit", removePidFile);
|
|
14000
|
-
await new Promise((
|
|
14268
|
+
await new Promise((resolve47, reject) => {
|
|
14001
14269
|
server.once("error", async (err) => {
|
|
14002
14270
|
if (err.code === "EADDRINUSE") {
|
|
14003
14271
|
const healthy = await probeExistingGatewayHealthy(env2);
|
|
@@ -14024,7 +14292,7 @@ async function runGateway(env2 = process.env, options = {}) {
|
|
|
14024
14292
|
}
|
|
14025
14293
|
const pidPath = writePidFile(process.pid);
|
|
14026
14294
|
log3(`gateway started pid=${process.pid} listening on http://${host}:${port} pidFile=${pidPath}`);
|
|
14027
|
-
|
|
14295
|
+
resolve47();
|
|
14028
14296
|
});
|
|
14029
14297
|
});
|
|
14030
14298
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -466,6 +466,7 @@ function parsePayload(raw, repositoryBindingId) {
|
|
|
466
466
|
optionalString("refreshTokenExpiresAt");
|
|
467
467
|
optionalString("clientRedeemKey");
|
|
468
468
|
optionalString("clientRefreshKey");
|
|
469
|
+
optionalString("supabaseUserId");
|
|
469
470
|
return payload;
|
|
470
471
|
}
|
|
471
472
|
async function readInstallationCredentials(repositoryBindingId, options = {}) {
|
|
@@ -827,7 +828,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
827
828
|
const installationPublicId = data?.installation_public_id;
|
|
828
829
|
const projectId = data?.project_id;
|
|
829
830
|
const repositoryBindingId = data?.repository_binding_id;
|
|
830
|
-
|
|
831
|
+
const supabaseUserId = data?.supabase_user_id;
|
|
832
|
+
if (typeof accessToken !== "string" || typeof refreshToken !== "string" || typeof installationPublicId !== "string" || typeof projectId !== "string" || typeof repositoryBindingId !== "string" || typeof supabaseUserId !== "string" || supabaseUserId.length === 0) {
|
|
831
833
|
throw new Error("[memoraone-mcp] Invalid redeem response");
|
|
832
834
|
}
|
|
833
835
|
let sourceId = null;
|
|
@@ -845,7 +847,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
845
847
|
project_id: projectId,
|
|
846
848
|
repository_binding_id: repositoryBindingId,
|
|
847
849
|
source_id: sourceId,
|
|
848
|
-
recovered: Boolean(data.recovered)
|
|
850
|
+
recovered: Boolean(data.recovered),
|
|
851
|
+
supabase_user_id: supabaseUserId
|
|
849
852
|
};
|
|
850
853
|
}
|
|
851
854
|
async function refreshLocalMcpToken(apiUrl, body, options = {}) {
|
|
@@ -11346,7 +11349,8 @@ async function runConnectCommand(options) {
|
|
|
11346
11349
|
accessTokenExpiresAt: redeemed.access_token_expires_at ?? void 0,
|
|
11347
11350
|
refreshTokenExpiresAt: redeemed.refresh_token_expires_at ?? void 0,
|
|
11348
11351
|
installationPublicId: redeemed.installation_public_id,
|
|
11349
|
-
projectId: redeemed.project_id
|
|
11352
|
+
projectId: redeemed.project_id,
|
|
11353
|
+
supabaseUserId: redeemed.supabase_user_id
|
|
11350
11354
|
},
|
|
11351
11355
|
{ ...options.credentialOptions, clearKeys: ["clientRedeemKey"] }
|
|
11352
11356
|
);
|
package/dist/pluginPairing.cjs
CHANGED
|
@@ -199,6 +199,7 @@ function parsePayload(raw, repositoryBindingId) {
|
|
|
199
199
|
optionalString("refreshTokenExpiresAt");
|
|
200
200
|
optionalString("clientRedeemKey");
|
|
201
201
|
optionalString("clientRefreshKey");
|
|
202
|
+
optionalString("supabaseUserId");
|
|
202
203
|
return payload;
|
|
203
204
|
}
|
|
204
205
|
async function readInstallationCredentials(repositoryBindingId, options = {}) {
|
|
@@ -539,7 +540,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
539
540
|
const installationPublicId = data?.installation_public_id;
|
|
540
541
|
const projectId = data?.project_id;
|
|
541
542
|
const repositoryBindingId = data?.repository_binding_id;
|
|
542
|
-
|
|
543
|
+
const supabaseUserId = data?.supabase_user_id;
|
|
544
|
+
if (typeof accessToken !== "string" || typeof refreshToken !== "string" || typeof installationPublicId !== "string" || typeof projectId !== "string" || typeof repositoryBindingId !== "string" || typeof supabaseUserId !== "string" || supabaseUserId.length === 0) {
|
|
543
545
|
throw new Error("[memoraone-mcp] Invalid redeem response");
|
|
544
546
|
}
|
|
545
547
|
let sourceId = null;
|
|
@@ -557,7 +559,8 @@ async function redeemLocalConnectCode(apiUrl, body, options = {}) {
|
|
|
557
559
|
project_id: projectId,
|
|
558
560
|
repository_binding_id: repositoryBindingId,
|
|
559
561
|
source_id: sourceId,
|
|
560
|
-
recovered: Boolean(data.recovered)
|
|
562
|
+
recovered: Boolean(data.recovered),
|
|
563
|
+
supabase_user_id: supabaseUserId
|
|
561
564
|
};
|
|
562
565
|
}
|
|
563
566
|
|
|
@@ -9641,7 +9644,8 @@ async function runConnectCommand(options) {
|
|
|
9641
9644
|
accessTokenExpiresAt: redeemed.access_token_expires_at ?? void 0,
|
|
9642
9645
|
refreshTokenExpiresAt: redeemed.refresh_token_expires_at ?? void 0,
|
|
9643
9646
|
installationPublicId: redeemed.installation_public_id,
|
|
9644
|
-
projectId: redeemed.project_id
|
|
9647
|
+
projectId: redeemed.project_id,
|
|
9648
|
+
supabaseUserId: redeemed.supabase_user_id
|
|
9645
9649
|
},
|
|
9646
9650
|
{ ...options.credentialOptions, clearKeys: ["clientRedeemKey"] }
|
|
9647
9651
|
);
|