@memoraone/mcp 0.1.44 → 0.1.46
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 +731 -23
- package/dist/daemon.cjs +2 -2
- package/dist/index.cjs +2 -2
- package/dist/pluginPairing.cjs +43 -4
- 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.46",
|
|
235
235
|
type: "module",
|
|
236
236
|
main: "dist/index.cjs",
|
|
237
237
|
exports: {
|
|
@@ -497,8 +497,8 @@ async function acquireLocalLock(lockName, options = {}) {
|
|
|
497
497
|
while (retries <= maxRetries) {
|
|
498
498
|
try {
|
|
499
499
|
try {
|
|
500
|
-
const
|
|
501
|
-
if (Date.now() -
|
|
500
|
+
const stat6 = await fs3.stat(lockPath);
|
|
501
|
+
if (Date.now() - stat6.mtimeMs > maxLockAgeMs) {
|
|
502
502
|
await fs3.unlink(lockPath);
|
|
503
503
|
}
|
|
504
504
|
} catch (err) {
|
|
@@ -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((resolve43) => setTimeout(resolve43, 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, path47, options = {}) {
|
|
1096
1096
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1097
|
-
const url = `${baseUrl.replace(/\/+$/, "")}${
|
|
1097
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${path47.startsWith("/") ? path47 : `/${path47}`}`;
|
|
1098
1098
|
const res = await fetchImpl(url, {
|
|
1099
1099
|
method,
|
|
1100
1100
|
headers: {
|
|
@@ -1117,6 +1117,85 @@ async function requestJson(baseUrl, method, path44, options = {}) {
|
|
|
1117
1117
|
}
|
|
1118
1118
|
return { status: res.status, statusText: res.statusText, ok: res.ok, json };
|
|
1119
1119
|
}
|
|
1120
|
+
function requireString(data, key, responseName) {
|
|
1121
|
+
const value = data[key];
|
|
1122
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
1123
|
+
throw new Error(`[memoraone-mcp] Invalid ${responseName} response`);
|
|
1124
|
+
}
|
|
1125
|
+
return value;
|
|
1126
|
+
}
|
|
1127
|
+
function normalizePluginPairingSource(source) {
|
|
1128
|
+
if (source !== "cursor" && source !== "jetbrains") {
|
|
1129
|
+
throw new Error("[memoraone-mcp] Pairing source must be cursor or jetbrains");
|
|
1130
|
+
}
|
|
1131
|
+
return source;
|
|
1132
|
+
}
|
|
1133
|
+
function pairingRepositoryName(workspaceRoot) {
|
|
1134
|
+
const repositoryName = path8.basename(path8.resolve(workspaceRoot)).trim();
|
|
1135
|
+
if (repositoryName.length === 0 || repositoryName.length > 256 || repositoryName.includes("/") || repositoryName.includes("\\")) {
|
|
1136
|
+
throw new Error("[memoraone-mcp] Workspace basename is not safe pairing metadata");
|
|
1137
|
+
}
|
|
1138
|
+
return repositoryName;
|
|
1139
|
+
}
|
|
1140
|
+
async function createPluginPairingRequest(apiUrl, input2, options = {}) {
|
|
1141
|
+
const body = {
|
|
1142
|
+
repository_name: pairingRepositoryName(input2.workspaceRoot),
|
|
1143
|
+
source: normalizePluginPairingSource(input2.source)
|
|
1144
|
+
};
|
|
1145
|
+
const res = await requestJson(apiUrl, "POST", "/v1/local-connect/pairing-requests", {
|
|
1146
|
+
body,
|
|
1147
|
+
fetchImpl: options.fetchImpl
|
|
1148
|
+
});
|
|
1149
|
+
const data = res.json ?? {};
|
|
1150
|
+
return {
|
|
1151
|
+
requestId: requireString(data, "request_id", "pairing create"),
|
|
1152
|
+
pollingVerifier: requireString(data, "polling_verifier", "pairing create"),
|
|
1153
|
+
authorizationUrl: requireString(data, "authorization_url", "pairing create"),
|
|
1154
|
+
expiresAt: requireString(data, "expires_at", "pairing create")
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
async function pollPluginPairingRequest(apiUrl, request, options = {}) {
|
|
1158
|
+
const requestId = encodeURIComponent(request.requestId);
|
|
1159
|
+
const res = await requestJson(
|
|
1160
|
+
apiUrl,
|
|
1161
|
+
"POST",
|
|
1162
|
+
`/v1/local-connect/pairing-requests/${requestId}/poll`,
|
|
1163
|
+
{
|
|
1164
|
+
body: { polling_verifier: request.pollingVerifier },
|
|
1165
|
+
fetchImpl: options.fetchImpl
|
|
1166
|
+
}
|
|
1167
|
+
);
|
|
1168
|
+
const data = res.json ?? {};
|
|
1169
|
+
const expiresAt = requireString(data, "expires_at", "pairing poll");
|
|
1170
|
+
if (data.status === "pending") {
|
|
1171
|
+
return { status: "pending", expiresAt };
|
|
1172
|
+
}
|
|
1173
|
+
if (data.status === "ready") {
|
|
1174
|
+
const code = requireString(data, "code", "pairing poll");
|
|
1175
|
+
if (!code.startsWith("mcc_")) {
|
|
1176
|
+
throw new Error("[memoraone-mcp] Invalid pairing poll response");
|
|
1177
|
+
}
|
|
1178
|
+
return { status: "ready", code, expiresAt };
|
|
1179
|
+
}
|
|
1180
|
+
throw new Error("[memoraone-mcp] Invalid pairing poll response");
|
|
1181
|
+
}
|
|
1182
|
+
async function cancelPluginPairingRequest(apiUrl, request, options = {}) {
|
|
1183
|
+
const requestId = encodeURIComponent(request.requestId);
|
|
1184
|
+
const res = await requestJson(
|
|
1185
|
+
apiUrl,
|
|
1186
|
+
"POST",
|
|
1187
|
+
`/v1/local-connect/pairing-requests/${requestId}/cancel`,
|
|
1188
|
+
{
|
|
1189
|
+
body: { polling_verifier: request.pollingVerifier },
|
|
1190
|
+
fetchImpl: options.fetchImpl
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
const data = res.json ?? {};
|
|
1194
|
+
if (data.status !== "cancelled") {
|
|
1195
|
+
throw new Error("[memoraone-mcp] Invalid pairing cancel response");
|
|
1196
|
+
}
|
|
1197
|
+
return { status: "cancelled" };
|
|
1198
|
+
}
|
|
1120
1199
|
function assertSafeRedeemBody(body) {
|
|
1121
1200
|
if ("canonical_root" in body) {
|
|
1122
1201
|
throw new Error("[memoraone-mcp] redeem body must not include canonical_root");
|
|
@@ -1273,6 +1352,30 @@ async function ensureRepositoryBindingForRoot(workspaceRoot, options = {}) {
|
|
|
1273
1352
|
legacyM1WarningPath
|
|
1274
1353
|
};
|
|
1275
1354
|
}
|
|
1355
|
+
async function lookupLocalBindingIdentity(workspaceRoot, options = {}) {
|
|
1356
|
+
const resolved = path9.resolve(workspaceRoot);
|
|
1357
|
+
const identity = await captureRootFilesystemIdentity(resolved, options.identityDeps);
|
|
1358
|
+
const index = await loadPathIndex(options.homeDir);
|
|
1359
|
+
const lookup = lookupPathIndex(index, resolved, identity);
|
|
1360
|
+
if (lookup.kind !== "path") {
|
|
1361
|
+
return { status: "unbound", workspaceRoot: resolved };
|
|
1362
|
+
}
|
|
1363
|
+
const record = await readBindingRecord(lookup.repositoryBindingId, options.homeDir);
|
|
1364
|
+
if (!record) {
|
|
1365
|
+
return { status: "unbound", workspaceRoot: resolved };
|
|
1366
|
+
}
|
|
1367
|
+
if (path9.resolve(record.workspaceRoot) !== resolved) {
|
|
1368
|
+
return { status: "unbound", workspaceRoot: resolved };
|
|
1369
|
+
}
|
|
1370
|
+
if (!identityMatchesStored(record.filesystemIdentity, identity)) {
|
|
1371
|
+
return { status: "unbound", workspaceRoot: resolved };
|
|
1372
|
+
}
|
|
1373
|
+
return {
|
|
1374
|
+
status: "resolved",
|
|
1375
|
+
workspaceRoot: resolved,
|
|
1376
|
+
repositoryBindingId: record.repositoryBindingId
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1276
1379
|
async function resolveLocalBinding(workspaceRoot, options = {}) {
|
|
1277
1380
|
const resolved = path9.resolve(workspaceRoot);
|
|
1278
1381
|
const { repositoryBindingId, legacyM1WarningPath } = await ensureRepositoryBindingForRoot(
|
|
@@ -3549,11 +3652,11 @@ var init_repoFingerprint = __esm({
|
|
|
3549
3652
|
};
|
|
3550
3653
|
resolveGitDir = (gitPath) => {
|
|
3551
3654
|
try {
|
|
3552
|
-
const
|
|
3553
|
-
if (
|
|
3655
|
+
const stat6 = fs13.statSync(gitPath);
|
|
3656
|
+
if (stat6.isDirectory()) {
|
|
3554
3657
|
return gitPath;
|
|
3555
3658
|
}
|
|
3556
|
-
if (
|
|
3659
|
+
if (stat6.isFile()) {
|
|
3557
3660
|
const content = fs13.readFileSync(gitPath, "utf8");
|
|
3558
3661
|
const match = content.match(/^gitdir:\s*(.+)$/m);
|
|
3559
3662
|
if (match) {
|
|
@@ -3644,8 +3747,8 @@ function getKnownJetBrainsMcpConfigLocations(homeDir, repoRoot) {
|
|
|
3644
3747
|
}
|
|
3645
3748
|
async function isZeroByteConfigFile(filePath) {
|
|
3646
3749
|
if (!await pathExists3(filePath)) return false;
|
|
3647
|
-
const
|
|
3648
|
-
return
|
|
3750
|
+
const stat6 = await fs14.stat(filePath);
|
|
3751
|
+
return stat6.size === 0;
|
|
3649
3752
|
}
|
|
3650
3753
|
function stripDebugEnvVars(env2) {
|
|
3651
3754
|
const next = {};
|
|
@@ -3847,7 +3950,7 @@ function formatOptionalJetBrainsHandshakeUnavailableDetail(detail) {
|
|
|
3847
3950
|
async function verifyJetBrainsMcpHandshake(options) {
|
|
3848
3951
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
3849
3952
|
const { server } = options;
|
|
3850
|
-
return new Promise((
|
|
3953
|
+
return new Promise((resolve43) => {
|
|
3851
3954
|
let settled = false;
|
|
3852
3955
|
const finish = (ok, detail, optionalUnavailable) => {
|
|
3853
3956
|
if (settled) return;
|
|
@@ -3857,7 +3960,7 @@ async function verifyJetBrainsMcpHandshake(options) {
|
|
|
3857
3960
|
child.kill();
|
|
3858
3961
|
} catch {
|
|
3859
3962
|
}
|
|
3860
|
-
|
|
3963
|
+
resolve43({ ok, detail, optionalUnavailable });
|
|
3861
3964
|
};
|
|
3862
3965
|
const child = (0, import_node_child_process4.spawn)(server.command, [...server.args], {
|
|
3863
3966
|
env: { ...process.env, ...server.env },
|
|
@@ -9528,6 +9631,14 @@ async function findRepoRoot(startDir) {
|
|
|
9528
9631
|
function stripLeadingLineComments18(text) {
|
|
9529
9632
|
return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
|
|
9530
9633
|
}
|
|
9634
|
+
function memoraUsagePolicy() {
|
|
9635
|
+
return `- For every user question, check whether MemoraOne can provide relevant context before answering. If it can, use MemoraOne first.
|
|
9636
|
+
- Do not silently substitute IDE session history, local files, git history, or model memory when MemoraOne can provide relevant context.
|
|
9637
|
+
- Use **\`memora_status\`** for repository/project connection identity.
|
|
9638
|
+
- Use **\`memora_ask_with_memory\`** for previous questions/answers, conversation/history, decisions, facts, preferences, project context, and questions like "what did I/you say, ask, decide, or do before?"
|
|
9639
|
+
- Skip MemoraOne only when the question is clearly unrelated.
|
|
9640
|
+
- After the interaction: if anything meaningful happened\u2014question, answer, decision, discovery, action, correction, failure, or change\u2014record it back to MemoraOne so the next agent knows about it, especially if MemoraOne was not used before answering. Use **\`memora_post_event\`** and **\`memora_log_change_summary\`** according to their intended semantics; do not write trivial noise.`;
|
|
9641
|
+
}
|
|
9531
9642
|
function cursorRuleBody() {
|
|
9532
9643
|
return `${MANAGED_MARKER}
|
|
9533
9644
|
|
|
@@ -9537,7 +9648,7 @@ This repository uses **MemoraOne** via the MCP server named **memoraone** (repo-
|
|
|
9537
9648
|
|
|
9538
9649
|
### Tools
|
|
9539
9650
|
|
|
9540
|
-
|
|
9651
|
+
${memoraUsagePolicy()}
|
|
9541
9652
|
- Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. \`cursor\`), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
|
|
9542
9653
|
- Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
|
|
9543
9654
|
|
|
@@ -9555,7 +9666,7 @@ This repo is set up to use **MemoraOne** through MCP where your editor exposes i
|
|
|
9555
9666
|
|
|
9556
9667
|
## Behavior
|
|
9557
9668
|
|
|
9558
|
-
|
|
9669
|
+
${memoraUsagePolicy()}
|
|
9559
9670
|
- Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. your agent name), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
|
|
9560
9671
|
- Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
|
|
9561
9672
|
|
|
@@ -10317,6 +10428,7 @@ async function runSetupIdeFiles(o) {
|
|
|
10317
10428
|
}
|
|
10318
10429
|
const cursorContent = `---
|
|
10319
10430
|
description: MemoraOne MCP \u2014 IDE agent instructions
|
|
10431
|
+
alwaysApply: true
|
|
10320
10432
|
---
|
|
10321
10433
|
|
|
10322
10434
|
` + cursorRuleBody();
|
|
@@ -12618,6 +12730,578 @@ var init_connectCommand = __esm({
|
|
|
12618
12730
|
}
|
|
12619
12731
|
});
|
|
12620
12732
|
|
|
12733
|
+
// src/pluginPairing.ts
|
|
12734
|
+
function backendErrorCode(error) {
|
|
12735
|
+
if (!(error instanceof MemoraOneHttpError) || !error.body || typeof error.body !== "object") {
|
|
12736
|
+
return void 0;
|
|
12737
|
+
}
|
|
12738
|
+
const code = error.body.error;
|
|
12739
|
+
return typeof code === "string" ? code : void 0;
|
|
12740
|
+
}
|
|
12741
|
+
function mapPairingError(error) {
|
|
12742
|
+
const code = backendErrorCode(error);
|
|
12743
|
+
if (code === "pairing_rejected") return "rejected";
|
|
12744
|
+
if (code === "pairing_cancelled") return "cancelled";
|
|
12745
|
+
if (code === "pairing_consumed") return "consumed";
|
|
12746
|
+
if (code === "pairing_expired") return "expired";
|
|
12747
|
+
if (code === "invalid_pairing_verifier" || code === "pairing_not_found" || code === "bad_request") {
|
|
12748
|
+
return "invalid";
|
|
12749
|
+
}
|
|
12750
|
+
if (error instanceof MemoraOneHttpError && error.status === 410) return "expired";
|
|
12751
|
+
if (error instanceof MemoraOneHttpError && (error.status === 400 || error.status === 401)) {
|
|
12752
|
+
return "invalid";
|
|
12753
|
+
}
|
|
12754
|
+
return "backend_failure";
|
|
12755
|
+
}
|
|
12756
|
+
async function beginPluginRepositoryPairing(options) {
|
|
12757
|
+
const workspaceRoot = path44.resolve(options.workspaceRoot);
|
|
12758
|
+
const apiUrl = (options.apiUrl ?? config2.apiUrl).replace(/\/+$/, "");
|
|
12759
|
+
if (options.source !== "cursor" && options.source !== "jetbrains") {
|
|
12760
|
+
throw new PluginPairingStartError("invalid");
|
|
12761
|
+
}
|
|
12762
|
+
try {
|
|
12763
|
+
pairingRepositoryName(workspaceRoot);
|
|
12764
|
+
} catch {
|
|
12765
|
+
throw new PluginPairingStartError("invalid");
|
|
12766
|
+
}
|
|
12767
|
+
let request;
|
|
12768
|
+
try {
|
|
12769
|
+
request = await createPluginPairingRequest(
|
|
12770
|
+
apiUrl,
|
|
12771
|
+
{ workspaceRoot, source: options.source },
|
|
12772
|
+
{ fetchImpl: options.fetchImpl }
|
|
12773
|
+
);
|
|
12774
|
+
} catch (error) {
|
|
12775
|
+
const status = mapPairingError(error);
|
|
12776
|
+
throw new PluginPairingStartError(status === "invalid" ? "invalid" : "backend_failure");
|
|
12777
|
+
}
|
|
12778
|
+
return new PluginRepositoryPairingSession(
|
|
12779
|
+
request,
|
|
12780
|
+
workspaceRoot,
|
|
12781
|
+
options.source,
|
|
12782
|
+
apiUrl,
|
|
12783
|
+
options.fetchImpl,
|
|
12784
|
+
options.connectOptions
|
|
12785
|
+
);
|
|
12786
|
+
}
|
|
12787
|
+
var path44, PluginPairingStartError, PluginRepositoryPairingSession;
|
|
12788
|
+
var init_pluginPairing = __esm({
|
|
12789
|
+
"src/pluginPairing.ts"() {
|
|
12790
|
+
path44 = __toESM(require("path"), 1);
|
|
12791
|
+
init_memoraClient();
|
|
12792
|
+
init_config();
|
|
12793
|
+
init_connectCommand();
|
|
12794
|
+
init_localConnectClient();
|
|
12795
|
+
PluginPairingStartError = class extends Error {
|
|
12796
|
+
constructor(status) {
|
|
12797
|
+
super(
|
|
12798
|
+
status === "invalid" ? "[memoraone-mcp] Invalid plugin pairing request" : "[memoraone-mcp] Plugin pairing request could not be created"
|
|
12799
|
+
);
|
|
12800
|
+
this.status = status;
|
|
12801
|
+
this.name = "PluginPairingStartError";
|
|
12802
|
+
}
|
|
12803
|
+
};
|
|
12804
|
+
PluginRepositoryPairingSession = class {
|
|
12805
|
+
constructor(request, workspaceRoot, source, apiUrl, fetchImpl, connectOptions = {}) {
|
|
12806
|
+
this.workspaceRoot = workspaceRoot;
|
|
12807
|
+
this.source = source;
|
|
12808
|
+
this.apiUrl = apiUrl;
|
|
12809
|
+
this.fetchImpl = fetchImpl;
|
|
12810
|
+
this.connectOptions = connectOptions;
|
|
12811
|
+
this.request = request;
|
|
12812
|
+
}
|
|
12813
|
+
/** Opaque, short-lived request metadata. Cleared when the session becomes terminal. */
|
|
12814
|
+
get requestId() {
|
|
12815
|
+
return this.request?.requestId;
|
|
12816
|
+
}
|
|
12817
|
+
get authorizationUrl() {
|
|
12818
|
+
return this.request?.authorizationUrl;
|
|
12819
|
+
}
|
|
12820
|
+
get expiresAt() {
|
|
12821
|
+
return this.request?.expiresAt;
|
|
12822
|
+
}
|
|
12823
|
+
finish(status) {
|
|
12824
|
+
this.terminalStatus = status;
|
|
12825
|
+
this.request = void 0;
|
|
12826
|
+
}
|
|
12827
|
+
async poll() {
|
|
12828
|
+
if (!this.request) {
|
|
12829
|
+
return {
|
|
12830
|
+
status: this.terminalStatus === "cancelled" ? "cancelled" : "consumed",
|
|
12831
|
+
operation: "poll"
|
|
12832
|
+
};
|
|
12833
|
+
}
|
|
12834
|
+
let polled;
|
|
12835
|
+
try {
|
|
12836
|
+
polled = await pollPluginPairingRequest(this.apiUrl, this.request, {
|
|
12837
|
+
fetchImpl: this.fetchImpl
|
|
12838
|
+
});
|
|
12839
|
+
} catch (error) {
|
|
12840
|
+
const status = mapPairingError(error);
|
|
12841
|
+
if (status !== "backend_failure") this.finish(status);
|
|
12842
|
+
return { status, operation: "poll" };
|
|
12843
|
+
}
|
|
12844
|
+
if (polled.status === "pending") {
|
|
12845
|
+
this.request.expiresAt = polled.expiresAt;
|
|
12846
|
+
return polled;
|
|
12847
|
+
}
|
|
12848
|
+
try {
|
|
12849
|
+
const connected = await runConnectCommand({
|
|
12850
|
+
...this.connectOptions,
|
|
12851
|
+
code: polled.code,
|
|
12852
|
+
cwd: this.workspaceRoot,
|
|
12853
|
+
apiUrl: this.apiUrl,
|
|
12854
|
+
ideType: this.source,
|
|
12855
|
+
fetchImpl: this.connectOptions.fetchImpl ?? this.fetchImpl,
|
|
12856
|
+
// Plugin pairing configures only the IDE that initiated the pairing.
|
|
12857
|
+
configureIdes: true,
|
|
12858
|
+
setupIdeOptions: {
|
|
12859
|
+
...this.connectOptions.setupIdeOptions,
|
|
12860
|
+
targets: {
|
|
12861
|
+
cursor: this.source === "cursor",
|
|
12862
|
+
vscode: false,
|
|
12863
|
+
jetbrains: this.source === "jetbrains",
|
|
12864
|
+
claudeCode: false,
|
|
12865
|
+
windsurf: false,
|
|
12866
|
+
opencode: false,
|
|
12867
|
+
codex: false,
|
|
12868
|
+
kiro: false,
|
|
12869
|
+
kiroCli: false,
|
|
12870
|
+
rooCode: false,
|
|
12871
|
+
cline: false,
|
|
12872
|
+
clineCli: false,
|
|
12873
|
+
claudeDesktop: false,
|
|
12874
|
+
zed: false,
|
|
12875
|
+
visualStudio: false,
|
|
12876
|
+
continue: false,
|
|
12877
|
+
copilotCli: false,
|
|
12878
|
+
auggie: false,
|
|
12879
|
+
geminiCli: false,
|
|
12880
|
+
antigravity: false,
|
|
12881
|
+
goose: false,
|
|
12882
|
+
junie: false,
|
|
12883
|
+
xcode: false,
|
|
12884
|
+
copilotJetBrains: false,
|
|
12885
|
+
copilotVisualStudio: false
|
|
12886
|
+
}
|
|
12887
|
+
}
|
|
12888
|
+
});
|
|
12889
|
+
if (connected.exitCode !== 0) {
|
|
12890
|
+
this.finish("backend_failure");
|
|
12891
|
+
return { status: "backend_failure", operation: "connect" };
|
|
12892
|
+
}
|
|
12893
|
+
this.finish("consumed");
|
|
12894
|
+
return {
|
|
12895
|
+
status: "ready",
|
|
12896
|
+
repositoryBindingId: connected.repositoryBindingId
|
|
12897
|
+
};
|
|
12898
|
+
} catch {
|
|
12899
|
+
this.finish("backend_failure");
|
|
12900
|
+
return { status: "backend_failure", operation: "connect" };
|
|
12901
|
+
}
|
|
12902
|
+
}
|
|
12903
|
+
async cancel() {
|
|
12904
|
+
if (!this.request) {
|
|
12905
|
+
const status = this.terminalStatus === "cancelled" ? "cancelled" : "consumed";
|
|
12906
|
+
return status === "cancelled" ? { status } : { status, operation: "cancel" };
|
|
12907
|
+
}
|
|
12908
|
+
try {
|
|
12909
|
+
await cancelPluginPairingRequest(this.apiUrl, this.request, {
|
|
12910
|
+
fetchImpl: this.fetchImpl
|
|
12911
|
+
});
|
|
12912
|
+
this.finish("cancelled");
|
|
12913
|
+
return { status: "cancelled" };
|
|
12914
|
+
} catch (error) {
|
|
12915
|
+
const status = mapPairingError(error);
|
|
12916
|
+
if (status !== "backend_failure") this.finish(status);
|
|
12917
|
+
return status === "cancelled" ? { status } : { status, operation: "cancel" };
|
|
12918
|
+
}
|
|
12919
|
+
}
|
|
12920
|
+
};
|
|
12921
|
+
}
|
|
12922
|
+
});
|
|
12923
|
+
|
|
12924
|
+
// src/pluginPairCommand.ts
|
|
12925
|
+
var pluginPairCommand_exports = {};
|
|
12926
|
+
__export(pluginPairCommand_exports, {
|
|
12927
|
+
DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS: () => DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS,
|
|
12928
|
+
PLUGIN_PAIR_USAGE: () => PLUGIN_PAIR_USAGE,
|
|
12929
|
+
cliPluginPair: () => cliPluginPair,
|
|
12930
|
+
parsePluginPairArgv: () => parsePluginPairArgv
|
|
12931
|
+
});
|
|
12932
|
+
function isPluginPairingSource(value) {
|
|
12933
|
+
return value === "cursor" || value === "jetbrains";
|
|
12934
|
+
}
|
|
12935
|
+
function parsePluginPairArgv(argv) {
|
|
12936
|
+
let workspaceRoot;
|
|
12937
|
+
let source;
|
|
12938
|
+
let apiUrl;
|
|
12939
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12940
|
+
const a = argv[i];
|
|
12941
|
+
if (a === "--workspace-root") {
|
|
12942
|
+
const value = argv[++i];
|
|
12943
|
+
if (!value || value.startsWith("-")) return { error: PLUGIN_PAIR_USAGE };
|
|
12944
|
+
workspaceRoot = value;
|
|
12945
|
+
continue;
|
|
12946
|
+
}
|
|
12947
|
+
if (a.startsWith("--workspace-root=")) {
|
|
12948
|
+
const value = a.slice("--workspace-root=".length);
|
|
12949
|
+
if (!value) return { error: PLUGIN_PAIR_USAGE };
|
|
12950
|
+
workspaceRoot = value;
|
|
12951
|
+
continue;
|
|
12952
|
+
}
|
|
12953
|
+
if (a === "--source") {
|
|
12954
|
+
const value = argv[++i];
|
|
12955
|
+
if (!value || value.startsWith("-")) return { error: PLUGIN_PAIR_USAGE };
|
|
12956
|
+
source = value;
|
|
12957
|
+
continue;
|
|
12958
|
+
}
|
|
12959
|
+
if (a.startsWith("--source=")) {
|
|
12960
|
+
const value = a.slice("--source=".length);
|
|
12961
|
+
if (!value) return { error: PLUGIN_PAIR_USAGE };
|
|
12962
|
+
source = value;
|
|
12963
|
+
continue;
|
|
12964
|
+
}
|
|
12965
|
+
if (a === "--api-url") {
|
|
12966
|
+
const value = argv[++i];
|
|
12967
|
+
if (!value || value.startsWith("-")) return { error: PLUGIN_PAIR_USAGE };
|
|
12968
|
+
apiUrl = value;
|
|
12969
|
+
continue;
|
|
12970
|
+
}
|
|
12971
|
+
if (a.startsWith("--api-url=")) {
|
|
12972
|
+
const value = a.slice("--api-url=".length);
|
|
12973
|
+
if (!value) return { error: PLUGIN_PAIR_USAGE };
|
|
12974
|
+
apiUrl = value;
|
|
12975
|
+
continue;
|
|
12976
|
+
}
|
|
12977
|
+
if (a.startsWith("-")) {
|
|
12978
|
+
return { error: `Unknown plugin-pair option: ${a}` };
|
|
12979
|
+
}
|
|
12980
|
+
return { error: PLUGIN_PAIR_USAGE };
|
|
12981
|
+
}
|
|
12982
|
+
return { workspaceRoot, source, apiUrl };
|
|
12983
|
+
}
|
|
12984
|
+
function writeEvent(event, stdoutWrite) {
|
|
12985
|
+
const line = `${JSON.stringify(event)}
|
|
12986
|
+
`;
|
|
12987
|
+
if (SECRET_OUTPUT_RE.test(line)) {
|
|
12988
|
+
stdoutWrite(`${JSON.stringify({ event: "fatal", message: "Plugin pairing failed" })}
|
|
12989
|
+
`);
|
|
12990
|
+
return;
|
|
12991
|
+
}
|
|
12992
|
+
stdoutWrite(line);
|
|
12993
|
+
}
|
|
12994
|
+
function delay(ms, signal) {
|
|
12995
|
+
if (ms <= 0 || signal?.aborted) return Promise.resolve();
|
|
12996
|
+
return new Promise((resolve43) => {
|
|
12997
|
+
const timer = setTimeout(finish, ms);
|
|
12998
|
+
const onAbort = () => finish();
|
|
12999
|
+
function finish() {
|
|
13000
|
+
clearTimeout(timer);
|
|
13001
|
+
signal?.removeEventListener("abort", onAbort);
|
|
13002
|
+
resolve43();
|
|
13003
|
+
}
|
|
13004
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
13005
|
+
});
|
|
13006
|
+
}
|
|
13007
|
+
async function resolveWorkspaceRoot(workspaceRoot) {
|
|
13008
|
+
const resolved = path45.resolve(workspaceRoot);
|
|
13009
|
+
try {
|
|
13010
|
+
const st = await fs38.stat(resolved);
|
|
13011
|
+
if (!st.isDirectory()) {
|
|
13012
|
+
return { error: "workspace-root must be a directory" };
|
|
13013
|
+
}
|
|
13014
|
+
} catch {
|
|
13015
|
+
return { error: "workspace-root does not exist" };
|
|
13016
|
+
}
|
|
13017
|
+
return resolved;
|
|
13018
|
+
}
|
|
13019
|
+
function isRetryablePollFailure(result) {
|
|
13020
|
+
return result.status === "backend_failure" && result.operation === "poll";
|
|
13021
|
+
}
|
|
13022
|
+
function terminalEventFromPoll(result) {
|
|
13023
|
+
if (result.status === "pending" || result.status === "ready") return void 0;
|
|
13024
|
+
if (result.operation) {
|
|
13025
|
+
return { event: result.status, operation: result.operation };
|
|
13026
|
+
}
|
|
13027
|
+
return { event: result.status };
|
|
13028
|
+
}
|
|
13029
|
+
async function runPluginPairSession(session, options) {
|
|
13030
|
+
const { workspaceRoot, source, pollIntervalMs, signal, stdoutWrite } = options;
|
|
13031
|
+
const requestId = session.requestId;
|
|
13032
|
+
const authorizationUrl = session.authorizationUrl;
|
|
13033
|
+
const expiresAt = session.expiresAt;
|
|
13034
|
+
if (!requestId || !authorizationUrl || !expiresAt) {
|
|
13035
|
+
writeEvent({ event: "fatal", message: "Plugin pairing failed" }, stdoutWrite);
|
|
13036
|
+
return 1;
|
|
13037
|
+
}
|
|
13038
|
+
writeEvent(
|
|
13039
|
+
{
|
|
13040
|
+
event: "started",
|
|
13041
|
+
requestId,
|
|
13042
|
+
authorizationUrl,
|
|
13043
|
+
expiresAt,
|
|
13044
|
+
workspaceRoot,
|
|
13045
|
+
source
|
|
13046
|
+
},
|
|
13047
|
+
stdoutWrite
|
|
13048
|
+
);
|
|
13049
|
+
const cancelAndExit = async () => {
|
|
13050
|
+
const cancelled = await session.cancel();
|
|
13051
|
+
if (cancelled.status === "cancelled") {
|
|
13052
|
+
writeEvent({ event: "cancelled" }, stdoutWrite);
|
|
13053
|
+
} else {
|
|
13054
|
+
writeEvent(
|
|
13055
|
+
{ event: cancelled.status, operation: cancelled.operation },
|
|
13056
|
+
stdoutWrite
|
|
13057
|
+
);
|
|
13058
|
+
}
|
|
13059
|
+
return 1;
|
|
13060
|
+
};
|
|
13061
|
+
while (!signal?.aborted) {
|
|
13062
|
+
const result = await session.poll();
|
|
13063
|
+
if (signal?.aborted) {
|
|
13064
|
+
if (result.status === "ready") {
|
|
13065
|
+
writeEvent(
|
|
13066
|
+
{
|
|
13067
|
+
event: "ready",
|
|
13068
|
+
repositoryBindingId: result.repositoryBindingId,
|
|
13069
|
+
workspaceRoot,
|
|
13070
|
+
source,
|
|
13071
|
+
status: "connected"
|
|
13072
|
+
},
|
|
13073
|
+
stdoutWrite
|
|
13074
|
+
);
|
|
13075
|
+
return 0;
|
|
13076
|
+
}
|
|
13077
|
+
return cancelAndExit();
|
|
13078
|
+
}
|
|
13079
|
+
if (result.status === "pending") {
|
|
13080
|
+
writeEvent({ event: "pending", expiresAt: result.expiresAt }, stdoutWrite);
|
|
13081
|
+
await delay(pollIntervalMs, signal);
|
|
13082
|
+
continue;
|
|
13083
|
+
}
|
|
13084
|
+
if (result.status === "ready") {
|
|
13085
|
+
writeEvent(
|
|
13086
|
+
{
|
|
13087
|
+
event: "ready",
|
|
13088
|
+
repositoryBindingId: result.repositoryBindingId,
|
|
13089
|
+
workspaceRoot,
|
|
13090
|
+
source,
|
|
13091
|
+
status: "connected"
|
|
13092
|
+
},
|
|
13093
|
+
stdoutWrite
|
|
13094
|
+
);
|
|
13095
|
+
return 0;
|
|
13096
|
+
}
|
|
13097
|
+
if (isRetryablePollFailure(result)) {
|
|
13098
|
+
await delay(pollIntervalMs, signal);
|
|
13099
|
+
continue;
|
|
13100
|
+
}
|
|
13101
|
+
const terminal = terminalEventFromPoll(result);
|
|
13102
|
+
if (terminal) {
|
|
13103
|
+
writeEvent(terminal, stdoutWrite);
|
|
13104
|
+
return 1;
|
|
13105
|
+
}
|
|
13106
|
+
writeEvent({ event: "fatal", message: "Plugin pairing failed" }, stdoutWrite);
|
|
13107
|
+
return 1;
|
|
13108
|
+
}
|
|
13109
|
+
return cancelAndExit();
|
|
13110
|
+
}
|
|
13111
|
+
async function cliPluginPair(argv, options = {}) {
|
|
13112
|
+
const stdoutWrite = options.stdoutWrite ?? ((chunk) => process.stdout.write(chunk));
|
|
13113
|
+
const parsed2 = parsePluginPairArgv(argv);
|
|
13114
|
+
if (parsed2.error) {
|
|
13115
|
+
writeEvent({ event: "fatal", message: parsed2.error }, stdoutWrite);
|
|
13116
|
+
return 1;
|
|
13117
|
+
}
|
|
13118
|
+
if (!parsed2.workspaceRoot || !parsed2.source) {
|
|
13119
|
+
writeEvent({ event: "fatal", message: PLUGIN_PAIR_USAGE }, stdoutWrite);
|
|
13120
|
+
return 1;
|
|
13121
|
+
}
|
|
13122
|
+
if (!isPluginPairingSource(parsed2.source)) {
|
|
13123
|
+
writeEvent({ event: "invalid" }, stdoutWrite);
|
|
13124
|
+
return 1;
|
|
13125
|
+
}
|
|
13126
|
+
const workspaceRootOrError = await resolveWorkspaceRoot(parsed2.workspaceRoot);
|
|
13127
|
+
if (typeof workspaceRootOrError !== "string") {
|
|
13128
|
+
writeEvent({ event: "fatal", message: workspaceRootOrError.error }, stdoutWrite);
|
|
13129
|
+
return 1;
|
|
13130
|
+
}
|
|
13131
|
+
const workspaceRoot = workspaceRootOrError;
|
|
13132
|
+
const source = parsed2.source;
|
|
13133
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS;
|
|
13134
|
+
const beginPairing = options.beginPairing ?? beginPluginRepositoryPairing;
|
|
13135
|
+
try {
|
|
13136
|
+
const binding = await resolveAuthoritativeBinding(workspaceRoot);
|
|
13137
|
+
if (binding.status === "connected") {
|
|
13138
|
+
return 0;
|
|
13139
|
+
}
|
|
13140
|
+
} catch (error) {
|
|
13141
|
+
if (!(error instanceof ReconnectRequiredError)) {
|
|
13142
|
+
throw error;
|
|
13143
|
+
}
|
|
13144
|
+
}
|
|
13145
|
+
const abortController = new AbortController();
|
|
13146
|
+
const signal = options.signal ?? abortController.signal;
|
|
13147
|
+
const onStop = () => abortController.abort();
|
|
13148
|
+
const installSignalHandlers = options.installSignalHandlers === true;
|
|
13149
|
+
if (installSignalHandlers) {
|
|
13150
|
+
process.on("SIGINT", onStop);
|
|
13151
|
+
process.on("SIGTERM", onStop);
|
|
13152
|
+
}
|
|
13153
|
+
try {
|
|
13154
|
+
let session;
|
|
13155
|
+
try {
|
|
13156
|
+
session = await beginPairing({
|
|
13157
|
+
workspaceRoot,
|
|
13158
|
+
source,
|
|
13159
|
+
apiUrl: parsed2.apiUrl,
|
|
13160
|
+
fetchImpl: options.fetchImpl,
|
|
13161
|
+
connectOptions: options.connectOptions
|
|
13162
|
+
});
|
|
13163
|
+
} catch (error) {
|
|
13164
|
+
if (error instanceof PluginPairingStartError) {
|
|
13165
|
+
writeEvent({ event: error.status, operation: "start" }, stdoutWrite);
|
|
13166
|
+
return 1;
|
|
13167
|
+
}
|
|
13168
|
+
writeEvent({ event: "fatal", message: "Plugin pairing failed" }, stdoutWrite);
|
|
13169
|
+
return 1;
|
|
13170
|
+
}
|
|
13171
|
+
return await runPluginPairSession(session, {
|
|
13172
|
+
workspaceRoot,
|
|
13173
|
+
source,
|
|
13174
|
+
pollIntervalMs,
|
|
13175
|
+
signal,
|
|
13176
|
+
stdoutWrite
|
|
13177
|
+
});
|
|
13178
|
+
} finally {
|
|
13179
|
+
if (installSignalHandlers) {
|
|
13180
|
+
process.off("SIGINT", onStop);
|
|
13181
|
+
process.off("SIGTERM", onStop);
|
|
13182
|
+
}
|
|
13183
|
+
}
|
|
13184
|
+
}
|
|
13185
|
+
var fs38, path45, PLUGIN_PAIR_USAGE, DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS, SECRET_OUTPUT_RE;
|
|
13186
|
+
var init_pluginPairCommand = __esm({
|
|
13187
|
+
"src/pluginPairCommand.ts"() {
|
|
13188
|
+
fs38 = __toESM(require("fs/promises"), 1);
|
|
13189
|
+
path45 = __toESM(require("path"), 1);
|
|
13190
|
+
init_pluginPairing();
|
|
13191
|
+
init_projectBinding();
|
|
13192
|
+
init_tokenRefreshCoordinator();
|
|
13193
|
+
PLUGIN_PAIR_USAGE = "Usage: memoraone-mcp plugin-pair --workspace-root <path> --source <cursor|jetbrains> [--api-url <url>]";
|
|
13194
|
+
DEFAULT_PLUGIN_PAIR_POLL_INTERVAL_MS = 1e3;
|
|
13195
|
+
SECRET_OUTPUT_RE = /\b(mcc_|mpv_|mia_|mir_|polling_verifier|pollingVerifier|access_token|refresh_token|api[_-]?key)\b/i;
|
|
13196
|
+
}
|
|
13197
|
+
});
|
|
13198
|
+
|
|
13199
|
+
// src/runtimeIdentityCommand.ts
|
|
13200
|
+
var runtimeIdentityCommand_exports = {};
|
|
13201
|
+
__export(runtimeIdentityCommand_exports, {
|
|
13202
|
+
RUNTIME_IDENTITY_USAGE: () => RUNTIME_IDENTITY_USAGE,
|
|
13203
|
+
cliRuntimeIdentity: () => cliRuntimeIdentity,
|
|
13204
|
+
parseRuntimeIdentityArgv: () => parseRuntimeIdentityArgv
|
|
13205
|
+
});
|
|
13206
|
+
function parseRuntimeIdentityArgv(argv) {
|
|
13207
|
+
let workspaceRoot;
|
|
13208
|
+
for (let i = 0; i < argv.length; i++) {
|
|
13209
|
+
const a = argv[i];
|
|
13210
|
+
if (a === "--workspace-root") {
|
|
13211
|
+
const value = argv[++i];
|
|
13212
|
+
if (!value || value.startsWith("-")) return { error: RUNTIME_IDENTITY_USAGE };
|
|
13213
|
+
workspaceRoot = value;
|
|
13214
|
+
continue;
|
|
13215
|
+
}
|
|
13216
|
+
if (a.startsWith("--workspace-root=")) {
|
|
13217
|
+
const value = a.slice("--workspace-root=".length);
|
|
13218
|
+
if (!value) return { error: RUNTIME_IDENTITY_USAGE };
|
|
13219
|
+
workspaceRoot = value;
|
|
13220
|
+
continue;
|
|
13221
|
+
}
|
|
13222
|
+
if (a.startsWith("-")) {
|
|
13223
|
+
return { error: `Unknown runtime-identity option: ${a}` };
|
|
13224
|
+
}
|
|
13225
|
+
return { error: RUNTIME_IDENTITY_USAGE };
|
|
13226
|
+
}
|
|
13227
|
+
return { workspaceRoot };
|
|
13228
|
+
}
|
|
13229
|
+
function writeJson(payload, stdoutWrite) {
|
|
13230
|
+
const line = `${JSON.stringify(payload)}
|
|
13231
|
+
`;
|
|
13232
|
+
if (SECRET_OUTPUT_RE2.test(line)) {
|
|
13233
|
+
stdoutWrite(`${JSON.stringify({ status: "error", error: "Runtime identity failed" })}
|
|
13234
|
+
`);
|
|
13235
|
+
return;
|
|
13236
|
+
}
|
|
13237
|
+
stdoutWrite(line);
|
|
13238
|
+
}
|
|
13239
|
+
async function resolveWorkspaceRoot2(workspaceRoot) {
|
|
13240
|
+
const resolved = path46.resolve(workspaceRoot);
|
|
13241
|
+
try {
|
|
13242
|
+
const st = await fs39.stat(resolved);
|
|
13243
|
+
if (!st.isDirectory()) {
|
|
13244
|
+
return { error: "workspace-root must be a directory", workspaceRoot: resolved };
|
|
13245
|
+
}
|
|
13246
|
+
} catch {
|
|
13247
|
+
return { error: "workspace-root does not exist", workspaceRoot: resolved };
|
|
13248
|
+
}
|
|
13249
|
+
return { workspaceRoot: resolved };
|
|
13250
|
+
}
|
|
13251
|
+
async function cliRuntimeIdentity(argv, options = {}) {
|
|
13252
|
+
const stdoutWrite = options.stdoutWrite ?? ((chunk) => process.stdout.write(chunk));
|
|
13253
|
+
const parsed2 = parseRuntimeIdentityArgv(argv);
|
|
13254
|
+
if (parsed2.error) {
|
|
13255
|
+
writeJson({ status: "error", error: parsed2.error }, stdoutWrite);
|
|
13256
|
+
return 1;
|
|
13257
|
+
}
|
|
13258
|
+
if (!parsed2.workspaceRoot) {
|
|
13259
|
+
writeJson({ status: "error", error: RUNTIME_IDENTITY_USAGE }, stdoutWrite);
|
|
13260
|
+
return 1;
|
|
13261
|
+
}
|
|
13262
|
+
const workspaceRootOrError = await resolveWorkspaceRoot2(parsed2.workspaceRoot);
|
|
13263
|
+
if ("error" in workspaceRootOrError) {
|
|
13264
|
+
writeJson(
|
|
13265
|
+
{
|
|
13266
|
+
status: "error",
|
|
13267
|
+
error: workspaceRootOrError.error,
|
|
13268
|
+
workspaceRoot: workspaceRootOrError.workspaceRoot
|
|
13269
|
+
},
|
|
13270
|
+
stdoutWrite
|
|
13271
|
+
);
|
|
13272
|
+
return 1;
|
|
13273
|
+
}
|
|
13274
|
+
const lookupIdentity = options.lookupIdentity ?? lookupLocalBindingIdentity;
|
|
13275
|
+
try {
|
|
13276
|
+
const result = await lookupIdentity(workspaceRootOrError.workspaceRoot, {
|
|
13277
|
+
homeDir: options.homeDir,
|
|
13278
|
+
identityDeps: options.identityDeps
|
|
13279
|
+
});
|
|
13280
|
+
writeJson(result, stdoutWrite);
|
|
13281
|
+
return 0;
|
|
13282
|
+
} catch {
|
|
13283
|
+
writeJson(
|
|
13284
|
+
{
|
|
13285
|
+
status: "error",
|
|
13286
|
+
error: "Runtime identity failed",
|
|
13287
|
+
workspaceRoot: workspaceRootOrError.workspaceRoot
|
|
13288
|
+
},
|
|
13289
|
+
stdoutWrite
|
|
13290
|
+
);
|
|
13291
|
+
return 1;
|
|
13292
|
+
}
|
|
13293
|
+
}
|
|
13294
|
+
var fs39, path46, RUNTIME_IDENTITY_USAGE, SECRET_OUTPUT_RE2;
|
|
13295
|
+
var init_runtimeIdentityCommand = __esm({
|
|
13296
|
+
"src/runtimeIdentityCommand.ts"() {
|
|
13297
|
+
fs39 = __toESM(require("fs/promises"), 1);
|
|
13298
|
+
path46 = __toESM(require("path"), 1);
|
|
13299
|
+
init_resolveLocalBinding();
|
|
13300
|
+
RUNTIME_IDENTITY_USAGE = "Usage: memoraone-mcp runtime-identity --workspace-root <path>";
|
|
13301
|
+
SECRET_OUTPUT_RE2 = /\b(mcc_|mpv_|mia_|mir_|polling_verifier|pollingVerifier|access_token|refresh_token|api[_-]?key|projectId|project_id)\b/i;
|
|
13302
|
+
}
|
|
13303
|
+
});
|
|
13304
|
+
|
|
12621
13305
|
// src/bridgeClientRoots.ts
|
|
12622
13306
|
function isInitializeDebugEnabled(env2 = process.env) {
|
|
12623
13307
|
return TRUTHY.has(String(env2.MEMORAONE_DEBUG_INIT ?? "").trim().toLowerCase());
|
|
@@ -12758,8 +13442,8 @@ var init_bridgeClientRoots = __esm({
|
|
|
12758
13442
|
if (this.closed) {
|
|
12759
13443
|
return null;
|
|
12760
13444
|
}
|
|
12761
|
-
return new Promise((
|
|
12762
|
-
this.waiters.push(
|
|
13445
|
+
return new Promise((resolve43) => {
|
|
13446
|
+
this.waiters.push(resolve43);
|
|
12763
13447
|
});
|
|
12764
13448
|
}
|
|
12765
13449
|
/** Re-queue lines read during an intermediate protocol step (e.g. roots/list) for the main bridge loop. */
|
|
@@ -12986,9 +13670,9 @@ function extractJsonRpcId(line) {
|
|
|
12986
13670
|
}
|
|
12987
13671
|
}
|
|
12988
13672
|
function connectWithRetry(socketPath, log, maxRetries, retryDelayMs, connect2) {
|
|
12989
|
-
return new Promise((
|
|
13673
|
+
return new Promise((resolve43, reject) => {
|
|
12990
13674
|
const tryConnect = (attempt) => {
|
|
12991
|
-
connect2(socketPath).then(
|
|
13675
|
+
connect2(socketPath).then(resolve43).catch((err) => {
|
|
12992
13676
|
if (attempt >= maxRetries) {
|
|
12993
13677
|
reject(err);
|
|
12994
13678
|
return;
|
|
@@ -13184,8 +13868,8 @@ var init_bridgeProxy = __esm({
|
|
|
13184
13868
|
this.maxRetries = options.maxRetries ?? 5;
|
|
13185
13869
|
this.retryDelayMs = options.retryDelayMs ?? 200;
|
|
13186
13870
|
this.lineReader = options.lineReader ?? null;
|
|
13187
|
-
this.connectImpl = options.connect ?? ((socketPath) => new Promise((
|
|
13188
|
-
const socket = net.connect(socketPath, () =>
|
|
13871
|
+
this.connectImpl = options.connect ?? ((socketPath) => new Promise((resolve43, reject) => {
|
|
13872
|
+
const socket = net.connect(socketPath, () => resolve43(socket));
|
|
13189
13873
|
socket.on("error", reject);
|
|
13190
13874
|
}));
|
|
13191
13875
|
this.spawnDaemonImpl = options.spawnDaemon ?? (async (binding) => {
|
|
@@ -13317,7 +14001,7 @@ var init_bridgeProxy = __esm({
|
|
|
13317
14001
|
}
|
|
13318
14002
|
waitForDaemonJsonRpcResponse(id, timeoutMs = 3e4) {
|
|
13319
14003
|
const key = this.waiterKey(id);
|
|
13320
|
-
return new Promise((
|
|
14004
|
+
return new Promise((resolve43, reject) => {
|
|
13321
14005
|
const timer = setTimeout(() => {
|
|
13322
14006
|
this.pendingDaemonResponseWaiters.delete(key);
|
|
13323
14007
|
reject(
|
|
@@ -13326,7 +14010,7 @@ var init_bridgeProxy = __esm({
|
|
|
13326
14010
|
)
|
|
13327
14011
|
);
|
|
13328
14012
|
}, timeoutMs);
|
|
13329
|
-
this.pendingDaemonResponseWaiters.set(key, { resolve:
|
|
14013
|
+
this.pendingDaemonResponseWaiters.set(key, { resolve: resolve43, reject, timer });
|
|
13330
14014
|
});
|
|
13331
14015
|
}
|
|
13332
14016
|
notifyDaemonResponseWaiters(line) {
|
|
@@ -13513,6 +14197,8 @@ if (args.includes("--help") || args.includes("-h")) {
|
|
|
13513
14197
|
console.log(
|
|
13514
14198
|
`Usage: memoraone-mcp [--version] [--help]
|
|
13515
14199
|
memoraone-mcp connect <code> [--api-url <url>] [--verbose]
|
|
14200
|
+
memoraone-mcp plugin-pair --workspace-root <path> --source <cursor|jetbrains> [--api-url <url>]
|
|
14201
|
+
memoraone-mcp runtime-identity --workspace-root <path>
|
|
13516
14202
|
memoraone-mcp [--daemon --binding-id <mrb_\u2026> [--ide ${IDE_TYPE_CLI_CHOICES}]]
|
|
13517
14203
|
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]
|
|
13518
14204
|
Cursor API environment (with --cursor or --all): --local (node + built cli.cjs + local API) | --staging (npx + staging API)
|
|
@@ -13535,6 +14221,28 @@ if (args[0] === "cleanup") {
|
|
|
13535
14221
|
`);
|
|
13536
14222
|
process.exit(1);
|
|
13537
14223
|
});
|
|
14224
|
+
} else if (args[0] === "plugin-pair") {
|
|
14225
|
+
Promise.resolve().then(() => (init_pluginPairCommand(), pluginPairCommand_exports)).then(
|
|
14226
|
+
({ cliPluginPair: cliPluginPair2 }) => cliPluginPair2(args.slice(1), { installSignalHandlers: true })
|
|
14227
|
+
).then((code) => process.exit(code)).catch((err) => {
|
|
14228
|
+
process.stdout.write(
|
|
14229
|
+
`${JSON.stringify({ event: "fatal", message: "Plugin pairing failed" })}
|
|
14230
|
+
`
|
|
14231
|
+
);
|
|
14232
|
+
process.stderr.write(`[memoraone-mcp] plugin-pair fatal: ${String(err)}
|
|
14233
|
+
`);
|
|
14234
|
+
process.exit(1);
|
|
14235
|
+
});
|
|
14236
|
+
} else if (args[0] === "runtime-identity") {
|
|
14237
|
+
Promise.resolve().then(() => (init_runtimeIdentityCommand(), runtimeIdentityCommand_exports)).then(({ cliRuntimeIdentity: cliRuntimeIdentity2 }) => cliRuntimeIdentity2(args.slice(1))).then((code) => process.exit(code)).catch((err) => {
|
|
14238
|
+
process.stdout.write(
|
|
14239
|
+
`${JSON.stringify({ status: "error", error: "Runtime identity failed" })}
|
|
14240
|
+
`
|
|
14241
|
+
);
|
|
14242
|
+
process.stderr.write(`[memoraone-mcp] runtime-identity fatal: ${String(err)}
|
|
14243
|
+
`);
|
|
14244
|
+
process.exit(1);
|
|
14245
|
+
});
|
|
13538
14246
|
} else if (args[0] === "setup-ide-files") {
|
|
13539
14247
|
Promise.resolve().then(() => (init_setupIdeFiles(), setupIdeFiles_exports)).then(({ cliSetupIdeFiles: cliSetupIdeFiles2 }) => cliSetupIdeFiles2(args.slice(1))).then((code) => process.exit(code)).catch((err) => {
|
|
13540
14248
|
process.stderr.write(`[memoraone-mcp] setup-ide-files fatal: ${String(err)}
|
package/dist/daemon.cjs
CHANGED
|
@@ -3437,7 +3437,7 @@ async function main(opts = {}) {
|
|
|
3437
3437
|
registeredToolNames.push("memora_get_personal_context");
|
|
3438
3438
|
server.tool(
|
|
3439
3439
|
"memora_status",
|
|
3440
|
-
"Return non-secret project binding metadata for this MCP session",
|
|
3440
|
+
"Return non-secret project binding metadata for this MCP session. Use this for repository/project connection identity: which repository or project this session is bound to. Prefer this over guessing from local files, git remotes, or IDE context.",
|
|
3441
3441
|
bindingStatusShape,
|
|
3442
3442
|
async () => runWithSessionContext(sessionContext, async () => {
|
|
3443
3443
|
if (!runtime.authoritativeBinding) return notInitializedResult;
|
|
@@ -3455,7 +3455,7 @@ async function main(opts = {}) {
|
|
|
3455
3455
|
runtime,
|
|
3456
3456
|
sessionContext,
|
|
3457
3457
|
"memora_ask_with_memory",
|
|
3458
|
-
|
|
3458
|
+
'Answer using saved workspace knowledge and timeline. Use this before answering previous questions/answers, conversation/history, decisions, facts, preferences, project context, or questions like "what did I/you say, ask, decide, or do before?". Do not silently substitute IDE session history, local files, git history, or model memory when MemoraOne can provide relevant context. Skip only when the question is clearly unrelated. In user-visible replies, state facts directly; do not attribute answers to tools, storage, retrieval, or memory-style framing unless the user asks how you know.',
|
|
3459
3459
|
askWithMemoryShape,
|
|
3460
3460
|
async (args) => {
|
|
3461
3461
|
const result = await handleAskWithMemory(runtime.client, args);
|
package/dist/index.cjs
CHANGED
|
@@ -3363,7 +3363,7 @@ async function main(opts = {}) {
|
|
|
3363
3363
|
registeredToolNames.push("memora_get_personal_context");
|
|
3364
3364
|
server.tool(
|
|
3365
3365
|
"memora_status",
|
|
3366
|
-
"Return non-secret project binding metadata for this MCP session",
|
|
3366
|
+
"Return non-secret project binding metadata for this MCP session. Use this for repository/project connection identity: which repository or project this session is bound to. Prefer this over guessing from local files, git remotes, or IDE context.",
|
|
3367
3367
|
bindingStatusShape,
|
|
3368
3368
|
async () => runWithSessionContext(sessionContext, async () => {
|
|
3369
3369
|
if (!runtime.authoritativeBinding) return notInitializedResult;
|
|
@@ -3381,7 +3381,7 @@ async function main(opts = {}) {
|
|
|
3381
3381
|
runtime,
|
|
3382
3382
|
sessionContext,
|
|
3383
3383
|
"memora_ask_with_memory",
|
|
3384
|
-
|
|
3384
|
+
'Answer using saved workspace knowledge and timeline. Use this before answering previous questions/answers, conversation/history, decisions, facts, preferences, project context, or questions like "what did I/you say, ask, decide, or do before?". Do not silently substitute IDE session history, local files, git history, or model memory when MemoraOne can provide relevant context. Skip only when the question is clearly unrelated. In user-visible replies, state facts directly; do not attribute answers to tools, storage, retrieval, or memory-style framing unless the user asks how you know.',
|
|
3385
3385
|
askWithMemoryShape,
|
|
3386
3386
|
async (args) => {
|
|
3387
3387
|
const result = await handleAskWithMemory(runtime.client, args);
|
package/dist/pluginPairing.cjs
CHANGED
|
@@ -7526,6 +7526,14 @@ async function findRepoRoot(startDir) {
|
|
|
7526
7526
|
function stripLeadingLineComments18(text) {
|
|
7527
7527
|
return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
|
|
7528
7528
|
}
|
|
7529
|
+
function memoraUsagePolicy() {
|
|
7530
|
+
return `- For every user question, check whether MemoraOne can provide relevant context before answering. If it can, use MemoraOne first.
|
|
7531
|
+
- Do not silently substitute IDE session history, local files, git history, or model memory when MemoraOne can provide relevant context.
|
|
7532
|
+
- Use **\`memora_status\`** for repository/project connection identity.
|
|
7533
|
+
- Use **\`memora_ask_with_memory\`** for previous questions/answers, conversation/history, decisions, facts, preferences, project context, and questions like "what did I/you say, ask, decide, or do before?"
|
|
7534
|
+
- Skip MemoraOne only when the question is clearly unrelated.
|
|
7535
|
+
- After the interaction: if anything meaningful happened\u2014question, answer, decision, discovery, action, correction, failure, or change\u2014record it back to MemoraOne so the next agent knows about it, especially if MemoraOne was not used before answering. Use **\`memora_post_event\`** and **\`memora_log_change_summary\`** according to their intended semantics; do not write trivial noise.`;
|
|
7536
|
+
}
|
|
7529
7537
|
function cursorRuleBody() {
|
|
7530
7538
|
return `${MANAGED_MARKER}
|
|
7531
7539
|
|
|
@@ -7535,7 +7543,7 @@ This repository uses **MemoraOne** via the MCP server named **memoraone** (repo-
|
|
|
7535
7543
|
|
|
7536
7544
|
### Tools
|
|
7537
7545
|
|
|
7538
|
-
|
|
7546
|
+
${memoraUsagePolicy()}
|
|
7539
7547
|
- Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. \`cursor\`), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
|
|
7540
7548
|
- Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
|
|
7541
7549
|
|
|
@@ -7553,7 +7561,7 @@ This repo is set up to use **MemoraOne** through MCP where your editor exposes i
|
|
|
7553
7561
|
|
|
7554
7562
|
## Behavior
|
|
7555
7563
|
|
|
7556
|
-
|
|
7564
|
+
${memoraUsagePolicy()}
|
|
7557
7565
|
- Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. your agent name), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
|
|
7558
7566
|
- Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
|
|
7559
7567
|
|
|
@@ -7927,6 +7935,7 @@ async function runSetupIdeFiles(o) {
|
|
|
7927
7935
|
}
|
|
7928
7936
|
const cursorContent = `---
|
|
7929
7937
|
description: MemoraOne MCP \u2014 IDE agent instructions
|
|
7938
|
+
alwaysApply: true
|
|
7930
7939
|
---
|
|
7931
7940
|
|
|
7932
7941
|
` + cursorRuleBody();
|
|
@@ -9972,8 +9981,38 @@ var PluginRepositoryPairingSession = class {
|
|
|
9972
9981
|
apiUrl: this.apiUrl,
|
|
9973
9982
|
ideType: this.source,
|
|
9974
9983
|
fetchImpl: this.connectOptions.fetchImpl ?? this.fetchImpl,
|
|
9975
|
-
//
|
|
9976
|
-
configureIdes:
|
|
9984
|
+
// Plugin pairing configures only the IDE that initiated the pairing.
|
|
9985
|
+
configureIdes: true,
|
|
9986
|
+
setupIdeOptions: {
|
|
9987
|
+
...this.connectOptions.setupIdeOptions,
|
|
9988
|
+
targets: {
|
|
9989
|
+
cursor: this.source === "cursor",
|
|
9990
|
+
vscode: false,
|
|
9991
|
+
jetbrains: this.source === "jetbrains",
|
|
9992
|
+
claudeCode: false,
|
|
9993
|
+
windsurf: false,
|
|
9994
|
+
opencode: false,
|
|
9995
|
+
codex: false,
|
|
9996
|
+
kiro: false,
|
|
9997
|
+
kiroCli: false,
|
|
9998
|
+
rooCode: false,
|
|
9999
|
+
cline: false,
|
|
10000
|
+
clineCli: false,
|
|
10001
|
+
claudeDesktop: false,
|
|
10002
|
+
zed: false,
|
|
10003
|
+
visualStudio: false,
|
|
10004
|
+
continue: false,
|
|
10005
|
+
copilotCli: false,
|
|
10006
|
+
auggie: false,
|
|
10007
|
+
geminiCli: false,
|
|
10008
|
+
antigravity: false,
|
|
10009
|
+
goose: false,
|
|
10010
|
+
junie: false,
|
|
10011
|
+
xcode: false,
|
|
10012
|
+
copilotJetBrains: false,
|
|
10013
|
+
copilotVisualStudio: false
|
|
10014
|
+
}
|
|
10015
|
+
}
|
|
9977
10016
|
});
|
|
9978
10017
|
if (connected.exitCode !== 0) {
|
|
9979
10018
|
this.finish("backend_failure");
|