@memoraone/mcp 0.1.52 → 0.1.54
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 +330 -48
- package/dist/daemon.cjs +42 -10
- package/dist/gateway.cjs +14002 -0
- package/dist/index.cjs +44 -10
- 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.54",
|
|
235
235
|
type: "module",
|
|
236
236
|
main: "dist/index.cjs",
|
|
237
237
|
exports: {
|
|
@@ -531,7 +531,7 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
531
531
|
`[memoraone-mcp] Failed to acquire lock ${lockName} after ${maxRetries} retries`
|
|
532
532
|
);
|
|
533
533
|
}
|
|
534
|
-
await new Promise((
|
|
534
|
+
await new Promise((resolve45) => setTimeout(resolve45, retryDelayMs));
|
|
535
535
|
continue;
|
|
536
536
|
}
|
|
537
537
|
throw err;
|
|
@@ -1092,9 +1092,9 @@ var init_memoraClient = __esm({
|
|
|
1092
1092
|
});
|
|
1093
1093
|
|
|
1094
1094
|
// src/localState/localConnectClient.ts
|
|
1095
|
-
async function requestJson(baseUrl, method,
|
|
1095
|
+
async function requestJson(baseUrl, method, path49, options = {}) {
|
|
1096
1096
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1097
|
-
const url = `${baseUrl.replace(/\/+$/, "")}${
|
|
1097
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${path49.startsWith("/") ? path49 : `/${path49}`}`;
|
|
1098
1098
|
const res = await fetchImpl(url, {
|
|
1099
1099
|
method,
|
|
1100
1100
|
headers: {
|
|
@@ -1511,7 +1511,10 @@ async function reconcileResolvedBindingWithDisk(cached, options = {}) {
|
|
|
1511
1511
|
);
|
|
1512
1512
|
}
|
|
1513
1513
|
const workspaceRoot = path10.resolve(record.workspaceRoot);
|
|
1514
|
-
const identity = await captureRootFilesystemIdentity(
|
|
1514
|
+
const identity = await captureRootFilesystemIdentity(
|
|
1515
|
+
workspaceRoot,
|
|
1516
|
+
options.identityDeps
|
|
1517
|
+
);
|
|
1515
1518
|
if (!identitiesMatch(record.filesystemIdentity, identity)) {
|
|
1516
1519
|
throw new ReconnectRequiredError(
|
|
1517
1520
|
"[memoraone-mcp] Workspace filesystem identity changed. Run: memoraone-mcp connect <code>"
|
|
@@ -3918,7 +3921,7 @@ function formatOptionalJetBrainsHandshakeUnavailableDetail(detail) {
|
|
|
3918
3921
|
async function verifyJetBrainsMcpHandshake(options) {
|
|
3919
3922
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
3920
3923
|
const { server } = options;
|
|
3921
|
-
return new Promise((
|
|
3924
|
+
return new Promise((resolve45) => {
|
|
3922
3925
|
let settled = false;
|
|
3923
3926
|
const finish = (ok, detail, optionalUnavailable) => {
|
|
3924
3927
|
if (settled) return;
|
|
@@ -3928,7 +3931,7 @@ async function verifyJetBrainsMcpHandshake(options) {
|
|
|
3928
3931
|
child.kill();
|
|
3929
3932
|
} catch {
|
|
3930
3933
|
}
|
|
3931
|
-
|
|
3934
|
+
resolve45({ ok, detail, optionalUnavailable });
|
|
3932
3935
|
};
|
|
3933
3936
|
const child = (0, import_node_child_process4.spawn)(server.command, [...server.args], {
|
|
3934
3937
|
env: { ...process.env, ...server.env },
|
|
@@ -12961,13 +12964,13 @@ function writeEvent(event, stdoutWrite) {
|
|
|
12961
12964
|
}
|
|
12962
12965
|
function delay(ms, signal) {
|
|
12963
12966
|
if (ms <= 0 || signal?.aborted) return Promise.resolve();
|
|
12964
|
-
return new Promise((
|
|
12967
|
+
return new Promise((resolve45) => {
|
|
12965
12968
|
const timer = setTimeout(finish, ms);
|
|
12966
12969
|
const onAbort = () => finish();
|
|
12967
12970
|
function finish() {
|
|
12968
12971
|
clearTimeout(timer);
|
|
12969
12972
|
signal?.removeEventListener("abort", onAbort);
|
|
12970
|
-
|
|
12973
|
+
resolve45();
|
|
12971
12974
|
}
|
|
12972
12975
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
12973
12976
|
});
|
|
@@ -13270,13 +13273,180 @@ var init_runtimeIdentityCommand = __esm({
|
|
|
13270
13273
|
}
|
|
13271
13274
|
});
|
|
13272
13275
|
|
|
13276
|
+
// src/gatewayConfig.ts
|
|
13277
|
+
function resolveGatewayPort(env2 = process.env) {
|
|
13278
|
+
const raw = env2.MEMORAONE_GATEWAY_PORT?.trim();
|
|
13279
|
+
if (!raw) return GATEWAY_PORT;
|
|
13280
|
+
const parsed2 = Number.parseInt(raw, 10);
|
|
13281
|
+
if (!Number.isInteger(parsed2) || parsed2 < 1 || parsed2 > 65535) {
|
|
13282
|
+
throw new Error(
|
|
13283
|
+
`[memoraone-mcp] Invalid MEMORAONE_GATEWAY_PORT=${JSON.stringify(raw)} (expected 1\u201365535)`
|
|
13284
|
+
);
|
|
13285
|
+
}
|
|
13286
|
+
return parsed2;
|
|
13287
|
+
}
|
|
13288
|
+
function resolveGatewayListenConfig(env2 = process.env) {
|
|
13289
|
+
return {
|
|
13290
|
+
host: GATEWAY_HOST,
|
|
13291
|
+
port: resolveGatewayPort(env2)
|
|
13292
|
+
};
|
|
13293
|
+
}
|
|
13294
|
+
function getGatewayBaseUrl(env2 = process.env) {
|
|
13295
|
+
const { host, port } = resolveGatewayListenConfig(env2);
|
|
13296
|
+
return `http://${host}:${port}`;
|
|
13297
|
+
}
|
|
13298
|
+
function getGatewayHealthUrl(env2 = process.env) {
|
|
13299
|
+
return `${getGatewayBaseUrl(env2)}${GATEWAY_HEALTH_PATH}`;
|
|
13300
|
+
}
|
|
13301
|
+
function buildGatewaySpawnArgs(scriptPath) {
|
|
13302
|
+
return [scriptPath, "--gateway"];
|
|
13303
|
+
}
|
|
13304
|
+
var path47, GATEWAY_HOST, GATEWAY_PORT, GATEWAY_LOCK_NAME, GATEWAY_HEALTH_PATH;
|
|
13305
|
+
var init_gatewayConfig = __esm({
|
|
13306
|
+
"src/gatewayConfig.ts"() {
|
|
13307
|
+
path47 = __toESM(require("path"), 1);
|
|
13308
|
+
GATEWAY_HOST = "127.0.0.1";
|
|
13309
|
+
GATEWAY_PORT = 17355;
|
|
13310
|
+
GATEWAY_LOCK_NAME = "gateway";
|
|
13311
|
+
GATEWAY_HEALTH_PATH = "/health";
|
|
13312
|
+
}
|
|
13313
|
+
});
|
|
13314
|
+
|
|
13315
|
+
// src/gatewayEnsure.ts
|
|
13316
|
+
var gatewayEnsure_exports = {};
|
|
13317
|
+
__export(gatewayEnsure_exports, {
|
|
13318
|
+
cliEnsureGateway: () => cliEnsureGateway,
|
|
13319
|
+
ensureGateway: () => ensureGateway,
|
|
13320
|
+
probeGatewayHealth: () => probeGatewayHealth
|
|
13321
|
+
});
|
|
13322
|
+
async function probeGatewayHealth(env2 = process.env) {
|
|
13323
|
+
const url = getGatewayHealthUrl(env2);
|
|
13324
|
+
try {
|
|
13325
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(750) });
|
|
13326
|
+
if (!res.ok) {
|
|
13327
|
+
return { healthy: false, reason: `HTTP ${res.status}` };
|
|
13328
|
+
}
|
|
13329
|
+
const body = await res.json();
|
|
13330
|
+
if (body?.ok !== true || typeof body.pid !== "number") {
|
|
13331
|
+
return { healthy: false, reason: "invalid health body" };
|
|
13332
|
+
}
|
|
13333
|
+
return { healthy: true, body: { ok: true, pid: body.pid } };
|
|
13334
|
+
} catch (err) {
|
|
13335
|
+
return { healthy: false, reason: String(err) };
|
|
13336
|
+
}
|
|
13337
|
+
}
|
|
13338
|
+
function defaultSpawnGateway(cliPath, env2) {
|
|
13339
|
+
return new Promise((resolve45, reject) => {
|
|
13340
|
+
let child;
|
|
13341
|
+
try {
|
|
13342
|
+
child = (0, import_node_child_process7.spawn)(process.execPath, buildGatewaySpawnArgs(cliPath), {
|
|
13343
|
+
detached: true,
|
|
13344
|
+
stdio: "ignore",
|
|
13345
|
+
env: { ...env2 }
|
|
13346
|
+
});
|
|
13347
|
+
} catch (err) {
|
|
13348
|
+
reject(err);
|
|
13349
|
+
return;
|
|
13350
|
+
}
|
|
13351
|
+
child.once("error", reject);
|
|
13352
|
+
child.unref();
|
|
13353
|
+
setImmediate(() => resolve45());
|
|
13354
|
+
});
|
|
13355
|
+
}
|
|
13356
|
+
async function waitUntilHealthy(probe, env2, waitTimeoutMs, pollIntervalMs) {
|
|
13357
|
+
const started = Date.now();
|
|
13358
|
+
let lastReason = "not started";
|
|
13359
|
+
while (Date.now() - started < waitTimeoutMs) {
|
|
13360
|
+
const result = await probe(env2);
|
|
13361
|
+
if (result.healthy) return result.body;
|
|
13362
|
+
lastReason = result.reason;
|
|
13363
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
13364
|
+
}
|
|
13365
|
+
const { host, port } = resolveGatewayListenConfig(env2);
|
|
13366
|
+
throw new Error(
|
|
13367
|
+
`[memoraone-mcp] Gateway did not become healthy on ${host}:${port} within ${waitTimeoutMs}ms (${lastReason})`
|
|
13368
|
+
);
|
|
13369
|
+
}
|
|
13370
|
+
async function ensureGateway(options = {}) {
|
|
13371
|
+
const env2 = options.env ?? process.env;
|
|
13372
|
+
const probe = options.probe ?? probeGatewayHealth;
|
|
13373
|
+
const spawnGateway = options.spawnGateway ?? defaultSpawnGateway;
|
|
13374
|
+
const waitTimeoutMs = options.waitTimeoutMs ?? 1e4;
|
|
13375
|
+
const pollIntervalMs = options.pollIntervalMs ?? 50;
|
|
13376
|
+
const listen = resolveGatewayListenConfig(env2);
|
|
13377
|
+
const lockRetryDelayMs = options.lockRetryDelayMs ?? 50;
|
|
13378
|
+
const lockMaxRetries = options.lockMaxRetries ?? Math.ceil(waitTimeoutMs / lockRetryDelayMs) + 40;
|
|
13379
|
+
const first = await probe(env2);
|
|
13380
|
+
if (first.healthy) {
|
|
13381
|
+
return { reused: true, pid: first.body.pid, listen };
|
|
13382
|
+
}
|
|
13383
|
+
return withLocalLock(
|
|
13384
|
+
GATEWAY_LOCK_NAME,
|
|
13385
|
+
async () => {
|
|
13386
|
+
const second = await probe(env2);
|
|
13387
|
+
if (second.healthy) {
|
|
13388
|
+
return { reused: true, pid: second.body.pid, listen };
|
|
13389
|
+
}
|
|
13390
|
+
const cliPath = options.cliPath ?? await resolveBuiltCliPathAsync() ?? process.argv[1];
|
|
13391
|
+
if (!cliPath) {
|
|
13392
|
+
throw new Error("[memoraone-mcp] Unable to resolve CLI path to spawn gateway");
|
|
13393
|
+
}
|
|
13394
|
+
log(`spawning gateway on ${listen.host}:${listen.port} via ${cliPath}`);
|
|
13395
|
+
await spawnGateway(cliPath, env2);
|
|
13396
|
+
const body = await waitUntilHealthy(probe, env2, waitTimeoutMs, pollIntervalMs);
|
|
13397
|
+
return { reused: false, pid: body.pid, listen };
|
|
13398
|
+
},
|
|
13399
|
+
{
|
|
13400
|
+
homeDir: options.homeDir,
|
|
13401
|
+
maxRetries: lockMaxRetries,
|
|
13402
|
+
retryDelayMs: lockRetryDelayMs,
|
|
13403
|
+
// Hold can be long while waiting for health; keep stale threshold above wait.
|
|
13404
|
+
maxLockAgeMs: Math.max(3e4, waitTimeoutMs + 5e3)
|
|
13405
|
+
}
|
|
13406
|
+
);
|
|
13407
|
+
}
|
|
13408
|
+
async function cliEnsureGateway(_argv = [], options = {}) {
|
|
13409
|
+
try {
|
|
13410
|
+
const result = await ensureGateway(options);
|
|
13411
|
+
const mode = result.reused ? "reused" : "started";
|
|
13412
|
+
process.stdout.write(
|
|
13413
|
+
`${JSON.stringify({
|
|
13414
|
+
status: "ok",
|
|
13415
|
+
mode,
|
|
13416
|
+
pid: result.pid,
|
|
13417
|
+
host: result.listen.host,
|
|
13418
|
+
port: result.listen.port
|
|
13419
|
+
})}
|
|
13420
|
+
`
|
|
13421
|
+
);
|
|
13422
|
+
return 0;
|
|
13423
|
+
} catch (err) {
|
|
13424
|
+
process.stderr.write(`[memoraone-mcp] ensure-gateway failed: ${String(err)}
|
|
13425
|
+
`);
|
|
13426
|
+
return 1;
|
|
13427
|
+
}
|
|
13428
|
+
}
|
|
13429
|
+
var import_node_child_process7, log;
|
|
13430
|
+
var init_gatewayEnsure = __esm({
|
|
13431
|
+
"src/gatewayEnsure.ts"() {
|
|
13432
|
+
import_node_child_process7 = require("child_process");
|
|
13433
|
+
init_localLocks();
|
|
13434
|
+
init_resolveBuiltCliPath();
|
|
13435
|
+
init_gatewayConfig();
|
|
13436
|
+
log = (msg) => {
|
|
13437
|
+
process.stderr.write(`[memoraone-mcp][ensure-gateway] ${msg}
|
|
13438
|
+
`);
|
|
13439
|
+
};
|
|
13440
|
+
}
|
|
13441
|
+
});
|
|
13442
|
+
|
|
13273
13443
|
// src/bridgeClientRoots.ts
|
|
13274
13444
|
function isInitializeDebugEnabled(env2 = process.env) {
|
|
13275
13445
|
return TRUTHY.has(String(env2.MEMORAONE_DEBUG_INIT ?? "").trim().toLowerCase());
|
|
13276
13446
|
}
|
|
13277
|
-
function logInitializeDebug(
|
|
13447
|
+
function logInitializeDebug(log2, env2, msg) {
|
|
13278
13448
|
if (isInitializeDebugEnabled(env2)) {
|
|
13279
|
-
|
|
13449
|
+
log2(`[init-debug] ${msg}`);
|
|
13280
13450
|
}
|
|
13281
13451
|
}
|
|
13282
13452
|
function clientDeclaresRootsCapability(params) {
|
|
@@ -13326,7 +13496,7 @@ function extractRootsUrisFromListResult(result) {
|
|
|
13326
13496
|
}
|
|
13327
13497
|
async function requestClientRootsListUris(options) {
|
|
13328
13498
|
const env2 = options.env ?? process.env;
|
|
13329
|
-
const
|
|
13499
|
+
const log2 = options.log ?? (() => {
|
|
13330
13500
|
});
|
|
13331
13501
|
const requestId = nextBridgeRequestId++;
|
|
13332
13502
|
const deferredLines = [];
|
|
@@ -13337,7 +13507,7 @@ async function requestClientRootsListUris(options) {
|
|
|
13337
13507
|
params: {}
|
|
13338
13508
|
};
|
|
13339
13509
|
logInitializeDebug(
|
|
13340
|
-
|
|
13510
|
+
log2,
|
|
13341
13511
|
env2,
|
|
13342
13512
|
`sending roots/list id=${requestId} clientDeclaresRoots=${clientDeclaresRootsCapability(options.initializeParams)}`
|
|
13343
13513
|
);
|
|
@@ -13356,12 +13526,12 @@ async function requestClientRootsListUris(options) {
|
|
|
13356
13526
|
try {
|
|
13357
13527
|
message = JSON.parse(trimmed);
|
|
13358
13528
|
} catch (err) {
|
|
13359
|
-
logInitializeDebug(
|
|
13529
|
+
logInitializeDebug(log2, env2, `ignored non-JSON line while waiting for roots/list: ${String(err)}`);
|
|
13360
13530
|
continue;
|
|
13361
13531
|
}
|
|
13362
13532
|
if (message.id !== requestId) {
|
|
13363
13533
|
logInitializeDebug(
|
|
13364
|
-
|
|
13534
|
+
log2,
|
|
13365
13535
|
env2,
|
|
13366
13536
|
`deferred JSON-RPC while waiting for roots/list id=${requestId}: ${trimmed.slice(0, 200)}`
|
|
13367
13537
|
);
|
|
@@ -13374,7 +13544,7 @@ async function requestClientRootsListUris(options) {
|
|
|
13374
13544
|
);
|
|
13375
13545
|
}
|
|
13376
13546
|
const uris = extractRootsUrisFromListResult(message.result);
|
|
13377
|
-
logInitializeDebug(
|
|
13547
|
+
logInitializeDebug(log2, env2, `roots/list id=${requestId} returned ${uris.length}: ${JSON.stringify(uris)}`);
|
|
13378
13548
|
return { uris, deferredLines };
|
|
13379
13549
|
}
|
|
13380
13550
|
}
|
|
@@ -13410,8 +13580,8 @@ var init_bridgeClientRoots = __esm({
|
|
|
13410
13580
|
if (this.closed) {
|
|
13411
13581
|
return null;
|
|
13412
13582
|
}
|
|
13413
|
-
return new Promise((
|
|
13414
|
-
this.waiters.push(
|
|
13583
|
+
return new Promise((resolve45) => {
|
|
13584
|
+
this.waiters.push(resolve45);
|
|
13415
13585
|
});
|
|
13416
13586
|
}
|
|
13417
13587
|
/** Re-queue lines read during an intermediate protocol step (e.g. roots/list) for the main bridge loop. */
|
|
@@ -13480,7 +13650,7 @@ function collectPluginStartupBindingRoots(options) {
|
|
|
13480
13650
|
if (raw === void 0 || raw === null || raw.trim() === "") {
|
|
13481
13651
|
return;
|
|
13482
13652
|
}
|
|
13483
|
-
const resolved =
|
|
13653
|
+
const resolved = path48.resolve(raw);
|
|
13484
13654
|
if (seen.has(resolved)) {
|
|
13485
13655
|
return;
|
|
13486
13656
|
}
|
|
@@ -13529,11 +13699,11 @@ async function isRetryablePluginStartupBindingError(err, options = {}) {
|
|
|
13529
13699
|
return false;
|
|
13530
13700
|
}
|
|
13531
13701
|
function defaultSleep(ms) {
|
|
13532
|
-
return new Promise((
|
|
13533
|
-
setTimeout(
|
|
13702
|
+
return new Promise((resolve45) => {
|
|
13703
|
+
setTimeout(resolve45, ms);
|
|
13534
13704
|
});
|
|
13535
13705
|
}
|
|
13536
|
-
async function waitForPluginStartupBinding(
|
|
13706
|
+
async function waitForPluginStartupBinding(resolve45, options = {}) {
|
|
13537
13707
|
const now = options.now ?? Date.now;
|
|
13538
13708
|
const sleep = options.sleep ?? defaultSleep;
|
|
13539
13709
|
const timeoutMs = options.timeoutMs ?? PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS;
|
|
@@ -13543,7 +13713,7 @@ async function waitForPluginStartupBinding(resolve44, options = {}) {
|
|
|
13543
13713
|
let lastError;
|
|
13544
13714
|
while (true) {
|
|
13545
13715
|
try {
|
|
13546
|
-
const binding = await
|
|
13716
|
+
const binding = await resolve45();
|
|
13547
13717
|
if (waitingLogged) {
|
|
13548
13718
|
options.log?.(
|
|
13549
13719
|
`local MemoraOne binding available after Cursor plugin pairing (${now() - startedAt}ms)`
|
|
@@ -13587,10 +13757,10 @@ async function waitForPluginStartupBinding(resolve44, options = {}) {
|
|
|
13587
13757
|
}
|
|
13588
13758
|
}
|
|
13589
13759
|
}
|
|
13590
|
-
var
|
|
13760
|
+
var path48, MEMORAONE_PLUGIN_ENV, MEMORAONE_CURSOR_PLUGIN, PLUGIN_STARTUP_BINDING_WAIT_TIMEOUT_MS, PLUGIN_STARTUP_BINDING_WAIT_INTERVAL_MS, UNBOUND_MESSAGE_MARKERS, CREDENTIALS_MISSING_MARKER, PLUGIN_STARTUP_SETUP_REQUIRED;
|
|
13591
13761
|
var init_pluginStartupBindingWait = __esm({
|
|
13592
13762
|
"src/pluginStartupBindingWait.ts"() {
|
|
13593
|
-
|
|
13763
|
+
path48 = __toESM(require("path"), 1);
|
|
13594
13764
|
init_initializeBinding();
|
|
13595
13765
|
init_bindingStore();
|
|
13596
13766
|
init_resolveLocalBinding();
|
|
@@ -13815,15 +13985,26 @@ function extractJsonRpcId(line) {
|
|
|
13815
13985
|
return void 0;
|
|
13816
13986
|
}
|
|
13817
13987
|
}
|
|
13818
|
-
function
|
|
13819
|
-
|
|
13988
|
+
function extractInitializeProtocolVersion(line) {
|
|
13989
|
+
try {
|
|
13990
|
+
const message = JSON.parse(line.trim());
|
|
13991
|
+
const version2 = message.params?.protocolVersion;
|
|
13992
|
+
if (typeof version2 === "string" && version2.trim() !== "") {
|
|
13993
|
+
return version2;
|
|
13994
|
+
}
|
|
13995
|
+
} catch {
|
|
13996
|
+
}
|
|
13997
|
+
return "2024-11-05";
|
|
13998
|
+
}
|
|
13999
|
+
function connectWithRetry(socketPath, log2, maxRetries, retryDelayMs, connect2) {
|
|
14000
|
+
return new Promise((resolve45, reject) => {
|
|
13820
14001
|
const tryConnect = (attempt) => {
|
|
13821
|
-
connect2(socketPath).then(
|
|
14002
|
+
connect2(socketPath).then(resolve45).catch((err) => {
|
|
13822
14003
|
if (attempt >= maxRetries) {
|
|
13823
14004
|
reject(err);
|
|
13824
14005
|
return;
|
|
13825
14006
|
}
|
|
13826
|
-
|
|
14007
|
+
log2(`connect attempt ${attempt + 1} failed, retrying in ${retryDelayMs}ms: ${String(err)}`);
|
|
13827
14008
|
setTimeout(() => tryConnect(attempt + 1), retryDelayMs);
|
|
13828
14009
|
});
|
|
13829
14010
|
};
|
|
@@ -13840,13 +14021,13 @@ async function resolveBridgeSessionBinding(params, env2 = process.env, options =
|
|
|
13840
14021
|
...bridgeOptions
|
|
13841
14022
|
});
|
|
13842
14023
|
}
|
|
13843
|
-
async function stopDaemonsForStaleBinding(stale, env2,
|
|
14024
|
+
async function stopDaemonsForStaleBinding(stale, env2, log2, ideTypeOverride) {
|
|
13844
14025
|
const ideType = resolveBindingIdeType(env2, ideTypeOverride);
|
|
13845
14026
|
let processes;
|
|
13846
14027
|
try {
|
|
13847
14028
|
processes = await defaultListDaemonProcesses();
|
|
13848
14029
|
} catch (err) {
|
|
13849
|
-
|
|
14030
|
+
log2(`could not list daemon processes while replacing stale binding: ${String(err)}`);
|
|
13850
14031
|
return;
|
|
13851
14032
|
}
|
|
13852
14033
|
for (const proc of processes) {
|
|
@@ -13855,11 +14036,11 @@ async function stopDaemonsForStaleBinding(stale, env2, log, ideTypeOverride) {
|
|
|
13855
14036
|
if (!matchesByBinding && !matchesByProject) continue;
|
|
13856
14037
|
try {
|
|
13857
14038
|
process.kill(proc.pid, "SIGTERM");
|
|
13858
|
-
|
|
14039
|
+
log2(
|
|
13859
14040
|
`stopped stale daemon pid=${proc.pid} binding=${stale.repositoryBindingId} project=${stale.projectId}`
|
|
13860
14041
|
);
|
|
13861
14042
|
} catch (err) {
|
|
13862
|
-
|
|
14043
|
+
log2(`failed to stop stale daemon pid=${proc.pid}: ${String(err)}`);
|
|
13863
14044
|
}
|
|
13864
14045
|
}
|
|
13865
14046
|
}
|
|
@@ -13926,14 +14107,14 @@ async function runBridgeProxy(options) {
|
|
|
13926
14107
|
ensureBaseDir();
|
|
13927
14108
|
const stdin = options.stdin ?? process.stdin;
|
|
13928
14109
|
const stdout = options.stdout ?? process.stdout;
|
|
13929
|
-
const
|
|
14110
|
+
const log2 = options.log ?? defaultLog;
|
|
13930
14111
|
const lineReader = options.lineReader ?? new StdioLineReader(stdin);
|
|
13931
14112
|
const router = new BridgeDaemonRouter({ ...options, stdout, lineReader });
|
|
13932
14113
|
try {
|
|
13933
14114
|
while (true) {
|
|
13934
14115
|
const line = await lineReader.readLine();
|
|
13935
14116
|
if (line === null) {
|
|
13936
|
-
|
|
14117
|
+
log2("stdin EOF; closing daemon socket");
|
|
13937
14118
|
break;
|
|
13938
14119
|
}
|
|
13939
14120
|
const trimmed = line.trim();
|
|
@@ -13947,17 +14128,25 @@ async function runBridgeProxy(options) {
|
|
|
13947
14128
|
throw new Error(`[memoraone-mcp] Invalid JSON-RPC on stdin: ${String(err)}`);
|
|
13948
14129
|
}
|
|
13949
14130
|
if (message.method === "initialize") {
|
|
13950
|
-
|
|
14131
|
+
log2("resolve binding from initialize request before daemon connect");
|
|
13951
14132
|
const params = message.params ?? {};
|
|
13952
|
-
await router.ensureDaemonForInitialize(params);
|
|
14133
|
+
const sessionKind = await router.ensureDaemonForInitialize(params);
|
|
14134
|
+
if (sessionKind === "setup_required") {
|
|
14135
|
+
router.completeSetupRequiredInitialize(trimmed);
|
|
14136
|
+
continue;
|
|
14137
|
+
}
|
|
13953
14138
|
await router.forwardInitializeToDaemon(trimmed);
|
|
13954
14139
|
await router.replayDeferredClientMessages();
|
|
13955
14140
|
continue;
|
|
13956
14141
|
}
|
|
14142
|
+
if (router.isSetupRequiredSession()) {
|
|
14143
|
+
router.handleSetupRequiredClientLine(trimmed);
|
|
14144
|
+
continue;
|
|
14145
|
+
}
|
|
13957
14146
|
if (!router.hasClientInitialize()) {
|
|
13958
14147
|
const method = typeof message.method === "string" && message.method.trim() !== "" ? message.method : "(unknown)";
|
|
13959
14148
|
if (message.id !== void 0 && message.id !== null) {
|
|
13960
|
-
|
|
14149
|
+
log2(
|
|
13961
14150
|
`pre-initialize request method=${method}; responding Method not found (legacy stdio probe)`
|
|
13962
14151
|
);
|
|
13963
14152
|
writePreInitializeJsonRpcError(
|
|
@@ -13966,7 +14155,7 @@ async function runBridgeProxy(options) {
|
|
|
13966
14155
|
method
|
|
13967
14156
|
);
|
|
13968
14157
|
} else {
|
|
13969
|
-
|
|
14158
|
+
log2(`ignoring pre-initialize notification method=${method}`);
|
|
13970
14159
|
}
|
|
13971
14160
|
continue;
|
|
13972
14161
|
}
|
|
@@ -13976,12 +14165,12 @@ async function runBridgeProxy(options) {
|
|
|
13976
14165
|
router.close();
|
|
13977
14166
|
}
|
|
13978
14167
|
}
|
|
13979
|
-
var net, readline4,
|
|
14168
|
+
var net, readline4, import_node_child_process8, defaultLog, JSONRPC_METHOD_NOT_FOUND, SETUP_REQUIRED_FINISH_SETUP_TOOL, BridgeDaemonRouter;
|
|
13980
14169
|
var init_bridgeProxy = __esm({
|
|
13981
14170
|
"src/bridgeProxy.ts"() {
|
|
13982
14171
|
net = __toESM(require("net"), 1);
|
|
13983
14172
|
readline4 = __toESM(require("readline"), 1);
|
|
13984
|
-
|
|
14173
|
+
import_node_child_process8 = require("child_process");
|
|
13985
14174
|
init_bindingIdentity();
|
|
13986
14175
|
init_bindingSidecar();
|
|
13987
14176
|
init_bridgeClientRoots();
|
|
@@ -13996,6 +14185,11 @@ var init_bridgeProxy = __esm({
|
|
|
13996
14185
|
`);
|
|
13997
14186
|
};
|
|
13998
14187
|
JSONRPC_METHOD_NOT_FOUND = -32601;
|
|
14188
|
+
SETUP_REQUIRED_FINISH_SETUP_TOOL = {
|
|
14189
|
+
name: "memora_finish_setup",
|
|
14190
|
+
description: "Start MemoraOne Cursor plugin pairing for this workspace so the user can finish setup. Returns an authorization URL and request metadata; does not open a browser. Use when this plugin-managed session is unbound (setup required).",
|
|
14191
|
+
inputSchema: { type: "object", properties: {} }
|
|
14192
|
+
};
|
|
13999
14193
|
BridgeDaemonRouter = class {
|
|
14000
14194
|
constructor(options) {
|
|
14001
14195
|
this.activeSocket = null;
|
|
@@ -14006,6 +14200,8 @@ var init_bridgeProxy = __esm({
|
|
|
14006
14200
|
this.pendingDeferredClientLines = [];
|
|
14007
14201
|
this.handshakeDeferredClientLines = [];
|
|
14008
14202
|
this.clientInitializeSeen = false;
|
|
14203
|
+
/** Unbound Cursor-plugin session: no daemon by design; handshake is local. */
|
|
14204
|
+
this.setupRequiredSession = false;
|
|
14009
14205
|
/** Waiters for daemon JSON-RPC responses (keyed by request id). */
|
|
14010
14206
|
this.pendingDaemonResponseWaiters = /* @__PURE__ */ new Map();
|
|
14011
14207
|
this.env = options.env ?? process.env;
|
|
@@ -14015,13 +14211,13 @@ var init_bridgeProxy = __esm({
|
|
|
14015
14211
|
this.maxRetries = options.maxRetries ?? 5;
|
|
14016
14212
|
this.retryDelayMs = options.retryDelayMs ?? 200;
|
|
14017
14213
|
this.lineReader = options.lineReader ?? null;
|
|
14018
|
-
this.connectImpl = options.connect ?? ((socketPath) => new Promise((
|
|
14019
|
-
const socket = net.connect(socketPath, () =>
|
|
14214
|
+
this.connectImpl = options.connect ?? ((socketPath) => new Promise((resolve45, reject) => {
|
|
14215
|
+
const socket = net.connect(socketPath, () => resolve45(socket));
|
|
14020
14216
|
socket.on("error", reject);
|
|
14021
14217
|
}));
|
|
14022
14218
|
this.spawnDaemonImpl = options.spawnDaemon ?? (async (binding) => {
|
|
14023
14219
|
const ideType = this.activeIdeType;
|
|
14024
|
-
const child = (0,
|
|
14220
|
+
const child = (0, import_node_child_process8.spawn)(
|
|
14025
14221
|
process.execPath,
|
|
14026
14222
|
buildDaemonSpawnArgs(this.cliPath, binding.repositoryBindingId, this.env, ideType),
|
|
14027
14223
|
{
|
|
@@ -14056,6 +14252,9 @@ var init_bridgeProxy = __esm({
|
|
|
14056
14252
|
hasClientInitialize() {
|
|
14057
14253
|
return this.clientInitializeSeen;
|
|
14058
14254
|
}
|
|
14255
|
+
isSetupRequiredSession() {
|
|
14256
|
+
return this.setupRequiredSession;
|
|
14257
|
+
}
|
|
14059
14258
|
async ensureDaemonForInitialize(params) {
|
|
14060
14259
|
logInitializeDebug(
|
|
14061
14260
|
this.log,
|
|
@@ -14115,14 +14314,22 @@ var init_bridgeProxy = __esm({
|
|
|
14115
14314
|
log: this.log
|
|
14116
14315
|
}) : await resolveBinding();
|
|
14117
14316
|
if (isPluginStartupSetupRequired(binding)) {
|
|
14118
|
-
|
|
14317
|
+
this.setupRequiredSession = true;
|
|
14318
|
+
this.activeBinding = null;
|
|
14319
|
+
this.detachSocketReader();
|
|
14320
|
+
const previousSocket = this.activeSocket;
|
|
14321
|
+
this.activeSocket = null;
|
|
14322
|
+
previousSocket?.destroy();
|
|
14323
|
+
this.log("setup_required: no active binding; initialize will complete locally");
|
|
14324
|
+
return "setup_required";
|
|
14119
14325
|
}
|
|
14326
|
+
this.setupRequiredSession = false;
|
|
14120
14327
|
const environmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
|
|
14121
14328
|
this.log(
|
|
14122
14329
|
`session binding binding=${binding.repositoryBindingId} project=${binding.projectId} workspace=${binding.workspaceRoot} source=${binding.bindingSource}${environmentLog}`
|
|
14123
14330
|
);
|
|
14124
14331
|
if (this.activeBinding && bindingsMatch(this.activeBinding, binding) && this.activeSocket) {
|
|
14125
|
-
return;
|
|
14332
|
+
return "connected";
|
|
14126
14333
|
}
|
|
14127
14334
|
if (this.activeBinding && !bindingsMatch(this.activeBinding, binding)) {
|
|
14128
14335
|
this.log(
|
|
@@ -14137,11 +14344,71 @@ var init_bridgeProxy = __esm({
|
|
|
14137
14344
|
this.activeBinding = binding;
|
|
14138
14345
|
await this.connectActiveDaemon();
|
|
14139
14346
|
this.log("bridge connected");
|
|
14347
|
+
return "connected";
|
|
14140
14348
|
}
|
|
14141
14349
|
recordClientInitialize(line) {
|
|
14142
14350
|
this.lastInitializeLine = line;
|
|
14143
14351
|
this.clientInitializeSeen = true;
|
|
14144
14352
|
}
|
|
14353
|
+
/**
|
|
14354
|
+
* Finish Setup session: answer initialize on the bridge. Does not connect,
|
|
14355
|
+
* spawn, or forward to a daemon — there is no binding by design.
|
|
14356
|
+
*/
|
|
14357
|
+
completeSetupRequiredInitialize(line) {
|
|
14358
|
+
this.recordClientInitialize(line);
|
|
14359
|
+
const initId = extractJsonRpcId(line);
|
|
14360
|
+
if (initId !== void 0) {
|
|
14361
|
+
this.stdout.write(
|
|
14362
|
+
`${JSON.stringify({
|
|
14363
|
+
jsonrpc: "2.0",
|
|
14364
|
+
id: initId,
|
|
14365
|
+
result: {
|
|
14366
|
+
protocolVersion: extractInitializeProtocolVersion(line),
|
|
14367
|
+
capabilities: { tools: { listChanged: true } },
|
|
14368
|
+
serverInfo: { name: "memoraone-mcp", version: "1.0.0" }
|
|
14369
|
+
}
|
|
14370
|
+
})}
|
|
14371
|
+
`
|
|
14372
|
+
);
|
|
14373
|
+
}
|
|
14374
|
+
this.log("setup_required: initialize completed locally");
|
|
14375
|
+
const deferred = this.pendingDeferredClientLines.slice();
|
|
14376
|
+
this.pendingDeferredClientLines = [];
|
|
14377
|
+
this.handshakeDeferredClientLines = deferred.slice();
|
|
14378
|
+
for (const deferredLine of deferred) {
|
|
14379
|
+
this.handleSetupRequiredClientLine(deferredLine);
|
|
14380
|
+
}
|
|
14381
|
+
}
|
|
14382
|
+
/** Local MCP replies for a setup_required session (no daemon socket). */
|
|
14383
|
+
handleSetupRequiredClientLine(line) {
|
|
14384
|
+
let message;
|
|
14385
|
+
try {
|
|
14386
|
+
message = JSON.parse(line.trim());
|
|
14387
|
+
} catch {
|
|
14388
|
+
return;
|
|
14389
|
+
}
|
|
14390
|
+
const method = typeof message.method === "string" ? message.method : "";
|
|
14391
|
+
const id = message.id;
|
|
14392
|
+
if (method === "tools/list" && (typeof id === "string" || typeof id === "number")) {
|
|
14393
|
+
this.stdout.write(
|
|
14394
|
+
`${JSON.stringify({
|
|
14395
|
+
jsonrpc: "2.0",
|
|
14396
|
+
id,
|
|
14397
|
+
result: { tools: [SETUP_REQUIRED_FINISH_SETUP_TOOL] }
|
|
14398
|
+
})}
|
|
14399
|
+
`
|
|
14400
|
+
);
|
|
14401
|
+
return;
|
|
14402
|
+
}
|
|
14403
|
+
if (id === void 0 || id === null) {
|
|
14404
|
+
return;
|
|
14405
|
+
}
|
|
14406
|
+
writePreInitializeJsonRpcError(
|
|
14407
|
+
this.stdout,
|
|
14408
|
+
id,
|
|
14409
|
+
method.trim() !== "" ? method : "(unknown)"
|
|
14410
|
+
);
|
|
14411
|
+
}
|
|
14145
14412
|
/**
|
|
14146
14413
|
* Forward initialize to the daemon and wait for its JSON-RPC response before
|
|
14147
14414
|
* returning. Prevents pipelined tools/call (e.g. Claude Code lazy MCP connect)
|
|
@@ -14161,7 +14428,7 @@ var init_bridgeProxy = __esm({
|
|
|
14161
14428
|
}
|
|
14162
14429
|
waitForDaemonJsonRpcResponse(id, timeoutMs = 3e4) {
|
|
14163
14430
|
const key = this.waiterKey(id);
|
|
14164
|
-
return new Promise((
|
|
14431
|
+
return new Promise((resolve45, reject) => {
|
|
14165
14432
|
const timer = setTimeout(() => {
|
|
14166
14433
|
this.pendingDaemonResponseWaiters.delete(key);
|
|
14167
14434
|
reject(
|
|
@@ -14170,7 +14437,7 @@ var init_bridgeProxy = __esm({
|
|
|
14170
14437
|
)
|
|
14171
14438
|
);
|
|
14172
14439
|
}, timeoutMs);
|
|
14173
|
-
this.pendingDaemonResponseWaiters.set(key, { resolve:
|
|
14440
|
+
this.pendingDaemonResponseWaiters.set(key, { resolve: resolve45, reject, timer });
|
|
14174
14441
|
});
|
|
14175
14442
|
}
|
|
14176
14443
|
notifyDaemonResponseWaiters(line) {
|
|
@@ -14241,6 +14508,7 @@ var init_bridgeProxy = __esm({
|
|
|
14241
14508
|
this.pendingDeferredClientLines = [];
|
|
14242
14509
|
this.handshakeDeferredClientLines = [];
|
|
14243
14510
|
this.clientInitializeSeen = false;
|
|
14511
|
+
this.setupRequiredSession = false;
|
|
14244
14512
|
this.activeBinding = null;
|
|
14245
14513
|
this.activeIdeType = void 0;
|
|
14246
14514
|
}
|
|
@@ -14359,6 +14627,8 @@ if (args.includes("--help") || args.includes("-h")) {
|
|
|
14359
14627
|
memoraone-mcp connect <code> [--api-url <url>] [--verbose]
|
|
14360
14628
|
memoraone-mcp plugin-pair --workspace-root <path> --source <cursor|jetbrains> [--api-url <url>]
|
|
14361
14629
|
memoraone-mcp runtime-identity --workspace-root <path>
|
|
14630
|
+
memoraone-mcp ensure-gateway
|
|
14631
|
+
memoraone-mcp --gateway
|
|
14362
14632
|
memoraone-mcp [--daemon --binding-id <mrb_\u2026> [--ide ${IDE_TYPE_CLI_CHOICES}]]
|
|
14363
14633
|
memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains|--claude-code|--windsurf|--opencode|--codex|--kiro|--kiro-cli|--cline|--cline-cli|--claude-desktop|--zed|--visual-studio|--copilot-cli|--auggie|--antigravity|--goose|--junie|--xcode|--copilot-jetbrains|--copilot-visual-studio] [--force] [--dry-run] [--no-gitignore] [--cleanup] [--dev] [--repair] [--workspace-root <path>] [--api-url <url>] [--verbose]
|
|
14364
14634
|
Cursor API environment (with --cursor or --all): --local (node + built cli.cjs + local API) | --staging (npx + staging API)
|
|
@@ -14409,6 +14679,18 @@ if (args[0] === "cleanup") {
|
|
|
14409
14679
|
`);
|
|
14410
14680
|
process.exit(1);
|
|
14411
14681
|
});
|
|
14682
|
+
} else if (args[0] === "ensure-gateway") {
|
|
14683
|
+
Promise.resolve().then(() => (init_gatewayEnsure(), gatewayEnsure_exports)).then(({ cliEnsureGateway: cliEnsureGateway2 }) => cliEnsureGateway2(args.slice(1))).then((code) => process.exit(code)).catch((err) => {
|
|
14684
|
+
process.stderr.write(`[memoraone-mcp] ensure-gateway fatal: ${String(err)}
|
|
14685
|
+
`);
|
|
14686
|
+
process.exit(1);
|
|
14687
|
+
});
|
|
14688
|
+
} else if (args.includes("--gateway")) {
|
|
14689
|
+
import("./gateway.cjs").then(({ runGateway }) => runGateway()).catch((err) => {
|
|
14690
|
+
process.stderr.write(`[memoraone-mcp] gateway fatal: ${String(err)}
|
|
14691
|
+
`);
|
|
14692
|
+
process.exit(1);
|
|
14693
|
+
});
|
|
14412
14694
|
} else if (args.includes("--daemon")) {
|
|
14413
14695
|
import("./daemon.cjs").then(({ runDaemon }) => runDaemon()).catch((err) => {
|
|
14414
14696
|
process.stderr.write(`[memoraone-mcp] daemon fatal: ${String(err)}
|