@bitbonsai/mcpvault 0.12.1 → 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.
package/dist/src/filesystem.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/src/filesystem.js
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 (
|
|
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 {
|
|
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 (
|
|
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 {
|
|
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,
|
package/package.json
CHANGED