clawvault 3.1.0 → 3.2.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 (273) hide show
  1. package/README.md +422 -141
  2. package/bin/clawvault.js +10 -2
  3. package/bin/command-registration.test.js +3 -1
  4. package/bin/command-runtime.js +9 -1
  5. package/bin/register-core-commands.js +23 -28
  6. package/bin/register-maintenance-commands.js +39 -3
  7. package/bin/register-query-commands.js +58 -29
  8. package/bin/register-tailscale-commands.js +106 -0
  9. package/bin/register-task-commands.js +18 -1
  10. package/bin/register-task-commands.test.js +16 -0
  11. package/bin/register-vault-operations-commands.js +29 -1
  12. package/bin/register-workgraph-commands.js +451 -0
  13. package/dashboard/lib/graph-diff.js +104 -0
  14. package/dashboard/lib/graph-diff.test.js +75 -0
  15. package/dashboard/lib/vault-parser.js +556 -0
  16. package/dashboard/lib/vault-parser.test.js +254 -0
  17. package/dashboard/public/app.js +796 -0
  18. package/dashboard/public/index.html +52 -0
  19. package/dashboard/public/styles.css +221 -0
  20. package/dashboard/server.js +374 -0
  21. package/dist/{chunk-C7OK5WKP.js → chunk-2JQ3O2YL.js} +4 -4
  22. package/dist/{chunk-VR5NE7PZ.js → chunk-2RAZ4ZFE.js} +1 -1
  23. package/dist/{chunk-F2JEUD4J.js → chunk-4ITRXIVT.js} +5 -7
  24. package/dist/{chunk-GUKMRGM7.js → chunk-4OXMU5S2.js} +1 -1
  25. package/dist/chunk-5PJ4STIC.js +465 -0
  26. package/dist/{chunk-62YTUT6J.js → chunk-AZYOKJYC.js} +2 -2
  27. package/dist/chunk-BSJ6RIT7.js +447 -0
  28. package/dist/chunk-ECRZL5XR.js +50 -0
  29. package/dist/chunk-ERNE2FZ5.js +189 -0
  30. package/dist/{chunk-WAZ3NLWL.js → chunk-F55HGNU4.js} +0 -47
  31. package/dist/{chunk-VGLOTGAS.js → chunk-FAKNOB7Y.js} +2 -2
  32. package/dist/{chunk-QK3UCXWL.js → chunk-FHFUXL6G.js} +2 -2
  33. package/dist/chunk-GNJL4YGR.js +79 -0
  34. package/dist/chunk-HR4KN6S2.js +152 -0
  35. package/dist/{chunk-OZ7RIXTO.js → chunk-IIOU45CK.js} +1 -1
  36. package/dist/chunk-IJBFGPCS.js +33 -0
  37. package/dist/chunk-IVRIKYFE.js +520 -0
  38. package/dist/chunk-K7PNYS45.js +93 -0
  39. package/dist/chunk-MDIH26GC.js +183 -0
  40. package/dist/{chunk-LYHGEHXG.js → chunk-MFAWT5O5.js} +0 -1
  41. package/dist/{chunk-H34S76MB.js → chunk-MNPUYCHQ.js} +6 -6
  42. package/dist/chunk-NTOPJI7W.js +207 -0
  43. package/dist/{chunk-QBLMXKF2.js → chunk-OIWVQYQF.js} +1 -1
  44. package/dist/chunk-PG56HX5T.js +154 -0
  45. package/dist/{chunk-LNJA2UGL.js → chunk-PI4WMLMG.js} +7 -84
  46. package/dist/chunk-QMHPQYUV.js +363 -0
  47. package/dist/{chunk-H62BP7RI.js → chunk-QPDDIHXE.js} +209 -43
  48. package/dist/{chunk-N2AXRYLC.js → chunk-QWQ3TIKS.js} +1 -1
  49. package/dist/{chunk-3DHXQHYG.js → chunk-R2MIW5G7.js} +1 -1
  50. package/dist/{chunk-SJSFRIYS.js → chunk-S5OJEGFG.js} +2 -2
  51. package/dist/chunk-SS4B7P7V.js +99 -0
  52. package/dist/chunk-TIGW564L.js +628 -0
  53. package/dist/chunk-U67V476Y.js +35 -0
  54. package/dist/{chunk-JY6FYXIT.js → chunk-UCQAOZHW.js} +6 -11
  55. package/dist/{chunk-ITPEXLHA.js → chunk-URXDAUVH.js} +24 -5
  56. package/dist/chunk-WIOLLGAD.js +190 -0
  57. package/dist/{chunk-3WRJEKN4.js → chunk-WJVWINEM.js} +72 -8
  58. package/dist/chunk-WMGIIABP.js +15 -0
  59. package/dist/{chunk-33UGEQRT.js → chunk-X3SPPUFG.js} +151 -64
  60. package/dist/{chunk-3NSBOUT3.js → chunk-Y3TIJEBP.js} +314 -79
  61. package/dist/chunk-Y6VJKXGL.js +373 -0
  62. package/dist/{chunk-LI4O6NVK.js → chunk-YDWHS4LJ.js} +49 -9
  63. package/dist/{chunk-U55BGUAU.js → chunk-YNIPYN4F.js} +5 -5
  64. package/dist/chunk-YXQCA6B7.js +226 -0
  65. package/dist/cli/index.js +26 -22
  66. package/dist/commands/archive.js +3 -3
  67. package/dist/commands/backlog.js +3 -3
  68. package/dist/commands/blocked.js +3 -3
  69. package/dist/commands/canvas.d.ts +15 -0
  70. package/dist/commands/canvas.js +200 -0
  71. package/dist/commands/checkpoint.js +2 -2
  72. package/dist/commands/compat.js +2 -2
  73. package/dist/commands/context.js +7 -5
  74. package/dist/commands/doctor.d.ts +11 -7
  75. package/dist/commands/doctor.js +16 -14
  76. package/dist/commands/embed.js +5 -6
  77. package/dist/commands/entities.js +2 -2
  78. package/dist/commands/graph.js +3 -3
  79. package/dist/commands/inject.d.ts +1 -1
  80. package/dist/commands/inject.js +4 -5
  81. package/dist/commands/kanban.js +4 -4
  82. package/dist/commands/link.js +2 -2
  83. package/dist/commands/migrate-observations.js +4 -4
  84. package/dist/commands/observe.d.ts +0 -1
  85. package/dist/commands/observe.js +13 -12
  86. package/dist/commands/project.js +5 -5
  87. package/dist/commands/rebuild-embeddings.d.ts +21 -0
  88. package/dist/commands/rebuild-embeddings.js +91 -0
  89. package/dist/commands/rebuild.js +12 -11
  90. package/dist/commands/recover.js +3 -3
  91. package/dist/commands/reflect.js +6 -7
  92. package/dist/commands/repair-session.js +1 -1
  93. package/dist/commands/replay.js +14 -14
  94. package/dist/commands/session-recap.js +1 -1
  95. package/dist/commands/setup.d.ts +2 -89
  96. package/dist/commands/setup.js +3 -21
  97. package/dist/commands/shell-init.js +1 -1
  98. package/dist/commands/sleep.d.ts +1 -1
  99. package/dist/commands/sleep.js +18 -17
  100. package/dist/commands/status.d.ts +2 -0
  101. package/dist/commands/status.js +40 -30
  102. package/dist/commands/sync-bd.d.ts +10 -0
  103. package/dist/commands/sync-bd.js +10 -0
  104. package/dist/commands/tailscale.d.ts +52 -0
  105. package/dist/commands/tailscale.js +26 -0
  106. package/dist/commands/task.js +4 -4
  107. package/dist/commands/template.js +2 -2
  108. package/dist/commands/wake.d.ts +1 -1
  109. package/dist/commands/wake.js +11 -10
  110. package/dist/index.d.ts +334 -191
  111. package/dist/index.js +432 -108
  112. package/dist/{inject-Bzi5E-By.d.ts → inject-DYUrDqQO.d.ts} +3 -3
  113. package/dist/ledger-B7g7jhqG.d.ts +44 -0
  114. package/dist/lib/auto-linker.js +1 -1
  115. package/dist/lib/canvas-layout.d.ts +115 -0
  116. package/dist/lib/canvas-layout.js +35 -0
  117. package/dist/lib/config.d.ts +27 -3
  118. package/dist/lib/config.js +4 -2
  119. package/dist/lib/entity-index.js +1 -1
  120. package/dist/lib/project-utils.js +4 -4
  121. package/dist/lib/session-repair.js +1 -1
  122. package/dist/lib/session-utils.js +1 -1
  123. package/dist/lib/tailscale.d.ts +225 -0
  124. package/dist/lib/tailscale.js +50 -0
  125. package/dist/lib/task-utils.js +3 -3
  126. package/dist/lib/template-engine.js +1 -1
  127. package/dist/lib/webdav.d.ts +109 -0
  128. package/dist/lib/webdav.js +35 -0
  129. package/dist/plugin/index.d.ts +344 -28
  130. package/dist/plugin/index.js +3919 -227
  131. package/dist/registry-BR4326o0.d.ts +30 -0
  132. package/dist/store-CA-6sKCJ.d.ts +34 -0
  133. package/dist/thread-B9LhXNU0.d.ts +41 -0
  134. package/dist/{types-Y2_Um2Ls.d.ts → types-BbWJoC1c.d.ts} +1 -44
  135. package/dist/workgraph/index.d.ts +5 -0
  136. package/dist/workgraph/index.js +23 -0
  137. package/dist/workgraph/ledger.d.ts +2 -0
  138. package/dist/workgraph/ledger.js +25 -0
  139. package/dist/workgraph/registry.d.ts +2 -0
  140. package/dist/workgraph/registry.js +19 -0
  141. package/dist/workgraph/store.d.ts +2 -0
  142. package/dist/workgraph/store.js +25 -0
  143. package/dist/workgraph/thread.d.ts +2 -0
  144. package/dist/workgraph/thread.js +25 -0
  145. package/dist/workgraph/types.d.ts +54 -0
  146. package/dist/workgraph/types.js +7 -0
  147. package/hooks/clawvault/HOOK.md +113 -0
  148. package/hooks/clawvault/handler.js +1559 -0
  149. package/hooks/clawvault/handler.test.js +510 -0
  150. package/hooks/clawvault/openclaw.plugin.json +72 -0
  151. package/openclaw.plugin.json +235 -30
  152. package/package.json +20 -20
  153. package/dist/chunk-3RG5ZIWI.js +0 -10
  154. package/dist/chunk-3ZIH425O.js +0 -871
  155. package/dist/chunk-6U6MK36V.js +0 -205
  156. package/dist/chunk-CMB7UL7C.js +0 -327
  157. package/dist/chunk-D2H45LON.js +0 -1074
  158. package/dist/chunk-E7MFQB6D.js +0 -163
  159. package/dist/chunk-GQSLDZTS.js +0 -560
  160. package/dist/chunk-MFM6K7PU.js +0 -374
  161. package/dist/chunk-MXSSG3QU.js +0 -42
  162. package/dist/chunk-OCGVIN3L.js +0 -88
  163. package/dist/chunk-PAH27GSN.js +0 -108
  164. package/dist/chunk-YCUNCH2I.js +0 -78
  165. package/dist/cli/index.cjs +0 -8584
  166. package/dist/cli/index.d.cts +0 -5
  167. package/dist/commands/archive.cjs +0 -287
  168. package/dist/commands/archive.d.cts +0 -11
  169. package/dist/commands/backlog.cjs +0 -721
  170. package/dist/commands/backlog.d.cts +0 -53
  171. package/dist/commands/blocked.cjs +0 -204
  172. package/dist/commands/blocked.d.cts +0 -26
  173. package/dist/commands/checkpoint.cjs +0 -244
  174. package/dist/commands/checkpoint.d.cts +0 -41
  175. package/dist/commands/compat.cjs +0 -294
  176. package/dist/commands/compat.d.cts +0 -28
  177. package/dist/commands/context.cjs +0 -2990
  178. package/dist/commands/context.d.cts +0 -2
  179. package/dist/commands/doctor.cjs +0 -2986
  180. package/dist/commands/doctor.d.cts +0 -21
  181. package/dist/commands/embed.cjs +0 -232
  182. package/dist/commands/embed.d.cts +0 -17
  183. package/dist/commands/entities.cjs +0 -141
  184. package/dist/commands/entities.d.cts +0 -7
  185. package/dist/commands/graph.cjs +0 -501
  186. package/dist/commands/graph.d.cts +0 -21
  187. package/dist/commands/inject.cjs +0 -1636
  188. package/dist/commands/inject.d.cts +0 -2
  189. package/dist/commands/kanban.cjs +0 -884
  190. package/dist/commands/kanban.d.cts +0 -63
  191. package/dist/commands/link.cjs +0 -965
  192. package/dist/commands/link.d.cts +0 -11
  193. package/dist/commands/migrate-observations.cjs +0 -362
  194. package/dist/commands/migrate-observations.d.cts +0 -19
  195. package/dist/commands/observe.cjs +0 -4099
  196. package/dist/commands/observe.d.cts +0 -23
  197. package/dist/commands/project.cjs +0 -1341
  198. package/dist/commands/project.d.cts +0 -85
  199. package/dist/commands/rebuild.cjs +0 -3136
  200. package/dist/commands/rebuild.d.cts +0 -11
  201. package/dist/commands/recover.cjs +0 -361
  202. package/dist/commands/recover.d.cts +0 -38
  203. package/dist/commands/reflect.cjs +0 -1008
  204. package/dist/commands/reflect.d.cts +0 -11
  205. package/dist/commands/repair-session.cjs +0 -457
  206. package/dist/commands/repair-session.d.cts +0 -38
  207. package/dist/commands/replay.cjs +0 -4103
  208. package/dist/commands/replay.d.cts +0 -16
  209. package/dist/commands/session-recap.cjs +0 -353
  210. package/dist/commands/session-recap.d.cts +0 -27
  211. package/dist/commands/setup.cjs +0 -1278
  212. package/dist/commands/setup.d.cts +0 -99
  213. package/dist/commands/shell-init.cjs +0 -75
  214. package/dist/commands/shell-init.d.cts +0 -7
  215. package/dist/commands/sleep.cjs +0 -6029
  216. package/dist/commands/sleep.d.cts +0 -36
  217. package/dist/commands/status.cjs +0 -2737
  218. package/dist/commands/status.d.cts +0 -52
  219. package/dist/commands/task.cjs +0 -1236
  220. package/dist/commands/task.d.cts +0 -97
  221. package/dist/commands/template.cjs +0 -457
  222. package/dist/commands/template.d.cts +0 -36
  223. package/dist/commands/wake.cjs +0 -2627
  224. package/dist/commands/wake.d.cts +0 -22
  225. package/dist/context-BUGaWpyL.d.cts +0 -46
  226. package/dist/index.cjs +0 -12373
  227. package/dist/index.d.cts +0 -854
  228. package/dist/inject-Bzi5E-By.d.cts +0 -137
  229. package/dist/lib/auto-linker.cjs +0 -176
  230. package/dist/lib/auto-linker.d.cts +0 -26
  231. package/dist/lib/config.cjs +0 -78
  232. package/dist/lib/config.d.cts +0 -11
  233. package/dist/lib/entity-index.cjs +0 -84
  234. package/dist/lib/entity-index.d.cts +0 -26
  235. package/dist/lib/project-utils.cjs +0 -864
  236. package/dist/lib/project-utils.d.cts +0 -97
  237. package/dist/lib/session-repair.cjs +0 -239
  238. package/dist/lib/session-repair.d.cts +0 -110
  239. package/dist/lib/session-utils.cjs +0 -209
  240. package/dist/lib/session-utils.d.cts +0 -63
  241. package/dist/lib/task-utils.cjs +0 -1137
  242. package/dist/lib/task-utils.d.cts +0 -208
  243. package/dist/lib/template-engine.cjs +0 -47
  244. package/dist/lib/template-engine.d.cts +0 -11
  245. package/dist/plugin/index.cjs +0 -1907
  246. package/dist/plugin/index.d.cts +0 -36
  247. package/dist/plugin/inject.cjs +0 -356
  248. package/dist/plugin/inject.d.cts +0 -54
  249. package/dist/plugin/inject.d.ts +0 -54
  250. package/dist/plugin/inject.js +0 -17
  251. package/dist/plugin/observe.cjs +0 -631
  252. package/dist/plugin/observe.d.cts +0 -39
  253. package/dist/plugin/observe.d.ts +0 -39
  254. package/dist/plugin/observe.js +0 -18
  255. package/dist/plugin/templates.cjs +0 -593
  256. package/dist/plugin/templates.d.cts +0 -52
  257. package/dist/plugin/templates.d.ts +0 -52
  258. package/dist/plugin/templates.js +0 -25
  259. package/dist/plugin/types.cjs +0 -18
  260. package/dist/plugin/types.d.cts +0 -209
  261. package/dist/plugin/types.d.ts +0 -209
  262. package/dist/plugin/types.js +0 -0
  263. package/dist/plugin/vault.cjs +0 -927
  264. package/dist/plugin/vault.d.cts +0 -68
  265. package/dist/plugin/vault.d.ts +0 -68
  266. package/dist/plugin/vault.js +0 -22
  267. package/dist/types-Y2_Um2Ls.d.cts +0 -205
  268. package/templates/memory-event.md +0 -67
  269. package/templates/party.md +0 -63
  270. package/templates/primitive-registry.yaml +0 -551
  271. package/templates/run.md +0 -68
  272. package/templates/trigger.md +0 -68
  273. package/templates/workspace.md +0 -50
@@ -0,0 +1,52 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>ClawVault Graph Dashboard</title>
7
+ <link rel="stylesheet" href="/styles.css">
8
+ </head>
9
+ <body>
10
+ <header class="toolbar">
11
+ <div class="toolbar-title">
12
+ <h1>ClawVault Live Graph</h1>
13
+ <p id="stats">Loading graph...</p>
14
+ </div>
15
+ <div class="toolbar-controls">
16
+ <input id="search" type="search" placeholder="Search nodes, ids, tags..." autocomplete="off">
17
+ <select id="category-filter">
18
+ <option value="all">All categories</option>
19
+ </select>
20
+ <select id="tag-filter">
21
+ <option value="all">All tags</option>
22
+ </select>
23
+ <select id="node-type-filter">
24
+ <option value="all">All node types</option>
25
+ <option value="resolved">Resolved only</option>
26
+ <option value="missing">Missing only</option>
27
+ </select>
28
+ <button id="tv-mode" type="button" aria-pressed="false">TV Mode</button>
29
+ <button id="refresh" type="button">Refresh</button>
30
+ <div class="status-pill" id="realtime-status">Realtime: connecting...</div>
31
+ </div>
32
+ </header>
33
+
34
+ <main class="layout">
35
+ <section id="graph" aria-label="Vault graph visualization"></section>
36
+ <aside class="details-panel">
37
+ <h2>Node Details</h2>
38
+ <div id="node-details">
39
+ <p>Select a node to inspect details and connections.</p>
40
+ </div>
41
+ </aside>
42
+ </main>
43
+
44
+ <script src="/vendor/force-graph.min.js"></script>
45
+ <script>
46
+ if (!window.ForceGraph) {
47
+ document.write('<script src="https://unpkg.com/force-graph"><\/script>');
48
+ }
49
+ </script>
50
+ <script type="module" src="/app.js"></script>
51
+ </body>
52
+ </html>
@@ -0,0 +1,221 @@
1
+ :root {
2
+ color-scheme: dark;
3
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
4
+ --bg: #070a10;
5
+ --bg-elevated: #0d1522;
6
+ --panel: #111b2a;
7
+ --panel-border: #213247;
8
+ --text: #dce9ff;
9
+ --muted: #90a7c3;
10
+ --accent: #75d3ff;
11
+ --accent-strong: #b7f0ff;
12
+ --ok: #58f1c6;
13
+ --warn: #ffd479;
14
+ }
15
+
16
+ * {
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ html,
21
+ body {
22
+ margin: 0;
23
+ width: 100%;
24
+ height: 100%;
25
+ background: radial-gradient(circle at top, #122235 0%, var(--bg) 46%, #05080d 100%);
26
+ color: var(--text);
27
+ }
28
+
29
+ body {
30
+ display: flex;
31
+ flex-direction: column;
32
+ overflow: hidden;
33
+ }
34
+
35
+ .toolbar {
36
+ border-bottom: 1px solid var(--panel-border);
37
+ background: rgba(10, 16, 26, 0.9);
38
+ backdrop-filter: blur(8px);
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: space-between;
42
+ gap: 0.8rem;
43
+ padding: 0.7rem 0.9rem;
44
+ }
45
+
46
+ .toolbar h1 {
47
+ margin: 0;
48
+ font-size: 1.05rem;
49
+ letter-spacing: 0.02em;
50
+ }
51
+
52
+ .toolbar p {
53
+ margin: 0.2rem 0 0;
54
+ color: var(--muted);
55
+ font-size: 0.82rem;
56
+ }
57
+
58
+ .toolbar-controls {
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 0.5rem;
62
+ flex-wrap: wrap;
63
+ }
64
+
65
+ input,
66
+ select,
67
+ button {
68
+ border: 1px solid var(--panel-border);
69
+ background: var(--panel);
70
+ color: var(--text);
71
+ border-radius: 9px;
72
+ padding: 0.5rem 0.7rem;
73
+ font-size: 0.88rem;
74
+ transition: border-color 160ms ease, transform 140ms ease, background-color 160ms ease;
75
+ }
76
+
77
+ input,
78
+ select {
79
+ min-width: 150px;
80
+ }
81
+
82
+ input {
83
+ min-width: 200px;
84
+ }
85
+
86
+ button {
87
+ cursor: pointer;
88
+ }
89
+
90
+ button:hover {
91
+ border-color: var(--accent);
92
+ }
93
+
94
+ button:active {
95
+ transform: translateY(1px);
96
+ }
97
+
98
+ .status-pill {
99
+ border: 1px solid #294464;
100
+ border-radius: 999px;
101
+ background: #14253a;
102
+ color: var(--muted);
103
+ font-size: 0.76rem;
104
+ line-height: 1;
105
+ padding: 0.4rem 0.65rem;
106
+ white-space: nowrap;
107
+ }
108
+
109
+ .status-pill.ok {
110
+ border-color: #1f6350;
111
+ color: #93f0d6;
112
+ background: #0f2f2a;
113
+ }
114
+
115
+ .status-pill.warn {
116
+ border-color: #7a5620;
117
+ color: #ffe0a3;
118
+ background: #342510;
119
+ }
120
+
121
+ .layout {
122
+ display: grid;
123
+ grid-template-columns: minmax(0, 1fr) 320px;
124
+ min-height: 0;
125
+ flex: 1;
126
+ }
127
+
128
+ #graph {
129
+ min-height: 0;
130
+ position: relative;
131
+ }
132
+
133
+ .details-panel {
134
+ border-left: 1px solid var(--panel-border);
135
+ padding: 1rem;
136
+ background: rgba(13, 21, 34, 0.87);
137
+ overflow-y: auto;
138
+ }
139
+
140
+ .details-panel h2 {
141
+ margin-top: 0;
142
+ font-size: 0.98rem;
143
+ letter-spacing: 0.01em;
144
+ }
145
+
146
+ .meta-label {
147
+ color: var(--muted);
148
+ font-size: 0.8rem;
149
+ margin-bottom: 0.15rem;
150
+ }
151
+
152
+ .meta-value {
153
+ margin: 0 0 0.72rem;
154
+ font-size: 0.9rem;
155
+ line-height: 1.4;
156
+ word-break: break-word;
157
+ }
158
+
159
+ .connection-list {
160
+ list-style: none;
161
+ margin: 0;
162
+ padding: 0;
163
+ }
164
+
165
+ .connection-list li {
166
+ border-bottom: 1px solid var(--panel-border);
167
+ padding: 0.36rem 0;
168
+ font-size: 0.88rem;
169
+ }
170
+
171
+ .connection-list li:last-child {
172
+ border-bottom: none;
173
+ }
174
+
175
+ .connection-link {
176
+ color: var(--accent);
177
+ text-decoration: none;
178
+ }
179
+
180
+ .connection-link:hover {
181
+ color: var(--accent-strong);
182
+ text-decoration: underline;
183
+ }
184
+
185
+ body.tv-mode .toolbar,
186
+ body.tv-mode .details-panel {
187
+ opacity: 0;
188
+ pointer-events: none;
189
+ transform: translateY(-8px);
190
+ transition: opacity 280ms ease, transform 280ms ease;
191
+ }
192
+
193
+ body.tv-mode .layout {
194
+ display: block;
195
+ }
196
+
197
+ body.tv-mode #graph {
198
+ width: 100vw;
199
+ height: 100vh;
200
+ }
201
+
202
+ body.tv-mode {
203
+ background: #03050a;
204
+ cursor: none;
205
+ }
206
+
207
+ @media (max-width: 1200px) {
208
+ .layout {
209
+ grid-template-columns: minmax(0, 1fr);
210
+ grid-template-rows: minmax(0, 1fr) auto;
211
+ }
212
+
213
+ #graph {
214
+ height: 64vh;
215
+ }
216
+
217
+ .details-panel {
218
+ border-left: 0;
219
+ border-top: 1px solid var(--panel-border);
220
+ }
221
+ }
@@ -0,0 +1,374 @@
1
+ import express from 'express';
2
+ import * as fs from 'node:fs/promises';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import chokidar from 'chokidar';
7
+ import { WebSocketServer } from 'ws';
8
+ import { buildVaultGraph } from './lib/vault-parser.js';
9
+ import { diffGraphs } from './lib/graph-diff.js';
10
+
11
+ const DEFAULT_PORT = 3377;
12
+ const HOST = '0.0.0.0';
13
+
14
+ export async function startDashboard(options = {}) {
15
+ const port = normalizePort(options.port ?? DEFAULT_PORT);
16
+ const vaultPath = resolveVaultPath(options.vaultPath);
17
+ await assertVaultPath(vaultPath);
18
+
19
+ const app = express();
20
+ const serverDir = path.dirname(fileURLToPath(import.meta.url));
21
+ const projectDir = path.resolve(serverDir, '..');
22
+ const publicDir = path.join(serverDir, 'public');
23
+ const forceGraphDistDir = path.join(projectDir, 'node_modules', 'force-graph', 'dist');
24
+
25
+ const graphStore = createLiveGraphStore(vaultPath);
26
+ await graphStore.init();
27
+
28
+ app.get('/api/graph', async (req, res) => {
29
+ try {
30
+ const shouldRefresh = req.query.refresh === '1';
31
+ if (shouldRefresh) {
32
+ await graphStore.refresh({ reason: 'api:refresh' });
33
+ }
34
+ res.json(graphStore.getGraph());
35
+ } catch (error) {
36
+ res.status(500).json({
37
+ error: 'Failed to build graph',
38
+ detail: error instanceof Error ? error.message : String(error)
39
+ });
40
+ }
41
+ });
42
+
43
+ app.get('/api/health', (_req, res) => {
44
+ res.json({
45
+ ok: true,
46
+ vaultPath
47
+ });
48
+ });
49
+
50
+ app.use('/vendor', express.static(forceGraphDistDir));
51
+ app.use(express.static(publicDir, { extensions: ['html'] }));
52
+
53
+ const server = await new Promise((resolve, reject) => {
54
+ const runningServer = app
55
+ .listen(port, HOST, () => resolve(runningServer))
56
+ .on('error', reject);
57
+ });
58
+
59
+ const wsServer = new WebSocketServer({
60
+ server,
61
+ path: '/ws'
62
+ });
63
+
64
+ const unsubscribeGraphUpdates = graphStore.subscribe((update) => {
65
+ broadcast(wsServer, {
66
+ type: 'graph:patch',
67
+ payload: {
68
+ version: update.version,
69
+ reason: update.reason,
70
+ changedPaths: update.changedPaths,
71
+ ...update.patch
72
+ }
73
+ });
74
+ });
75
+
76
+ wsServer.on('connection', (socket) => {
77
+ socket.send(
78
+ JSON.stringify({
79
+ type: 'graph:init',
80
+ payload: {
81
+ version: graphStore.getVersion(),
82
+ graph: graphStore.getGraph()
83
+ }
84
+ })
85
+ );
86
+ });
87
+
88
+ const heartbeatInterval = setInterval(() => {
89
+ for (const client of wsServer.clients) {
90
+ if (client.readyState === 1) {
91
+ client.ping();
92
+ }
93
+ }
94
+ }, 20_000);
95
+
96
+ await graphStore.startWatching();
97
+
98
+ logStartup({
99
+ port,
100
+ vaultPath
101
+ });
102
+
103
+ let isShuttingDown = false;
104
+ const shutdown = async () => {
105
+ if (isShuttingDown) {
106
+ return;
107
+ }
108
+ isShuttingDown = true;
109
+ clearInterval(heartbeatInterval);
110
+ unsubscribeGraphUpdates();
111
+ await graphStore.close();
112
+ await new Promise((resolve) => wsServer.close(() => resolve()));
113
+ server.close(() => {
114
+ process.exit(0);
115
+ });
116
+ };
117
+
118
+ process.on('SIGINT', () => {
119
+ void shutdown();
120
+ });
121
+ process.on('SIGTERM', () => {
122
+ void shutdown();
123
+ });
124
+
125
+ return server;
126
+ }
127
+
128
+ function createLiveGraphStore(vaultPath) {
129
+ const subscribers = new Set();
130
+ const changedPathBuffer = new Set();
131
+ const refreshDebounceMs = 240;
132
+ let graph = null;
133
+ let version = 0;
134
+ let refreshTimer = null;
135
+ let watcher = null;
136
+ let inFlightRefresh = null;
137
+ let refreshQueued = false;
138
+
139
+ async function init() {
140
+ graph = await buildVaultGraph(vaultPath);
141
+ version = 1;
142
+ }
143
+
144
+ function getGraph() {
145
+ return graph;
146
+ }
147
+
148
+ function getVersion() {
149
+ return version;
150
+ }
151
+
152
+ function subscribe(listener) {
153
+ subscribers.add(listener);
154
+ return () => {
155
+ subscribers.delete(listener);
156
+ };
157
+ }
158
+
159
+ function emit(update) {
160
+ for (const listener of subscribers) {
161
+ listener(update);
162
+ }
163
+ }
164
+
165
+ function queueRefresh({ reason, changedPath }) {
166
+ if (changedPath) {
167
+ changedPathBuffer.add(changedPath);
168
+ }
169
+ if (refreshTimer) {
170
+ clearTimeout(refreshTimer);
171
+ }
172
+ refreshTimer = setTimeout(() => {
173
+ refreshTimer = null;
174
+ void refresh({ reason });
175
+ }, refreshDebounceMs);
176
+ }
177
+
178
+ async function refresh({ reason = 'manual' } = {}) {
179
+ if (inFlightRefresh) {
180
+ refreshQueued = true;
181
+ return inFlightRefresh;
182
+ }
183
+
184
+ const changedPaths = Array.from(changedPathBuffer).sort((a, b) => a.localeCompare(b));
185
+ changedPathBuffer.clear();
186
+
187
+ inFlightRefresh = buildVaultGraph(vaultPath)
188
+ .then((nextGraph) => {
189
+ const patch = diffGraphs(graph, nextGraph);
190
+ graph = nextGraph;
191
+ if (!patch.hasChanges) {
192
+ return;
193
+ }
194
+ version += 1;
195
+ emit({
196
+ version,
197
+ reason,
198
+ changedPaths,
199
+ patch
200
+ });
201
+ })
202
+ .finally(async () => {
203
+ inFlightRefresh = null;
204
+ if (refreshQueued) {
205
+ refreshQueued = false;
206
+ await refresh({ reason: 'coalesced' });
207
+ }
208
+ });
209
+
210
+ return inFlightRefresh;
211
+ }
212
+
213
+ async function startWatching() {
214
+ watcher = chokidar.watch(path.join(vaultPath, '**', '*.md'), {
215
+ persistent: true,
216
+ ignoreInitial: true,
217
+ awaitWriteFinish: {
218
+ stabilityThreshold: 180,
219
+ pollInterval: 50
220
+ },
221
+ ignored: (watchedPath) => isIgnoredPath(vaultPath, watchedPath)
222
+ });
223
+
224
+ watcher
225
+ .on('add', (filePath) => {
226
+ queueRefresh({
227
+ reason: 'fs:add',
228
+ changedPath: toRelativeVaultPath(vaultPath, filePath)
229
+ });
230
+ })
231
+ .on('change', (filePath) => {
232
+ queueRefresh({
233
+ reason: 'fs:change',
234
+ changedPath: toRelativeVaultPath(vaultPath, filePath)
235
+ });
236
+ })
237
+ .on('unlink', (filePath) => {
238
+ queueRefresh({
239
+ reason: 'fs:unlink',
240
+ changedPath: toRelativeVaultPath(vaultPath, filePath)
241
+ });
242
+ })
243
+ .on('error', (error) => {
244
+ console.error(`Dashboard file watcher error: ${error instanceof Error ? error.message : String(error)}`);
245
+ });
246
+ }
247
+
248
+ async function close() {
249
+ if (refreshTimer) {
250
+ clearTimeout(refreshTimer);
251
+ refreshTimer = null;
252
+ }
253
+ if (watcher) {
254
+ await watcher.close();
255
+ watcher = null;
256
+ }
257
+ }
258
+
259
+ return {
260
+ init,
261
+ getGraph,
262
+ getVersion,
263
+ subscribe,
264
+ refresh,
265
+ startWatching,
266
+ close
267
+ };
268
+ }
269
+
270
+ function broadcast(wsServer, data) {
271
+ const payload = JSON.stringify(data);
272
+ for (const client of wsServer.clients) {
273
+ if (client.readyState === 1) {
274
+ client.send(payload);
275
+ }
276
+ }
277
+ }
278
+
279
+ function isIgnoredPath(vaultPath, watchedPath) {
280
+ const relativePath = toRelativeVaultPath(vaultPath, watchedPath);
281
+ const segments = relativePath.split('/').filter(Boolean);
282
+
283
+ return segments.some((segment) =>
284
+ segment === '.git' || segment === '.obsidian' || segment === '.trash' || segment === 'node_modules'
285
+ );
286
+ }
287
+
288
+ function toRelativeVaultPath(vaultPath, absolutePath) {
289
+ return path.relative(vaultPath, absolutePath).split(path.sep).join('/');
290
+ }
291
+
292
+ function parseArgs(argv) {
293
+ const options = {
294
+ port: DEFAULT_PORT,
295
+ vaultPath: undefined
296
+ };
297
+
298
+ for (let i = 0; i < argv.length; i += 1) {
299
+ const arg = argv[i];
300
+ if (arg === '--port' || arg === '-p') {
301
+ options.port = argv[i + 1];
302
+ i += 1;
303
+ continue;
304
+ }
305
+ if (arg === '--vault' || arg === '-v') {
306
+ options.vaultPath = argv[i + 1];
307
+ i += 1;
308
+ }
309
+ }
310
+
311
+ return options;
312
+ }
313
+
314
+ function normalizePort(value) {
315
+ const parsed = Number.parseInt(String(value), 10);
316
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
317
+ throw new Error(`Invalid port: ${value}`);
318
+ }
319
+ return parsed;
320
+ }
321
+
322
+ function resolveVaultPath(input) {
323
+ const candidate = input || process.env.CLAWVAULT_PATH || process.cwd();
324
+ return path.resolve(candidate);
325
+ }
326
+
327
+ async function assertVaultPath(vaultPath) {
328
+ let stat;
329
+ try {
330
+ stat = await fs.stat(vaultPath);
331
+ } catch (error) {
332
+ throw new Error(`Vault path not found: ${vaultPath}`);
333
+ }
334
+
335
+ if (!stat.isDirectory()) {
336
+ throw new Error(`Vault path is not a directory: ${vaultPath}`);
337
+ }
338
+ }
339
+
340
+ function logStartup({ port, vaultPath }) {
341
+ const interfaces = os.networkInterfaces();
342
+ const networkUrls = [];
343
+
344
+ for (const addresses of Object.values(interfaces)) {
345
+ for (const address of addresses ?? []) {
346
+ if (address.family !== 'IPv4' || address.internal) {
347
+ continue;
348
+ }
349
+ networkUrls.push(`http://${address.address}:${port}`);
350
+ }
351
+ }
352
+
353
+ console.log('\nClawVault Dashboard');
354
+ console.log(`Vault: ${vaultPath}`);
355
+ console.log(`Local: http://localhost:${port}`);
356
+ for (const url of networkUrls) {
357
+ console.log(`Network: ${url}`);
358
+ }
359
+ console.log('\nPress Ctrl+C to stop.\n');
360
+ }
361
+
362
+ const currentFile = fileURLToPath(import.meta.url);
363
+ const executedFile = process.argv[1] ? path.resolve(process.argv[1]) : '';
364
+
365
+ if (currentFile === executedFile) {
366
+ startDashboard(parseArgs(process.argv.slice(2))).catch((error) => {
367
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'EADDRINUSE') {
368
+ console.error('Port already in use.');
369
+ } else {
370
+ console.error(error instanceof Error ? error.message : String(error));
371
+ }
372
+ process.exit(1);
373
+ });
374
+ }
@@ -1,11 +1,11 @@
1
- import {
2
- resolveVaultPath
3
- } from "./chunk-MXSSG3QU.js";
4
1
  import {
5
2
  DATE_HEADING_RE,
6
3
  parseObservationLine,
7
4
  renderScoredObservationLine
8
- } from "./chunk-QK3UCXWL.js";
5
+ } from "./chunk-FHFUXL6G.js";
6
+ import {
7
+ resolveVaultPath
8
+ } from "./chunk-GNJL4YGR.js";
9
9
  import {
10
10
  listObservationFiles
11
11
  } from "./chunk-Z2XBWN7A.js";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-MQUJNOHK.js";
4
4
  import {
5
5
  resolveVaultPath
6
- } from "./chunk-MXSSG3QU.js";
6
+ } from "./chunk-GNJL4YGR.js";
7
7
 
8
8
  // src/commands/archive.ts
9
9
  function parsePositiveInteger(raw, label) {