context-vault 2.8.4 → 2.8.6
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/bin/cli.js +165 -282
- package/node_modules/@context-vault/core/package.json +1 -1
- package/node_modules/@context-vault/core/src/retrieve/index.js +2 -2
- package/node_modules/@context-vault/core/src/server/tools/save-context.js +10 -0
- package/package.json +2 -3
- package/scripts/postinstall.js +10 -1
- package/scripts/prepack.js +0 -16
- package/scripts/local-server.js +0 -799
package/bin/cli.js
CHANGED
|
@@ -21,9 +21,8 @@ import {
|
|
|
21
21
|
} from "node:fs";
|
|
22
22
|
import { join, resolve, dirname } from "node:path";
|
|
23
23
|
import { homedir, platform } from "node:os";
|
|
24
|
-
import { execSync, execFile
|
|
24
|
+
import { execSync, execFile } from "node:child_process";
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
|
-
import { createServer as createNetServer } from "node:net";
|
|
27
26
|
|
|
28
27
|
const __filename = fileURLToPath(import.meta.url);
|
|
29
28
|
const __dirname = dirname(__filename);
|
|
@@ -222,8 +221,8 @@ ${bold("Usage:")}
|
|
|
222
221
|
${bold("Commands:")}
|
|
223
222
|
${cyan("setup")} Interactive MCP server installer
|
|
224
223
|
${cyan("connect")} --key cv_... Connect AI tools to hosted vault
|
|
224
|
+
${cyan("switch")} local|hosted Switch between local and hosted MCP modes
|
|
225
225
|
${cyan("serve")} Start the MCP server (used by AI clients)
|
|
226
|
-
${cyan("ui")} [--port 3141] Launch web dashboard
|
|
227
226
|
${cyan("reindex")} Rebuild search index from knowledge files
|
|
228
227
|
${cyan("status")} Show vault diagnostics
|
|
229
228
|
${cyan("update")} Check for and install updates
|
|
@@ -231,8 +230,6 @@ ${bold("Commands:")}
|
|
|
231
230
|
${cyan("import")} <path> Import entries from file or directory
|
|
232
231
|
${cyan("export")} Export vault to JSON or CSV
|
|
233
232
|
${cyan("ingest")} <url> Fetch URL and save as vault entry
|
|
234
|
-
${cyan("link")} --key cv_... Link local vault to hosted account
|
|
235
|
-
${cyan("sync")} Sync entries between local and hosted
|
|
236
233
|
${cyan("migrate")} Migrate vault between local and hosted
|
|
237
234
|
|
|
238
235
|
${bold("Options:")}
|
|
@@ -451,6 +448,7 @@ async function runSetup() {
|
|
|
451
448
|
vaultConfig.dataDir = dataDir;
|
|
452
449
|
vaultConfig.dbPath = join(dataDir, "vault.db");
|
|
453
450
|
vaultConfig.devDir = join(HOME, "dev");
|
|
451
|
+
vaultConfig.mode = "local";
|
|
454
452
|
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + "\n");
|
|
455
453
|
console.log(`\n ${green("+")} Wrote ${configPath}`);
|
|
456
454
|
|
|
@@ -557,17 +555,6 @@ async function runSetup() {
|
|
|
557
555
|
);
|
|
558
556
|
}
|
|
559
557
|
|
|
560
|
-
// Offer to launch UI
|
|
561
|
-
console.log();
|
|
562
|
-
if (!isNonInteractive) {
|
|
563
|
-
const launchUi = await prompt(` Launch web dashboard? (y/N):`, "N");
|
|
564
|
-
if (launchUi.toLowerCase() === "y") {
|
|
565
|
-
console.log();
|
|
566
|
-
runUi();
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
558
|
// Health check
|
|
572
559
|
console.log(`\n ${dim("[5/5]")}${bold(" Health check...")}\n`);
|
|
573
560
|
const okResults = results.filter((r) => r.ok);
|
|
@@ -606,7 +593,6 @@ async function runSetup() {
|
|
|
606
593
|
``,
|
|
607
594
|
` ${bold("CLI Commands:")}`,
|
|
608
595
|
` context-vault status Show vault health`,
|
|
609
|
-
` context-vault ui Launch web dashboard`,
|
|
610
596
|
` context-vault update Check for updates`,
|
|
611
597
|
];
|
|
612
598
|
const innerWidth = Math.max(...boxLines.map((l) => l.length)) + 2;
|
|
@@ -635,10 +621,11 @@ async function configureClaude(tool, vaultDir) {
|
|
|
635
621
|
|
|
636
622
|
try {
|
|
637
623
|
if (isInstalledPackage()) {
|
|
638
|
-
const
|
|
624
|
+
const launcherPath = join(HOME, ".context-mcp", "server.mjs");
|
|
625
|
+
const cmdArgs = [`"${launcherPath}"`];
|
|
639
626
|
if (vaultDir) cmdArgs.push("--vault-dir", `"${vaultDir}"`);
|
|
640
627
|
execSync(
|
|
641
|
-
`claude mcp add -s user context-vault --
|
|
628
|
+
`claude mcp add -s user context-vault -- node ${cmdArgs.join(" ")}`,
|
|
642
629
|
{ stdio: "pipe", env },
|
|
643
630
|
);
|
|
644
631
|
} else {
|
|
@@ -667,12 +654,12 @@ async function configureCodex(tool, vaultDir) {
|
|
|
667
654
|
|
|
668
655
|
try {
|
|
669
656
|
if (isInstalledPackage()) {
|
|
670
|
-
const
|
|
657
|
+
const launcherPath = join(HOME, ".context-mcp", "server.mjs");
|
|
658
|
+
const cmdArgs = [`"${launcherPath}"`];
|
|
671
659
|
if (vaultDir) cmdArgs.push("--vault-dir", `"${vaultDir}"`);
|
|
672
|
-
execSync(
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
);
|
|
660
|
+
execSync(`codex mcp add context-vault -- node ${cmdArgs.join(" ")}`, {
|
|
661
|
+
stdio: "pipe",
|
|
662
|
+
});
|
|
676
663
|
} else {
|
|
677
664
|
const cmdArgs = [`"${SERVER_PATH}"`];
|
|
678
665
|
if (vaultDir) cmdArgs.push("--vault-dir", `"${vaultDir}"`);
|
|
@@ -715,11 +702,12 @@ function configureJsonTool(tool, vaultDir) {
|
|
|
715
702
|
delete config[tool.configKey]["context-mcp"];
|
|
716
703
|
|
|
717
704
|
if (isInstalledPackage()) {
|
|
718
|
-
const
|
|
705
|
+
const launcherPath = join(HOME, ".context-mcp", "server.mjs");
|
|
706
|
+
const serverArgs = [];
|
|
719
707
|
if (vaultDir) serverArgs.push("--vault-dir", vaultDir);
|
|
720
708
|
config[tool.configKey]["context-vault"] = {
|
|
721
|
-
command: "
|
|
722
|
-
args: serverArgs,
|
|
709
|
+
command: "node",
|
|
710
|
+
args: [launcherPath, ...serverArgs],
|
|
723
711
|
};
|
|
724
712
|
} else {
|
|
725
713
|
const serverArgs = [SERVER_PATH];
|
|
@@ -948,6 +936,19 @@ async function runConnect() {
|
|
|
948
936
|
}
|
|
949
937
|
}
|
|
950
938
|
|
|
939
|
+
// Persist mode in config
|
|
940
|
+
const modeConfigPath = join(HOME, ".context-mcp", "config.json");
|
|
941
|
+
let modeConfig = {};
|
|
942
|
+
if (existsSync(modeConfigPath)) {
|
|
943
|
+
try {
|
|
944
|
+
modeConfig = JSON.parse(readFileSync(modeConfigPath, "utf-8"));
|
|
945
|
+
} catch {}
|
|
946
|
+
}
|
|
947
|
+
modeConfig.mode = "hosted";
|
|
948
|
+
modeConfig.hostedUrl = hostedUrl;
|
|
949
|
+
mkdirSync(join(HOME, ".context-mcp"), { recursive: true });
|
|
950
|
+
writeFileSync(modeConfigPath, JSON.stringify(modeConfig, null, 2) + "\n");
|
|
951
|
+
|
|
951
952
|
console.log();
|
|
952
953
|
console.log(
|
|
953
954
|
green(" ✓ Connected! Your AI tools can now access your hosted vault."),
|
|
@@ -1033,85 +1034,133 @@ function configureJsonToolHosted(tool, apiKey, hostedUrl) {
|
|
|
1033
1034
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1034
1035
|
}
|
|
1035
1036
|
|
|
1036
|
-
function
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1037
|
+
async function runSwitch() {
|
|
1038
|
+
const target = args[1];
|
|
1039
|
+
if (target !== "local" && target !== "hosted") {
|
|
1040
|
+
console.log(`\n ${bold("context-vault switch")} <local|hosted>\n`);
|
|
1041
|
+
console.log(` Switch between local and hosted MCP modes.\n`);
|
|
1042
|
+
console.log(
|
|
1043
|
+
` ${cyan("switch local")} Use local vault (SQLite + files on this device)`,
|
|
1044
|
+
);
|
|
1045
|
+
console.log(
|
|
1046
|
+
` ${cyan("switch hosted")} Use hosted vault (requires API key)\n`,
|
|
1047
|
+
);
|
|
1048
|
+
console.log(` Options:`);
|
|
1049
|
+
console.log(` --key <key> API key for hosted mode (cv_...)`);
|
|
1050
|
+
console.log(
|
|
1051
|
+
` --url <url> Hosted server URL (default: https://api.context-vault.com)\n`,
|
|
1052
|
+
);
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1045
1055
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
PLATFORM === "darwin"
|
|
1051
|
-
? "open"
|
|
1052
|
-
: PLATFORM === "win32"
|
|
1053
|
-
? "start"
|
|
1054
|
-
: "xdg-open";
|
|
1056
|
+
const dataDir = join(HOME, ".context-mcp");
|
|
1057
|
+
const configPath = join(dataDir, "config.json");
|
|
1058
|
+
let vaultConfig = {};
|
|
1059
|
+
if (existsSync(configPath)) {
|
|
1055
1060
|
try {
|
|
1056
|
-
|
|
1061
|
+
vaultConfig = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1057
1062
|
} catch {}
|
|
1058
|
-
return;
|
|
1059
1063
|
}
|
|
1060
1064
|
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
if (
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1065
|
+
const { detected } = await detectAllTools();
|
|
1066
|
+
|
|
1067
|
+
if (target === "local") {
|
|
1068
|
+
const launcherPath = join(dataDir, "server.mjs");
|
|
1069
|
+
if (!existsSync(launcherPath)) {
|
|
1070
|
+
const serverAbs = resolve(ROOT, "src", "server", "index.js");
|
|
1071
|
+
mkdirSync(dataDir, { recursive: true });
|
|
1072
|
+
writeFileSync(launcherPath, `import "${serverAbs}";\n`);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
vaultConfig.mode = "local";
|
|
1076
|
+
mkdirSync(dataDir, { recursive: true });
|
|
1077
|
+
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + "\n");
|
|
1067
1078
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1079
|
+
console.log();
|
|
1080
|
+
console.log(` ${bold("◇ context-vault")} ${dim("switch → local")}`);
|
|
1081
|
+
console.log();
|
|
1082
|
+
|
|
1083
|
+
const defaultVDir = join(HOME, "vault");
|
|
1084
|
+
const customVaultDir =
|
|
1085
|
+
vaultConfig.vaultDir &&
|
|
1086
|
+
resolve(vaultConfig.vaultDir) !== resolve(defaultVDir)
|
|
1087
|
+
? vaultConfig.vaultDir
|
|
1088
|
+
: null;
|
|
1089
|
+
|
|
1090
|
+
for (const tool of detected) {
|
|
1091
|
+
try {
|
|
1092
|
+
if (tool.configType === "cli" && tool.id === "codex") {
|
|
1093
|
+
await configureCodex(tool, customVaultDir);
|
|
1094
|
+
} else if (tool.configType === "cli") {
|
|
1095
|
+
await configureClaude(tool, customVaultDir);
|
|
1096
|
+
} else {
|
|
1097
|
+
configureJsonTool(tool, customVaultDir);
|
|
1098
|
+
}
|
|
1099
|
+
console.log(` ${green("+")} ${tool.name} — switched to local`);
|
|
1100
|
+
} catch (e) {
|
|
1101
|
+
console.log(` ${red("x")} ${tool.name} — ${e.message}`);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
console.log();
|
|
1105
|
+
console.log(green(" ✓ Switched to local mode."));
|
|
1106
|
+
console.log(dim(` Server: node ${launcherPath}`));
|
|
1107
|
+
console.log();
|
|
1108
|
+
} else {
|
|
1109
|
+
const hostedUrl =
|
|
1110
|
+
getFlag("--url") ||
|
|
1111
|
+
vaultConfig.hostedUrl ||
|
|
1112
|
+
"https://api.context-vault.com";
|
|
1113
|
+
const apiKey = getFlag("--key") || vaultConfig.apiKey;
|
|
1114
|
+
|
|
1115
|
+
if (!apiKey) {
|
|
1116
|
+
console.error(
|
|
1117
|
+
red(` --key <api_key> required. Get yours at ${hostedUrl}/dashboard`),
|
|
1118
|
+
);
|
|
1074
1119
|
process.exit(1);
|
|
1075
1120
|
}
|
|
1076
|
-
// Other error — let the fork handle it
|
|
1077
|
-
probe.close();
|
|
1078
|
-
launchServer(port, localServer);
|
|
1079
|
-
});
|
|
1080
|
-
probe.listen(port, () => {
|
|
1081
|
-
probe.close(() => {
|
|
1082
|
-
launchServer(port, localServer);
|
|
1083
|
-
});
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
1121
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1122
|
+
console.log();
|
|
1123
|
+
console.log(` ${bold("◇ context-vault")} ${dim("switch → hosted")}`);
|
|
1124
|
+
console.log();
|
|
1125
|
+
console.log(dim(" Verifying API key..."));
|
|
1090
1126
|
|
|
1091
|
-
// Open browser after a short delay — prefer hosted UI if reachable
|
|
1092
|
-
setTimeout(async () => {
|
|
1093
1127
|
try {
|
|
1094
|
-
|
|
1128
|
+
const response = await fetch(`${hostedUrl}/api/me`, {
|
|
1129
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
1130
|
+
});
|
|
1131
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1132
|
+
const user = await response.json();
|
|
1133
|
+
console.log(` ${green("+")} Verified — ${user.email}\n`);
|
|
1134
|
+
} catch (e) {
|
|
1135
|
+
console.error(red(` Verification failed: ${e.message}`));
|
|
1136
|
+
process.exit(1);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
vaultConfig.mode = "hosted";
|
|
1140
|
+
vaultConfig.hostedUrl = hostedUrl;
|
|
1141
|
+
vaultConfig.apiKey = apiKey;
|
|
1142
|
+
mkdirSync(dataDir, { recursive: true });
|
|
1143
|
+
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + "\n");
|
|
1144
|
+
|
|
1145
|
+
for (const tool of detected) {
|
|
1095
1146
|
try {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
} catch {
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
} catch {}
|
|
1114
|
-
}, 1500);
|
|
1147
|
+
if (tool.configType === "cli" && tool.id === "codex") {
|
|
1148
|
+
configureCodexHosted(apiKey, hostedUrl);
|
|
1149
|
+
} else if (tool.configType === "cli") {
|
|
1150
|
+
configureClaudeHosted(apiKey, hostedUrl);
|
|
1151
|
+
} else {
|
|
1152
|
+
configureJsonToolHosted(tool, apiKey, hostedUrl);
|
|
1153
|
+
}
|
|
1154
|
+
console.log(` ${green("+")} ${tool.name} — switched to hosted`);
|
|
1155
|
+
} catch (e) {
|
|
1156
|
+
console.log(` ${red("x")} ${tool.name} — ${e.message}`);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
console.log();
|
|
1160
|
+
console.log(green(" ✓ Switched to hosted mode."));
|
|
1161
|
+
console.log(dim(` Endpoint: ${hostedUrl}/mcp`));
|
|
1162
|
+
console.log();
|
|
1163
|
+
}
|
|
1115
1164
|
}
|
|
1116
1165
|
|
|
1117
1166
|
async function runReindex() {
|
|
@@ -1157,6 +1206,24 @@ async function runStatus() {
|
|
|
1157
1206
|
const { gatherVaultStatus } = await import("@context-vault/core/core/status");
|
|
1158
1207
|
|
|
1159
1208
|
const config = resolveConfig();
|
|
1209
|
+
|
|
1210
|
+
let mode = "local";
|
|
1211
|
+
let modeDetail = "";
|
|
1212
|
+
const rawConfigPath = join(HOME, ".context-mcp", "config.json");
|
|
1213
|
+
if (existsSync(rawConfigPath)) {
|
|
1214
|
+
try {
|
|
1215
|
+
const raw = JSON.parse(readFileSync(rawConfigPath, "utf-8"));
|
|
1216
|
+
mode = raw.mode || "local";
|
|
1217
|
+
if (mode === "hosted" && raw.hostedUrl) {
|
|
1218
|
+
const email = raw.email ? ` · ${raw.email}` : "";
|
|
1219
|
+
modeDetail = ` (${raw.hostedUrl}${email})`;
|
|
1220
|
+
} else {
|
|
1221
|
+
const launcherPath = join(HOME, ".context-mcp", "server.mjs");
|
|
1222
|
+
modeDetail = ` (node ${launcherPath})`;
|
|
1223
|
+
}
|
|
1224
|
+
} catch {}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1160
1227
|
const db = await initDatabase(config.dbPath);
|
|
1161
1228
|
|
|
1162
1229
|
const status = gatherVaultStatus({ db, config });
|
|
@@ -1166,6 +1233,7 @@ async function runStatus() {
|
|
|
1166
1233
|
console.log();
|
|
1167
1234
|
console.log(` ${bold("◇ context-vault")} ${dim(`v${VERSION}`)}`);
|
|
1168
1235
|
console.log();
|
|
1236
|
+
console.log(` Mode: ${mode}${dim(modeDetail)}`);
|
|
1169
1237
|
console.log(
|
|
1170
1238
|
` Vault: ${config.vaultDir} ${dim(`(${config.vaultDirExists ? status.fileCount + " files" : "missing"})`)}`,
|
|
1171
1239
|
);
|
|
@@ -1715,185 +1783,6 @@ async function runIngest() {
|
|
|
1715
1783
|
console.log();
|
|
1716
1784
|
}
|
|
1717
1785
|
|
|
1718
|
-
async function runLink() {
|
|
1719
|
-
const apiKey = getFlag("--key");
|
|
1720
|
-
const hostedUrl = getFlag("--url") || "https://api.context-vault.com";
|
|
1721
|
-
|
|
1722
|
-
if (!apiKey) {
|
|
1723
|
-
console.log(`\n ${bold("context-vault link")} --key cv_...\n`);
|
|
1724
|
-
console.log(` Link your local vault to a hosted Context Vault account.\n`);
|
|
1725
|
-
console.log(` Options:`);
|
|
1726
|
-
console.log(` --key <key> API key (required)`);
|
|
1727
|
-
console.log(
|
|
1728
|
-
` --url <url> Hosted server URL (default: https://api.context-vault.com)`,
|
|
1729
|
-
);
|
|
1730
|
-
console.log();
|
|
1731
|
-
return;
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
console.log(dim(" Verifying API key..."));
|
|
1735
|
-
|
|
1736
|
-
let user;
|
|
1737
|
-
try {
|
|
1738
|
-
const response = await fetch(`${hostedUrl}/api/me`, {
|
|
1739
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
1740
|
-
});
|
|
1741
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1742
|
-
user = await response.json();
|
|
1743
|
-
} catch (e) {
|
|
1744
|
-
console.error(red(` Verification failed: ${e.message}`));
|
|
1745
|
-
console.error(dim(` Check your API key and server URL.`));
|
|
1746
|
-
process.exit(1);
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
// Store credentials in config
|
|
1750
|
-
const dataDir = join(HOME, ".context-mcp");
|
|
1751
|
-
const configPath = join(dataDir, "config.json");
|
|
1752
|
-
let config = {};
|
|
1753
|
-
if (existsSync(configPath)) {
|
|
1754
|
-
try {
|
|
1755
|
-
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1756
|
-
} catch {}
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
config.hostedUrl = hostedUrl;
|
|
1760
|
-
config.apiKey = apiKey;
|
|
1761
|
-
config.userId = user.userId || user.id;
|
|
1762
|
-
config.email = user.email;
|
|
1763
|
-
config.linkedAt = new Date().toISOString();
|
|
1764
|
-
|
|
1765
|
-
mkdirSync(dataDir, { recursive: true });
|
|
1766
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
1767
|
-
|
|
1768
|
-
console.log();
|
|
1769
|
-
console.log(green(` ✓ Linked to ${user.email}`));
|
|
1770
|
-
console.log(dim(` Tier: ${user.tier || "free"}`));
|
|
1771
|
-
console.log(dim(` Server: ${hostedUrl}`));
|
|
1772
|
-
console.log(dim(` Config: ${configPath}`));
|
|
1773
|
-
console.log();
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
async function runSync() {
|
|
1777
|
-
const dryRun = flags.has("--dry-run");
|
|
1778
|
-
const pushOnly = flags.has("--push-only");
|
|
1779
|
-
const pullOnly = flags.has("--pull-only");
|
|
1780
|
-
|
|
1781
|
-
// Read credentials
|
|
1782
|
-
const dataDir = join(HOME, ".context-mcp");
|
|
1783
|
-
const configPath = join(dataDir, "config.json");
|
|
1784
|
-
let storedConfig = {};
|
|
1785
|
-
if (existsSync(configPath)) {
|
|
1786
|
-
try {
|
|
1787
|
-
storedConfig = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1788
|
-
} catch {}
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
const apiKey = getFlag("--key") || storedConfig.apiKey;
|
|
1792
|
-
const hostedUrl =
|
|
1793
|
-
getFlag("--url") ||
|
|
1794
|
-
storedConfig.hostedUrl ||
|
|
1795
|
-
"https://api.context-vault.com";
|
|
1796
|
-
|
|
1797
|
-
if (!apiKey) {
|
|
1798
|
-
console.error(
|
|
1799
|
-
red(" Not linked. Run `context-vault link --key cv_...` first."),
|
|
1800
|
-
);
|
|
1801
|
-
process.exit(1);
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
const { resolveConfig } = await import("@context-vault/core/core/config");
|
|
1805
|
-
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
1806
|
-
await import("@context-vault/core/index/db");
|
|
1807
|
-
const { embed } = await import("@context-vault/core/index/embed");
|
|
1808
|
-
const {
|
|
1809
|
-
buildLocalManifest,
|
|
1810
|
-
fetchRemoteManifest,
|
|
1811
|
-
computeSyncPlan,
|
|
1812
|
-
executeSync,
|
|
1813
|
-
} = await import("@context-vault/core/sync");
|
|
1814
|
-
|
|
1815
|
-
const config = resolveConfig();
|
|
1816
|
-
if (!config.vaultDirExists) {
|
|
1817
|
-
console.error(red(` Vault directory not found: ${config.vaultDir}`));
|
|
1818
|
-
process.exit(1);
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
const db = await initDatabase(config.dbPath);
|
|
1822
|
-
const stmts = prepareStatements(db);
|
|
1823
|
-
const ctx = {
|
|
1824
|
-
db,
|
|
1825
|
-
config,
|
|
1826
|
-
stmts,
|
|
1827
|
-
embed,
|
|
1828
|
-
insertVec: (r, e) => insertVec(stmts, r, e),
|
|
1829
|
-
deleteVec: (r) => deleteVec(stmts, r),
|
|
1830
|
-
};
|
|
1831
|
-
|
|
1832
|
-
console.log(dim(" Building manifests..."));
|
|
1833
|
-
const local = buildLocalManifest(ctx);
|
|
1834
|
-
|
|
1835
|
-
let remote;
|
|
1836
|
-
try {
|
|
1837
|
-
remote = await fetchRemoteManifest(hostedUrl, apiKey);
|
|
1838
|
-
} catch (e) {
|
|
1839
|
-
db.close();
|
|
1840
|
-
console.error(red(` Failed to fetch remote manifest: ${e.message}`));
|
|
1841
|
-
process.exit(1);
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
const plan = computeSyncPlan(local, remote);
|
|
1845
|
-
|
|
1846
|
-
// Apply push-only / pull-only filters
|
|
1847
|
-
if (pushOnly) plan.toPull = [];
|
|
1848
|
-
if (pullOnly) plan.toPush = [];
|
|
1849
|
-
|
|
1850
|
-
console.log();
|
|
1851
|
-
console.log(` ${bold("Sync Plan")}`);
|
|
1852
|
-
console.log(` Push (local → remote): ${plan.toPush.length} entries`);
|
|
1853
|
-
console.log(` Pull (remote → local): ${plan.toPull.length} entries`);
|
|
1854
|
-
console.log(` Up to date: ${plan.upToDate.length} entries`);
|
|
1855
|
-
|
|
1856
|
-
if (plan.toPush.length === 0 && plan.toPull.length === 0) {
|
|
1857
|
-
db.close();
|
|
1858
|
-
console.log(green("\n ✓ Everything in sync."));
|
|
1859
|
-
console.log();
|
|
1860
|
-
return;
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
if (dryRun) {
|
|
1864
|
-
db.close();
|
|
1865
|
-
console.log(dim("\n Dry run — no changes were made."));
|
|
1866
|
-
console.log();
|
|
1867
|
-
return;
|
|
1868
|
-
}
|
|
1869
|
-
|
|
1870
|
-
console.log(dim("\n Syncing..."));
|
|
1871
|
-
|
|
1872
|
-
const result = await executeSync(ctx, {
|
|
1873
|
-
hostedUrl,
|
|
1874
|
-
apiKey,
|
|
1875
|
-
plan,
|
|
1876
|
-
onProgress: (phase, current, total) => {
|
|
1877
|
-
process.stdout.write(
|
|
1878
|
-
`\r ${phase === "push" ? "Pushing" : "Pulling"}... ${current}/${total}`,
|
|
1879
|
-
);
|
|
1880
|
-
},
|
|
1881
|
-
});
|
|
1882
|
-
|
|
1883
|
-
db.close();
|
|
1884
|
-
|
|
1885
|
-
console.log(`\r ${green("✓")} Sync complete `);
|
|
1886
|
-
console.log(` ${green("↑")} ${result.pushed} pushed`);
|
|
1887
|
-
console.log(` ${green("↓")} ${result.pulled} pulled`);
|
|
1888
|
-
if (result.failed > 0) {
|
|
1889
|
-
console.log(` ${red("x")} ${result.failed} failed`);
|
|
1890
|
-
for (const err of result.errors.slice(0, 5)) {
|
|
1891
|
-
console.log(` ${dim(err)}`);
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
console.log();
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
1786
|
async function runServe() {
|
|
1898
1787
|
await import("../src/server/index.js");
|
|
1899
1788
|
}
|
|
@@ -1916,12 +1805,12 @@ async function main() {
|
|
|
1916
1805
|
case "connect":
|
|
1917
1806
|
await runConnect();
|
|
1918
1807
|
break;
|
|
1808
|
+
case "switch":
|
|
1809
|
+
await runSwitch();
|
|
1810
|
+
break;
|
|
1919
1811
|
case "serve":
|
|
1920
1812
|
await runServe();
|
|
1921
1813
|
break;
|
|
1922
|
-
case "ui":
|
|
1923
|
-
runUi();
|
|
1924
|
-
break;
|
|
1925
1814
|
case "import":
|
|
1926
1815
|
await runImport();
|
|
1927
1816
|
break;
|
|
@@ -1931,12 +1820,6 @@ async function main() {
|
|
|
1931
1820
|
case "ingest":
|
|
1932
1821
|
await runIngest();
|
|
1933
1822
|
break;
|
|
1934
|
-
case "link":
|
|
1935
|
-
await runLink();
|
|
1936
|
-
break;
|
|
1937
|
-
case "sync":
|
|
1938
|
-
await runSync();
|
|
1939
|
-
break;
|
|
1940
1823
|
case "reindex":
|
|
1941
1824
|
await runReindex();
|
|
1942
1825
|
break;
|
|
@@ -16,8 +16,8 @@ const VEC_WEIGHT = 0.6;
|
|
|
16
16
|
*/
|
|
17
17
|
export function buildFtsQuery(query) {
|
|
18
18
|
const words = query
|
|
19
|
-
.split(
|
|
20
|
-
.map((w) => w.replace(/[*"()
|
|
19
|
+
.split(/[\s-]+/)
|
|
20
|
+
.map((w) => w.replace(/[*"():^~{}]/g, ""))
|
|
21
21
|
.filter((w) => w.length > 0);
|
|
22
22
|
if (!words.length) return null;
|
|
23
23
|
return words.map((w) => `"${w}"`).join(" AND ");
|
|
@@ -26,6 +26,7 @@ function validateSaveInput({
|
|
|
26
26
|
meta,
|
|
27
27
|
source,
|
|
28
28
|
identity_key,
|
|
29
|
+
expires_at,
|
|
29
30
|
}) {
|
|
30
31
|
if (kind !== undefined && kind !== null) {
|
|
31
32
|
if (typeof kind !== "string" || kind.length > MAX_KIND_LENGTH) {
|
|
@@ -93,6 +94,14 @@ function validateSaveInput({
|
|
|
93
94
|
);
|
|
94
95
|
}
|
|
95
96
|
}
|
|
97
|
+
if (expires_at !== undefined && expires_at !== null) {
|
|
98
|
+
if (
|
|
99
|
+
typeof expires_at !== "string" ||
|
|
100
|
+
isNaN(new Date(expires_at).getTime())
|
|
101
|
+
) {
|
|
102
|
+
return err("expires_at must be a valid ISO date string", "INVALID_INPUT");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
96
105
|
return null;
|
|
97
106
|
}
|
|
98
107
|
|
|
@@ -178,6 +187,7 @@ export async function handler(
|
|
|
178
187
|
meta,
|
|
179
188
|
source,
|
|
180
189
|
identity_key,
|
|
190
|
+
expires_at,
|
|
181
191
|
});
|
|
182
192
|
if (inputErr) return inputErr;
|
|
183
193
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-vault",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Persistent memory for AI agents — saves and searches knowledge across sessions",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"bin/",
|
|
21
21
|
"src/",
|
|
22
22
|
"scripts/",
|
|
23
|
-
"app-dist/",
|
|
24
23
|
"README.md",
|
|
25
24
|
"LICENSE"
|
|
26
25
|
],
|
|
@@ -56,7 +55,7 @@
|
|
|
56
55
|
"@context-vault/core"
|
|
57
56
|
],
|
|
58
57
|
"dependencies": {
|
|
59
|
-
"@context-vault/core": "^2.8.
|
|
58
|
+
"@context-vault/core": "^2.8.6",
|
|
60
59
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
61
60
|
"better-sqlite3": "^12.6.2",
|
|
62
61
|
"sqlite-vec": "^0.1.0"
|
package/scripts/postinstall.js
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { execSync } from "node:child_process";
|
|
13
|
-
import { existsSync } from "node:fs";
|
|
13
|
+
import { existsSync, writeFileSync, mkdirSync } from "node:fs";
|
|
14
14
|
import { join, dirname } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { homedir } from "node:os";
|
|
16
17
|
|
|
17
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
19
|
const PKG_ROOT = join(__dirname, "..");
|
|
@@ -88,6 +89,14 @@ async function main() {
|
|
|
88
89
|
);
|
|
89
90
|
}
|
|
90
91
|
}
|
|
92
|
+
|
|
93
|
+
// ── 3. Write local server launcher ───────────────────────────────────
|
|
94
|
+
const SERVER_ABS = join(PKG_ROOT, "src", "server", "index.js");
|
|
95
|
+
const DATA_DIR = join(homedir(), ".context-mcp");
|
|
96
|
+
const LAUNCHER = join(DATA_DIR, "server.mjs");
|
|
97
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
98
|
+
writeFileSync(LAUNCHER, `import "${SERVER_ABS}";\n`);
|
|
99
|
+
console.log("[context-vault] Local server launcher written to " + LAUNCHER);
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
main().catch(() => {});
|