@gopherine/obsidian-mcp 0.1.1

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 (223) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +661 -0
  3. package/README.md +235 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +805 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/brainstorm.d.ts +9 -0
  8. package/dist/commands/brainstorm.js +37 -0
  9. package/dist/commands/brainstorm.js.map +1 -0
  10. package/dist/commands/brainstorm.test.d.ts +1 -0
  11. package/dist/commands/brainstorm.test.js +110 -0
  12. package/dist/commands/brainstorm.test.js.map +1 -0
  13. package/dist/commands/context.d.ts +18 -0
  14. package/dist/commands/context.js +88 -0
  15. package/dist/commands/context.js.map +1 -0
  16. package/dist/commands/context.test.d.ts +1 -0
  17. package/dist/commands/context.test.js +230 -0
  18. package/dist/commands/context.test.js.map +1 -0
  19. package/dist/commands/decide.d.ts +12 -0
  20. package/dist/commands/decide.js +40 -0
  21. package/dist/commands/decide.js.map +1 -0
  22. package/dist/commands/decide.test.d.ts +1 -0
  23. package/dist/commands/decide.test.js +109 -0
  24. package/dist/commands/decide.test.js.map +1 -0
  25. package/dist/commands/graph.d.ts +15 -0
  26. package/dist/commands/graph.js +89 -0
  27. package/dist/commands/graph.js.map +1 -0
  28. package/dist/commands/graph.test.d.ts +1 -0
  29. package/dist/commands/graph.test.js +215 -0
  30. package/dist/commands/graph.test.js.map +1 -0
  31. package/dist/commands/init.d.ts +11 -0
  32. package/dist/commands/init.js +256 -0
  33. package/dist/commands/init.js.map +1 -0
  34. package/dist/commands/learn.d.ts +25 -0
  35. package/dist/commands/learn.js +92 -0
  36. package/dist/commands/learn.js.map +1 -0
  37. package/dist/commands/learn.test.d.ts +1 -0
  38. package/dist/commands/learn.test.js +241 -0
  39. package/dist/commands/learn.test.js.map +1 -0
  40. package/dist/commands/prune.d.ts +58 -0
  41. package/dist/commands/prune.js +246 -0
  42. package/dist/commands/prune.js.map +1 -0
  43. package/dist/commands/read.d.ts +8 -0
  44. package/dist/commands/read.js +7 -0
  45. package/dist/commands/read.js.map +1 -0
  46. package/dist/commands/read.test.d.ts +1 -0
  47. package/dist/commands/read.test.js +48 -0
  48. package/dist/commands/read.test.js.map +1 -0
  49. package/dist/commands/resume.d.ts +20 -0
  50. package/dist/commands/resume.js +138 -0
  51. package/dist/commands/resume.js.map +1 -0
  52. package/dist/commands/resume.test.d.ts +1 -0
  53. package/dist/commands/resume.test.js +243 -0
  54. package/dist/commands/resume.test.js.map +1 -0
  55. package/dist/commands/search.d.ts +8 -0
  56. package/dist/commands/search.js +25 -0
  57. package/dist/commands/search.js.map +1 -0
  58. package/dist/commands/search.test.d.ts +1 -0
  59. package/dist/commands/search.test.js +61 -0
  60. package/dist/commands/search.test.js.map +1 -0
  61. package/dist/commands/session.d.ts +22 -0
  62. package/dist/commands/session.js +79 -0
  63. package/dist/commands/session.js.map +1 -0
  64. package/dist/commands/session.test.d.ts +1 -0
  65. package/dist/commands/session.test.js +185 -0
  66. package/dist/commands/session.test.js.map +1 -0
  67. package/dist/commands/skill/index.d.ts +30 -0
  68. package/dist/commands/skill/index.js +61 -0
  69. package/dist/commands/skill/index.js.map +1 -0
  70. package/dist/commands/skill/index.test.d.ts +1 -0
  71. package/dist/commands/skill/index.test.js +137 -0
  72. package/dist/commands/skill/index.test.js.map +1 -0
  73. package/dist/commands/skill/install.d.ts +14 -0
  74. package/dist/commands/skill/install.js +107 -0
  75. package/dist/commands/skill/install.js.map +1 -0
  76. package/dist/commands/skill/install.test.d.ts +1 -0
  77. package/dist/commands/skill/install.test.js +225 -0
  78. package/dist/commands/skill/install.test.js.map +1 -0
  79. package/dist/commands/skill/list.d.ts +6 -0
  80. package/dist/commands/skill/list.js +5 -0
  81. package/dist/commands/skill/list.js.map +1 -0
  82. package/dist/commands/skill/list.test.d.ts +1 -0
  83. package/dist/commands/skill/list.test.js +101 -0
  84. package/dist/commands/skill/list.test.js.map +1 -0
  85. package/dist/commands/skill/schema.d.ts +27 -0
  86. package/dist/commands/skill/schema.js +55 -0
  87. package/dist/commands/skill/schema.js.map +1 -0
  88. package/dist/commands/skill/schema.test.d.ts +1 -0
  89. package/dist/commands/skill/schema.test.js +142 -0
  90. package/dist/commands/skill/schema.test.js.map +1 -0
  91. package/dist/commands/skill/validate.d.ts +10 -0
  92. package/dist/commands/skill/validate.js +39 -0
  93. package/dist/commands/skill/validate.js.map +1 -0
  94. package/dist/commands/skill/validate.test.d.ts +1 -0
  95. package/dist/commands/skill/validate.test.js +171 -0
  96. package/dist/commands/skill/validate.test.js.map +1 -0
  97. package/dist/commands/task.d.ts +34 -0
  98. package/dist/commands/task.js +160 -0
  99. package/dist/commands/task.js.map +1 -0
  100. package/dist/commands/task.test.d.ts +1 -0
  101. package/dist/commands/task.test.js +395 -0
  102. package/dist/commands/task.test.js.map +1 -0
  103. package/dist/commands/todo.d.ts +15 -0
  104. package/dist/commands/todo.js +99 -0
  105. package/dist/commands/todo.js.map +1 -0
  106. package/dist/commands/todo.test.d.ts +1 -0
  107. package/dist/commands/todo.test.js +324 -0
  108. package/dist/commands/todo.test.js.map +1 -0
  109. package/dist/commands/write.d.ts +12 -0
  110. package/dist/commands/write.js +40 -0
  111. package/dist/commands/write.js.map +1 -0
  112. package/dist/commands/write.test.d.ts +1 -0
  113. package/dist/commands/write.test.js +79 -0
  114. package/dist/commands/write.test.js.map +1 -0
  115. package/dist/config.d.ts +15 -0
  116. package/dist/config.js +48 -0
  117. package/dist/config.js.map +1 -0
  118. package/dist/config.test.d.ts +1 -0
  119. package/dist/config.test.js +147 -0
  120. package/dist/config.test.js.map +1 -0
  121. package/dist/core/executor.d.ts +11 -0
  122. package/dist/core/executor.js +42 -0
  123. package/dist/core/executor.js.map +1 -0
  124. package/dist/core/executor.test.d.ts +1 -0
  125. package/dist/core/executor.test.js +206 -0
  126. package/dist/core/executor.test.js.map +1 -0
  127. package/dist/core/middleware/error-handler.d.ts +2 -0
  128. package/dist/core/middleware/error-handler.js +29 -0
  129. package/dist/core/middleware/error-handler.js.map +1 -0
  130. package/dist/core/middleware/index.d.ts +2 -0
  131. package/dist/core/middleware/index.js +3 -0
  132. package/dist/core/middleware/index.js.map +1 -0
  133. package/dist/core/middleware/logging.d.ts +2 -0
  134. package/dist/core/middleware/logging.js +18 -0
  135. package/dist/core/middleware/logging.js.map +1 -0
  136. package/dist/core/registry.d.ts +12 -0
  137. package/dist/core/registry.js +407 -0
  138. package/dist/core/registry.js.map +1 -0
  139. package/dist/core/registry.test.d.ts +1 -0
  140. package/dist/core/registry.test.js +162 -0
  141. package/dist/core/registry.test.js.map +1 -0
  142. package/dist/core/types.d.ts +38 -0
  143. package/dist/core/types.js +2 -0
  144. package/dist/core/types.js.map +1 -0
  145. package/dist/integration/coordination.test.d.ts +1 -0
  146. package/dist/integration/coordination.test.js +286 -0
  147. package/dist/integration/coordination.test.js.map +1 -0
  148. package/dist/integration/project-artifacts.test.d.ts +1 -0
  149. package/dist/integration/project-artifacts.test.js +275 -0
  150. package/dist/integration/project-artifacts.test.js.map +1 -0
  151. package/dist/integration/registry-graph.test.d.ts +1 -0
  152. package/dist/integration/registry-graph.test.js +157 -0
  153. package/dist/integration/registry-graph.test.js.map +1 -0
  154. package/dist/integration/vault-lifecycle.test.d.ts +1 -0
  155. package/dist/integration/vault-lifecycle.test.js +183 -0
  156. package/dist/integration/vault-lifecycle.test.js.map +1 -0
  157. package/dist/lib/auto-number.d.ts +10 -0
  158. package/dist/lib/auto-number.js +33 -0
  159. package/dist/lib/auto-number.js.map +1 -0
  160. package/dist/lib/auto-number.test.d.ts +1 -0
  161. package/dist/lib/auto-number.test.js +88 -0
  162. package/dist/lib/auto-number.test.js.map +1 -0
  163. package/dist/lib/escape-regex.d.ts +4 -0
  164. package/dist/lib/escape-regex.js +7 -0
  165. package/dist/lib/escape-regex.js.map +1 -0
  166. package/dist/lib/escape-regex.test.d.ts +1 -0
  167. package/dist/lib/escape-regex.test.js +27 -0
  168. package/dist/lib/escape-regex.test.js.map +1 -0
  169. package/dist/lib/frontmatter.d.ts +34 -0
  170. package/dist/lib/frontmatter.js +74 -0
  171. package/dist/lib/frontmatter.js.map +1 -0
  172. package/dist/lib/frontmatter.test.d.ts +1 -0
  173. package/dist/lib/frontmatter.test.js +192 -0
  174. package/dist/lib/frontmatter.test.js.map +1 -0
  175. package/dist/lib/project-detector.d.ts +12 -0
  176. package/dist/lib/project-detector.js +123 -0
  177. package/dist/lib/project-detector.js.map +1 -0
  178. package/dist/lib/project-detector.test.d.ts +1 -0
  179. package/dist/lib/project-detector.test.js +117 -0
  180. package/dist/lib/project-detector.test.js.map +1 -0
  181. package/dist/lib/safe-external-path.d.ts +10 -0
  182. package/dist/lib/safe-external-path.js +46 -0
  183. package/dist/lib/safe-external-path.js.map +1 -0
  184. package/dist/lib/safe-external-path.test.d.ts +1 -0
  185. package/dist/lib/safe-external-path.test.js +99 -0
  186. package/dist/lib/safe-external-path.test.js.map +1 -0
  187. package/dist/lib/search-engine.d.ts +19 -0
  188. package/dist/lib/search-engine.js +157 -0
  189. package/dist/lib/search-engine.js.map +1 -0
  190. package/dist/lib/search-engine.test.d.ts +1 -0
  191. package/dist/lib/search-engine.test.js +120 -0
  192. package/dist/lib/search-engine.test.js.map +1 -0
  193. package/dist/lib/session-registry.d.ts +59 -0
  194. package/dist/lib/session-registry.js +230 -0
  195. package/dist/lib/session-registry.js.map +1 -0
  196. package/dist/lib/session-registry.test.d.ts +1 -0
  197. package/dist/lib/session-registry.test.js +199 -0
  198. package/dist/lib/session-registry.test.js.map +1 -0
  199. package/dist/lib/skill-registry.d.ts +13 -0
  200. package/dist/lib/skill-registry.js +76 -0
  201. package/dist/lib/skill-registry.js.map +1 -0
  202. package/dist/lib/token-estimator.d.ts +19 -0
  203. package/dist/lib/token-estimator.js +58 -0
  204. package/dist/lib/token-estimator.js.map +1 -0
  205. package/dist/lib/token-estimator.test.d.ts +1 -0
  206. package/dist/lib/token-estimator.test.js +65 -0
  207. package/dist/lib/token-estimator.test.js.map +1 -0
  208. package/dist/lib/vault-fs.d.ts +39 -0
  209. package/dist/lib/vault-fs.js +183 -0
  210. package/dist/lib/vault-fs.js.map +1 -0
  211. package/dist/lib/vault-fs.test.d.ts +1 -0
  212. package/dist/lib/vault-fs.test.js +210 -0
  213. package/dist/lib/vault-fs.test.js.map +1 -0
  214. package/dist/mcp-server.d.ts +2 -0
  215. package/dist/mcp-server.js +385 -0
  216. package/dist/mcp-server.js.map +1 -0
  217. package/dist/test-helpers.d.ts +20 -0
  218. package/dist/test-helpers.js +46 -0
  219. package/dist/test-helpers.js.map +1 -0
  220. package/dist/test-helpers.test.d.ts +1 -0
  221. package/dist/test-helpers.test.js +90 -0
  222. package/dist/test-helpers.test.js.map +1 -0
  223. package/package.json +72 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,65 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { estimateTokens, truncateToTokenBudget } from "./token-estimator.js";
3
+ describe("estimateTokens", () => {
4
+ const cases = [
5
+ { name: "empty string", input: "", expected: 0 },
6
+ { name: "short text", input: "hello", expected: 2 },
7
+ { name: "exactly 4 chars", input: "abcd", expected: 2 },
8
+ { name: "exactly 8 chars", input: "abcdefgh", expected: 3 },
9
+ { name: "long text", input: "a".repeat(100), expected: 29 },
10
+ { name: "code with brackets", input: "function() { return []; }", expected: 8 },
11
+ { name: "markdown with headers", input: "# Title\n\nParagraph", expected: 6 },
12
+ ];
13
+ for (const c of cases) {
14
+ it(c.name, () => {
15
+ expect(estimateTokens(c.input)).toBe(c.expected);
16
+ });
17
+ }
18
+ });
19
+ describe("truncateToTokenBudget", () => {
20
+ it("returns unchanged text when under budget", () => {
21
+ const text = "Short text";
22
+ const result = truncateToTokenBudget(text, 100);
23
+ expect(result.text).toBe(text);
24
+ expect(result.truncated).toBe(false);
25
+ });
26
+ it("truncates at section boundary when possible", () => {
27
+ const text = "Intro content that is long enough to take up some tokens before we hit the section.\n\n## Section 1\nThis is the content for section one with enough text to matter.\n\n## Section 2\nContent for section two that will be truncated away.";
28
+ const result = truncateToTokenBudget(text, 25);
29
+ expect(result.truncated).toBe(true);
30
+ expect(result.text).toContain("Intro content");
31
+ expect(result.text).not.toContain("Section 2");
32
+ expect(result.text).toContain("[truncated");
33
+ });
34
+ it("truncates at paragraph boundary when no section", () => {
35
+ const text = "Para 1\n\nPara 2\n\nPara 3\n\nPara 4";
36
+ const result = truncateToTokenBudget(text, 5);
37
+ expect(result.truncated).toBe(true);
38
+ expect(result.text).toContain("[truncated");
39
+ });
40
+ it("truncates at newline when no paragraph boundary", () => {
41
+ const text = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5";
42
+ const result = truncateToTokenBudget(text, 5);
43
+ expect(result.truncated).toBe(true);
44
+ expect(result.text).toContain("[truncated");
45
+ });
46
+ it("includes truncation suffix", () => {
47
+ const text = "a".repeat(1000);
48
+ const result = truncateToTokenBudget(text, 10);
49
+ expect(result.text).toContain("[truncated");
50
+ expect(result.text).toContain("vault_project_context");
51
+ });
52
+ it("handles text shorter than budget", () => {
53
+ const text = "Tiny";
54
+ const result = truncateToTokenBudget(text, 1000);
55
+ expect(result.text).toBe(text);
56
+ expect(result.truncated).toBe(false);
57
+ });
58
+ it("handles markdown with frontmatter", () => {
59
+ const text = "---\ntitle: Test\n---\n\n# Title\n\nContent here\n\n## Section\nMore content";
60
+ const result = truncateToTokenBudget(text, 10);
61
+ expect(result.truncated).toBe(true);
62
+ expect(result.text).toContain("[truncated");
63
+ });
64
+ });
65
+ //# sourceMappingURL=token-estimator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-estimator.test.js","sourceRoot":"","sources":["../../src/lib/token-estimator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Safe filesystem operations on the vault.
3
+ * Every path is resolved against the vault root and validated.
4
+ */
5
+ export declare class VaultFS {
6
+ private readonly _root;
7
+ constructor(_root: string);
8
+ get root(): string;
9
+ /**
10
+ * Resolve a relative vault path to an absolute path.
11
+ * Rejects traversal attacks, absolute paths, and personal vault access.
12
+ */
13
+ private resolve;
14
+ read(relativePath: string): Promise<string>;
15
+ write(relativePath: string, content: string): Promise<{
16
+ path: string;
17
+ bytes: number;
18
+ }>;
19
+ append(relativePath: string, content: string): Promise<{
20
+ path: string;
21
+ bytes: number;
22
+ }>;
23
+ list(relativePath: string, depth?: number): Promise<string[]>;
24
+ private listRecursive;
25
+ delete(relativePath: string): Promise<void>;
26
+ move(relativePath: string, newRelativePath: string): Promise<{
27
+ from: string;
28
+ to: string;
29
+ }>;
30
+ exists(relativePath: string): Promise<boolean>;
31
+ /**
32
+ * Verify a path doesn't follow symlinks outside the vault.
33
+ */
34
+ verifyNoSymlinkEscape(relativePath: string): Promise<void>;
35
+ }
36
+ export declare class VaultError extends Error {
37
+ readonly code: string;
38
+ constructor(code: string, message: string);
39
+ }
@@ -0,0 +1,183 @@
1
+ import { readFile, writeFile, appendFile, mkdir, readdir, stat, realpath, unlink, rename } from "fs/promises";
2
+ import { join, resolve, relative, dirname } from "path";
3
+ /**
4
+ * Safe filesystem operations on the vault.
5
+ * Every path is resolved against the vault root and validated.
6
+ */
7
+ export class VaultFS {
8
+ _root;
9
+ constructor(_root) {
10
+ this._root = _root;
11
+ }
12
+ get root() {
13
+ return this._root;
14
+ }
15
+ /**
16
+ * Resolve a relative vault path to an absolute path.
17
+ * Rejects traversal attacks, absolute paths, and personal vault access.
18
+ */
19
+ resolve(relativePath) {
20
+ // Reject non-ASCII characters (prevents Unicode homoglyph attacks on APFS)
21
+ if (/[^\x20-\x7E]/.test(relativePath)) {
22
+ throw new VaultError("PERMISSION_DENIED", `Non-ASCII characters not allowed in paths: ${relativePath}`);
23
+ }
24
+ // Reject absolute paths
25
+ if (relativePath.startsWith("/") || relativePath.startsWith("~")) {
26
+ throw new VaultError("PERMISSION_DENIED", `Absolute paths not allowed: ${relativePath}`);
27
+ }
28
+ // Reject traversal
29
+ if (relativePath.includes("..")) {
30
+ throw new VaultError("PERMISSION_DENIED", `Path traversal not allowed: ${relativePath}`);
31
+ }
32
+ // Reject personal vault references (case-insensitive, segment-level match)
33
+ const segments = relativePath.toLowerCase().split("/");
34
+ if (segments.some((seg) => seg === "personal")) {
35
+ throw new VaultError("PERMISSION_DENIED", `Cannot access personal vault: ${relativePath}`);
36
+ }
37
+ const resolved = resolve(this._root, relativePath);
38
+ // Double-check the resolved path is within vault
39
+ const rel = relative(this._root, resolved);
40
+ if (rel.startsWith("..")) {
41
+ throw new VaultError("PERMISSION_DENIED", `Path escapes vault: ${relativePath}`);
42
+ }
43
+ return resolved;
44
+ }
45
+ async read(relativePath) {
46
+ const abs = this.resolve(relativePath);
47
+ await this.verifyNoSymlinkEscape(relativePath);
48
+ try {
49
+ return await readFile(abs, "utf-8");
50
+ }
51
+ catch (e) {
52
+ if (e.code === "ENOENT") {
53
+ throw new VaultError("FILE_NOT_FOUND", `Not found: ${relativePath}`);
54
+ }
55
+ throw e;
56
+ }
57
+ }
58
+ async write(relativePath, content) {
59
+ const abs = this.resolve(relativePath);
60
+ await this.verifyNoSymlinkEscape(relativePath);
61
+ await mkdir(dirname(abs), { recursive: true });
62
+ await writeFile(abs, content, "utf-8");
63
+ return { path: relativePath, bytes: Buffer.byteLength(content, "utf-8") };
64
+ }
65
+ async append(relativePath, content) {
66
+ const abs = this.resolve(relativePath);
67
+ await this.verifyNoSymlinkEscape(relativePath);
68
+ if (!(await this.exists(relativePath))) {
69
+ throw new VaultError("FILE_NOT_FOUND", `Cannot append to non-existent file: ${relativePath}`);
70
+ }
71
+ await appendFile(abs, content, "utf-8");
72
+ return { path: relativePath, bytes: Buffer.byteLength(content, "utf-8") };
73
+ }
74
+ async list(relativePath, depth = 1) {
75
+ const abs = this.resolve(relativePath);
76
+ await this.verifyNoSymlinkEscape(relativePath);
77
+ const results = [];
78
+ await this.listRecursive(abs, relativePath, depth, 0, results);
79
+ return results;
80
+ }
81
+ async listRecursive(absDir, relDir, maxDepth, currentDepth, results) {
82
+ if (currentDepth >= maxDepth)
83
+ return;
84
+ let entries;
85
+ try {
86
+ entries = await readdir(absDir, { withFileTypes: true });
87
+ }
88
+ catch (e) {
89
+ if (e.code === "ENOENT") {
90
+ throw new VaultError("FILE_NOT_FOUND", `Directory not found: ${relDir}`);
91
+ }
92
+ throw e;
93
+ }
94
+ for (const entry of entries) {
95
+ // Skip hidden files/dirs (.obsidian, .git, etc.)
96
+ if (entry.name.startsWith("."))
97
+ continue;
98
+ // Skip symlinks to prevent following links outside the vault
99
+ if (entry.isSymbolicLink())
100
+ continue;
101
+ const entryRel = relDir ? `${relDir}/${entry.name}` : entry.name;
102
+ if (entry.isDirectory()) {
103
+ results.push(`${entryRel}/`);
104
+ await this.listRecursive(join(absDir, entry.name), entryRel, maxDepth, currentDepth + 1, results);
105
+ }
106
+ else {
107
+ results.push(entryRel);
108
+ }
109
+ }
110
+ }
111
+ async delete(relativePath) {
112
+ const abs = this.resolve(relativePath);
113
+ await this.verifyNoSymlinkEscape(relativePath);
114
+ try {
115
+ await unlink(abs);
116
+ }
117
+ catch (e) {
118
+ if (e.code === "ENOENT") {
119
+ throw new VaultError("FILE_NOT_FOUND", `Not found: ${relativePath}`);
120
+ }
121
+ throw e;
122
+ }
123
+ }
124
+ async move(relativePath, newRelativePath) {
125
+ const absFrom = this.resolve(relativePath);
126
+ const absTo = this.resolve(newRelativePath);
127
+ await this.verifyNoSymlinkEscape(relativePath);
128
+ await this.verifyNoSymlinkEscape(newRelativePath);
129
+ await mkdir(dirname(absTo), { recursive: true });
130
+ try {
131
+ await rename(absFrom, absTo);
132
+ }
133
+ catch (e) {
134
+ if (e.code === "ENOENT") {
135
+ throw new VaultError("FILE_NOT_FOUND", `Not found: ${relativePath}`);
136
+ }
137
+ throw e;
138
+ }
139
+ return { from: relativePath, to: newRelativePath };
140
+ }
141
+ async exists(relativePath) {
142
+ try {
143
+ const abs = this.resolve(relativePath);
144
+ await stat(abs);
145
+ return true;
146
+ }
147
+ catch {
148
+ return false;
149
+ }
150
+ }
151
+ /**
152
+ * Verify a path doesn't follow symlinks outside the vault.
153
+ */
154
+ async verifyNoSymlinkEscape(relativePath) {
155
+ const abs = this.resolve(relativePath);
156
+ if (!(await this.exists(relativePath))) {
157
+ return;
158
+ }
159
+ try {
160
+ const [realAbs, realRoot] = await Promise.all([realpath(abs), realpath(this._root)]);
161
+ const rel = relative(realRoot, realAbs);
162
+ if (rel.startsWith("..")) {
163
+ throw new VaultError("PERMISSION_DENIED", `Symlink escapes vault: ${relativePath}`);
164
+ }
165
+ }
166
+ catch (e) {
167
+ if (e instanceof VaultError)
168
+ throw e;
169
+ if (e.code === "ENOENT")
170
+ return;
171
+ throw e;
172
+ }
173
+ }
174
+ }
175
+ export class VaultError extends Error {
176
+ code;
177
+ constructor(code, message) {
178
+ super(message);
179
+ this.code = code;
180
+ this.name = "VaultError";
181
+ }
182
+ }
183
+ //# sourceMappingURL=vault-fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-fs.js","sourceRoot":"","sources":["../../src/lib/vault-fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,210 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdir, writeFile, rm, symlink } from "fs/promises";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ import { VaultFS, VaultError } from "./vault-fs.js";
6
+ describe("VaultFS", () => {
7
+ let vaultRoot;
8
+ let vaultFs;
9
+ beforeEach(async () => {
10
+ vaultRoot = join(homedir(), `.vault-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
11
+ await mkdir(vaultRoot, { recursive: true });
12
+ vaultFs = new VaultFS(vaultRoot);
13
+ });
14
+ afterEach(async () => {
15
+ await rm(vaultRoot, { recursive: true, force: true });
16
+ });
17
+ describe("read", () => {
18
+ it("reads existing file", async () => {
19
+ await writeFile(join(vaultRoot, "test.md"), "content");
20
+ const result = await vaultFs.read("test.md");
21
+ expect(result).toBe("content");
22
+ });
23
+ it("throws FILE_NOT_FOUND for missing file", async () => {
24
+ await expect(vaultFs.read("missing.md")).rejects.toThrow(VaultError);
25
+ await expect(vaultFs.read("missing.md")).rejects.toMatchObject({ code: "FILE_NOT_FOUND" });
26
+ });
27
+ it("rejects traversal attack with ..", async () => {
28
+ await expect(vaultFs.read("../outside.md")).rejects.toThrow(VaultError);
29
+ await expect(vaultFs.read("../outside.md")).rejects.toMatchObject({ code: "PERMISSION_DENIED" });
30
+ });
31
+ it("rejects absolute path starting with /", async () => {
32
+ await expect(vaultFs.read("/etc/passwd")).rejects.toThrow(VaultError);
33
+ await expect(vaultFs.read("/etc/passwd")).rejects.toMatchObject({ code: "PERMISSION_DENIED" });
34
+ });
35
+ it("rejects absolute path starting with ~", async () => {
36
+ await expect(vaultFs.read("~/secret")).rejects.toThrow(VaultError);
37
+ await expect(vaultFs.read("~/secret")).rejects.toMatchObject({ code: "PERMISSION_DENIED" });
38
+ });
39
+ it("rejects personal vault segment", async () => {
40
+ await expect(vaultFs.read("personal/notes.md")).rejects.toThrow(VaultError);
41
+ await expect(vaultFs.read("personal/notes.md")).rejects.toMatchObject({ code: "PERMISSION_DENIED" });
42
+ });
43
+ it("rejects personal vault segment case-insensitive", async () => {
44
+ await expect(vaultFs.read("Personal/notes.md")).rejects.toThrow(VaultError);
45
+ await expect(vaultFs.read("PERSONAL/notes.md")).rejects.toMatchObject({ code: "PERMISSION_DENIED" });
46
+ });
47
+ it("rejects non-ASCII characters", async () => {
48
+ await expect(vaultFs.read("test-日本語.md")).rejects.toThrow(VaultError);
49
+ await expect(vaultFs.read("test-日本語.md")).rejects.toMatchObject({ code: "PERMISSION_DENIED" });
50
+ });
51
+ it("reads from nested directory", async () => {
52
+ await mkdir(join(vaultRoot, "subdir"), { recursive: true });
53
+ await writeFile(join(vaultRoot, "subdir/nested.md"), "nested content");
54
+ const result = await vaultFs.read("subdir/nested.md");
55
+ expect(result).toBe("nested content");
56
+ });
57
+ });
58
+ describe("write", () => {
59
+ it("creates new file", async () => {
60
+ const result = await vaultFs.write("new.md", "content");
61
+ expect(result.path).toBe("new.md");
62
+ expect(result.bytes).toBe(7);
63
+ const content = await vaultFs.read("new.md");
64
+ expect(content).toBe("content");
65
+ });
66
+ it("creates nested directories", async () => {
67
+ await vaultFs.write("deep/nested/path/file.md", "deep content");
68
+ const content = await vaultFs.read("deep/nested/path/file.md");
69
+ expect(content).toBe("deep content");
70
+ });
71
+ it("overwrites existing file", async () => {
72
+ await vaultFs.write("existing.md", "old");
73
+ await vaultFs.write("existing.md", "new content");
74
+ const content = await vaultFs.read("existing.md");
75
+ expect(content).toBe("new content");
76
+ });
77
+ it("reports correct byte count for UTF-8", async () => {
78
+ const result = await vaultFs.write("utf8.md", "hello");
79
+ expect(result.bytes).toBe(5);
80
+ });
81
+ });
82
+ describe("append", () => {
83
+ it("appends to existing file", async () => {
84
+ await vaultFs.write("append.md", "line1\n");
85
+ const result = await vaultFs.append("append.md", "line2\n");
86
+ expect(result.bytes).toBe(6);
87
+ const content = await vaultFs.read("append.md");
88
+ expect(content).toBe("line1\nline2\n");
89
+ });
90
+ it("throws FILE_NOT_FOUND when appending to missing file", async () => {
91
+ await expect(vaultFs.append("missing.md", "content")).rejects.toMatchObject({ code: "FILE_NOT_FOUND" });
92
+ });
93
+ });
94
+ describe("list", () => {
95
+ beforeEach(async () => {
96
+ await vaultFs.write("file1.md", "1");
97
+ await vaultFs.write("file2.md", "2");
98
+ await vaultFs.write("subdir/file3.md", "3");
99
+ await vaultFs.write("subdir/deep/file4.md", "4");
100
+ await writeFile(join(vaultRoot, ".hidden"), "hidden");
101
+ });
102
+ it("lists files at depth 1", async () => {
103
+ const result = await vaultFs.list(".", 1);
104
+ expect(result).toContain("./file1.md");
105
+ expect(result).toContain("./file2.md");
106
+ expect(result).toContain("./subdir/");
107
+ expect(result).not.toContain("./subdir/file3.md");
108
+ });
109
+ it("lists files at depth 2", async () => {
110
+ const result = await vaultFs.list(".", 2);
111
+ expect(result).toContain("./file1.md");
112
+ expect(result).toContain("./subdir/file3.md");
113
+ expect(result).toContain("./subdir/deep/");
114
+ expect(result).not.toContain("./subdir/deep/file4.md");
115
+ });
116
+ it("excludes hidden files", async () => {
117
+ const result = await vaultFs.list(".", 1);
118
+ expect(result).not.toContain(".hidden");
119
+ });
120
+ it("excludes .obsidian directory", async () => {
121
+ await mkdir(join(vaultRoot, ".obsidian"));
122
+ await writeFile(join(vaultRoot, ".obsidian/config"), "config");
123
+ const result = await vaultFs.list(".", 2);
124
+ expect(result.some(p => p.includes(".obsidian"))).toBe(false);
125
+ });
126
+ it("skips symlinks", async () => {
127
+ await symlink(join(vaultRoot, "file1.md"), join(vaultRoot, "link.md"));
128
+ const result = await vaultFs.list(".", 1);
129
+ expect(result).toContain("./file1.md");
130
+ expect(result).not.toContain("./link.md");
131
+ });
132
+ it("throws FILE_NOT_FOUND for missing directory", async () => {
133
+ await expect(vaultFs.list("missing-dir", 1)).rejects.toThrow(VaultError);
134
+ await expect(vaultFs.list("missing-dir", 1)).rejects.toMatchObject({ code: "FILE_NOT_FOUND" });
135
+ });
136
+ });
137
+ describe("delete", () => {
138
+ it("deletes existing file", async () => {
139
+ await vaultFs.write("to-delete.md", "content");
140
+ await vaultFs.delete("to-delete.md");
141
+ expect(await vaultFs.exists("to-delete.md")).toBe(false);
142
+ });
143
+ it("throws FILE_NOT_FOUND for missing file", async () => {
144
+ await expect(vaultFs.delete("missing.md")).rejects.toThrow(VaultError);
145
+ await expect(vaultFs.delete("missing.md")).rejects.toMatchObject({ code: "FILE_NOT_FOUND" });
146
+ });
147
+ });
148
+ describe("move", () => {
149
+ it("moves existing file", async () => {
150
+ await vaultFs.write("old.md", "content");
151
+ const result = await vaultFs.move("old.md", "new.md");
152
+ expect(result.from).toBe("old.md");
153
+ expect(result.to).toBe("new.md");
154
+ expect(await vaultFs.exists("old.md")).toBe(false);
155
+ const content = await vaultFs.read("new.md");
156
+ expect(content).toBe("content");
157
+ });
158
+ it("moves to nested path creating directories", async () => {
159
+ await vaultFs.write("move-me.md", "content");
160
+ await vaultFs.move("move-me.md", "deep/nested/moved.md");
161
+ expect(await vaultFs.exists("move-me.md")).toBe(false);
162
+ const content = await vaultFs.read("deep/nested/moved.md");
163
+ expect(content).toBe("content");
164
+ });
165
+ it("throws FILE_NOT_FOUND for missing source", async () => {
166
+ await expect(vaultFs.move("missing.md", "target.md")).rejects.toThrow(VaultError);
167
+ await expect(vaultFs.move("missing.md", "target.md")).rejects.toMatchObject({ code: "FILE_NOT_FOUND" });
168
+ });
169
+ });
170
+ describe("exists", () => {
171
+ it("returns true for existing file", async () => {
172
+ await vaultFs.write("exists.md", "content");
173
+ expect(await vaultFs.exists("exists.md")).toBe(true);
174
+ });
175
+ it("returns false for missing file", async () => {
176
+ expect(await vaultFs.exists("missing.md")).toBe(false);
177
+ });
178
+ it("returns true for directory", async () => {
179
+ await mkdir(join(vaultRoot, "mydir"));
180
+ expect(await vaultFs.exists("mydir")).toBe(true);
181
+ });
182
+ });
183
+ describe("verifyNoSymlinkEscape", () => {
184
+ it("allows regular file", async () => {
185
+ await writeFile(join(vaultRoot, "regular.md"), "content");
186
+ await expect(vaultFs.verifyNoSymlinkEscape("regular.md")).resolves.toBeUndefined();
187
+ });
188
+ it("allows symlink inside vault", async () => {
189
+ await writeFile(join(vaultRoot, "target.md"), "content");
190
+ await symlink(join(vaultRoot, "target.md"), join(vaultRoot, "link.md"));
191
+ await expect(vaultFs.verifyNoSymlinkEscape("link.md")).resolves.toBeUndefined();
192
+ });
193
+ it("rejects symlink escaping vault", async () => {
194
+ const outsideDir = join(homedir(), `.outside-${Date.now()}`);
195
+ await mkdir(outsideDir, { recursive: true });
196
+ await writeFile(join(outsideDir, "outside.md"), "outside");
197
+ await symlink(join(outsideDir, "outside.md"), join(vaultRoot, "escape.md"));
198
+ try {
199
+ await vaultFs.read("escape.md");
200
+ expect.fail("Should have thrown");
201
+ }
202
+ catch (e) {
203
+ expect(e).toBeInstanceOf(VaultError);
204
+ expect(e.code).toBe("PERMISSION_DENIED");
205
+ }
206
+ await rm(outsideDir, { recursive: true, force: true });
207
+ });
208
+ });
209
+ });
210
+ //# sourceMappingURL=vault-fs.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-fs.test.js","sourceRoot":"","sources":["../../src/lib/vault-fs.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};