@bitbonsai/mcpvault 0.12.0 → 0.12.2

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.
@@ -16,6 +16,12 @@ export declare class FileSystemService {
16
16
  private frontmatterHandler;
17
17
  private pathFilter;
18
18
  constructor(vaultPath: string, pathFilter?: PathFilter, frontmatterHandler?: FrontmatterHandler);
19
+ /**
20
+ * Normalize an incoming path to be vault-relative. Strips leading slashes
21
+ * and the vault path prefix when a caller accidentally passes an absolute path
22
+ * (e.g. "/Users/me/vault/wiki/note.md" instead of "wiki/note.md").
23
+ */
24
+ private normalizePath;
19
25
  private resolvePath;
20
26
  readNote(path: string): Promise<ParsedNote>;
21
27
  writeNote(params: NoteWriteParams): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/filesystem.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzT;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAiBtE;AAED,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,SAAS;IAJnB,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,UAAU,CAAa;gBAGrB,SAAS,EAAE,MAAM,EACzB,UAAU,CAAC,EAAE,UAAU,EACvB,kBAAkB,CAAC,EAAE,kBAAkB;IAazC,OAAO,CAAC,WAAW;IA6Db,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgC3C,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA2EjD,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IA2F5D,aAAa,CAAC,IAAI,GAAE,MAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8D3D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAetC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAe3C,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAyG3D,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAoFrD,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IA6HrD,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAgDpE,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCjE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA2ClD,UAAU,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAsG3E,YAAY,IAAI,MAAM;IAIhB,aAAa,CAAC,WAAW,GAAE,MAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAyD3D,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAkDpE"}
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/filesystem.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,EAAE,QAAQ,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzT;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAiBtE;AAED,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,SAAS;IAJnB,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,UAAU,CAAa;gBAGrB,SAAS,EAAE,MAAM,EACzB,UAAU,CAAC,EAAE,UAAU,EACvB,kBAAkB,CAAC,EAAE,kBAAkB;IAazC;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,WAAW;IAkDb,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAiC3C,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA4EjD,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IA8F5D,aAAa,CAAC,IAAI,GAAE,MAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8D3D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBtC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgB3C,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IA2G3D,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAsFrD,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAiIrD,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAiDpE,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCjE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA4ClD,UAAU,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAuG3E,YAAY,IAAI,MAAM;IAIhB,aAAa,CAAC,WAAW,GAAE,MAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAyD3D,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAkDpE"}
@@ -1,4 +1,5 @@
1
1
  import { join, resolve, relative, dirname } from 'path';
2
+ import { homedir } from 'os';
2
3
  import { readdir, stat, readFile, writeFile, unlink, mkdir, access, rename, copyFile } from 'node:fs/promises';
3
4
  import { constants, realpathSync } from 'node:fs';
4
5
  import trash from 'trash';
@@ -49,17 +50,37 @@ export class FileSystemService {
49
50
  this.pathFilter = pathFilter || new PathFilter();
50
51
  this.frontmatterHandler = frontmatterHandler || new FrontmatterHandler();
51
52
  }
53
+ /**
54
+ * Normalize an incoming path to be vault-relative. Strips leading slashes
55
+ * and the vault path prefix when a caller accidentally passes an absolute path
56
+ * (e.g. "/Users/me/vault/wiki/note.md" instead of "wiki/note.md").
57
+ */
58
+ normalizePath(inputPath) {
59
+ if (!inputPath)
60
+ return '';
61
+ let p = inputPath.trim();
62
+ // Expand ~ to home directory so "~/vault/note.md" can be matched
63
+ if (p.startsWith('~/') || p === '~') {
64
+ p = p.replace('~', homedir());
65
+ }
66
+ // Normalize path separators for cross-platform comparison (Windows backslashes)
67
+ const normalized = p.replace(/\\/g, '/');
68
+ const vaultPrefix = this.vaultPath.replace(/\\/g, '/');
69
+ // Strip vault path prefix before stripping leading slash, so absolute paths
70
+ // like "/Users/me/vault/wiki/note.md" are handled correctly.
71
+ if (normalized.startsWith(vaultPrefix + '/')) {
72
+ p = normalized.slice(vaultPrefix.length + 1);
73
+ }
74
+ else if (normalized === vaultPrefix) {
75
+ p = '';
76
+ }
77
+ else if (p.startsWith('/')) {
78
+ p = p.slice(1);
79
+ }
80
+ return p;
81
+ }
52
82
  resolvePath(relativePath) {
53
- // Handle undefined or null path
54
- if (!relativePath) {
55
- relativePath = '';
56
- }
57
- // Trim whitespace from path
58
- relativePath = relativePath.trim();
59
- // Normalize and resolve the path within the vault
60
- const normalizedPath = relativePath.startsWith('/')
61
- ? relativePath.slice(1)
62
- : relativePath;
83
+ const normalizedPath = this.normalizePath(relativePath);
63
84
  const fullPath = resolve(join(this.vaultPath, normalizedPath));
64
85
  // Security check: ensure path is within vault (lexical)
65
86
  const relativeToVault = relative(this.vaultPath, fullPath);
@@ -110,6 +131,7 @@ export class FileSystemService {
110
131
  return fullPath;
111
132
  }
112
133
  async readNote(path) {
134
+ path = this.normalizePath(path);
113
135
  const fullPath = this.resolvePath(path);
114
136
  if (!this.pathFilter.isAllowed(path)) {
115
137
  throw new Error(`Access denied: ${path}. This path is restricted (system files like .obsidian, .git, and dotfiles are not accessible).`);
@@ -139,7 +161,8 @@ export class FileSystemService {
139
161
  }
140
162
  }
141
163
  async writeNote(params) {
142
- const { path, content, frontmatter, mode = 'overwrite' } = params;
164
+ const { content, frontmatter, mode = 'overwrite' } = params;
165
+ const path = this.normalizePath(params.path);
143
166
  const fullPath = this.resolvePath(path);
144
167
  if (!this.pathFilter.isAllowed(path)) {
145
168
  throw new Error(`Access denied: ${path}. This path is restricted (system files like .obsidian, .git, and dotfiles are not accessible).`);
@@ -201,7 +224,8 @@ export class FileSystemService {
201
224
  }
202
225
  }
203
226
  async patchNote(params) {
204
- const { path, oldString, newString, replaceAll = false } = params;
227
+ const { oldString, newString, replaceAll = false } = params;
228
+ const path = this.normalizePath(params.path);
205
229
  if (!this.pathFilter.isAllowed(path)) {
206
230
  return {
207
231
  success: false,
@@ -257,9 +281,11 @@ export class FileSystemService {
257
281
  };
258
282
  }
259
283
  // Perform the replacement
284
+ // Use a replacer function so newString is inserted literally,
285
+ // without $ replacement pattern expansion ($$, $&, $`, $')
260
286
  const updatedContent = replaceAll
261
287
  ? fullContent.split(oldString).join(newString)
262
- : fullContent.replace(oldString, newString);
288
+ : fullContent.replace(oldString, () => newString);
263
289
  // Write the updated content
264
290
  const fullPath = this.resolvePath(path);
265
291
  await writeFile(fullPath, updatedContent, 'utf-8');
@@ -279,8 +305,8 @@ export class FileSystemService {
279
305
  }
280
306
  }
281
307
  async listDirectory(path = '') {
282
- // Normalize path: treat '.' as root directory
283
- const normalizedPath = path === '.' ? '' : path;
308
+ // Normalize path: treat '.' as root directory, strip vault prefix
309
+ const normalizedPath = path === '.' ? '' : this.normalizePath(path);
284
310
  const fullPath = this.resolvePath(normalizedPath);
285
311
  try {
286
312
  const entries = await readdir(fullPath, { withFileTypes: true });
@@ -340,6 +366,7 @@ export class FileSystemService {
340
366
  }
341
367
  }
342
368
  async exists(path) {
369
+ path = this.normalizePath(path);
343
370
  const fullPath = this.resolvePath(path);
344
371
  if (!this.pathFilter.isAllowed(path)) {
345
372
  return false;
@@ -353,6 +380,7 @@ export class FileSystemService {
353
380
  }
354
381
  }
355
382
  async isDirectory(path) {
383
+ path = this.normalizePath(path);
356
384
  const fullPath = this.resolvePath(path);
357
385
  if (!this.pathFilter.isAllowed(path)) {
358
386
  return false;
@@ -366,7 +394,9 @@ export class FileSystemService {
366
394
  }
367
395
  }
368
396
  async deleteNote(params) {
369
- const { path, confirmPath, trashMode = 'none' } = params;
397
+ const { trashMode = 'none' } = params;
398
+ const path = this.normalizePath(params.path);
399
+ const confirmPath = this.normalizePath(params.confirmPath);
370
400
  // Confirmation check - paths must match exactly
371
401
  if (path !== confirmPath) {
372
402
  return {
@@ -459,7 +489,9 @@ export class FileSystemService {
459
489
  }
460
490
  }
461
491
  async moveNote(params) {
462
- const { oldPath, newPath, overwrite = false } = params;
492
+ const { overwrite = false } = params;
493
+ const oldPath = this.normalizePath(params.oldPath);
494
+ const newPath = this.normalizePath(params.newPath);
463
495
  if (!this.pathFilter.isAllowed(oldPath)) {
464
496
  return {
465
497
  success: false,
@@ -537,7 +569,11 @@ export class FileSystemService {
537
569
  }
538
570
  }
539
571
  async moveFile(params) {
540
- const { oldPath, newPath, confirmOldPath, confirmNewPath, overwrite = false } = params;
572
+ const { overwrite = false } = params;
573
+ const oldPath = this.normalizePath(params.oldPath);
574
+ const newPath = this.normalizePath(params.newPath);
575
+ const confirmOldPath = this.normalizePath(params.confirmOldPath);
576
+ const confirmNewPath = this.normalizePath(params.confirmNewPath);
541
577
  if (oldPath !== confirmOldPath || newPath !== confirmNewPath) {
542
578
  return {
543
579
  success: false,
@@ -661,7 +697,8 @@ export class FileSystemService {
661
697
  if (paths.length > 10) {
662
698
  throw new Error('Maximum 10 files per batch read request');
663
699
  }
664
- const results = await Promise.allSettled(paths.map(async (path) => {
700
+ const results = await Promise.allSettled(paths.map(async (rawPath) => {
701
+ const path = this.normalizePath(rawPath);
665
702
  if (!this.pathFilter.isAllowed(path)) {
666
703
  throw new Error(`Access denied: ${path}. This path is restricted (system files like .obsidian, .git, and dotfiles are not accessible).`);
667
704
  }
@@ -694,7 +731,8 @@ export class FileSystemService {
694
731
  return { successful, failed };
695
732
  }
696
733
  async updateFrontmatter(params) {
697
- const { path, frontmatter, merge = true } = params;
734
+ const { frontmatter, merge = true } = params;
735
+ const path = this.normalizePath(params.path);
698
736
  if (!this.pathFilter.isAllowed(path)) {
699
737
  throw new Error(`Access denied: ${path}. This path is restricted (system files like .obsidian, .git, and dotfiles are not accessible).`);
700
738
  }
@@ -725,7 +763,8 @@ export class FileSystemService {
725
763
  }
726
764
  }
727
765
  async getNotesInfo(paths) {
728
- const results = await Promise.allSettled(paths.map(async (path) => {
766
+ const results = await Promise.allSettled(paths.map(async (rawPath) => {
767
+ const path = this.normalizePath(rawPath);
729
768
  if (!this.pathFilter.isAllowed(path)) {
730
769
  throw new Error(`Access denied: ${path}. This path is restricted (system files like .obsidian, .git, and dotfiles are not accessible).`);
731
770
  }
@@ -760,7 +799,8 @@ export class FileSystemService {
760
799
  .map(result => result.value);
761
800
  }
762
801
  async manageTags(params) {
763
- const { path, operation, tags = [] } = params;
802
+ const { operation, tags = [] } = params;
803
+ const path = this.normalizePath(params.path);
764
804
  if (!this.pathFilter.isAllowed(path)) {
765
805
  return {
766
806
  path,
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/frontmatter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAmB5E;AAED,qBAAa,kBAAkB;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;IAoBlC,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAaxE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,2BAA2B;IAqB3E,OAAO,CAAC,yBAAyB;IAmDjC,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAyB3F,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAKxD,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;CAWzE"}
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/frontmatter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAkB1E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAmB5E;AAED,qBAAa,kBAAkB;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;IAoBlC,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAaxE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,2BAA2B;IAqB3E,OAAO,CAAC,yBAAyB;IAmDjC,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAyB3F,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAKxD,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;CAWzE"}
@@ -1,5 +1,19 @@
1
1
  import matter from 'gray-matter';
2
- import { parseDocument } from 'yaml';
2
+ import { parse, stringify, parseDocument } from 'yaml';
3
+ // Use the 'yaml' package as gray-matter's engine instead of js-yaml.
4
+ // js-yaml@3.x (pinned by gray-matter@4) has known quadratic-DoS via
5
+ // crafted YAML merge-key aliases (CVE-2023-44270). The 'yaml' package
6
+ // is already a dependency (used in preserveStringify) and is not affected.
7
+ const yamlEngine = {
8
+ // YAML 1.2 schema (default). Dates stay as strings, but this avoids the
9
+ // yaml-1.1 bug where single-letter keys like 'y'/'n' become booleans.
10
+ // Merge keys (<<) are not resolved, which is fine for Obsidian frontmatter.
11
+ parse: (str) => parse(str),
12
+ stringify: (data) => stringify(data),
13
+ };
14
+ function withYamlEngine(options = {}) {
15
+ return { ...options, engines: { yaml: yamlEngine } };
16
+ }
3
17
  /**
4
18
  * Parse a frontmatter value that may be a JSON string (LLM clients sometimes
5
19
  * pass frontmatter as a serialized JSON string instead of an object).
@@ -29,7 +43,7 @@ export function parseFrontmatter(value) {
29
43
  export class FrontmatterHandler {
30
44
  parse(content) {
31
45
  try {
32
- const parsed = matter(content);
46
+ const parsed = matter(content, withYamlEngine());
33
47
  return {
34
48
  frontmatter: parsed.data,
35
49
  content: parsed.content,
@@ -53,7 +67,7 @@ export class FrontmatterHandler {
53
67
  if (!frontmatterData || Object.keys(frontmatterData).length === 0) {
54
68
  return content;
55
69
  }
56
- return matter.stringify(content, frontmatterData);
70
+ return matter.stringify(content, frontmatterData, withYamlEngine());
57
71
  }
58
72
  catch (error) {
59
73
  throw new Error(`Failed to stringify frontmatter: ${error instanceof Error ? error.message : 'Unknown error'}`);
@@ -67,7 +81,7 @@ export class FrontmatterHandler {
67
81
  };
68
82
  try {
69
83
  // Test if the frontmatter can be serialized to valid YAML using gray-matter
70
- matter.stringify('', frontmatterData);
84
+ matter.stringify('', frontmatterData, withYamlEngine());
71
85
  }
72
86
  catch (error) {
73
87
  result.isValid = false;
@@ -123,7 +137,7 @@ export class FrontmatterHandler {
123
137
  if (!updates || Object.keys(updates).length === 0) {
124
138
  return content;
125
139
  }
126
- return matter.stringify(content, updates);
140
+ return matter.stringify(content, updates, withYamlEngine());
127
141
  }
128
142
  const doc = parseDocument(rawMatter.trimStart());
129
143
  for (const [key, value] of Object.entries(updates)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitbonsai/mcpvault",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "Universal AI bridge for Obsidian vaults - connect any MCP-compatible assistant",
5
5
  "homepage": "https://mcpvault.org",
6
6
  "author": "bitbonsai",
@@ -42,7 +42,7 @@
42
42
  "yaml": "^2.8.3"
43
43
  },
44
44
  "devDependencies": {
45
- "@types/node": "^25.3.3",
45
+ "@types/node": "^26.0.0",
46
46
  "tsx": "^4.20.6",
47
47
  "typescript": "^6.0.2",
48
48
  "vitest": "^4.0.15"