@memoraone/mcp 0.1.41 → 0.1.43
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 +654 -661
- package/dist/daemon.cjs +110 -71
- package/dist/index.cjs +110 -73
- package/dist/pluginPairing.cjs +10046 -0
- package/package.json +8 -1
package/dist/daemon.cjs
CHANGED
|
@@ -170,11 +170,11 @@ function ensureBaseDir() {
|
|
|
170
170
|
|
|
171
171
|
// src/projectBinding.ts
|
|
172
172
|
var fs7 = __toESM(require("fs/promises"), 1);
|
|
173
|
-
var
|
|
173
|
+
var path10 = __toESM(require("path"), 1);
|
|
174
174
|
|
|
175
175
|
// src/localState/resolveLocalBinding.ts
|
|
176
176
|
var fs6 = __toESM(require("fs/promises"), 1);
|
|
177
|
-
var
|
|
177
|
+
var path9 = __toESM(require("path"), 1);
|
|
178
178
|
|
|
179
179
|
// src/localState/bindingStore.ts
|
|
180
180
|
var fs4 = __toESM(require("fs/promises"), 1);
|
|
@@ -416,7 +416,7 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
416
416
|
`[memoraone-mcp] Failed to acquire lock ${lockName} after ${maxRetries} retries`
|
|
417
417
|
);
|
|
418
418
|
}
|
|
419
|
-
await new Promise((
|
|
419
|
+
await new Promise((resolve10) => setTimeout(resolve10, retryDelayMs));
|
|
420
420
|
continue;
|
|
421
421
|
}
|
|
422
422
|
throw err;
|
|
@@ -868,6 +868,9 @@ function identityMatchesStored(stored, current) {
|
|
|
868
868
|
return identitiesMatch(stored, current);
|
|
869
869
|
}
|
|
870
870
|
|
|
871
|
+
// src/localState/localConnectClient.ts
|
|
872
|
+
var path8 = __toESM(require("path"), 1);
|
|
873
|
+
|
|
871
874
|
// src/client/memoraClient.ts
|
|
872
875
|
var crypto2 = __toESM(require("crypto"), 1);
|
|
873
876
|
var PROJECT_ID_HEADER = "x-project-id";
|
|
@@ -977,9 +980,9 @@ var MemoraClient = class {
|
|
|
977
980
|
...options?.headers ?? {}
|
|
978
981
|
};
|
|
979
982
|
}
|
|
980
|
-
async perform(method,
|
|
983
|
+
async perform(method, path15, body, options, retried = false) {
|
|
981
984
|
const nonce = crypto2.randomBytes(8).toString("hex");
|
|
982
|
-
const url = `${this.baseUrl}${
|
|
985
|
+
const url = `${this.baseUrl}${path15.startsWith("/") ? path15 : `/${path15}`}`;
|
|
983
986
|
this.resolveProjectId();
|
|
984
987
|
console.error(
|
|
985
988
|
`[memoraone-mcp][info] requestJson nonce=${nonce} stage=before_fetch method=${method} url=${url}`
|
|
@@ -1003,10 +1006,10 @@ var MemoraClient = class {
|
|
|
1003
1006
|
if (accepted) {
|
|
1004
1007
|
return res.text ? JSON.parse(res.text) : null;
|
|
1005
1008
|
}
|
|
1006
|
-
if (
|
|
1009
|
+
if (res.status === 401 && !options?.skipAuthRefresh && !retried) {
|
|
1007
1010
|
try {
|
|
1008
1011
|
await this.refreshAccessToken();
|
|
1009
|
-
return this.perform(method,
|
|
1012
|
+
return this.perform(method, path15, body, options, true);
|
|
1010
1013
|
} catch (err) {
|
|
1011
1014
|
if (err instanceof ReconnectRequiredError) {
|
|
1012
1015
|
await this.markReconnectRequired();
|
|
@@ -1014,9 +1017,6 @@ var MemoraClient = class {
|
|
|
1014
1017
|
throw err;
|
|
1015
1018
|
}
|
|
1016
1019
|
}
|
|
1017
|
-
if (res.status === 401 || res.status === 403) {
|
|
1018
|
-
await this.markReconnectRequired();
|
|
1019
|
-
}
|
|
1020
1020
|
const quiet = options?.quietHttpStatuses?.includes(res.status);
|
|
1021
1021
|
if (!quiet) {
|
|
1022
1022
|
const snippet = res.text.length > 200 ? `${res.text.slice(0, 200)}...` : res.text;
|
|
@@ -1029,22 +1029,22 @@ var MemoraClient = class {
|
|
|
1029
1029
|
}
|
|
1030
1030
|
return res.text ? JSON.parse(res.text) : null;
|
|
1031
1031
|
}
|
|
1032
|
-
async post(
|
|
1033
|
-
console.error(`[memoraone-mcp][info] MemoraClient.post ENTER path=${
|
|
1034
|
-
const result = await this.perform("POST",
|
|
1035
|
-
console.error(`[memoraone-mcp][info] MemoraClient.post EXIT path=${
|
|
1032
|
+
async post(path15, body, options) {
|
|
1033
|
+
console.error(`[memoraone-mcp][info] MemoraClient.post ENTER path=${path15}`);
|
|
1034
|
+
const result = await this.perform("POST", path15, body, options);
|
|
1035
|
+
console.error(`[memoraone-mcp][info] MemoraClient.post EXIT path=${path15}`);
|
|
1036
1036
|
return result;
|
|
1037
1037
|
}
|
|
1038
|
-
async get(
|
|
1039
|
-
return this.perform("GET",
|
|
1038
|
+
async get(path15, options) {
|
|
1039
|
+
return this.perform("GET", path15, void 0, options);
|
|
1040
1040
|
}
|
|
1041
1041
|
};
|
|
1042
1042
|
var memoraClient_default = MemoraClient;
|
|
1043
1043
|
|
|
1044
1044
|
// src/localState/localConnectClient.ts
|
|
1045
|
-
async function requestJson2(baseUrl, method,
|
|
1045
|
+
async function requestJson2(baseUrl, method, path15, options = {}) {
|
|
1046
1046
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1047
|
-
const url = `${baseUrl.replace(/\/+$/, "")}${
|
|
1047
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${path15.startsWith("/") ? path15 : `/${path15}`}`;
|
|
1048
1048
|
const res = await fetchImpl(url, {
|
|
1049
1049
|
method,
|
|
1050
1050
|
headers: {
|
|
@@ -1150,7 +1150,7 @@ async function refreshInstallationAccessToken(options) {
|
|
|
1150
1150
|
|
|
1151
1151
|
// src/localState/resolveLocalBinding.ts
|
|
1152
1152
|
async function detectLegacyM1Warning(workspaceRoot) {
|
|
1153
|
-
const candidate =
|
|
1153
|
+
const candidate = path9.join(path9.resolve(workspaceRoot), CANONICAL_M1_FILENAME);
|
|
1154
1154
|
try {
|
|
1155
1155
|
await fs6.access(candidate);
|
|
1156
1156
|
return candidate;
|
|
@@ -1159,7 +1159,7 @@ async function detectLegacyM1Warning(workspaceRoot) {
|
|
|
1159
1159
|
}
|
|
1160
1160
|
}
|
|
1161
1161
|
async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
|
|
1162
|
-
const resolved =
|
|
1162
|
+
const resolved = path9.resolve(workspaceRoot);
|
|
1163
1163
|
const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
|
|
1164
1164
|
const legacyM1WarningPath = await detectLegacyM1Warning(resolved);
|
|
1165
1165
|
const index = await loadPathIndex(options.homeDir);
|
|
@@ -1167,7 +1167,7 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
|
|
|
1167
1167
|
if (lookup.kind === "path") {
|
|
1168
1168
|
const record = await readBindingRecord(lookup.repositoryBindingId, options.homeDir);
|
|
1169
1169
|
if (record && identityMatchesStored(record.filesystemIdentity, identity)) {
|
|
1170
|
-
if (
|
|
1170
|
+
if (path9.resolve(record.workspaceRoot) !== resolved) {
|
|
1171
1171
|
const updated = {
|
|
1172
1172
|
...record,
|
|
1173
1173
|
workspaceRoot: resolved,
|
|
@@ -1229,7 +1229,7 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
|
|
|
1229
1229
|
};
|
|
1230
1230
|
}
|
|
1231
1231
|
async function resolveLocalBinding(workspaceRoot, options = {}) {
|
|
1232
|
-
const resolved =
|
|
1232
|
+
const resolved = path9.resolve(workspaceRoot);
|
|
1233
1233
|
const { repositoryBindingId, legacyM1WarningPath } = await ensureRepositoryBindingForRoot(
|
|
1234
1234
|
resolved,
|
|
1235
1235
|
{ ...options, createIfMissing: false }
|
|
@@ -1301,7 +1301,7 @@ function toResolvedBinding(local) {
|
|
|
1301
1301
|
};
|
|
1302
1302
|
}
|
|
1303
1303
|
async function warnLegacyM1IfPresent(workspaceRoot) {
|
|
1304
|
-
const candidate =
|
|
1304
|
+
const candidate = path10.join(path10.resolve(workspaceRoot), CANONICAL_M1_FILENAME);
|
|
1305
1305
|
try {
|
|
1306
1306
|
await fs7.access(candidate);
|
|
1307
1307
|
process.stderr.write(
|
|
@@ -1360,7 +1360,7 @@ function normalizeWorkspaceSearchRoots(workspaceRoot) {
|
|
|
1360
1360
|
if (raw === void 0) continue;
|
|
1361
1361
|
const trimmed = String(raw).trim();
|
|
1362
1362
|
if (trimmed === "") continue;
|
|
1363
|
-
const resolved =
|
|
1363
|
+
const resolved = path10.resolve(trimmed);
|
|
1364
1364
|
if (!seen.has(resolved)) {
|
|
1365
1365
|
seen.add(resolved);
|
|
1366
1366
|
out.push(resolved);
|
|
@@ -1371,17 +1371,17 @@ function normalizeWorkspaceSearchRoots(workspaceRoot) {
|
|
|
1371
1371
|
function bindingRelevantValuesMatch(a, b) {
|
|
1372
1372
|
const envA = a.environment ?? void 0;
|
|
1373
1373
|
const envB = b.environment ?? void 0;
|
|
1374
|
-
return a.repositoryBindingId === b.repositoryBindingId && a.projectId.trim().toLowerCase() === b.projectId.trim().toLowerCase() &&
|
|
1374
|
+
return a.repositoryBindingId === b.repositoryBindingId && a.projectId.trim().toLowerCase() === b.projectId.trim().toLowerCase() && path10.resolve(a.workspaceRoot) === path10.resolve(b.workspaceRoot) && (a.installationPublicId ?? void 0) === (b.installationPublicId ?? void 0) && envA === envB && a.status === b.status;
|
|
1375
1375
|
}
|
|
1376
|
-
async function reconcileResolvedBindingWithDisk(cached) {
|
|
1376
|
+
async function reconcileResolvedBindingWithDisk(cached, options = {}) {
|
|
1377
1377
|
const repositoryBindingId = assertRepositoryBindingId(cached.repositoryBindingId);
|
|
1378
|
-
const record = await readBindingRecord(repositoryBindingId);
|
|
1378
|
+
const record = await readBindingRecord(repositoryBindingId, options.homeDir);
|
|
1379
1379
|
if (!record) {
|
|
1380
1380
|
throw new ReconnectRequiredError(
|
|
1381
1381
|
`[memoraone-mcp] Cached binding missing for ${repositoryBindingId}. Run: memoraone-mcp connect <code>`
|
|
1382
1382
|
);
|
|
1383
1383
|
}
|
|
1384
|
-
const workspaceRoot =
|
|
1384
|
+
const workspaceRoot = path10.resolve(record.workspaceRoot);
|
|
1385
1385
|
const identity = await captureRootFilesystemIdentity(workspaceRoot);
|
|
1386
1386
|
if (!identitiesMatch(record.filesystemIdentity, identity)) {
|
|
1387
1387
|
throw new ReconnectRequiredError(
|
|
@@ -1407,6 +1407,21 @@ async function reconcileResolvedBindingWithDisk(cached) {
|
|
|
1407
1407
|
}
|
|
1408
1408
|
return { binding: fresh, cacheRefreshed: true };
|
|
1409
1409
|
}
|
|
1410
|
+
async function checkRuntimeBindingValidity(cached, options = {}) {
|
|
1411
|
+
try {
|
|
1412
|
+
const { binding: current } = await reconcileResolvedBindingWithDisk(cached, options);
|
|
1413
|
+
if (current.status !== "connected") {
|
|
1414
|
+
return { valid: false, reason: `binding status is ${current.status}` };
|
|
1415
|
+
}
|
|
1416
|
+
if (!bindingRelevantValuesMatch(cached, current)) {
|
|
1417
|
+
return { valid: false, reason: "binding metadata changed" };
|
|
1418
|
+
}
|
|
1419
|
+
return { valid: true };
|
|
1420
|
+
} catch (err) {
|
|
1421
|
+
const reason = err instanceof Error && err.message.trim() ? err.message : String(err);
|
|
1422
|
+
return { valid: false, reason };
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1410
1425
|
function encodeResolvedBinding(binding) {
|
|
1411
1426
|
const payload = {
|
|
1412
1427
|
repositoryBindingId: binding.repositoryBindingId,
|
|
@@ -1467,7 +1482,7 @@ function decodeResolvedBinding(value) {
|
|
|
1467
1482
|
|
|
1468
1483
|
// src/bindingSidecar.ts
|
|
1469
1484
|
var fs8 = __toESM(require("fs"), 1);
|
|
1470
|
-
var
|
|
1485
|
+
var path11 = __toESM(require("path"), 1);
|
|
1471
1486
|
function bindingSidecarPath(socketPath) {
|
|
1472
1487
|
if (socketPath.endsWith(".sock")) {
|
|
1473
1488
|
return `${socketPath.slice(0, -".sock".length)}.binding.json`;
|
|
@@ -1508,7 +1523,7 @@ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
|
1508
1523
|
// src/config.ts
|
|
1509
1524
|
var process2 = __toESM(require("process"), 1);
|
|
1510
1525
|
var fs9 = __toESM(require("fs"), 1);
|
|
1511
|
-
var
|
|
1526
|
+
var path12 = __toESM(require("path"), 1);
|
|
1512
1527
|
var dotenv = __toESM(require("dotenv"), 1);
|
|
1513
1528
|
var import_v4 = require("zod/v4");
|
|
1514
1529
|
|
|
@@ -1531,7 +1546,7 @@ function resolveApiUrl(env2) {
|
|
|
1531
1546
|
}
|
|
1532
1547
|
|
|
1533
1548
|
// src/config.ts
|
|
1534
|
-
var dotenvPath =
|
|
1549
|
+
var dotenvPath = path12.resolve(process2.cwd(), ".env");
|
|
1535
1550
|
if (fs9.existsSync(dotenvPath)) {
|
|
1536
1551
|
try {
|
|
1537
1552
|
dotenv.config({ path: dotenvPath });
|
|
@@ -1601,7 +1616,7 @@ var config2 = {
|
|
|
1601
1616
|
};
|
|
1602
1617
|
|
|
1603
1618
|
// src/initializeBinding.ts
|
|
1604
|
-
var
|
|
1619
|
+
var path13 = __toESM(require("path"), 1);
|
|
1605
1620
|
var import_node_url = require("url");
|
|
1606
1621
|
var MEMORAONE_WORKSPACE_ROOT_ENV = "MEMORAONE_WORKSPACE_ROOT";
|
|
1607
1622
|
function getBridgeBindingResolveOptions(env2 = process.env) {
|
|
@@ -1621,8 +1636,8 @@ function getEnvWorkspaceRootCandidates() {
|
|
|
1621
1636
|
const raw = process.env.WORKSPACE_FOLDER_PATHS;
|
|
1622
1637
|
const parts = [];
|
|
1623
1638
|
if (raw !== void 0 && raw.trim() !== "") {
|
|
1624
|
-
for (const p of raw.split(
|
|
1625
|
-
parts.push(
|
|
1639
|
+
for (const p of raw.split(path13.delimiter).map((s) => s.trim()).filter(Boolean)) {
|
|
1640
|
+
parts.push(path13.resolve(p));
|
|
1626
1641
|
}
|
|
1627
1642
|
}
|
|
1628
1643
|
parts.push(process.cwd());
|
|
@@ -1646,7 +1661,7 @@ function extractWorkspaceRootsFromInitialize(params) {
|
|
|
1646
1661
|
if (uri === void 0 || uri.trim() === "") {
|
|
1647
1662
|
return;
|
|
1648
1663
|
}
|
|
1649
|
-
const resolved =
|
|
1664
|
+
const resolved = path13.resolve(uriToPath(uri));
|
|
1650
1665
|
if (!seen.has(resolved)) {
|
|
1651
1666
|
seen.add(resolved);
|
|
1652
1667
|
roots.push(resolved);
|
|
@@ -1668,7 +1683,7 @@ function getRepoScopedWorkspaceHint(env2 = process.env) {
|
|
|
1668
1683
|
if (raw === void 0 || raw.trim() === "") {
|
|
1669
1684
|
return null;
|
|
1670
1685
|
}
|
|
1671
|
-
return
|
|
1686
|
+
return path13.resolve(raw.trim());
|
|
1672
1687
|
}
|
|
1673
1688
|
function formatWorkspaceAmbiguityError(bindings) {
|
|
1674
1689
|
const lines = bindings.map(
|
|
@@ -1751,8 +1766,8 @@ async function resolveBindingFromInitializeParams(params, options = {}) {
|
|
|
1751
1766
|
const rootsListUris = options.rootsListUris ?? [];
|
|
1752
1767
|
const rootsListPaths = rootsListUris.map((uri) => uriToPath(uri)).filter(Boolean);
|
|
1753
1768
|
if (rootsListPaths.length > 1 && repoHint !== null) {
|
|
1754
|
-
const hintResolved =
|
|
1755
|
-
const matchingRoot = rootsListPaths.find((root) =>
|
|
1769
|
+
const hintResolved = path13.resolve(repoHint);
|
|
1770
|
+
const matchingRoot = rootsListPaths.find((root) => path13.resolve(root) === hintResolved);
|
|
1756
1771
|
if (!matchingRoot) {
|
|
1757
1772
|
throw new Error(formatRepoHintNotInRootsListError(repoHint, rootsListPaths));
|
|
1758
1773
|
}
|
|
@@ -2212,9 +2227,9 @@ function buildPersonalContextPath(parsed2) {
|
|
|
2212
2227
|
}
|
|
2213
2228
|
async function handleGetPersonalContext(client, args) {
|
|
2214
2229
|
const parsed2 = getPersonalContextInputSchema.parse(args ?? {});
|
|
2215
|
-
const
|
|
2230
|
+
const path15 = buildPersonalContextPath(parsed2);
|
|
2216
2231
|
try {
|
|
2217
|
-
const result = await client.get(
|
|
2232
|
+
const result = await client.get(path15);
|
|
2218
2233
|
return { ok: true, result };
|
|
2219
2234
|
} catch (err) {
|
|
2220
2235
|
if (err instanceof MemoraOneHttpError) {
|
|
@@ -2686,8 +2701,8 @@ async function handleCreateConceptVersion(client, args) {
|
|
|
2686
2701
|
if (input.tags !== void 0) body.tags = input.tags;
|
|
2687
2702
|
if (input.parent_id !== void 0) body.parent_id = input.parent_id;
|
|
2688
2703
|
try {
|
|
2689
|
-
const
|
|
2690
|
-
return projectCreatedConceptVersionResponse(await client.post(
|
|
2704
|
+
const path15 = `/concepts/${encodeURIComponent(input.id)}/versions`;
|
|
2705
|
+
return projectCreatedConceptVersionResponse(await client.post(path15, body));
|
|
2691
2706
|
} catch (error) {
|
|
2692
2707
|
rethrowToolRequestError("memora_create_concept_version", error);
|
|
2693
2708
|
}
|
|
@@ -2722,7 +2737,7 @@ function handleBindingStatus(binding, options = {}) {
|
|
|
2722
2737
|
// src/heartbeat.ts
|
|
2723
2738
|
var crypto5 = __toESM(require("crypto"), 1);
|
|
2724
2739
|
var fs10 = __toESM(require("fs"), 1);
|
|
2725
|
-
var
|
|
2740
|
+
var path14 = __toESM(require("path"), 1);
|
|
2726
2741
|
|
|
2727
2742
|
// src/localState/mcpSessionId.ts
|
|
2728
2743
|
var import_node_crypto4 = require("crypto");
|
|
@@ -2755,9 +2770,9 @@ function resolvePackageVersion() {
|
|
|
2755
2770
|
if (fromEnv) return fromEnv;
|
|
2756
2771
|
const moduleDir = typeof __dirname !== "undefined" ? __dirname : void 0;
|
|
2757
2772
|
const candidates = [
|
|
2758
|
-
...moduleDir ? [
|
|
2759
|
-
|
|
2760
|
-
|
|
2773
|
+
...moduleDir ? [path14.join(moduleDir, "..", "package.json"), path14.join(moduleDir, "package.json")] : [],
|
|
2774
|
+
path14.join(process.cwd(), "package.json"),
|
|
2775
|
+
path14.join(process.cwd(), "packages", "mcp", "package.json")
|
|
2761
2776
|
];
|
|
2762
2777
|
for (const candidate of candidates) {
|
|
2763
2778
|
try {
|
|
@@ -2833,19 +2848,6 @@ async function sendProjectHeartbeat(client, ctx) {
|
|
|
2833
2848
|
const raw = await client.post("/v1/local-mcp/heartbeat", body, { log: false });
|
|
2834
2849
|
return { active: parseActiveFlag(raw) };
|
|
2835
2850
|
} catch (err) {
|
|
2836
|
-
if (err instanceof ReconnectRequiredError) {
|
|
2837
|
-
try {
|
|
2838
|
-
const record = await readBindingRecord(ctx.repositoryBindingId);
|
|
2839
|
-
if (record) {
|
|
2840
|
-
await writeBindingRecord({
|
|
2841
|
-
...record,
|
|
2842
|
-
status: "reconnect_required",
|
|
2843
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2844
|
-
});
|
|
2845
|
-
}
|
|
2846
|
-
} catch {
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
2851
|
process.stderr.write(
|
|
2850
2852
|
`[memoraone-mcp][info] heartbeat error (silent) ${redactSensitiveText(String(err))}
|
|
2851
2853
|
`
|
|
@@ -2860,6 +2862,8 @@ function createDaemonHeartbeat(opts) {
|
|
|
2860
2862
|
let announced = false;
|
|
2861
2863
|
let studioActive = null;
|
|
2862
2864
|
let starting = false;
|
|
2865
|
+
let tickInFlight = false;
|
|
2866
|
+
let bindingAuthorityEnded = false;
|
|
2863
2867
|
let waitingForIdeType = false;
|
|
2864
2868
|
const sessionId = opts.sessionId ?? generateMcpSessionId();
|
|
2865
2869
|
const packageVersion = opts.packageVersion ?? resolvePackageVersion();
|
|
@@ -2875,6 +2879,29 @@ function createDaemonHeartbeat(opts) {
|
|
|
2875
2879
|
process.stderr.write(`[memoraone-mcp][daemon-heartbeat] ${redactSensitiveText(msg)}
|
|
2876
2880
|
`);
|
|
2877
2881
|
});
|
|
2882
|
+
const validateBinding = opts.validateBinding ?? checkRuntimeBindingValidity;
|
|
2883
|
+
const endBindingAuthority = (reason) => {
|
|
2884
|
+
if (bindingAuthorityEnded) return;
|
|
2885
|
+
bindingAuthorityEnded = true;
|
|
2886
|
+
studioActive = false;
|
|
2887
|
+
runGeneration += 1;
|
|
2888
|
+
if (interval) {
|
|
2889
|
+
clearInterval(interval);
|
|
2890
|
+
interval = null;
|
|
2891
|
+
}
|
|
2892
|
+
log2(`heartbeat authority ended: ${redactSensitiveText(reason)}`);
|
|
2893
|
+
};
|
|
2894
|
+
const bindingIsValid = async () => {
|
|
2895
|
+
try {
|
|
2896
|
+
const result = await validateBinding(opts.binding);
|
|
2897
|
+
if (result.valid) return true;
|
|
2898
|
+
endBindingAuthority("reason" in result ? result.reason : "binding is no longer valid");
|
|
2899
|
+
return false;
|
|
2900
|
+
} catch (err) {
|
|
2901
|
+
endBindingAuthority(`binding validation failed: ${String(err)}`);
|
|
2902
|
+
return false;
|
|
2903
|
+
}
|
|
2904
|
+
};
|
|
2878
2905
|
const ensureClient = async () => {
|
|
2879
2906
|
if (client) return client;
|
|
2880
2907
|
const creds = await readInstallationCredentials(opts.binding.repositoryBindingId);
|
|
@@ -2925,10 +2952,17 @@ function createDaemonHeartbeat(opts) {
|
|
|
2925
2952
|
}
|
|
2926
2953
|
};
|
|
2927
2954
|
const tick = async (generation) => {
|
|
2928
|
-
if (!client || generation !== runGeneration) return;
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2955
|
+
if (!client || generation !== runGeneration || bindingAuthorityEnded || tickInFlight) return;
|
|
2956
|
+
tickInFlight = true;
|
|
2957
|
+
try {
|
|
2958
|
+
if (!await bindingIsValid()) return;
|
|
2959
|
+
if (generation !== runGeneration || !interval) return;
|
|
2960
|
+
const outcome = await sendProjectHeartbeat(client, ctx);
|
|
2961
|
+
if (generation !== runGeneration || !interval) return;
|
|
2962
|
+
applyHeartbeatOutcome(outcome);
|
|
2963
|
+
} finally {
|
|
2964
|
+
tickInFlight = false;
|
|
2965
|
+
}
|
|
2932
2966
|
};
|
|
2933
2967
|
const beginInterval = () => {
|
|
2934
2968
|
if (interval) return;
|
|
@@ -2956,8 +2990,13 @@ function createDaemonHeartbeat(opts) {
|
|
|
2956
2990
|
log2("already starting (skipped duplicate start)");
|
|
2957
2991
|
return;
|
|
2958
2992
|
}
|
|
2993
|
+
if (bindingAuthorityEnded) {
|
|
2994
|
+
log2("binding authority already ended; heartbeat will not restart");
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
2959
2997
|
starting = true;
|
|
2960
2998
|
try {
|
|
2999
|
+
if (!await bindingIsValid()) return;
|
|
2961
3000
|
const activeClient = await ensureClient();
|
|
2962
3001
|
if (!activeClient) return;
|
|
2963
3002
|
const ok = await ensureAnnounced(activeClient);
|
|
@@ -3252,8 +3291,8 @@ function registerToolWithWorklog(server, runtime, sessionContext, toolName, desc
|
|
|
3252
3291
|
async function main(opts = {}) {
|
|
3253
3292
|
let bindingReadyResolve = null;
|
|
3254
3293
|
let bindingReadyReject = null;
|
|
3255
|
-
const bindingReady = new Promise((
|
|
3256
|
-
bindingReadyResolve =
|
|
3294
|
+
const bindingReady = new Promise((resolve10, reject) => {
|
|
3295
|
+
bindingReadyResolve = resolve10;
|
|
3257
3296
|
bindingReadyReject = reject;
|
|
3258
3297
|
});
|
|
3259
3298
|
const devMode = Boolean(config2.devMode);
|
|
@@ -3726,10 +3765,10 @@ async function main(opts = {}) {
|
|
|
3726
3765
|
console.error("[memoraone-mcp] MCP server ready");
|
|
3727
3766
|
}
|
|
3728
3767
|
if (opts.sessionSocket) {
|
|
3729
|
-
await new Promise((
|
|
3768
|
+
await new Promise((resolve10) => {
|
|
3730
3769
|
opts.sessionSocket.once("close", () => {
|
|
3731
3770
|
shutdown("session closed", false);
|
|
3732
|
-
|
|
3771
|
+
resolve10();
|
|
3733
3772
|
});
|
|
3734
3773
|
});
|
|
3735
3774
|
}
|
|
@@ -3773,7 +3812,7 @@ async function ensureSocketClean(socketPath) {
|
|
|
3773
3812
|
} catch {
|
|
3774
3813
|
return;
|
|
3775
3814
|
}
|
|
3776
|
-
return new Promise((
|
|
3815
|
+
return new Promise((resolve10) => {
|
|
3777
3816
|
const client = net.createConnection({ path: socketPath }, () => {
|
|
3778
3817
|
client.destroy();
|
|
3779
3818
|
log("daemon already running, exiting");
|
|
@@ -3785,7 +3824,7 @@ async function ensureSocketClean(socketPath) {
|
|
|
3785
3824
|
log("stale socket removed");
|
|
3786
3825
|
} catch {
|
|
3787
3826
|
}
|
|
3788
|
-
|
|
3827
|
+
resolve10();
|
|
3789
3828
|
});
|
|
3790
3829
|
});
|
|
3791
3830
|
}
|
|
@@ -3876,14 +3915,14 @@ async function runDaemon() {
|
|
|
3876
3915
|
process.on("SIGINT", () => shutdownNow("SIGINT"));
|
|
3877
3916
|
process.on("SIGTERM", () => shutdownNow("SIGTERM"));
|
|
3878
3917
|
process.on("exit", cleanupSocketFile);
|
|
3879
|
-
return new Promise((
|
|
3918
|
+
return new Promise((resolve10) => {
|
|
3880
3919
|
server.listen(socketPath, () => {
|
|
3881
3920
|
writeBindingSidecar(socketPath, binding, ideType ?? "");
|
|
3882
3921
|
log(`daemon started, listening on ${socketPath}`);
|
|
3883
3922
|
void daemonHeartbeat.start().catch((err) => {
|
|
3884
3923
|
log(`daemon heartbeat start error: ${String(err)}`);
|
|
3885
3924
|
});
|
|
3886
|
-
|
|
3925
|
+
resolve10();
|
|
3887
3926
|
});
|
|
3888
3927
|
});
|
|
3889
3928
|
}
|