virlow-mcp 3.14.3 → 3.14.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.
Files changed (2) hide show
  1. package/dist/cli.js +50 -8
  2. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -120,7 +120,7 @@ var VirlowApi = class {
120
120
  // ../../packages/mcp-core/dist/vault.js
121
121
  var LockedError = class extends Error {
122
122
  };
123
- var Vault = class {
123
+ var Vault = class _Vault {
124
124
  autoLockMs;
125
125
  _session = null;
126
126
  _timerHandle = null;
@@ -177,10 +177,50 @@ var Vault = class {
177
177
  }
178
178
  return this._session;
179
179
  }
180
+ /**
181
+ * The largest delay `setTimeout` can actually hold: a 32-bit signed integer
182
+ * of milliseconds, about 24.9 days.
183
+ */
184
+ static MAX_TIMEOUT_MS = 2147483647;
185
+ /**
186
+ * Arm the auto-lock, unless the configured delay cannot be one.
187
+ *
188
+ * The guard is the whole point. `setTimeout` does not treat an out-of-range
189
+ * delay as "later" or as an error — it coerces it to **1 millisecond** and
190
+ * fires:
191
+ *
192
+ * TimeoutOverflowWarning: Infinity does not fit into a 32-bit signed
193
+ * integer. Timeout duration was set to 1.
194
+ *
195
+ * So `new Vault(Number.POSITIVE_INFINITY)`, which reads as "never auto-lock,
196
+ * something else owns the timeout", locked the vault a millisecond after
197
+ * every unlock and every touch. The hosted connector is built exactly that
198
+ * way — its session store owns the idle timeout — and the result was a vault
199
+ * that reported itself unlocked on the request that opened it and locked on
200
+ * every request after:
201
+ *
202
+ * status -> {"locked": false, "email": "..."}
203
+ * list_folders -> "Vault is locked." (2 seconds later, same session)
204
+ *
205
+ * Nothing in the store was wrong, which is what made it so hard to see: the
206
+ * session was found on every request, and the vault built from it had already
207
+ * locked itself. touch() re-armed the same 1ms timer, so activity made it
208
+ * worse rather than better.
209
+ *
210
+ * A delay that cannot be scheduled therefore means no auto-lock at all, which
211
+ * is what every caller passing one intends. Zero and negatives are the same
212
+ * case — a vault that locks on the next tick is not a security posture, it is
213
+ * a broken one — and a caller wanting an immediate lock has `lock()`.
214
+ */
180
215
  _scheduleAutoLock() {
216
+ const ms = this.autoLockMs;
217
+ if (!Number.isFinite(ms) || ms <= 0 || ms > _Vault.MAX_TIMEOUT_MS) {
218
+ this._timerHandle = null;
219
+ return;
220
+ }
181
221
  this._timerHandle = setTimeout(() => {
182
222
  this.lock();
183
- }, this.autoLockMs);
223
+ }, ms);
184
224
  this._timerHandle.unref?.();
185
225
  }
186
226
  _clearTimer() {
@@ -1286,6 +1326,8 @@ function mapError(err) {
1286
1326
  return err.message;
1287
1327
  return String(err);
1288
1328
  }
1329
+ var CONTENT_FORMAT = 'CONTENT FORMAT: content is HTML, not Markdown. Virlow renders it in a rich-text editor, so Markdown syntax is stored and displayed literally \u2014 "# Heading" appears as those characters, not as a heading. Use <h1>-<h4>, <p>, <strong>, <em>, <u>, <s>, <mark>, <ul>/<ol>/<li>, <blockquote>, <pre><code>, <a href>, <hr>, <img src>, and <table>/<tr>/<th>/<td>. Task lists are <ul data-type="taskList"> with <li data-checked="true|false">. Wrap every paragraph in <p>; a bare newline is not a paragraph break. STYLE: never use em-dashes (\u2014). Use a comma, a colon, or two sentences.';
1330
+ var HTML_FIELD_HINT = "Note body as HTML (not Markdown). Paragraphs in <p>, headings in <h1>-<h4>. See the tool description for the full tag list.";
1289
1331
  function wrap(fn) {
1290
1332
  return async (...args) => {
1291
1333
  try {
@@ -1495,7 +1537,7 @@ page ${pagination.page} of ${pagination.pages} (${pagination.total} total)` + fi
1495
1537
  return textResult(body + notice);
1496
1538
  }));
1497
1539
  server.registerTool("read_note", {
1498
- description: "Read the full title and content of a note by id. Requires the vault to be unlocked.",
1540
+ description: "Read the full title and content of a note by id. Requires the vault to be unlocked. Content comes back as the HTML the editor stores \u2014 write it back in the same form.",
1499
1541
  inputSchema: {
1500
1542
  id: z.string().min(1)
1501
1543
  }
@@ -1506,10 +1548,10 @@ page ${pagination.page} of ${pagination.pages} (${pagination.total} total)` + fi
1506
1548
  ${note.content}`);
1507
1549
  }));
1508
1550
  server.registerTool("create_note", {
1509
- description: "Create a new note with a title and content, optionally placed in a folder. Requires the vault to be unlocked.",
1551
+ description: "Create a new note with a title and content, optionally placed in a folder. Requires the vault to be unlocked.\n\n" + CONTENT_FORMAT,
1510
1552
  inputSchema: {
1511
1553
  title: z.string().min(1).max(200),
1512
- content: z.string().max(5e4).optional(),
1554
+ content: z.string().max(5e4).optional().describe(HTML_FIELD_HINT),
1513
1555
  folderId: z.string().optional(),
1514
1556
  type: z.string().max(50).optional()
1515
1557
  }
@@ -1518,11 +1560,11 @@ ${note.content}`);
1518
1560
  return textResult(`created note ${note.id}`);
1519
1561
  }));
1520
1562
  server.registerTool("update_note", {
1521
- description: "Update the title and/or content of an existing note by id. Only the provided fields are changed. Requires the vault to be unlocked.",
1563
+ description: "Update the title and/or content of an existing note by id. Only the provided fields are changed. Content replaces the note body entirely \u2014 read the note first if you mean to add to it. Requires the vault to be unlocked.\n\n" + CONTENT_FORMAT,
1522
1564
  inputSchema: {
1523
1565
  id: z.string().min(1),
1524
1566
  title: z.string().min(1).max(200).optional(),
1525
- content: z.string().max(5e4).optional()
1567
+ content: z.string().max(5e4).optional().describe(HTML_FIELD_HINT)
1526
1568
  }
1527
1569
  }, wrap(async (args) => {
1528
1570
  await notesService.updateNote(args.id, { title: args.title, content: args.content });
@@ -1881,7 +1923,7 @@ function startUnlockServer(api, vault, opts = {}) {
1881
1923
  }
1882
1924
 
1883
1925
  // src/version.ts
1884
- var VERSION = true ? "3.14.3" : "dev";
1926
+ var VERSION = true ? "3.14.5" : "dev";
1885
1927
 
1886
1928
  // src/server.ts
1887
1929
  function defaultEmbeddingCacheDir() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "virlow-mcp",
3
- "version": "3.14.3",
3
+ "version": "3.14.5",
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": "MIT",
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.14.3",
38
- "@batalabs/virlow-memory": "3.14.3",
39
- "@batalabs/virlow-mcp-core": "3.14.3"
37
+ "@batalabs/virlow-crypto": "3.14.5",
38
+ "@batalabs/virlow-memory": "3.14.5",
39
+ "@batalabs/virlow-mcp-core": "3.14.5"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "node build.mjs",