virlow-mcp 3.81.1 → 3.82.0
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/README.md +5 -0
- package/dist/cli.js +88 -14
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -185,6 +185,11 @@ existing config to fix that. Pin an exact version (`virlow-mcp@3.81.0`) only if
|
|
|
185
185
|
updates to wait for you. The desktop app updates itself and its built-in server, and the
|
|
186
186
|
hosted connector is updated on Virlow's side.
|
|
187
187
|
|
|
188
|
+
The server checks npm once at startup, and the `status` tool and the unlock page say when a
|
|
189
|
+
newer version exists. The request tells npm only that someone at your IP checked
|
|
190
|
+
virlow-mcp's version; it carries no account data. Turn it off with
|
|
191
|
+
`VIRLOW_MCP_NO_UPDATE_CHECK=1`.
|
|
192
|
+
|
|
188
193
|
## First use
|
|
189
194
|
|
|
190
195
|
1. Ask your AI client to run the **`unlock`** tool. This opens a browser window pointed
|
package/dist/cli.js
CHANGED
|
@@ -3859,7 +3859,8 @@ function buildServer(opts) {
|
|
|
3859
3859
|
server.registerTool("status", {
|
|
3860
3860
|
description: "Report whether the vault is currently locked or unlocked, and which account it's unlocked as. Call this after unlock to confirm the user finished the browser form, and before any memory/notes tool if you are unsure of the current lock state."
|
|
3861
3861
|
}, wrap(async () => {
|
|
3862
|
-
|
|
3862
|
+
const notice = opts.updateNotice?.() ?? null;
|
|
3863
|
+
return textResult(JSON.stringify(notice === null ? vault.status() : { ...vault.status(), update: notice }));
|
|
3863
3864
|
}));
|
|
3864
3865
|
const requireMemories = async () => {
|
|
3865
3866
|
if (!vault.isUnlocked())
|
|
@@ -4518,8 +4519,9 @@ async function performUnlock(api, vault, creds) {
|
|
|
4518
4519
|
vault.touch();
|
|
4519
4520
|
};
|
|
4520
4521
|
}
|
|
4521
|
-
function renderForm(nonce, error) {
|
|
4522
|
+
function renderForm(nonce, error, updateNotice) {
|
|
4522
4523
|
const errorHtml = error ? `<p class="error">${escapeHtml(error)}</p>` : "";
|
|
4524
|
+
const updateHtml = updateNotice ? `<p class="update">${escapeHtml(updateNotice)}</p>` : "";
|
|
4523
4525
|
return `<!doctype html>
|
|
4524
4526
|
<html lang="en">
|
|
4525
4527
|
<head>
|
|
@@ -4528,6 +4530,7 @@ function renderForm(nonce, error) {
|
|
|
4528
4530
|
<style>
|
|
4529
4531
|
body { font-family: system-ui, sans-serif; max-width: 420px; margin: 48px auto; padding: 0 16px; color: #1a1a1a; }
|
|
4530
4532
|
h1 { font-size: 1.4rem; }
|
|
4533
|
+
p.update { color: #555; font-size: 0.9rem; }
|
|
4531
4534
|
label { display: block; margin-top: 12px; font-weight: 600; }
|
|
4532
4535
|
input { width: 100%; padding: 8px; margin-top: 4px; box-sizing: border-box; font-size: 1rem; }
|
|
4533
4536
|
button { margin-top: 20px; padding: 10px 16px; font-size: 1rem; cursor: pointer; }
|
|
@@ -4542,6 +4545,7 @@ function renderForm(nonce, error) {
|
|
|
4542
4545
|
Virlow API. They are never seen by any AI model or logged.
|
|
4543
4546
|
</p>
|
|
4544
4547
|
${errorHtml}
|
|
4548
|
+
${updateHtml}
|
|
4545
4549
|
<form method="POST" action="/unlock?nonce=${encodeURIComponent(nonce)}" autocomplete="off">
|
|
4546
4550
|
<label for="email">Email</label>
|
|
4547
4551
|
<input type="email" id="email" name="email" autocomplete="off" required />
|
|
@@ -4686,7 +4690,7 @@ function startUnlockServer(api, vault, opts = {}) {
|
|
|
4686
4690
|
harden(res);
|
|
4687
4691
|
res.statusCode = 200;
|
|
4688
4692
|
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
4689
|
-
res.end(renderForm(nonce));
|
|
4693
|
+
res.end(renderForm(nonce, void 0, opts.updateNotice?.() ?? null));
|
|
4690
4694
|
return;
|
|
4691
4695
|
}
|
|
4692
4696
|
if (req.method === "POST") {
|
|
@@ -4710,7 +4714,7 @@ function startUnlockServer(api, vault, opts = {}) {
|
|
|
4710
4714
|
harden(res);
|
|
4711
4715
|
res.statusCode = 200;
|
|
4712
4716
|
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
4713
|
-
res.end(renderForm(nonce, message));
|
|
4717
|
+
res.end(renderForm(nonce, message, opts.updateNotice?.() ?? null));
|
|
4714
4718
|
}
|
|
4715
4719
|
return;
|
|
4716
4720
|
}
|
|
@@ -4742,25 +4746,30 @@ function startUnlockServer(api, vault, opts = {}) {
|
|
|
4742
4746
|
}
|
|
4743
4747
|
|
|
4744
4748
|
// src/version.ts
|
|
4745
|
-
var VERSION = true ? "3.
|
|
4749
|
+
var VERSION = true ? "3.82.0" : "dev";
|
|
4746
4750
|
|
|
4747
4751
|
// src/server.ts
|
|
4748
4752
|
function defaultEmbeddingCacheDir() {
|
|
4749
4753
|
return path3.join(os.homedir(), ".virlow-mcp", "cache");
|
|
4750
4754
|
}
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
}
|
|
4755
|
+
function localBrowserUnlock(updateNotice) {
|
|
4756
|
+
return {
|
|
4757
|
+
description: "Open a local browser window where the USER signs in and enters their Virlow master password. This tool returns immediately once the browser is opened \u2014 it does NOT wait for the user to finish. Call the status tool afterward to confirm the vault unlocked. Never ask the user for their password or account credentials yourself, and never relay a password through this tool or any other \u2014 credentials are entered only in the browser form and never seen by any AI model.",
|
|
4758
|
+
async begin({ api, vault }) {
|
|
4759
|
+
const { url } = await startUnlockServer(api, vault, {
|
|
4760
|
+
openBrowser: true,
|
|
4761
|
+
...updateNotice ? { updateNotice } : {}
|
|
4762
|
+
});
|
|
4763
|
+
return `Browser opened at ${url}. Complete the unlock form there, then run status to confirm.`;
|
|
4764
|
+
}
|
|
4765
|
+
};
|
|
4766
|
+
}
|
|
4758
4767
|
function withLocalDefaults(opts) {
|
|
4759
4768
|
const { embeddingCacheDir, ...rest } = opts;
|
|
4760
4769
|
return {
|
|
4761
4770
|
...rest,
|
|
4762
4771
|
serverInfo: { name: "virlow-mcp", version: VERSION },
|
|
4763
|
-
unlock: localBrowserUnlock,
|
|
4772
|
+
unlock: localBrowserUnlock(opts.updateNotice),
|
|
4764
4773
|
createEmbeddingCache: (userId) => new EmbeddingCache({
|
|
4765
4774
|
dir: embeddingCacheDir ?? defaultEmbeddingCacheDir(),
|
|
4766
4775
|
userId,
|
|
@@ -4828,6 +4837,63 @@ async function runConnectBridge(opts) {
|
|
|
4828
4837
|
};
|
|
4829
4838
|
}
|
|
4830
4839
|
|
|
4840
|
+
// src/update-check.ts
|
|
4841
|
+
var REGISTRY_URL = "https://registry.npmjs.org/virlow-mcp/latest";
|
|
4842
|
+
var TIMEOUT_MS = 3e3;
|
|
4843
|
+
var OPT_OUT_ENV = "VIRLOW_MCP_NO_UPDATE_CHECK";
|
|
4844
|
+
function parseVersion(v) {
|
|
4845
|
+
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(v);
|
|
4846
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
4847
|
+
}
|
|
4848
|
+
function isNewer(candidate, current) {
|
|
4849
|
+
const a = parseVersion(candidate);
|
|
4850
|
+
const b = parseVersion(current);
|
|
4851
|
+
if (!a || !b) return false;
|
|
4852
|
+
for (let i = 0; i < 3; i++) {
|
|
4853
|
+
if (a[i] !== b[i]) return a[i] > b[i];
|
|
4854
|
+
}
|
|
4855
|
+
return false;
|
|
4856
|
+
}
|
|
4857
|
+
function optedOut(env) {
|
|
4858
|
+
const v = env[OPT_OUT_ENV];
|
|
4859
|
+
return v === "1" || v === "true";
|
|
4860
|
+
}
|
|
4861
|
+
function updateCheckApplies(argv, env) {
|
|
4862
|
+
if (optedOut(env)) return false;
|
|
4863
|
+
if (argv.includes("--version") || argv.includes("-v")) return false;
|
|
4864
|
+
if (argv.some((a) => a === "--connect" || a.startsWith("--connect="))) return false;
|
|
4865
|
+
return true;
|
|
4866
|
+
}
|
|
4867
|
+
function startUpdateCheck(opts) {
|
|
4868
|
+
const env = opts.env ?? process.env;
|
|
4869
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
4870
|
+
const log = opts.log ?? ((line) => process.stderr.write(`${line}
|
|
4871
|
+
`));
|
|
4872
|
+
let text = null;
|
|
4873
|
+
if (optedOut(env)) return { notice: () => null, done: Promise.resolve() };
|
|
4874
|
+
const done = (async () => {
|
|
4875
|
+
try {
|
|
4876
|
+
const res = await fetchImpl(REGISTRY_URL, {
|
|
4877
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
4878
|
+
headers: { accept: "application/json" }
|
|
4879
|
+
});
|
|
4880
|
+
if (!res.ok) return;
|
|
4881
|
+
const body = await res.json();
|
|
4882
|
+
const latest = body.version;
|
|
4883
|
+
if (typeof latest === "string" && isNewer(latest, opts.current)) {
|
|
4884
|
+
text = `virlow-mcp ${latest} is available (you are running ${opts.current}). Restart your AI client to update. If your MCP config runs plain virlow-mcp, change it to virlow-mcp@latest.`;
|
|
4885
|
+
}
|
|
4886
|
+
} catch (err) {
|
|
4887
|
+
const why = err instanceof Error ? err.message : String(err);
|
|
4888
|
+
try {
|
|
4889
|
+
log(`virlow-mcp: update check skipped: ${why.replace(/\s+/g, " ")}`);
|
|
4890
|
+
} catch {
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
})();
|
|
4894
|
+
return { notice: () => text, done };
|
|
4895
|
+
}
|
|
4896
|
+
|
|
4831
4897
|
// src/cli.ts
|
|
4832
4898
|
function flagValue(name) {
|
|
4833
4899
|
const prefix = `--${name}=`;
|
|
@@ -4860,7 +4926,15 @@ async function main() {
|
|
|
4860
4926
|
const http2 = process.argv.includes("--http");
|
|
4861
4927
|
const host = flagValue("host") ?? "127.0.0.1";
|
|
4862
4928
|
const fileAccess = localFileAccess({ http: http2, host });
|
|
4863
|
-
const
|
|
4929
|
+
const updates = updateCheckApplies(process.argv, process.env) ? startUpdateCheck({ current: VERSION }) : null;
|
|
4930
|
+
const serverOpts = {
|
|
4931
|
+
apiUrl,
|
|
4932
|
+
modelCacheDir,
|
|
4933
|
+
embeddingCacheDir,
|
|
4934
|
+
memoriesEnabled,
|
|
4935
|
+
fileAccess,
|
|
4936
|
+
...updates ? { updateNotice: () => updates.notice() } : {}
|
|
4937
|
+
};
|
|
4864
4938
|
const services = createServices2(serverOpts);
|
|
4865
4939
|
const { server } = buildServer2({ ...serverOpts, services });
|
|
4866
4940
|
if (http2) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "virlow-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.82.0",
|
|
4
4
|
"description": "Local MCP server for Virlow Secure Notes: end-to-end-encrypted AI memories and notes for Cursor, Claude, Codex, and any MCP client.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"esbuild": "^0.25.0",
|
|
35
35
|
"typescript": "^5.8.0",
|
|
36
36
|
"vitest": "^3.0.0",
|
|
37
|
-
"@batalabs/virlow-crypto": "3.
|
|
38
|
-
"@batalabs/virlow-
|
|
39
|
-
"@batalabs/virlow-
|
|
37
|
+
"@batalabs/virlow-crypto": "3.82.0",
|
|
38
|
+
"@batalabs/virlow-memory": "3.82.0",
|
|
39
|
+
"@batalabs/virlow-mcp-core": "3.82.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "node build.mjs",
|