clawvault 2.6.0 → 3.0.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.
Files changed (232) hide show
  1. package/bin/command-registration.test.js +1 -3
  2. package/bin/register-core-commands.js +10 -23
  3. package/bin/register-maintenance-commands.js +3 -20
  4. package/bin/register-query-commands.js +23 -0
  5. package/bin/register-task-commands.js +1 -18
  6. package/bin/register-task-commands.test.js +0 -16
  7. package/bin/register-vault-operations-commands.js +1 -29
  8. package/dist/{chunk-QVMXF7FY.js → chunk-3D6BCTP6.js} +39 -1
  9. package/dist/{chunk-R2MIW5G7.js → chunk-3DHXQHYG.js} +1 -1
  10. package/dist/{chunk-Q2J5YTUF.js → chunk-3NSBOUT3.js} +73 -36
  11. package/dist/chunk-3RG5ZIWI.js +10 -0
  12. package/dist/{chunk-AZYOKJYC.js → chunk-62YTUT6J.js} +2 -2
  13. package/dist/chunk-6U6MK36V.js +205 -0
  14. package/dist/{chunk-4QYGFWRM.js → chunk-7R7O6STJ.js} +4 -4
  15. package/dist/{chunk-VXEOHTSL.js → chunk-C7OK5WKP.js} +4 -4
  16. package/dist/chunk-CMB7UL7C.js +327 -0
  17. package/dist/chunk-DEFFDRVP.js +938 -0
  18. package/dist/{chunk-K3CDT7IH.js → chunk-E7MFQB6D.js} +61 -20
  19. package/dist/{chunk-ME37YNW3.js → chunk-F2JEUD4J.js} +6 -4
  20. package/dist/chunk-GAJV4IGR.js +82 -0
  21. package/dist/chunk-GQSLDZTS.js +560 -0
  22. package/dist/{chunk-4OXMU5S2.js → chunk-GUKMRGM7.js} +1 -1
  23. package/dist/{chunk-YOSEUUNB.js → chunk-H34S76MB.js} +6 -6
  24. package/dist/{chunk-4TE4JMLA.js → chunk-JY6FYXIT.js} +10 -5
  25. package/dist/chunk-K234IDRJ.js +1073 -0
  26. package/dist/{chunk-IEVLHNLU.js → chunk-LNJA2UGL.js} +86 -9
  27. package/dist/{chunk-MFAWT5O5.js → chunk-LYHGEHXG.js} +1 -0
  28. package/dist/chunk-MFM6K7PU.js +374 -0
  29. package/dist/{chunk-QWQ3TIKS.js → chunk-N2AXRYLC.js} +1 -1
  30. package/dist/chunk-PAH27GSN.js +108 -0
  31. package/dist/{chunk-OIWVQYQF.js → chunk-QBLMXKF2.js} +1 -1
  32. package/dist/{chunk-FHFUXL6G.js → chunk-QK3UCXWL.js} +2 -2
  33. package/dist/{chunk-2YDBJS7M.js → chunk-SJSFRIYS.js} +1 -1
  34. package/dist/{chunk-GSD4ALSI.js → chunk-U55BGUAU.js} +2 -2
  35. package/dist/{chunk-PBEE567J.js → chunk-VGLOTGAS.js} +1 -1
  36. package/dist/{chunk-F55HGNU4.js → chunk-WAZ3NLWL.js} +47 -0
  37. package/dist/{chunk-KL4NAOMO.js → chunk-WGRQ6HDV.js} +1 -1
  38. package/dist/{chunk-UEOUADMO.js → chunk-YKTA5JOJ.js} +13 -10
  39. package/dist/{chunk-XAVB4GB4.js → chunk-ZVVFWOLW.js} +4 -4
  40. package/dist/cli/index.cjs +10033 -0
  41. package/dist/cli/index.d.cts +5 -0
  42. package/dist/cli/index.js +20 -18
  43. package/dist/commands/archive.cjs +287 -0
  44. package/dist/commands/archive.d.cts +11 -0
  45. package/dist/commands/archive.js +1 -0
  46. package/dist/commands/backlog.cjs +721 -0
  47. package/dist/commands/backlog.d.cts +53 -0
  48. package/dist/commands/backlog.js +3 -2
  49. package/dist/commands/blocked.cjs +204 -0
  50. package/dist/commands/blocked.d.cts +26 -0
  51. package/dist/commands/blocked.js +3 -2
  52. package/dist/commands/checkpoint.cjs +244 -0
  53. package/dist/commands/checkpoint.d.cts +41 -0
  54. package/dist/commands/checkpoint.js +2 -1
  55. package/dist/commands/compat.cjs +369 -0
  56. package/dist/commands/compat.d.cts +28 -0
  57. package/dist/commands/compat.js +2 -1
  58. package/dist/commands/context.cjs +2989 -0
  59. package/dist/commands/context.d.cts +2 -0
  60. package/dist/commands/context.js +5 -4
  61. package/dist/commands/doctor.cjs +3062 -0
  62. package/dist/commands/doctor.d.cts +21 -0
  63. package/dist/commands/doctor.d.ts +6 -1
  64. package/dist/commands/doctor.js +13 -11
  65. package/dist/commands/embed.cjs +232 -0
  66. package/dist/commands/embed.d.cts +17 -0
  67. package/dist/commands/embed.js +5 -2
  68. package/dist/commands/entities.cjs +141 -0
  69. package/dist/commands/entities.d.cts +7 -0
  70. package/dist/commands/entities.js +1 -0
  71. package/dist/commands/graph.cjs +501 -0
  72. package/dist/commands/graph.d.cts +21 -0
  73. package/dist/commands/graph.js +1 -0
  74. package/dist/commands/inject.cjs +1636 -0
  75. package/dist/commands/inject.d.cts +2 -0
  76. package/dist/commands/inject.d.ts +1 -1
  77. package/dist/commands/inject.js +4 -2
  78. package/dist/commands/kanban.cjs +884 -0
  79. package/dist/commands/kanban.d.cts +63 -0
  80. package/dist/commands/kanban.js +4 -3
  81. package/dist/commands/link.cjs +965 -0
  82. package/dist/commands/link.d.cts +11 -0
  83. package/dist/commands/link.js +1 -0
  84. package/dist/commands/migrate-observations.cjs +362 -0
  85. package/dist/commands/migrate-observations.d.cts +19 -0
  86. package/dist/commands/migrate-observations.js +3 -2
  87. package/dist/commands/observe.cjs +4099 -0
  88. package/dist/commands/observe.d.cts +23 -0
  89. package/dist/commands/observe.d.ts +1 -0
  90. package/dist/commands/observe.js +11 -9
  91. package/dist/commands/project.cjs +1341 -0
  92. package/dist/commands/project.d.cts +85 -0
  93. package/dist/commands/project.js +5 -4
  94. package/dist/commands/rebuild.cjs +3136 -0
  95. package/dist/commands/rebuild.d.cts +11 -0
  96. package/dist/commands/rebuild.js +10 -8
  97. package/dist/commands/recover.cjs +361 -0
  98. package/dist/commands/recover.d.cts +38 -0
  99. package/dist/commands/recover.js +3 -2
  100. package/dist/commands/reflect.cjs +1008 -0
  101. package/dist/commands/reflect.d.cts +11 -0
  102. package/dist/commands/reflect.js +6 -4
  103. package/dist/commands/repair-session.cjs +457 -0
  104. package/dist/commands/repair-session.d.cts +38 -0
  105. package/dist/commands/repair-session.js +1 -0
  106. package/dist/commands/replay.cjs +4103 -0
  107. package/dist/commands/replay.d.cts +16 -0
  108. package/dist/commands/replay.js +12 -10
  109. package/dist/commands/session-recap.cjs +353 -0
  110. package/dist/commands/session-recap.d.cts +27 -0
  111. package/dist/commands/session-recap.js +1 -0
  112. package/dist/commands/setup.cjs +1345 -0
  113. package/dist/commands/setup.d.cts +100 -0
  114. package/dist/commands/setup.d.ts +90 -2
  115. package/dist/commands/setup.js +21 -2
  116. package/dist/commands/shell-init.cjs +75 -0
  117. package/dist/commands/shell-init.d.cts +7 -0
  118. package/dist/commands/shell-init.js +2 -0
  119. package/dist/commands/sleep.cjs +6028 -0
  120. package/dist/commands/sleep.d.cts +36 -0
  121. package/dist/commands/sleep.d.ts +1 -1
  122. package/dist/commands/sleep.js +17 -15
  123. package/dist/commands/status.cjs +2736 -0
  124. package/dist/commands/status.d.cts +52 -0
  125. package/dist/commands/status.js +12 -10
  126. package/dist/commands/tailscale.cjs +1532 -0
  127. package/dist/commands/tailscale.d.cts +52 -0
  128. package/dist/commands/tailscale.js +1 -0
  129. package/dist/commands/task.cjs +1236 -0
  130. package/dist/commands/task.d.cts +97 -0
  131. package/dist/commands/task.js +4 -3
  132. package/dist/commands/template.cjs +457 -0
  133. package/dist/commands/template.d.cts +36 -0
  134. package/dist/commands/template.js +2 -1
  135. package/dist/commands/wake.cjs +2626 -0
  136. package/dist/commands/wake.d.cts +22 -0
  137. package/dist/commands/wake.d.ts +1 -1
  138. package/dist/commands/wake.js +12 -11
  139. package/dist/context-BUGaWpyL.d.cts +46 -0
  140. package/dist/index.cjs +14526 -0
  141. package/dist/index.d.cts +858 -0
  142. package/dist/index.d.ts +192 -7
  143. package/dist/index.js +101 -75
  144. package/dist/{inject-x65KXWPk.d.ts → inject-Bzi5E-By.d.cts} +1 -1
  145. package/dist/inject-Bzi5E-By.d.ts +137 -0
  146. package/dist/lib/auto-linker.cjs +176 -0
  147. package/dist/lib/auto-linker.d.cts +26 -0
  148. package/dist/lib/auto-linker.js +1 -0
  149. package/dist/lib/canvas-layout.cjs +136 -0
  150. package/dist/lib/canvas-layout.d.cts +31 -0
  151. package/dist/lib/canvas-layout.d.ts +16 -100
  152. package/dist/lib/canvas-layout.js +78 -20
  153. package/dist/lib/config.cjs +78 -0
  154. package/dist/lib/config.d.cts +11 -0
  155. package/dist/lib/config.js +1 -0
  156. package/dist/lib/entity-index.cjs +84 -0
  157. package/dist/lib/entity-index.d.cts +26 -0
  158. package/dist/lib/entity-index.js +1 -0
  159. package/dist/lib/project-utils.cjs +864 -0
  160. package/dist/lib/project-utils.d.cts +97 -0
  161. package/dist/lib/project-utils.js +4 -3
  162. package/dist/lib/session-repair.cjs +239 -0
  163. package/dist/lib/session-repair.d.cts +110 -0
  164. package/dist/lib/session-repair.js +1 -0
  165. package/dist/lib/session-utils.cjs +209 -0
  166. package/dist/lib/session-utils.d.cts +63 -0
  167. package/dist/lib/session-utils.js +1 -0
  168. package/dist/lib/tailscale.cjs +1183 -0
  169. package/dist/lib/tailscale.d.cts +225 -0
  170. package/dist/lib/tailscale.js +1 -0
  171. package/dist/lib/task-utils.cjs +1137 -0
  172. package/dist/lib/task-utils.d.cts +208 -0
  173. package/dist/lib/task-utils.js +3 -2
  174. package/dist/lib/template-engine.cjs +47 -0
  175. package/dist/lib/template-engine.d.cts +11 -0
  176. package/dist/lib/template-engine.js +1 -0
  177. package/dist/lib/webdav.cjs +568 -0
  178. package/dist/lib/webdav.d.cts +109 -0
  179. package/dist/lib/webdav.js +1 -0
  180. package/dist/plugin/index.cjs +1907 -0
  181. package/dist/plugin/index.d.cts +36 -0
  182. package/dist/plugin/index.d.ts +36 -0
  183. package/dist/plugin/index.js +572 -0
  184. package/dist/plugin/inject.cjs +356 -0
  185. package/dist/plugin/inject.d.cts +54 -0
  186. package/dist/plugin/inject.d.ts +54 -0
  187. package/dist/plugin/inject.js +17 -0
  188. package/dist/plugin/observe.cjs +631 -0
  189. package/dist/plugin/observe.d.cts +39 -0
  190. package/dist/plugin/observe.d.ts +39 -0
  191. package/dist/plugin/observe.js +18 -0
  192. package/dist/plugin/templates.cjs +593 -0
  193. package/dist/plugin/templates.d.cts +52 -0
  194. package/dist/plugin/templates.d.ts +52 -0
  195. package/dist/plugin/templates.js +25 -0
  196. package/dist/plugin/types.cjs +18 -0
  197. package/dist/plugin/types.d.cts +209 -0
  198. package/dist/plugin/types.d.ts +209 -0
  199. package/dist/plugin/types.js +0 -0
  200. package/dist/plugin/vault.cjs +927 -0
  201. package/dist/plugin/vault.d.cts +68 -0
  202. package/dist/plugin/vault.d.ts +68 -0
  203. package/dist/plugin/vault.js +22 -0
  204. package/dist/{types-C74wgGL1.d.ts → types-Y2_Um2Ls.d.cts} +44 -1
  205. package/dist/types-Y2_Um2Ls.d.ts +205 -0
  206. package/hooks/clawvault/handler.js +70 -7
  207. package/hooks/clawvault/handler.test.js +91 -0
  208. package/openclaw.plugin.json +56 -0
  209. package/package.json +17 -7
  210. package/templates/memory-event.md +67 -0
  211. package/templates/party.md +63 -0
  212. package/templates/primitive-registry.yaml +551 -0
  213. package/templates/run.md +68 -0
  214. package/templates/trigger.md +68 -0
  215. package/templates/workspace.md +50 -0
  216. package/dashboard/lib/graph-diff.js +0 -104
  217. package/dashboard/lib/graph-diff.test.js +0 -75
  218. package/dashboard/lib/vault-parser.js +0 -556
  219. package/dashboard/lib/vault-parser.test.js +0 -254
  220. package/dashboard/public/app.js +0 -796
  221. package/dashboard/public/index.html +0 -52
  222. package/dashboard/public/styles.css +0 -221
  223. package/dashboard/server.js +0 -374
  224. package/dist/chunk-HA5M6KJB.js +0 -33
  225. package/dist/chunk-MAKNAHAW.js +0 -375
  226. package/dist/chunk-MDIH26GC.js +0 -183
  227. package/dist/chunk-MGDEINGP.js +0 -99
  228. package/dist/chunk-RVYA52PY.js +0 -363
  229. package/dist/commands/canvas.d.ts +0 -15
  230. package/dist/commands/canvas.js +0 -199
  231. package/dist/commands/sync-bd.d.ts +0 -10
  232. package/dist/commands/sync-bd.js +0 -9
@@ -0,0 +1,568 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/lib/webdav.ts
31
+ var webdav_exports = {};
32
+ __export(webdav_exports, {
33
+ WEBDAV_PREFIX: () => WEBDAV_PREFIX,
34
+ checkAuth: () => checkAuth,
35
+ createWebDAVHandler: () => createWebDAVHandler,
36
+ generatePropfindResponse: () => generatePropfindResponse,
37
+ handleCopy: () => handleCopy,
38
+ handleDelete: () => handleDelete,
39
+ handleGet: () => handleGet,
40
+ handleHead: () => handleHead,
41
+ handleMkcol: () => handleMkcol,
42
+ handleMove: () => handleMove,
43
+ handleOptions: () => handleOptions,
44
+ handlePropfind: () => handlePropfind,
45
+ handlePut: () => handlePut,
46
+ isPathSafe: () => isPathSafe,
47
+ resolveWebDAVPath: () => resolveWebDAVPath
48
+ });
49
+ module.exports = __toCommonJS(webdav_exports);
50
+ var fs = __toESM(require("fs"), 1);
51
+ var path = __toESM(require("path"), 1);
52
+ var WEBDAV_PREFIX = "/webdav";
53
+ var BLOCKED_PATHS = [
54
+ ".clawvault",
55
+ ".git",
56
+ ".obsidian",
57
+ "node_modules"
58
+ ];
59
+ var SUPPORTED_METHODS = ["GET", "PUT", "DELETE", "MKCOL", "PROPFIND", "OPTIONS", "HEAD", "MOVE", "COPY"];
60
+ function toRequestSegments(requestPath) {
61
+ return requestPath.replace(/\\/g, "/").split("/").filter(Boolean);
62
+ }
63
+ function isWithinRoot(fullPath, rootPath) {
64
+ const resolvedRoot = path.resolve(rootPath);
65
+ const relative2 = path.relative(resolvedRoot, fullPath);
66
+ return !(relative2.startsWith("..") || path.isAbsolute(relative2));
67
+ }
68
+ function isPathSafe(requestPath, rootPath) {
69
+ const pathParts = toRequestSegments(requestPath);
70
+ if (pathParts.includes("..")) {
71
+ return false;
72
+ }
73
+ const normalizedRelativePath = path.normalize(pathParts.join(path.sep));
74
+ const fullPath = path.resolve(rootPath, normalizedRelativePath);
75
+ if (!isWithinRoot(fullPath, rootPath)) {
76
+ return false;
77
+ }
78
+ for (const part of pathParts) {
79
+ if (BLOCKED_PATHS.includes(part)) {
80
+ return false;
81
+ }
82
+ }
83
+ return true;
84
+ }
85
+ function resolveWebDAVPath(requestPath, rootPath) {
86
+ const pathParts = toRequestSegments(requestPath);
87
+ if (pathParts.includes("..")) {
88
+ return null;
89
+ }
90
+ const normalizedRelativePath = path.normalize(pathParts.join(path.sep));
91
+ const fullPath = path.resolve(rootPath, normalizedRelativePath);
92
+ if (!isWithinRoot(fullPath, rootPath)) {
93
+ return null;
94
+ }
95
+ return fullPath;
96
+ }
97
+ function checkAuth(req, auth) {
98
+ if (!auth) {
99
+ return true;
100
+ }
101
+ const authHeader = req.headers.authorization;
102
+ if (!authHeader || !authHeader.startsWith("Basic ")) {
103
+ return false;
104
+ }
105
+ const base64Credentials = authHeader.slice(6);
106
+ const credentials = Buffer.from(base64Credentials, "base64").toString("utf-8");
107
+ const [username, password] = credentials.split(":");
108
+ return username === auth.username && password === auth.password;
109
+ }
110
+ function escapeXml(str) {
111
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
112
+ }
113
+ function formatWebDAVDate(date) {
114
+ return date.toUTCString();
115
+ }
116
+ function generatePropfindEntry(href, stats, isCollection) {
117
+ const resourceType = isCollection ? "<D:resourcetype><D:collection/></D:resourcetype>" : "<D:resourcetype/>";
118
+ const contentLength = stats && !isCollection ? `<D:getcontentlength>${stats.size}</D:getcontentlength>` : "";
119
+ const lastModified = stats ? `<D:getlastmodified>${formatWebDAVDate(stats.mtime)}</D:getlastmodified>` : "";
120
+ const etag = stats ? `<D:getetag>"${stats.mtime.getTime().toString(16)}-${stats.size.toString(16)}"</D:getetag>` : "";
121
+ const contentType = !isCollection ? "<D:getcontenttype>application/octet-stream</D:getcontenttype>" : "";
122
+ return ` <D:response>
123
+ <D:href>${escapeXml(href)}</D:href>
124
+ <D:propstat>
125
+ <D:prop>
126
+ ${resourceType}
127
+ ${contentLength}
128
+ ${lastModified}
129
+ ${etag}
130
+ ${contentType}
131
+ </D:prop>
132
+ <D:status>HTTP/1.1 200 OK</D:status>
133
+ </D:propstat>
134
+ </D:response>`;
135
+ }
136
+ function generatePropfindResponse(entries) {
137
+ const responseEntries = entries.map(
138
+ (e) => generatePropfindEntry(e.href, e.stats, e.isCollection)
139
+ ).join("\n");
140
+ return `<?xml version="1.0" encoding="utf-8"?>
141
+ <D:multistatus xmlns:D="DAV:">
142
+ ${responseEntries}
143
+ </D:multistatus>`;
144
+ }
145
+ function handleOptions(res, prefix) {
146
+ res.writeHead(200, {
147
+ "Allow": SUPPORTED_METHODS.join(", "),
148
+ "DAV": "1, 2",
149
+ "Content-Length": "0",
150
+ "Access-Control-Allow-Origin": "*",
151
+ "Access-Control-Allow-Methods": SUPPORTED_METHODS.join(", "),
152
+ "Access-Control-Allow-Headers": "Content-Type, Depth, Destination, Overwrite, Authorization",
153
+ "MS-Author-Via": "DAV"
154
+ });
155
+ res.end();
156
+ }
157
+ function handleHead(res, filePath) {
158
+ try {
159
+ const stats = fs.statSync(filePath);
160
+ if (stats.isDirectory()) {
161
+ res.writeHead(200, {
162
+ "Content-Type": "httpd/unix-directory",
163
+ "Last-Modified": formatWebDAVDate(stats.mtime),
164
+ "ETag": `"${stats.mtime.getTime().toString(16)}"`,
165
+ "Access-Control-Allow-Origin": "*"
166
+ });
167
+ } else {
168
+ res.writeHead(200, {
169
+ "Content-Type": "application/octet-stream",
170
+ "Content-Length": stats.size.toString(),
171
+ "Last-Modified": formatWebDAVDate(stats.mtime),
172
+ "ETag": `"${stats.mtime.getTime().toString(16)}-${stats.size.toString(16)}"`,
173
+ "Access-Control-Allow-Origin": "*"
174
+ });
175
+ }
176
+ res.end();
177
+ } catch (err) {
178
+ res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
179
+ res.end("Not Found");
180
+ }
181
+ }
182
+ function handleGet(res, filePath) {
183
+ try {
184
+ const stats = fs.statSync(filePath);
185
+ if (stats.isDirectory()) {
186
+ const entries = fs.readdirSync(filePath);
187
+ const listing = entries.join("\n");
188
+ res.writeHead(200, {
189
+ "Content-Type": "text/plain",
190
+ "Content-Length": Buffer.byteLength(listing).toString(),
191
+ "Access-Control-Allow-Origin": "*"
192
+ });
193
+ res.end(listing);
194
+ } else {
195
+ const content = fs.readFileSync(filePath);
196
+ res.writeHead(200, {
197
+ "Content-Type": "application/octet-stream",
198
+ "Content-Length": content.length.toString(),
199
+ "Last-Modified": formatWebDAVDate(stats.mtime),
200
+ "ETag": `"${stats.mtime.getTime().toString(16)}-${stats.size.toString(16)}"`,
201
+ "Access-Control-Allow-Origin": "*"
202
+ });
203
+ res.end(content);
204
+ }
205
+ } catch (err) {
206
+ res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
207
+ res.end("Not Found");
208
+ }
209
+ }
210
+ function handlePut(res, filePath, body) {
211
+ try {
212
+ const exists = fs.existsSync(filePath);
213
+ const dir = path.dirname(filePath);
214
+ if (!fs.existsSync(dir)) {
215
+ fs.mkdirSync(dir, { recursive: true });
216
+ }
217
+ fs.writeFileSync(filePath, body);
218
+ const status = exists ? 204 : 201;
219
+ res.writeHead(status, {
220
+ "Content-Length": "0",
221
+ "Access-Control-Allow-Origin": "*"
222
+ });
223
+ res.end();
224
+ } catch (err) {
225
+ res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
226
+ res.end(`Error: ${err}`);
227
+ }
228
+ }
229
+ function handleDelete(res, filePath) {
230
+ try {
231
+ if (!fs.existsSync(filePath)) {
232
+ res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
233
+ res.end("Not Found");
234
+ return;
235
+ }
236
+ const stats = fs.statSync(filePath);
237
+ if (stats.isDirectory()) {
238
+ fs.rmSync(filePath, { recursive: true });
239
+ } else {
240
+ fs.unlinkSync(filePath);
241
+ }
242
+ res.writeHead(204, {
243
+ "Content-Length": "0",
244
+ "Access-Control-Allow-Origin": "*"
245
+ });
246
+ res.end();
247
+ } catch (err) {
248
+ res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
249
+ res.end(`Error: ${err}`);
250
+ }
251
+ }
252
+ function handleMkcol(res, filePath) {
253
+ try {
254
+ if (fs.existsSync(filePath)) {
255
+ res.writeHead(405, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
256
+ res.end("Resource already exists");
257
+ return;
258
+ }
259
+ const parent = path.dirname(filePath);
260
+ if (!fs.existsSync(parent)) {
261
+ res.writeHead(409, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
262
+ res.end("Parent directory does not exist");
263
+ return;
264
+ }
265
+ fs.mkdirSync(filePath);
266
+ res.writeHead(201, {
267
+ "Content-Length": "0",
268
+ "Access-Control-Allow-Origin": "*"
269
+ });
270
+ res.end();
271
+ } catch (err) {
272
+ res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
273
+ res.end(`Error: ${err}`);
274
+ }
275
+ }
276
+ function handlePropfind(res, filePath, webdavPath, prefix, depth) {
277
+ try {
278
+ if (!fs.existsSync(filePath)) {
279
+ res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
280
+ res.end("Not Found");
281
+ return;
282
+ }
283
+ const stats = fs.statSync(filePath);
284
+ const entries = [];
285
+ const normalizedWebdavPath = webdavPath.startsWith("/") ? webdavPath : "/" + webdavPath;
286
+ const href = prefix + normalizedWebdavPath;
287
+ entries.push({
288
+ href: href.endsWith("/") || stats.isDirectory() ? href : href,
289
+ stats,
290
+ isCollection: stats.isDirectory()
291
+ });
292
+ if (stats.isDirectory() && depth !== "0") {
293
+ try {
294
+ const children = fs.readdirSync(filePath);
295
+ for (const child of children) {
296
+ if (BLOCKED_PATHS.includes(child)) {
297
+ continue;
298
+ }
299
+ const childPath = path.join(filePath, child);
300
+ const childWebdavPath = normalizedWebdavPath.endsWith("/") ? normalizedWebdavPath + child : normalizedWebdavPath + "/" + child;
301
+ try {
302
+ const childStats = fs.statSync(childPath);
303
+ entries.push({
304
+ href: prefix + childWebdavPath,
305
+ stats: childStats,
306
+ isCollection: childStats.isDirectory()
307
+ });
308
+ } catch {
309
+ }
310
+ }
311
+ } catch {
312
+ }
313
+ }
314
+ const xml = generatePropfindResponse(entries);
315
+ res.writeHead(207, {
316
+ "Content-Type": "application/xml; charset=utf-8",
317
+ "Content-Length": Buffer.byteLength(xml).toString(),
318
+ "Access-Control-Allow-Origin": "*"
319
+ });
320
+ res.end(xml);
321
+ } catch (err) {
322
+ res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
323
+ res.end(`Error: ${err}`);
324
+ }
325
+ }
326
+ function handleMove(res, sourcePath, destinationPath, overwrite) {
327
+ try {
328
+ if (!fs.existsSync(sourcePath)) {
329
+ res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
330
+ res.end("Source not found");
331
+ return;
332
+ }
333
+ if (!destinationPath) {
334
+ res.writeHead(400, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
335
+ res.end("Destination header required");
336
+ return;
337
+ }
338
+ const destExists = fs.existsSync(destinationPath);
339
+ if (destExists && !overwrite) {
340
+ res.writeHead(412, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
341
+ res.end("Destination exists and Overwrite is F");
342
+ return;
343
+ }
344
+ const destDir = path.dirname(destinationPath);
345
+ if (!fs.existsSync(destDir)) {
346
+ fs.mkdirSync(destDir, { recursive: true });
347
+ }
348
+ if (destExists) {
349
+ const destStats = fs.statSync(destinationPath);
350
+ if (destStats.isDirectory()) {
351
+ fs.rmSync(destinationPath, { recursive: true });
352
+ } else {
353
+ fs.unlinkSync(destinationPath);
354
+ }
355
+ }
356
+ fs.renameSync(sourcePath, destinationPath);
357
+ const status = destExists ? 204 : 201;
358
+ res.writeHead(status, {
359
+ "Content-Length": "0",
360
+ "Access-Control-Allow-Origin": "*"
361
+ });
362
+ res.end();
363
+ } catch (err) {
364
+ res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
365
+ res.end(`Error: ${err}`);
366
+ }
367
+ }
368
+ function handleCopy(res, sourcePath, destinationPath, overwrite) {
369
+ try {
370
+ if (!fs.existsSync(sourcePath)) {
371
+ res.writeHead(404, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
372
+ res.end("Source not found");
373
+ return;
374
+ }
375
+ if (!destinationPath) {
376
+ res.writeHead(400, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
377
+ res.end("Destination header required");
378
+ return;
379
+ }
380
+ const destExists = fs.existsSync(destinationPath);
381
+ if (destExists && !overwrite) {
382
+ res.writeHead(412, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
383
+ res.end("Destination exists and Overwrite is F");
384
+ return;
385
+ }
386
+ const destDir = path.dirname(destinationPath);
387
+ if (!fs.existsSync(destDir)) {
388
+ fs.mkdirSync(destDir, { recursive: true });
389
+ }
390
+ const sourceStats = fs.statSync(sourcePath);
391
+ if (sourceStats.isDirectory()) {
392
+ copyDirRecursive(sourcePath, destinationPath);
393
+ } else {
394
+ fs.copyFileSync(sourcePath, destinationPath);
395
+ }
396
+ const status = destExists ? 204 : 201;
397
+ res.writeHead(status, {
398
+ "Content-Length": "0",
399
+ "Access-Control-Allow-Origin": "*"
400
+ });
401
+ res.end();
402
+ } catch (err) {
403
+ res.writeHead(500, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
404
+ res.end(`Error: ${err}`);
405
+ }
406
+ }
407
+ function copyDirRecursive(src, dest) {
408
+ if (!fs.existsSync(dest)) {
409
+ fs.mkdirSync(dest, { recursive: true });
410
+ }
411
+ const entries = fs.readdirSync(src, { withFileTypes: true });
412
+ for (const entry of entries) {
413
+ const srcPath = path.join(src, entry.name);
414
+ const destPath = path.join(dest, entry.name);
415
+ if (entry.isDirectory()) {
416
+ copyDirRecursive(srcPath, destPath);
417
+ } else {
418
+ fs.copyFileSync(srcPath, destPath);
419
+ }
420
+ }
421
+ }
422
+ function parseDestinationHeader(destinationHeader, prefix, rootPath) {
423
+ if (!destinationHeader) {
424
+ return null;
425
+ }
426
+ try {
427
+ let destPath;
428
+ if (destinationHeader.startsWith("http://") || destinationHeader.startsWith("https://")) {
429
+ const url = new URL(destinationHeader);
430
+ destPath = decodeURIComponent(url.pathname);
431
+ } else {
432
+ destPath = decodeURIComponent(destinationHeader);
433
+ }
434
+ if (destPath.startsWith(prefix)) {
435
+ destPath = destPath.slice(prefix.length);
436
+ }
437
+ return resolveWebDAVPath(destPath, rootPath);
438
+ } catch {
439
+ return null;
440
+ }
441
+ }
442
+ function createWebDAVHandler(config) {
443
+ const { rootPath, prefix = WEBDAV_PREFIX, auth } = config;
444
+ return async (req, res) => {
445
+ const rawUrl = req.url || "/";
446
+ if (rawUrl.includes("..")) {
447
+ if (rawUrl.startsWith(prefix)) {
448
+ res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
449
+ res.end("Forbidden");
450
+ return true;
451
+ }
452
+ }
453
+ const url = new URL(rawUrl, `http://${req.headers.host || "localhost"}`);
454
+ const pathname = decodeURIComponent(url.pathname);
455
+ if (!pathname.startsWith(prefix)) {
456
+ return false;
457
+ }
458
+ let webdavPath = pathname.slice(prefix.length);
459
+ if (!webdavPath.startsWith("/")) {
460
+ webdavPath = "/" + webdavPath;
461
+ }
462
+ if (req.method === "OPTIONS") {
463
+ handleOptions(res, prefix);
464
+ return true;
465
+ }
466
+ if (!checkAuth(req, auth)) {
467
+ res.writeHead(401, {
468
+ "WWW-Authenticate": 'Basic realm="ClawVault WebDAV"',
469
+ "Content-Type": "text/plain",
470
+ "Access-Control-Allow-Origin": "*"
471
+ });
472
+ res.end("Unauthorized");
473
+ return true;
474
+ }
475
+ if (!isPathSafe(webdavPath, rootPath)) {
476
+ res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
477
+ res.end("Forbidden");
478
+ return true;
479
+ }
480
+ const filePath = resolveWebDAVPath(webdavPath, rootPath);
481
+ if (!filePath) {
482
+ res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
483
+ res.end("Forbidden");
484
+ return true;
485
+ }
486
+ const depth = req.headers.depth || "infinity";
487
+ const overwrite = req.headers.overwrite?.toUpperCase() !== "F";
488
+ const destinationHeader = req.headers.destination;
489
+ switch (req.method) {
490
+ case "HEAD":
491
+ handleHead(res, filePath);
492
+ return true;
493
+ case "GET":
494
+ handleGet(res, filePath);
495
+ return true;
496
+ case "PUT": {
497
+ const chunks = [];
498
+ for await (const chunk of req) {
499
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
500
+ }
501
+ const body = Buffer.concat(chunks);
502
+ handlePut(res, filePath, body);
503
+ return true;
504
+ }
505
+ case "DELETE":
506
+ handleDelete(res, filePath);
507
+ return true;
508
+ case "MKCOL":
509
+ handleMkcol(res, filePath);
510
+ return true;
511
+ case "PROPFIND":
512
+ handlePropfind(res, filePath, webdavPath, prefix, depth);
513
+ return true;
514
+ case "MOVE": {
515
+ const destPath = parseDestinationHeader(destinationHeader, prefix, rootPath);
516
+ if (destPath && destinationHeader) {
517
+ const destWebdavPath = destinationHeader.includes(prefix) ? destinationHeader.slice(destinationHeader.indexOf(prefix) + prefix.length) : destinationHeader;
518
+ if (!isPathSafe(destWebdavPath, rootPath)) {
519
+ res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
520
+ res.end("Forbidden");
521
+ return true;
522
+ }
523
+ }
524
+ handleMove(res, filePath, destPath, overwrite);
525
+ return true;
526
+ }
527
+ case "COPY": {
528
+ const destPath = parseDestinationHeader(destinationHeader, prefix, rootPath);
529
+ if (destPath && destinationHeader) {
530
+ const destWebdavPath = destinationHeader.includes(prefix) ? destinationHeader.slice(destinationHeader.indexOf(prefix) + prefix.length) : destinationHeader;
531
+ if (!isPathSafe(destWebdavPath, rootPath)) {
532
+ res.writeHead(403, { "Content-Type": "text/plain", "Access-Control-Allow-Origin": "*" });
533
+ res.end("Forbidden");
534
+ return true;
535
+ }
536
+ }
537
+ handleCopy(res, filePath, destPath, overwrite);
538
+ return true;
539
+ }
540
+ default:
541
+ res.writeHead(405, {
542
+ "Allow": SUPPORTED_METHODS.join(", "),
543
+ "Content-Type": "text/plain",
544
+ "Access-Control-Allow-Origin": "*"
545
+ });
546
+ res.end("Method Not Allowed");
547
+ return true;
548
+ }
549
+ };
550
+ }
551
+ // Annotate the CommonJS export names for ESM import in node:
552
+ 0 && (module.exports = {
553
+ WEBDAV_PREFIX,
554
+ checkAuth,
555
+ createWebDAVHandler,
556
+ generatePropfindResponse,
557
+ handleCopy,
558
+ handleDelete,
559
+ handleGet,
560
+ handleHead,
561
+ handleMkcol,
562
+ handleMove,
563
+ handleOptions,
564
+ handlePropfind,
565
+ handlePut,
566
+ isPathSafe,
567
+ resolveWebDAVPath
568
+ });
@@ -0,0 +1,109 @@
1
+ import * as fs from 'fs';
2
+ import { IncomingMessage, ServerResponse } from 'http';
3
+
4
+ /**
5
+ * WebDAV Handler for ClawVault
6
+ *
7
+ * Implements WebDAV protocol support for Obsidian mobile sync via Remotely Save plugin.
8
+ * Uses only Node built-in modules (http, fs, path) - zero external dependencies.
9
+ *
10
+ * Supported methods:
11
+ * - GET: Serve file contents
12
+ * - PUT: Write/create file (creates parent dirs if needed)
13
+ * - DELETE: Delete file or directory
14
+ * - MKCOL: Create directory
15
+ * - PROPFIND: List directory contents or file properties (XML response)
16
+ * - OPTIONS: Return allowed methods + DAV header
17
+ * - HEAD: File metadata without body
18
+ * - MOVE: Rename/move file (uses Destination header)
19
+ * - COPY: Copy file
20
+ */
21
+
22
+ interface WebDAVConfig {
23
+ /** Root path for WebDAV files (vault path) */
24
+ rootPath: string;
25
+ /** URL prefix for WebDAV routes (default: /webdav) */
26
+ prefix?: string;
27
+ /** Optional Basic Auth credentials */
28
+ auth?: {
29
+ username: string;
30
+ password: string;
31
+ };
32
+ }
33
+ interface WebDAVRequest {
34
+ method: string;
35
+ path: string;
36
+ headers: Record<string, string | string[] | undefined>;
37
+ body?: string;
38
+ }
39
+ interface WebDAVResponse {
40
+ status: number;
41
+ headers: Record<string, string>;
42
+ body?: string;
43
+ }
44
+ declare const WEBDAV_PREFIX = "/webdav";
45
+ /**
46
+ * Check if a path is safe (no traversal attacks, not blocked)
47
+ */
48
+ declare function isPathSafe(requestPath: string, rootPath: string): boolean;
49
+ /**
50
+ * Resolve a WebDAV path to filesystem path
51
+ */
52
+ declare function resolveWebDAVPath(requestPath: string, rootPath: string): string | null;
53
+ /**
54
+ * Check Basic Auth credentials
55
+ */
56
+ declare function checkAuth(req: IncomingMessage, auth?: {
57
+ username: string;
58
+ password: string;
59
+ }): boolean;
60
+ /**
61
+ * Generate full PROPFIND response XML
62
+ */
63
+ declare function generatePropfindResponse(entries: Array<{
64
+ href: string;
65
+ stats: fs.Stats | null;
66
+ isCollection: boolean;
67
+ }>): string;
68
+ /**
69
+ * Handle OPTIONS request
70
+ */
71
+ declare function handleOptions(res: ServerResponse, prefix: string): void;
72
+ /**
73
+ * Handle HEAD request
74
+ */
75
+ declare function handleHead(res: ServerResponse, filePath: string): void;
76
+ /**
77
+ * Handle GET request
78
+ */
79
+ declare function handleGet(res: ServerResponse, filePath: string): void;
80
+ /**
81
+ * Handle PUT request
82
+ */
83
+ declare function handlePut(res: ServerResponse, filePath: string, body: Buffer): void;
84
+ /**
85
+ * Handle DELETE request
86
+ */
87
+ declare function handleDelete(res: ServerResponse, filePath: string): void;
88
+ /**
89
+ * Handle MKCOL request (create directory)
90
+ */
91
+ declare function handleMkcol(res: ServerResponse, filePath: string): void;
92
+ /**
93
+ * Handle PROPFIND request
94
+ */
95
+ declare function handlePropfind(res: ServerResponse, filePath: string, webdavPath: string, prefix: string, depth: string): void;
96
+ /**
97
+ * Handle MOVE request
98
+ */
99
+ declare function handleMove(res: ServerResponse, sourcePath: string, destinationPath: string | null, overwrite: boolean): void;
100
+ /**
101
+ * Handle COPY request
102
+ */
103
+ declare function handleCopy(res: ServerResponse, sourcePath: string, destinationPath: string | null, overwrite: boolean): void;
104
+ /**
105
+ * Create WebDAV request handler
106
+ */
107
+ declare function createWebDAVHandler(config: WebDAVConfig): (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
108
+
109
+ export { WEBDAV_PREFIX, type WebDAVConfig, type WebDAVRequest, type WebDAVResponse, checkAuth, createWebDAVHandler, generatePropfindResponse, handleCopy, handleDelete, handleGet, handleHead, handleMkcol, handleMove, handleOptions, handlePropfind, handlePut, isPathSafe, resolveWebDAVPath };
@@ -15,6 +15,7 @@ import {
15
15
  isPathSafe,
16
16
  resolveWebDAVPath
17
17
  } from "../chunk-IVRIKYFE.js";
18
+ import "../chunk-3RG5ZIWI.js";
18
19
  export {
19
20
  WEBDAV_PREFIX,
20
21
  checkAuth,