context-vault 2.8.4 → 2.8.5

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 CHANGED
@@ -222,6 +222,7 @@ ${bold("Usage:")}
222
222
  ${bold("Commands:")}
223
223
  ${cyan("setup")} Interactive MCP server installer
224
224
  ${cyan("connect")} --key cv_... Connect AI tools to hosted vault
225
+ ${cyan("switch")} local|hosted Switch between local and hosted MCP modes
225
226
  ${cyan("serve")} Start the MCP server (used by AI clients)
226
227
  ${cyan("ui")} [--port 3141] Launch web dashboard
227
228
  ${cyan("reindex")} Rebuild search index from knowledge files
@@ -451,6 +452,7 @@ async function runSetup() {
451
452
  vaultConfig.dataDir = dataDir;
452
453
  vaultConfig.dbPath = join(dataDir, "vault.db");
453
454
  vaultConfig.devDir = join(HOME, "dev");
455
+ vaultConfig.mode = "local";
454
456
  writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + "\n");
455
457
  console.log(`\n ${green("+")} Wrote ${configPath}`);
456
458
 
@@ -635,10 +637,11 @@ async function configureClaude(tool, vaultDir) {
635
637
 
636
638
  try {
637
639
  if (isInstalledPackage()) {
638
- const cmdArgs = ["serve"];
640
+ const launcherPath = join(HOME, ".context-mcp", "server.mjs");
641
+ const cmdArgs = [`"${launcherPath}"`];
639
642
  if (vaultDir) cmdArgs.push("--vault-dir", `"${vaultDir}"`);
640
643
  execSync(
641
- `claude mcp add -s user context-vault -- context-vault ${cmdArgs.join(" ")}`,
644
+ `claude mcp add -s user context-vault -- node ${cmdArgs.join(" ")}`,
642
645
  { stdio: "pipe", env },
643
646
  );
644
647
  } else {
@@ -667,12 +670,12 @@ async function configureCodex(tool, vaultDir) {
667
670
 
668
671
  try {
669
672
  if (isInstalledPackage()) {
670
- const cmdArgs = ["serve"];
673
+ const launcherPath = join(HOME, ".context-mcp", "server.mjs");
674
+ const cmdArgs = [`"${launcherPath}"`];
671
675
  if (vaultDir) cmdArgs.push("--vault-dir", `"${vaultDir}"`);
672
- execSync(
673
- `codex mcp add context-vault -- context-vault ${cmdArgs.join(" ")}`,
674
- { stdio: "pipe" },
675
- );
676
+ execSync(`codex mcp add context-vault -- node ${cmdArgs.join(" ")}`, {
677
+ stdio: "pipe",
678
+ });
676
679
  } else {
677
680
  const cmdArgs = [`"${SERVER_PATH}"`];
678
681
  if (vaultDir) cmdArgs.push("--vault-dir", `"${vaultDir}"`);
@@ -715,11 +718,12 @@ function configureJsonTool(tool, vaultDir) {
715
718
  delete config[tool.configKey]["context-mcp"];
716
719
 
717
720
  if (isInstalledPackage()) {
718
- const serverArgs = ["serve"];
721
+ const launcherPath = join(HOME, ".context-mcp", "server.mjs");
722
+ const serverArgs = [];
719
723
  if (vaultDir) serverArgs.push("--vault-dir", vaultDir);
720
724
  config[tool.configKey]["context-vault"] = {
721
- command: "context-vault",
722
- args: serverArgs,
725
+ command: "node",
726
+ args: [launcherPath, ...serverArgs],
723
727
  };
724
728
  } else {
725
729
  const serverArgs = [SERVER_PATH];
@@ -948,6 +952,19 @@ async function runConnect() {
948
952
  }
949
953
  }
950
954
 
955
+ // Persist mode in config
956
+ const modeConfigPath = join(HOME, ".context-mcp", "config.json");
957
+ let modeConfig = {};
958
+ if (existsSync(modeConfigPath)) {
959
+ try {
960
+ modeConfig = JSON.parse(readFileSync(modeConfigPath, "utf-8"));
961
+ } catch {}
962
+ }
963
+ modeConfig.mode = "hosted";
964
+ modeConfig.hostedUrl = hostedUrl;
965
+ mkdirSync(join(HOME, ".context-mcp"), { recursive: true });
966
+ writeFileSync(modeConfigPath, JSON.stringify(modeConfig, null, 2) + "\n");
967
+
951
968
  console.log();
952
969
  console.log(
953
970
  green(" ✓ Connected! Your AI tools can now access your hosted vault."),
@@ -1033,31 +1050,136 @@ function configureJsonToolHosted(tool, apiKey, hostedUrl) {
1033
1050
  writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
1034
1051
  }
1035
1052
 
1036
- function runUi() {
1037
- // Try bundled path first (npm install), then workspace path (local dev)
1038
- const bundledDist = resolve(ROOT, "app-dist");
1039
- const workspaceDist = resolve(ROOT, "..", "app", "dist");
1040
- const appDist = existsSync(join(bundledDist, "index.html"))
1041
- ? bundledDist
1042
- : existsSync(join(workspaceDist, "index.html"))
1043
- ? workspaceDist
1044
- : null;
1053
+ async function runSwitch() {
1054
+ const target = args[1];
1055
+ if (target !== "local" && target !== "hosted") {
1056
+ console.log(`\n ${bold("context-vault switch")} <local|hosted>\n`);
1057
+ console.log(` Switch between local and hosted MCP modes.\n`);
1058
+ console.log(
1059
+ ` ${cyan("switch local")} Use local vault (SQLite + files on this device)`,
1060
+ );
1061
+ console.log(
1062
+ ` ${cyan("switch hosted")} Use hosted vault (requires API key)\n`,
1063
+ );
1064
+ console.log(` Options:`);
1065
+ console.log(` --key <key> API key for hosted mode (cv_...)`);
1066
+ console.log(
1067
+ ` --url <url> Hosted server URL (default: https://api.context-vault.com)\n`,
1068
+ );
1069
+ return;
1070
+ }
1045
1071
 
1046
- if (!appDist) {
1047
- const cloudUrl = "https://app.context-vault.com";
1048
- console.log(`Opening ${cloudUrl}`);
1049
- const open =
1050
- PLATFORM === "darwin"
1051
- ? "open"
1052
- : PLATFORM === "win32"
1053
- ? "start"
1054
- : "xdg-open";
1072
+ const dataDir = join(HOME, ".context-mcp");
1073
+ const configPath = join(dataDir, "config.json");
1074
+ let vaultConfig = {};
1075
+ if (existsSync(configPath)) {
1055
1076
  try {
1056
- execSync(`${open} ${cloudUrl}`, { stdio: "ignore" });
1077
+ vaultConfig = JSON.parse(readFileSync(configPath, "utf-8"));
1057
1078
  } catch {}
1058
- return;
1059
1079
  }
1060
1080
 
1081
+ const { detected } = await detectAllTools();
1082
+
1083
+ if (target === "local") {
1084
+ const launcherPath = join(dataDir, "server.mjs");
1085
+ if (!existsSync(launcherPath)) {
1086
+ const serverAbs = resolve(ROOT, "src", "server", "index.js");
1087
+ mkdirSync(dataDir, { recursive: true });
1088
+ writeFileSync(launcherPath, `import "${serverAbs}";\n`);
1089
+ }
1090
+
1091
+ vaultConfig.mode = "local";
1092
+ mkdirSync(dataDir, { recursive: true });
1093
+ writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + "\n");
1094
+
1095
+ console.log();
1096
+ console.log(` ${bold("◇ context-vault")} ${dim("switch → local")}`);
1097
+ console.log();
1098
+
1099
+ const defaultVDir = join(HOME, "vault");
1100
+ const customVaultDir =
1101
+ vaultConfig.vaultDir &&
1102
+ resolve(vaultConfig.vaultDir) !== resolve(defaultVDir)
1103
+ ? vaultConfig.vaultDir
1104
+ : null;
1105
+
1106
+ for (const tool of detected) {
1107
+ try {
1108
+ if (tool.configType === "cli" && tool.id === "codex") {
1109
+ await configureCodex(tool, customVaultDir);
1110
+ } else if (tool.configType === "cli") {
1111
+ await configureClaude(tool, customVaultDir);
1112
+ } else {
1113
+ configureJsonTool(tool, customVaultDir);
1114
+ }
1115
+ console.log(` ${green("+")} ${tool.name} — switched to local`);
1116
+ } catch (e) {
1117
+ console.log(` ${red("x")} ${tool.name} — ${e.message}`);
1118
+ }
1119
+ }
1120
+ console.log();
1121
+ console.log(green(" ✓ Switched to local mode."));
1122
+ console.log(dim(` Server: node ${launcherPath}`));
1123
+ console.log();
1124
+ } else {
1125
+ const hostedUrl =
1126
+ getFlag("--url") ||
1127
+ vaultConfig.hostedUrl ||
1128
+ "https://api.context-vault.com";
1129
+ const apiKey = getFlag("--key") || vaultConfig.apiKey;
1130
+
1131
+ if (!apiKey) {
1132
+ console.error(
1133
+ red(` --key <api_key> required. Get yours at ${hostedUrl}/dashboard`),
1134
+ );
1135
+ process.exit(1);
1136
+ }
1137
+
1138
+ console.log();
1139
+ console.log(` ${bold("◇ context-vault")} ${dim("switch → hosted")}`);
1140
+ console.log();
1141
+ console.log(dim(" Verifying API key..."));
1142
+
1143
+ try {
1144
+ const response = await fetch(`${hostedUrl}/api/me`, {
1145
+ headers: { Authorization: `Bearer ${apiKey}` },
1146
+ });
1147
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
1148
+ const user = await response.json();
1149
+ console.log(` ${green("+")} Verified — ${user.email}\n`);
1150
+ } catch (e) {
1151
+ console.error(red(` Verification failed: ${e.message}`));
1152
+ process.exit(1);
1153
+ }
1154
+
1155
+ vaultConfig.mode = "hosted";
1156
+ vaultConfig.hostedUrl = hostedUrl;
1157
+ vaultConfig.apiKey = apiKey;
1158
+ mkdirSync(dataDir, { recursive: true });
1159
+ writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + "\n");
1160
+
1161
+ for (const tool of detected) {
1162
+ try {
1163
+ if (tool.configType === "cli" && tool.id === "codex") {
1164
+ configureCodexHosted(apiKey, hostedUrl);
1165
+ } else if (tool.configType === "cli") {
1166
+ configureClaudeHosted(apiKey, hostedUrl);
1167
+ } else {
1168
+ configureJsonToolHosted(tool, apiKey, hostedUrl);
1169
+ }
1170
+ console.log(` ${green("+")} ${tool.name} — switched to hosted`);
1171
+ } catch (e) {
1172
+ console.log(` ${red("x")} ${tool.name} — ${e.message}`);
1173
+ }
1174
+ }
1175
+ console.log();
1176
+ console.log(green(" ✓ Switched to hosted mode."));
1177
+ console.log(dim(` Endpoint: ${hostedUrl}/mcp`));
1178
+ console.log();
1179
+ }
1180
+ }
1181
+
1182
+ function runUi() {
1061
1183
  const port = parseInt(getFlag("--port") || "3141", 10);
1062
1184
  const localServer = join(ROOT, "scripts", "local-server.js");
1063
1185
  if (!existsSync(localServer)) {
@@ -1088,20 +1210,9 @@ function launchServer(port, localServer) {
1088
1210
  const child = fork(localServer, [`--port=${port}`], { stdio: "inherit" });
1089
1211
  child.on("exit", (code) => process.exit(code ?? 0));
1090
1212
 
1091
- // Open browser after a short delay — prefer hosted UI if reachable
1092
- setTimeout(async () => {
1213
+ setTimeout(() => {
1093
1214
  try {
1094
- let url = `http://localhost:${port}`;
1095
- try {
1096
- const controller = new AbortController();
1097
- const timeout = setTimeout(() => controller.abort(), 3000);
1098
- await fetch("https://context-vault.com", {
1099
- method: "HEAD",
1100
- signal: controller.signal,
1101
- });
1102
- clearTimeout(timeout);
1103
- url = `https://context-vault.com?local=${port}`;
1104
- } catch {}
1215
+ const url = `https://app.context-vault.com?local=${port}`;
1105
1216
  console.log(`Opening ${url}`);
1106
1217
  const open =
1107
1218
  PLATFORM === "darwin"
@@ -1157,6 +1268,24 @@ async function runStatus() {
1157
1268
  const { gatherVaultStatus } = await import("@context-vault/core/core/status");
1158
1269
 
1159
1270
  const config = resolveConfig();
1271
+
1272
+ let mode = "local";
1273
+ let modeDetail = "";
1274
+ const rawConfigPath = join(HOME, ".context-mcp", "config.json");
1275
+ if (existsSync(rawConfigPath)) {
1276
+ try {
1277
+ const raw = JSON.parse(readFileSync(rawConfigPath, "utf-8"));
1278
+ mode = raw.mode || "local";
1279
+ if (mode === "hosted" && raw.hostedUrl) {
1280
+ const email = raw.email ? ` · ${raw.email}` : "";
1281
+ modeDetail = ` (${raw.hostedUrl}${email})`;
1282
+ } else {
1283
+ const launcherPath = join(HOME, ".context-mcp", "server.mjs");
1284
+ modeDetail = ` (node ${launcherPath})`;
1285
+ }
1286
+ } catch {}
1287
+ }
1288
+
1160
1289
  const db = await initDatabase(config.dbPath);
1161
1290
 
1162
1291
  const status = gatherVaultStatus({ db, config });
@@ -1166,6 +1295,7 @@ async function runStatus() {
1166
1295
  console.log();
1167
1296
  console.log(` ${bold("◇ context-vault")} ${dim(`v${VERSION}`)}`);
1168
1297
  console.log();
1298
+ console.log(` Mode: ${mode}${dim(modeDetail)}`);
1169
1299
  console.log(
1170
1300
  ` Vault: ${config.vaultDir} ${dim(`(${config.vaultDirExists ? status.fileCount + " files" : "missing"})`)}`,
1171
1301
  );
@@ -1916,6 +2046,9 @@ async function main() {
1916
2046
  case "connect":
1917
2047
  await runConnect();
1918
2048
  break;
2049
+ case "switch":
2050
+ await runSwitch();
2051
+ break;
1919
2052
  case "serve":
1920
2053
  await runServe();
1921
2054
  break;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@context-vault/core",
3
- "version": "2.8.4",
3
+ "version": "2.8.5",
4
4
  "type": "module",
5
5
  "description": "Shared core: capture, index, retrieve, tools, and utilities for context-vault",
6
6
  "main": "src/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-vault",
3
- "version": "2.8.4",
3
+ "version": "2.8.5",
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.4",
58
+ "@context-vault/core": "^2.8.5",
60
59
  "@modelcontextprotocol/sdk": "^1.26.0",
61
60
  "better-sqlite3": "^12.6.2",
62
61
  "sqlite-vec": "^0.1.0"
@@ -46,13 +46,7 @@ import {
46
46
 
47
47
  const __dirname = dirname(fileURLToPath(import.meta.url));
48
48
  const LOCAL_ROOT = resolve(__dirname, "..");
49
-
50
- // Try bundled path first (npm install), then workspace path (local dev)
51
- const bundledDist = resolve(LOCAL_ROOT, "app-dist");
52
- const workspaceDist = resolve(LOCAL_ROOT, "..", "app", "dist");
53
- const APP_DIST = existsSync(join(bundledDist, "index.html"))
54
- ? bundledDist
55
- : workspaceDist;
49
+ const APP_DIST = resolve(LOCAL_ROOT, "app-dist");
56
50
 
57
51
  const MIME = {
58
52
  ".html": "text/html",
@@ -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(() => {});
@@ -6,7 +6,6 @@ import {
6
6
  mkdirSync,
7
7
  readFileSync,
8
8
  writeFileSync,
9
- existsSync,
10
9
  } from "node:fs";
11
10
  import { join, dirname } from "node:path";
12
11
  import { fileURLToPath } from "node:url";
@@ -16,8 +15,6 @@ const LOCAL_ROOT = join(__dirname, "..");
16
15
  const NODE_MODULES = join(LOCAL_ROOT, "node_modules");
17
16
  const CORE_SRC = join(LOCAL_ROOT, "..", "core");
18
17
  const CORE_DEST = join(NODE_MODULES, "@context-vault", "core");
19
- const APP_SRC = join(LOCAL_ROOT, "..", "app", "dist");
20
- const APP_DEST = join(LOCAL_ROOT, "app-dist");
21
18
 
22
19
  // Clean node_modules to prevent workspace deps from leaking into the tarball.
23
20
  // Only @context-vault/core should be bundled.
@@ -44,16 +41,3 @@ delete corePkg.dependencies;
44
41
  writeFileSync(corePkgPath, JSON.stringify(corePkg, null, 2) + "\n");
45
42
 
46
43
  console.log("[prepack] Bundled @context-vault/core into node_modules");
47
-
48
- // Copy pre-built web dashboard into app-dist/ (optional — UI falls back to cloud)
49
- if (!existsSync(join(APP_SRC, "index.html"))) {
50
- console.warn(
51
- "[prepack] WARNING: Web dashboard not built — app-dist/ will not be bundled.\n" +
52
- "[prepack] `context-vault ui` will open app.context-vault.com instead.\n" +
53
- "[prepack] To bundle locally: build packages/app first.",
54
- );
55
- } else {
56
- rmSync(APP_DEST, { recursive: true, force: true });
57
- cpSync(APP_SRC, APP_DEST, { recursive: true });
58
- console.log("[prepack] Bundled web dashboard into app-dist/");
59
- }