@memoraone/mcp 0.1.21 → 0.1.23
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 +559 -56
- package/dist/daemon.cjs +294 -77
- package/dist/index.cjs +181 -74
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,6 @@ __export(index_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
var path6 = __toESM(require("path"), 1);
|
|
37
|
-
var crypto5 = __toESM(require("crypto"), 1);
|
|
38
37
|
var import_node_url2 = require("url");
|
|
39
38
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
40
39
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
@@ -281,6 +280,13 @@ var memoraClient_default = MemoraClient;
|
|
|
281
280
|
var fs2 = __toESM(require("fs/promises"), 1);
|
|
282
281
|
var path2 = __toESM(require("path"), 1);
|
|
283
282
|
var uuidRegex2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
283
|
+
function normalizeEnvironment(raw) {
|
|
284
|
+
if (raw === void 0 || raw === null || typeof raw !== "string") {
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
const trimmed = raw.trim();
|
|
288
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
289
|
+
}
|
|
284
290
|
function parseAndValidateM1(content, markerPath) {
|
|
285
291
|
let parsed2;
|
|
286
292
|
try {
|
|
@@ -297,7 +303,8 @@ function parseAndValidateM1(content, markerPath) {
|
|
|
297
303
|
}
|
|
298
304
|
const apiKeyRaw = parsed2?.MEMORAONE_API_KEY ?? parsed2?.api_key;
|
|
299
305
|
const apiKey = apiKeyRaw !== void 0 && apiKeyRaw !== null && typeof apiKeyRaw === "string" && apiKeyRaw.trim() !== "" ? apiKeyRaw.trim() : null;
|
|
300
|
-
|
|
306
|
+
const environment = normalizeEnvironment(parsed2?.environment);
|
|
307
|
+
return environment === void 0 ? { projectId: projectId.trim(), apiKey } : { projectId: projectId.trim(), apiKey, environment };
|
|
301
308
|
}
|
|
302
309
|
async function resolveProjectIdFromExplicitM1Path() {
|
|
303
310
|
const raw = process.env.MEMORAONE_M1_PATH;
|
|
@@ -307,8 +314,8 @@ async function resolveProjectIdFromExplicitM1Path() {
|
|
|
307
314
|
const markerPath = path2.resolve(raw);
|
|
308
315
|
try {
|
|
309
316
|
const content = await fs2.readFile(markerPath, "utf8");
|
|
310
|
-
const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
|
|
311
|
-
return { projectId, apiKey, foundAt: markerPath };
|
|
317
|
+
const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
|
|
318
|
+
return environment === void 0 ? { projectId, apiKey, foundAt: markerPath } : { projectId, apiKey, environment, foundAt: markerPath };
|
|
312
319
|
} catch (err) {
|
|
313
320
|
if (err?.code === "ENOENT") {
|
|
314
321
|
return null;
|
|
@@ -322,9 +329,9 @@ async function findM1WalkingUp(workspaceRoot) {
|
|
|
322
329
|
const markerPath = path2.join(current, "memoraone.m1");
|
|
323
330
|
try {
|
|
324
331
|
const content = await fs2.readFile(markerPath, "utf8");
|
|
325
|
-
const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
|
|
332
|
+
const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
|
|
326
333
|
const repoRoot = path2.dirname(markerPath);
|
|
327
|
-
return { projectId, apiKey, repoRoot, markerPath };
|
|
334
|
+
return environment === void 0 ? { projectId, apiKey, repoRoot, markerPath } : { projectId, apiKey, environment, repoRoot, markerPath };
|
|
328
335
|
} catch (err) {
|
|
329
336
|
if (err?.code !== "ENOENT") {
|
|
330
337
|
throw err;
|
|
@@ -384,6 +391,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
|
|
|
384
391
|
workspaceRoot: path2.dirname(explicitBinding.foundAt),
|
|
385
392
|
m1Path: explicitBinding.foundAt,
|
|
386
393
|
apiKey: resolved.apiKey,
|
|
394
|
+
...explicitBinding.environment !== void 0 ? { environment: explicitBinding.environment } : {},
|
|
387
395
|
bindingSource: "explicit-m1-path",
|
|
388
396
|
apiKeySource: resolved.apiKeySource
|
|
389
397
|
};
|
|
@@ -401,6 +409,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
|
|
|
401
409
|
workspaceRoot: binding.repoRoot,
|
|
402
410
|
m1Path: binding.markerPath,
|
|
403
411
|
apiKey: resolved.apiKey,
|
|
412
|
+
...binding.environment !== void 0 ? { environment: binding.environment } : {},
|
|
404
413
|
bindingSource: "workspace-search",
|
|
405
414
|
apiKeySource: resolved.apiKeySource
|
|
406
415
|
};
|
|
@@ -481,6 +490,7 @@ async function registerRepoSource(client, projectId, repoPath, ideType) {
|
|
|
481
490
|
|
|
482
491
|
// src/tools/postEvent.ts
|
|
483
492
|
var import_v42 = require("zod/v4");
|
|
493
|
+
var postEventDescription = 'Append a durable project-change note to the MemoraOne timeline. Use after meaningful repository or project changes: decisions, fixes, new endpoints, schema changes, migrations, important wiring, or durable product behavior changes \u2014 not for trivial edits, formatting-only changes, or temporary WIP. Recommended shape: kind "note"; content.title (concise title); content.body (one durable, fact-promotable project-change statement); metadata.source (agent name, e.g. "cursor"); metadata.purpose "dev-log"; metadata.schema "v1".';
|
|
484
494
|
var postEventShape = {
|
|
485
495
|
kind: import_v42.z.string().min(1),
|
|
486
496
|
actor: import_v42.z.object({
|
|
@@ -589,6 +599,9 @@ var setProjectShape = {
|
|
|
589
599
|
projectId: import_v411.z.string().min(1).optional()
|
|
590
600
|
};
|
|
591
601
|
|
|
602
|
+
// src/tools/bindingStatus.ts
|
|
603
|
+
var bindingStatusShape = {};
|
|
604
|
+
|
|
592
605
|
// src/tools/handlers/postEvent.ts
|
|
593
606
|
var import_v412 = require("zod/v4");
|
|
594
607
|
var crypto3 = __toESM(require("crypto"), 1);
|
|
@@ -661,6 +674,32 @@ var postEventInputSchema = import_v412.z.object({
|
|
|
661
674
|
content: import_v412.z.record(import_v412.z.string(), import_v412.z.any()),
|
|
662
675
|
metadata: import_v412.z.record(import_v412.z.string(), import_v412.z.any()).optional()
|
|
663
676
|
});
|
|
677
|
+
function buildPostEventContentFields(content) {
|
|
678
|
+
if (typeof content.message === "string") {
|
|
679
|
+
return { message: content.message };
|
|
680
|
+
}
|
|
681
|
+
if (typeof content.text === "string") {
|
|
682
|
+
return { message: content.text };
|
|
683
|
+
}
|
|
684
|
+
const title = typeof content.title === "string" ? content.title : void 0;
|
|
685
|
+
const body = typeof content.body === "string" ? content.body : void 0;
|
|
686
|
+
if (title !== void 0 || body !== void 0) {
|
|
687
|
+
const message = body ?? title ?? "";
|
|
688
|
+
const structuredContent = {};
|
|
689
|
+
for (const [key, value] of Object.entries(content)) {
|
|
690
|
+
if (key === "message" || key === "text") continue;
|
|
691
|
+
structuredContent[key] = value;
|
|
692
|
+
}
|
|
693
|
+
const new_value = {
|
|
694
|
+
message,
|
|
695
|
+
...title !== void 0 ? { title } : {},
|
|
696
|
+
...body !== void 0 ? { body } : {},
|
|
697
|
+
content: structuredContent
|
|
698
|
+
};
|
|
699
|
+
return { message, new_value };
|
|
700
|
+
}
|
|
701
|
+
return { message: JSON.stringify(content) };
|
|
702
|
+
}
|
|
664
703
|
async function handlePostEvent(client, args) {
|
|
665
704
|
const nonce = crypto3.randomBytes(8).toString("hex");
|
|
666
705
|
console.error(
|
|
@@ -672,7 +711,7 @@ async function handlePostEvent(client, args) {
|
|
|
672
711
|
throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
|
|
673
712
|
}
|
|
674
713
|
const content = parsed2.content ?? {};
|
|
675
|
-
const
|
|
714
|
+
const { message, new_value } = buildPostEventContentFields(content);
|
|
676
715
|
const body = {
|
|
677
716
|
kind: parsed2.kind,
|
|
678
717
|
message,
|
|
@@ -682,6 +721,7 @@ async function handlePostEvent(client, args) {
|
|
|
682
721
|
identifier: config2.agentName,
|
|
683
722
|
...parsed2.actor.id ? { id: parsed2.actor.id } : {}
|
|
684
723
|
},
|
|
724
|
+
...new_value ? { new_value } : {},
|
|
685
725
|
...parsed2.metadata ? { metadata: parsed2.metadata } : {}
|
|
686
726
|
};
|
|
687
727
|
try {
|
|
@@ -866,7 +906,8 @@ async function handleAskWithMemory(client, args) {
|
|
|
866
906
|
}
|
|
867
907
|
return {
|
|
868
908
|
answer: res.answer,
|
|
869
|
-
used: res.used ?? {}
|
|
909
|
+
used: res.used ?? {},
|
|
910
|
+
...res.retrieval !== void 0 ? { retrieval: res.retrieval } : {}
|
|
870
911
|
};
|
|
871
912
|
}
|
|
872
913
|
|
|
@@ -1411,47 +1452,79 @@ async function handleSetProject(args) {
|
|
|
1411
1452
|
return { ok: true, projectKey: resolvedProjectKey };
|
|
1412
1453
|
}
|
|
1413
1454
|
|
|
1414
|
-
// src/
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
};
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
if (uri.startsWith("file://")) {
|
|
1426
|
-
return (0, import_node_url2.fileURLToPath)(uri);
|
|
1455
|
+
// src/tools/handlers/bindingStatus.ts
|
|
1456
|
+
function buildBindingStatus(binding) {
|
|
1457
|
+
const status = {
|
|
1458
|
+
projectId: binding.projectId,
|
|
1459
|
+
workspaceRoot: binding.workspaceRoot,
|
|
1460
|
+
m1Path: binding.m1Path,
|
|
1461
|
+
bindingSource: binding.bindingSource,
|
|
1462
|
+
apiKeySource: binding.apiKeySource
|
|
1463
|
+
};
|
|
1464
|
+
if (binding.environment !== void 0) {
|
|
1465
|
+
status.environment = binding.environment;
|
|
1427
1466
|
}
|
|
1428
|
-
return
|
|
1429
|
-
}
|
|
1430
|
-
function
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
return void 0;
|
|
1467
|
+
return status;
|
|
1468
|
+
}
|
|
1469
|
+
function handleBindingStatus(binding) {
|
|
1470
|
+
if (!binding) {
|
|
1471
|
+
throw new Error("[memoraone-mcp] Binding status unavailable (not initialized)");
|
|
1434
1472
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1473
|
+
return buildBindingStatus(binding);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// src/heartbeat.ts
|
|
1477
|
+
var crypto5 = __toESM(require("crypto"), 1);
|
|
1478
|
+
function fingerprintApiKey(apiKey) {
|
|
1479
|
+
return crypto5.createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
|
|
1438
1480
|
}
|
|
1439
1481
|
function isHeartbeatDebugEnabled() {
|
|
1440
1482
|
const value = String(process.env.MEMORAONE_DEBUG_HEARTBEAT ?? "").trim().toLowerCase();
|
|
1441
1483
|
return ["1", "true", "yes", "on"].includes(value);
|
|
1442
1484
|
}
|
|
1443
|
-
function
|
|
1444
|
-
return
|
|
1485
|
+
function resolveHeartbeatIntervalMs() {
|
|
1486
|
+
return Number.isFinite(config2.heartbeatIntervalMs) ? Math.max(1e3, config2.heartbeatIntervalMs) : 3e4;
|
|
1487
|
+
}
|
|
1488
|
+
async function sendProjectHeartbeat(client, ctx) {
|
|
1489
|
+
try {
|
|
1490
|
+
const pid = ctx.projectId?.trim();
|
|
1491
|
+
if (isHeartbeatDebugEnabled()) {
|
|
1492
|
+
process.stderr.write(
|
|
1493
|
+
`[memoraone-mcp][diag] heartbeat projectId=${pid ?? "unknown"} apiKeySource=${ctx.apiKeySource ?? "unknown"} apiKeyFingerprint=${ctx.apiKeyFingerprint ?? "unknown"} ideType=${ctx.ideType ?? "unknown"}
|
|
1494
|
+
`
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
if (!pid) {
|
|
1498
|
+
throw new Error("[memoraone-mcp] Cannot send heartbeat without an active project binding");
|
|
1499
|
+
}
|
|
1500
|
+
const body = {};
|
|
1501
|
+
if (ctx.ideType) body.ide_type = ctx.ideType;
|
|
1502
|
+
await client.post(`/v1/projects/${pid}/heartbeat`, body, {
|
|
1503
|
+
log: false,
|
|
1504
|
+
headers: {
|
|
1505
|
+
"x-project-id": pid
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
} catch (err) {
|
|
1509
|
+
process.stderr.write(
|
|
1510
|
+
`[memoraone-mcp][info] heartbeat error (silent) ${String(err)}
|
|
1511
|
+
`
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1445
1514
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1515
|
+
|
|
1516
|
+
// src/ideType.ts
|
|
1517
|
+
function inferIdeType(params, options = {}) {
|
|
1518
|
+
const env2 = options.env ?? process.env;
|
|
1519
|
+
const argv = (options.argv ?? process.argv).join(" ").toLowerCase();
|
|
1520
|
+
const configIdeType = options.configIdeType ?? config2.ideType;
|
|
1521
|
+
if (configIdeType) {
|
|
1522
|
+
return configIdeType;
|
|
1449
1523
|
}
|
|
1450
1524
|
const clientInfoName = String(params?.clientInfo?.name ?? "").toLowerCase();
|
|
1451
1525
|
const clientInfoVersion = String(params?.clientInfo?.version ?? "").toLowerCase();
|
|
1452
|
-
const termProgram = String(
|
|
1453
|
-
const
|
|
1454
|
-
const envKeys = Object.keys(process.env);
|
|
1526
|
+
const termProgram = String(env2.TERM_PROGRAM ?? "").toLowerCase();
|
|
1527
|
+
const envKeys = Object.keys(env2);
|
|
1455
1528
|
const hasCursorSignals = envKeys.some((key) => key.startsWith("CURSOR_")) || termProgram === "cursor" || clientInfoName.includes("cursor") || clientInfoVersion.includes("cursor") || argv.includes("cursor");
|
|
1456
1529
|
if (hasCursorSignals) {
|
|
1457
1530
|
return "cursor";
|
|
@@ -1463,7 +1536,7 @@ function inferIdeType(params) {
|
|
|
1463
1536
|
"JETBRAINS_REMOTE_RUN",
|
|
1464
1537
|
"INTELLIJ_ENVIRONMENT_READER"
|
|
1465
1538
|
].includes(key)
|
|
1466
|
-
) || String(
|
|
1539
|
+
) || String(env2.TERMINAL_EMULATOR ?? "").toLowerCase().includes("jetbrains") || /(jetbrains|intellij|pycharm|webstorm|goland|rubymine|clion|phpstorm|rider|datagrip)/.test(
|
|
1467
1540
|
clientInfoName
|
|
1468
1541
|
) || /(jetbrains|intellij|pycharm|webstorm|goland|rubymine|clion|phpstorm|rider|datagrip)/.test(
|
|
1469
1542
|
argv
|
|
@@ -1477,32 +1550,31 @@ function inferIdeType(params) {
|
|
|
1477
1550
|
}
|
|
1478
1551
|
return void 0;
|
|
1479
1552
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
);
|
|
1488
|
-
}
|
|
1489
|
-
if (!pid) {
|
|
1490
|
-
throw new Error("[memoraone-mcp] Cannot send heartbeat without an active project binding");
|
|
1553
|
+
|
|
1554
|
+
// src/index.ts
|
|
1555
|
+
var notInitializedResult = {
|
|
1556
|
+
content: [
|
|
1557
|
+
{
|
|
1558
|
+
type: "text",
|
|
1559
|
+
text: "MemoraOne MCP not initialized (project binding missing)."
|
|
1491
1560
|
}
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
}
|
|
1499
|
-
});
|
|
1500
|
-
} catch (err) {
|
|
1501
|
-
process.stderr.write(
|
|
1502
|
-
`[memoraone-mcp][info] heartbeat error (silent) ${String(err)}
|
|
1503
|
-
`
|
|
1504
|
-
);
|
|
1561
|
+
]
|
|
1562
|
+
};
|
|
1563
|
+
var initializeDiagDumped = false;
|
|
1564
|
+
var uriToPath = (uri) => {
|
|
1565
|
+
if (uri.startsWith("file://")) {
|
|
1566
|
+
return (0, import_node_url2.fileURLToPath)(uri);
|
|
1505
1567
|
}
|
|
1568
|
+
return uri;
|
|
1569
|
+
};
|
|
1570
|
+
function getCursorWorkspaceRootFromEnv() {
|
|
1571
|
+
const raw = process.env.WORKSPACE_FOLDER_PATHS;
|
|
1572
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
1573
|
+
return void 0;
|
|
1574
|
+
}
|
|
1575
|
+
const parts = raw.split(path6.delimiter).map((p) => p.trim()).filter(Boolean);
|
|
1576
|
+
const first = parts[0];
|
|
1577
|
+
return first ? path6.resolve(first) : void 0;
|
|
1506
1578
|
}
|
|
1507
1579
|
function redactSensitiveFields(obj) {
|
|
1508
1580
|
if (obj === null || obj === void 0) return obj;
|
|
@@ -1594,6 +1666,7 @@ async function main(opts = {}) {
|
|
|
1594
1666
|
projectId: null,
|
|
1595
1667
|
apiKeySource: null,
|
|
1596
1668
|
apiKeyFingerprint: null,
|
|
1669
|
+
authoritativeBinding: opts.authoritativeBinding ?? null,
|
|
1597
1670
|
ideType: void 0
|
|
1598
1671
|
};
|
|
1599
1672
|
let workspaceRoot;
|
|
@@ -1643,7 +1716,7 @@ async function main(opts = {}) {
|
|
|
1643
1716
|
const registeredToolNames = [];
|
|
1644
1717
|
server.tool(
|
|
1645
1718
|
"memora_post_event",
|
|
1646
|
-
|
|
1719
|
+
postEventDescription,
|
|
1647
1720
|
postEventShape,
|
|
1648
1721
|
async (args) => runWithSessionContext(sessionContext, async () => {
|
|
1649
1722
|
if (!runtime.client || !runtime.projectId) return notInitializedResult;
|
|
@@ -1719,6 +1792,19 @@ async function main(opts = {}) {
|
|
|
1719
1792
|
})
|
|
1720
1793
|
);
|
|
1721
1794
|
registeredToolNames.push("memora_set_project");
|
|
1795
|
+
server.tool(
|
|
1796
|
+
"memora_status",
|
|
1797
|
+
"Return non-secret project binding metadata for this MCP session",
|
|
1798
|
+
bindingStatusShape,
|
|
1799
|
+
async () => runWithSessionContext(sessionContext, async () => {
|
|
1800
|
+
if (!runtime.authoritativeBinding) return notInitializedResult;
|
|
1801
|
+
const result = handleBindingStatus(runtime.authoritativeBinding);
|
|
1802
|
+
return {
|
|
1803
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
1804
|
+
};
|
|
1805
|
+
})
|
|
1806
|
+
);
|
|
1807
|
+
registeredToolNames.push("memora_status");
|
|
1722
1808
|
registerToolWithWorklog(
|
|
1723
1809
|
server,
|
|
1724
1810
|
runtime,
|
|
@@ -1800,6 +1886,9 @@ async function main(opts = {}) {
|
|
|
1800
1886
|
try {
|
|
1801
1887
|
const params = request.params;
|
|
1802
1888
|
runtime.ideType = inferIdeType(params);
|
|
1889
|
+
if (runtime.ideType && opts.onSessionIdeTypeKnown) {
|
|
1890
|
+
opts.onSessionIdeTypeKnown(runtime.ideType);
|
|
1891
|
+
}
|
|
1803
1892
|
if (!initializeDiagDumped) {
|
|
1804
1893
|
initializeDiagDumped = true;
|
|
1805
1894
|
const folders = Array.isArray(params.workspaceFolders) ? params.workspaceFolders.map((f) => ({ name: f?.name, uri: f?.uri })) : params.workspaceFolders;
|
|
@@ -1869,10 +1958,12 @@ async function main(opts = {}) {
|
|
|
1869
1958
|
runtime.projectId = projectId;
|
|
1870
1959
|
runtime.apiKeySource = binding.apiKeySource;
|
|
1871
1960
|
runtime.apiKeyFingerprint = fingerprintApiKey(apiKeyToUse);
|
|
1961
|
+
runtime.authoritativeBinding = binding;
|
|
1872
1962
|
runtime.client = new memoraClient_default(config2, projectId, apiKeyToUse);
|
|
1873
1963
|
workspaceRoot = binding.workspaceRoot;
|
|
1964
|
+
const environmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
|
|
1874
1965
|
process.stderr.write(
|
|
1875
|
-
`[memoraone-mcp] registering workspace source bindingSource=${binding.bindingSource} workspaceRoot=${workspaceRoot ?? "(unset)"} m1Path=${binding.m1Path}
|
|
1966
|
+
`[memoraone-mcp] registering workspace source bindingSource=${binding.bindingSource} workspaceRoot=${workspaceRoot ?? "(unset)"} m1Path=${binding.m1Path}${environmentLog}
|
|
1876
1967
|
`
|
|
1877
1968
|
);
|
|
1878
1969
|
await registerRepoSource(
|
|
@@ -1886,8 +1977,9 @@ async function main(opts = {}) {
|
|
|
1886
1977
|
console.error("[memoraone-mcp][auth] project_id:", projectId);
|
|
1887
1978
|
console.error("[memoraone-mcp][auth] api_key source:", binding.apiKeySource);
|
|
1888
1979
|
}
|
|
1980
|
+
const bindingEnvironmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
|
|
1889
1981
|
console.error(
|
|
1890
|
-
`[memoraone-mcp] ${sessionLabel} authoritative binding: project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}`
|
|
1982
|
+
`[memoraone-mcp] ${sessionLabel} authoritative binding: project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}${bindingEnvironmentLog}`
|
|
1891
1983
|
);
|
|
1892
1984
|
bindingReadyResolve?.(runtime.client);
|
|
1893
1985
|
return server.server._oninitialize(request);
|
|
@@ -1904,14 +1996,25 @@ async function main(opts = {}) {
|
|
|
1904
1996
|
await server.connect(transport);
|
|
1905
1997
|
const activeClient = await bindingReady;
|
|
1906
1998
|
let heartbeatInterval = null;
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1999
|
+
const daemonSession = Boolean(opts.sessionSocket);
|
|
2000
|
+
if (config2.heartbeatEnabled && daemonSession) {
|
|
2001
|
+
console.error(
|
|
2002
|
+
`[memoraone-mcp] ${sessionLabel} defers heartbeat to daemon for project ${runtime.projectId}`
|
|
2003
|
+
);
|
|
2004
|
+
} else if (config2.heartbeatEnabled) {
|
|
2005
|
+
const intervalMs = resolveHeartbeatIntervalMs();
|
|
2006
|
+
const heartbeatCtx = {
|
|
2007
|
+
projectId: runtime.projectId,
|
|
2008
|
+
ideType: runtime.ideType,
|
|
2009
|
+
apiKeySource: runtime.apiKeySource,
|
|
2010
|
+
apiKeyFingerprint: runtime.apiKeyFingerprint
|
|
2011
|
+
};
|
|
1910
2012
|
console.error(
|
|
1911
2013
|
`[memoraone-mcp] ${sessionLabel} owns heartbeat for project ${runtime.projectId} interval=${intervalMs}ms`
|
|
1912
2014
|
);
|
|
2015
|
+
await sendProjectHeartbeat(activeClient, heartbeatCtx);
|
|
1913
2016
|
heartbeatInterval = setInterval(() => {
|
|
1914
|
-
|
|
2017
|
+
sendProjectHeartbeat(activeClient, heartbeatCtx).catch(() => {
|
|
1915
2018
|
});
|
|
1916
2019
|
}, intervalMs);
|
|
1917
2020
|
}
|
|
@@ -1920,10 +2023,14 @@ async function main(opts = {}) {
|
|
|
1920
2023
|
const shutdown = (signal, exitProcess = true) => {
|
|
1921
2024
|
process.off("SIGINT", onSigInt);
|
|
1922
2025
|
process.off("SIGTERM", onSigTerm);
|
|
1923
|
-
if (heartbeatInterval)
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
2026
|
+
if (heartbeatInterval) {
|
|
2027
|
+
clearInterval(heartbeatInterval);
|
|
2028
|
+
heartbeatInterval = null;
|
|
2029
|
+
if (runtime.projectId) {
|
|
2030
|
+
console.error(
|
|
2031
|
+
`[memoraone-mcp] ${sessionLabel} released session heartbeat for project ${runtime.projectId}`
|
|
2032
|
+
);
|
|
2033
|
+
}
|
|
1927
2034
|
}
|
|
1928
2035
|
if (devMode) {
|
|
1929
2036
|
console.error(`[memoraone-mcp] ${sessionLabel} received ${signal}, shutting down`);
|