volute 0.30.1 → 0.32.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 (227) hide show
  1. package/README.md +15 -22
  2. package/dist/{accept-E3PAH3QJ.js → accept-74M7I4RZ.js} +5 -4
  3. package/dist/{activity-events-BKBPPUBP.js → activity-events-HETAODOK.js} +3 -2
  4. package/dist/{ai-service-VAJT5UBS.js → ai-service-ZIPCV3MX.js} +20 -5
  5. package/dist/api.d.ts +341 -397
  6. package/dist/{archive-WWDBWYN2.js → archive-INXYFVCW.js} +3 -2
  7. package/dist/auth-6DMGES3I.js +44 -0
  8. package/dist/{bridge-RO37CUFM.js → bridge-BVCBTGPF.js} +5 -4
  9. package/dist/{chat-TCUNPFGO.js → chat-XT4OBJBU.js} +8 -8
  10. package/dist/{chunk-P7VFDSSG.js → chunk-2FLJ63GU.js} +2 -2
  11. package/dist/{chunk-ZWKTUQEL.js → chunk-2NGTS5UU.js} +1 -1
  12. package/dist/{chunk-JGFRDMR6.js → chunk-ALEF47VT.js} +1 -1
  13. package/dist/{chunk-MDPCSXZ4.js → chunk-D5G5YOPL.js} +163 -15
  14. package/dist/{chunk-VGWJSNHS.js → chunk-G53F3JA4.js} +1 -35
  15. package/dist/{chunk-A6TUJJ3L.js → chunk-G6BSYHPK.js} +2 -2
  16. package/dist/{chunk-DTC6EH5I.js → chunk-I5KY25PQ.js} +1 -9
  17. package/dist/{chunk-NSBFETWP.js → chunk-IYDIE3HG.js} +64 -26
  18. package/dist/{chunk-W5OOPLNP.js → chunk-JJ7W6WSB.js} +3 -3
  19. package/dist/{chunk-G3GBKZGG.js → chunk-LGB6JBHI.js} +54 -2
  20. package/dist/chunk-LRCG2JLP.js +251 -0
  21. package/dist/{chunk-FXHXHI2A.js → chunk-LSGWR54X.js} +3 -6
  22. package/dist/{chunk-S5LR3XYJ.js → chunk-M7UL5S3Q.js} +1 -1
  23. package/dist/chunk-PB65JZK2.js +85 -0
  24. package/dist/chunk-PVY5W6QN.js +41 -0
  25. package/dist/{chunk-QVAQ5454.js → chunk-QBQ424EM.js} +3007 -2126
  26. package/dist/{chunk-P27RV5WM.js → chunk-QZANELPX.js} +6 -2
  27. package/dist/{chunk-FSM45XD5.js → chunk-R7E6CRVQ.js} +1 -1
  28. package/dist/{chunk-HHTXM4JT.js → chunk-RPZZSXV3.js} +39 -195
  29. package/dist/{chunk-UPA6COHU.js → chunk-RSX4OPZY.js} +5 -5
  30. package/dist/{chunk-2C2VXEBB.js → chunk-S6NFERDC.js} +21 -57
  31. package/dist/chunk-SKLSMHXO.js +208 -0
  32. package/dist/{chunk-IKHDUZRH.js → chunk-SX5TKJBZ.js} +2 -2
  33. package/dist/chunk-TDRYEPH4.js +185 -0
  34. package/dist/chunk-TSXLLQZW.js +46 -0
  35. package/dist/{chunk-EFVHR7KH.js → chunk-UKVWJRKN.js} +24 -5
  36. package/dist/{chunk-2NDZC3S7.js → chunk-WKF5FEFK.js} +688 -389
  37. package/dist/cli.js +93 -24
  38. package/dist/{clock-G3ALCMLJ.js → clock-2UOZ6JPU.js} +11 -8
  39. package/dist/{cloud-sync-JV4LJOK3.js → cloud-sync-JN3NWKEM.js} +16 -14
  40. package/dist/config-H2H4UIF7.js +72 -0
  41. package/dist/connectors/discord-bridge.js +1 -1
  42. package/dist/connectors/slack-bridge.js +1 -1
  43. package/dist/connectors/telegram-bridge.js +1 -1
  44. package/dist/{conversations-7KVQV7EZ.js → conversations-3O5O6AS3.js} +8 -7
  45. package/dist/{create-JTLS7GX3.js → create-RNLNCORE.js} +5 -4
  46. package/dist/{create-VQSQHJQW.js → create-WBBYI6V7.js} +6 -2
  47. package/dist/daemon-client-6QXHZ7US.js +12 -0
  48. package/dist/{daemon-restart-4JGBHEJ4.js → daemon-restart-NGFHFAUF.js} +7 -7
  49. package/dist/daemon.js +2446 -1999
  50. package/dist/{db-HMFPIRO2.js → db-F34YLV7D.js} +2 -1
  51. package/dist/db-RA45JBFG.js +16 -0
  52. package/dist/{delete-JESHKE7F.js → delete-QTGWEDBI.js} +1 -1
  53. package/dist/delivery-manager-SDVXFD4W.js +28 -0
  54. package/dist/delivery-router-FL45JL7N.js +21 -0
  55. package/dist/down-TB3ESMNP.js +14 -0
  56. package/dist/{env-CLXXT7M2.js → env-RLYQBOOP.js} +5 -4
  57. package/dist/{export-EGA5M5PB.js → export-SUYRLI5Q.js} +4 -3
  58. package/dist/{extension-WZ4SUPJB.js → extension-FQ5D3NCC.js} +6 -6
  59. package/dist/{extensions-ECO4RPFQ.js → extensions-GDYWQXC4.js} +9 -7
  60. package/dist/{files-4VEJDASH.js → files-EAMPO2SJ.js} +6 -5
  61. package/dist/{history-EJMMLXDO.js → history-FO5PHBQ5.js} +9 -4
  62. package/dist/{import-YCGPMBSI.js → import-DDUFE7AY.js} +4 -3
  63. package/dist/{join-2GBJKZEN.js → join-I5QEE3LG.js} +1 -1
  64. package/dist/{list-Q6O7FGAN.js → list-DW2VRTOZ.js} +5 -4
  65. package/dist/{login-RL6AU2SM.js → login-7CHPW2PN.js} +5 -4
  66. package/dist/{login-RET5WESK.js → login-RIJF2F4G.js} +3 -2
  67. package/dist/{logout-CGAGJN3L.js → logout-5MLHZALK.js} +3 -2
  68. package/dist/{logout-JRPBEMMR.js → logout-UZJRGY4Z.js} +3 -2
  69. package/dist/message-delivery-2FIM7QKO.js +32 -0
  70. package/dist/{mind-LUWRQUQ5.js → mind-2B6M7Y25.js} +18 -18
  71. package/dist/{mind-activity-tracker-VYN2ZZ2M.js → mind-activity-tracker-NZZT2NTT.js} +4 -3
  72. package/dist/{mind-list-V5WW5DUA.js → mind-list-WUPMQDYQ.js} +3 -2
  73. package/dist/mind-manager-BNCMGYXW.js +28 -0
  74. package/dist/mind-service-AV273WT4.js +34 -0
  75. package/dist/{mind-sleep-R6PTNNW4.js → mind-sleep-B7BHJLH7.js} +5 -4
  76. package/dist/{mind-status-I4ISFJ6I.js → mind-status-L3EFFRPR.js} +3 -2
  77. package/dist/{mind-wake-67ZQEWAV.js → mind-wake-GY3RFX7Y.js} +5 -4
  78. package/dist/{package-OYUD4ZJ4.js → package-PK6JUFL3.js} +3 -3
  79. package/dist/read-5AMJRO3D.js +75 -0
  80. package/dist/{register-NZDSTLP3.js → register-V2JZZKFK.js} +5 -4
  81. package/dist/{registry-ODSALQQL.js → registry-PJ4S5PHQ.js} +8 -1
  82. package/dist/{reject-2HZOJEIJ.js → reject-33HEZMZ4.js} +5 -4
  83. package/dist/{restart-QHS3NT64.js → restart-3UCMRUVC.js} +5 -4
  84. package/dist/{sandbox-O5FUSF43.js → sandbox-JANNTX6U.js} +4 -3
  85. package/dist/schema-PA3M5ZKH.js +32 -0
  86. package/dist/seed-ALUQ55FF.js +112 -0
  87. package/dist/{send-OAN3RYYY.js → send-3MI36LEF.js} +58 -69
  88. package/dist/{setup-QMDK5RZX.js → setup-SZIARWI6.js} +5 -4
  89. package/dist/{setup-XJH3E7YM.js → setup-WENLVPVP.js} +9 -9
  90. package/dist/{skill-FZIN4W4Q.js → skill-TUVOTW4Z.js} +5 -4
  91. package/dist/skills/dreaming/SKILL.md +6 -4
  92. package/dist/skills/dreaming/references/INSTALL.md +4 -5
  93. package/dist/skills/dreaming/scripts/dream.ts +5 -27
  94. package/dist/skills/dreaming/scripts/wake-context-dreams.sh +1 -1
  95. package/dist/skills/imagegen/SKILL.md +6 -5
  96. package/dist/skills/imagegen/references/INSTALL.md +1 -1
  97. package/dist/skills/resonance/SKILL.md +4 -1
  98. package/dist/skills/resonance/references/INSTALL.md +2 -2
  99. package/dist/skills/resonance/scripts/resonance-hook.sh +2 -0
  100. package/dist/skills/resonance/scripts/resonance.ts +35 -5
  101. package/dist/skills/volute-admin/SKILL.md +83 -0
  102. package/dist/skills/volute-mind/SKILL.md +12 -12
  103. package/dist/skills-XNZK6P4K.js +61 -0
  104. package/dist/sleep-manager-53DZOWW7.js +32 -0
  105. package/dist/spirit-N4W4UQRH.js +217 -0
  106. package/dist/{split-EXYGGGQN.js → split-STOROBYJ.js} +1 -1
  107. package/dist/{sprout-AXQ6H5DB.js → sprout-L2GFOVF7.js} +9 -8
  108. package/dist/{start-MTOVL6SY.js → start-K2NCUUCG.js} +5 -4
  109. package/dist/{status-ZRO37MWR.js → status-TCUMUO6M.js} +5 -5
  110. package/dist/{stop-OK5WEPVC.js → stop-H26JZDXF.js} +5 -4
  111. package/dist/system-chat-NPYFYZVI.js +32 -0
  112. package/dist/{systems-W3BBMSOZ.js → systems-DHBKVYEY.js} +6 -5
  113. package/dist/{tailscale-BM72RXCJ.js → tailscale-XHQBZROW.js} +2 -1
  114. package/dist/{template-hash-3HOR4UAJ.js → template-hash-A6VVKOXJ.js} +2 -1
  115. package/dist/up-6I6BHRTO.js +17 -0
  116. package/dist/{update-PLPHMMZ2.js → update-QVPRF6GR.js} +5 -5
  117. package/dist/{update-check-CVCN7MF6.js → update-check-ZD6OOIYQ.js} +3 -2
  118. package/dist/{upgrade-I6NPCYUU.js → upgrade-O4Q7WJM3.js} +12 -14
  119. package/dist/{version-notify-2NTWVEHL.js → version-notify-TCKWBZZG.js} +22 -23
  120. package/dist/web-assets/assets/index-Bui7U9Uu.css +1 -0
  121. package/dist/web-assets/assets/index-e36DIo1b.js +73 -0
  122. package/dist/web-assets/ext-theme.css +94 -0
  123. package/dist/web-assets/index.html +2 -2
  124. package/drizzle/0000_baseline.sql +152 -0
  125. package/drizzle/0001_add_conversation_private.sql +1 -0
  126. package/drizzle/0002_turns.sql +21 -0
  127. package/drizzle/0003_turn_feed_links.sql +11 -0
  128. package/drizzle/0004_spirits.sql +5 -0
  129. package/drizzle/meta/0000_snapshot.json +3 -223
  130. package/drizzle/meta/0001_snapshot.json +3 -294
  131. package/drizzle/meta/0002_snapshot.json +3 -335
  132. package/drizzle/meta/0003_snapshot.json +3 -413
  133. package/drizzle/meta/0004_snapshot.json +3 -406
  134. package/drizzle/meta/_journal.json +10 -101
  135. package/package.json +3 -3
  136. package/packages/extensions/notes/dist/ui/assets/index-8jWEv9SA.js +61 -0
  137. package/packages/extensions/notes/dist/ui/assets/index-DkaB7Ytd.css +1 -0
  138. package/packages/extensions/notes/dist/ui/index.html +2 -2
  139. package/packages/extensions/notes/skills/notes/SKILL.md +8 -8
  140. package/packages/extensions/pages/skills/pages/SKILL.md +17 -44
  141. package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +40 -0
  142. package/templates/_base/.init/.local/bin/volute +27 -0
  143. package/templates/_base/.init/.local/hooks/pre-prompt/session-activity.ts +40 -0
  144. package/templates/_base/.init/.local/hooks/startup-context.ts +58 -0
  145. package/templates/_base/home/.config/routes.json +1 -1
  146. package/templates/_base/src/lib/auto-commit.ts +82 -43
  147. package/templates/_base/src/lib/daemon-client.ts +40 -36
  148. package/templates/_base/src/lib/format-prefix.ts +1 -0
  149. package/templates/_base/src/lib/hook-loader.ts +155 -0
  150. package/templates/_base/src/lib/router.ts +17 -1
  151. package/templates/_base/src/lib/startup.ts +17 -12
  152. package/templates/_base/src/lib/transparency.ts +2 -2
  153. package/templates/_base/src/lib/volute-server.ts +2 -5
  154. package/templates/claude/.init/.claude/settings.json +1 -1
  155. package/templates/claude/.init/.config/routes.json +2 -2
  156. package/templates/claude/src/agent.ts +97 -14
  157. package/templates/claude/src/lib/hooks/auto-commit.ts +7 -3
  158. package/templates/claude/src/lib/message-channel.ts +7 -2
  159. package/templates/claude/src/server.ts +0 -9
  160. package/templates/codex/.init/.config/routes.json +11 -0
  161. package/templates/codex/.init/AGENTS.md +29 -0
  162. package/templates/codex/home/.config/config.json.tmpl +7 -0
  163. package/templates/codex/package.json.tmpl +20 -0
  164. package/templates/codex/src/agent.ts +553 -0
  165. package/templates/codex/src/lib/content.ts +16 -0
  166. package/templates/codex/src/lib/session-store.ts +56 -0
  167. package/templates/codex/src/server.ts +59 -0
  168. package/templates/codex/volute-template.json +8 -0
  169. package/templates/pi/.init/.config/routes.json +2 -2
  170. package/templates/pi/package.json.tmpl +1 -1
  171. package/templates/pi/src/agent.ts +63 -9
  172. package/templates/pi/src/lib/event-handler.ts +6 -4
  173. package/templates/pi/src/lib/reply-instructions-extension.ts +32 -11
  174. package/dist/chunk-7D47T4RB.js +0 -84
  175. package/dist/chunk-CVH6Y2YG.js +0 -59
  176. package/dist/chunk-EFP3PE6C.js +0 -232
  177. package/dist/chunk-LIRWLNAK.js +0 -729
  178. package/dist/daemon-client-BCTFGVCZ.js +0 -9
  179. package/dist/down-NGBMGORS.js +0 -14
  180. package/dist/message-delivery-6YMVNOEC.js +0 -28
  181. package/dist/migrate-registry-to-db-FK35IPEH.js +0 -110
  182. package/dist/mind-manager-YFCOIAAX.js +0 -18
  183. package/dist/pages-watcher-Z3PKNROC.js +0 -21
  184. package/dist/read-WQMPTSN2.js +0 -46
  185. package/dist/seed-WUQMPLDM.js +0 -71
  186. package/dist/skills/sessions/SKILL.md +0 -49
  187. package/dist/sleep-manager-O7YQFCV5.js +0 -30
  188. package/dist/up-BXUAIDXB.js +0 -17
  189. package/dist/web-assets/assets/index--kREqKl9.js +0 -72
  190. package/dist/web-assets/assets/index-BXYTG0nJ.css +0 -1
  191. package/drizzle/0000_flaky_mariko_yashida.sql +0 -34
  192. package/drizzle/0001_careless_warpath.sql +0 -12
  193. package/drizzle/0002_wealthy_the_call.sql +0 -6
  194. package/drizzle/0003_clean_ego.sql +0 -12
  195. package/drizzle/0004_magical_silverclaw.sql +0 -1
  196. package/drizzle/0005_rename_agents_to_minds.sql +0 -11
  197. package/drizzle/0006_mind_history.sql +0 -20
  198. package/drizzle/0007_system_prompts.sql +0 -5
  199. package/drizzle/0008_volute_channels.sql +0 -24
  200. package/drizzle/0009_shared_skills.sql +0 -9
  201. package/drizzle/0010_delivery_queue.sql +0 -12
  202. package/drizzle/0011_rename_human_to_brain.sql +0 -1
  203. package/drizzle/0012_activity.sql +0 -11
  204. package/drizzle/0013_user_profiles.sql +0 -3
  205. package/drizzle/0014_conversation_reads.sql +0 -7
  206. package/drizzle/0015_notes.sql +0 -23
  207. package/drizzle/0016_note_reactions_and_replies.sql +0 -15
  208. package/drizzle/0017_minds.sql +0 -16
  209. package/drizzle/meta/0005_snapshot.json +0 -410
  210. package/drizzle/meta/0006_snapshot.json +0 -7
  211. package/drizzle/meta/0007_snapshot.json +0 -7
  212. package/drizzle/meta/0008_snapshot.json +0 -7
  213. package/drizzle/meta/0009_snapshot.json +0 -7
  214. package/drizzle/meta/0010_snapshot.json +0 -7
  215. package/drizzle/meta/0011_snapshot.json +0 -7
  216. package/drizzle/meta/0012_snapshot.json +0 -7
  217. package/drizzle/meta/0013_snapshot.json +0 -7
  218. package/packages/extensions/notes/dist/ui/assets/index-DgawVO5g.css +0 -1
  219. package/packages/extensions/notes/dist/ui/assets/index-qUWoeC4c.js +0 -2
  220. package/packages/extensions/notes/skills/notes/scripts/notes.mjs +0 -185
  221. package/templates/_base/.init/.config/hooks/startup-context.sh +0 -46
  222. package/templates/_base/.init/.config/scripts/session-reader.ts +0 -59
  223. package/templates/_base/home/public/.gitkeep +0 -0
  224. package/templates/_base/src/lib/session-monitor.ts +0 -400
  225. package/templates/claude/src/lib/hooks/session-context.ts +0 -32
  226. package/templates/pi/src/lib/session-context-extension.ts +0 -35
  227. /package/templates/_base/.init/{.config → .local}/hooks/wake-context.sh +0 -0
@@ -4,17 +4,17 @@ import {
4
4
  modeLabel,
5
5
  pollHealth,
6
6
  startService
7
- } from "./chunk-FXHXHI2A.js";
7
+ } from "./chunk-LSGWR54X.js";
8
8
  import {
9
9
  readGlobalConfig
10
- } from "./chunk-7D47T4RB.js";
10
+ } from "./chunk-TSXLLQZW.js";
11
11
  import {
12
12
  parseArgs
13
13
  } from "./chunk-D424ZQGI.js";
14
14
  import {
15
15
  voluteHome,
16
16
  voluteSystemDir
17
- } from "./chunk-HHTXM4JT.js";
17
+ } from "./chunk-LRCG2JLP.js";
18
18
 
19
19
  // src/commands/up.ts
20
20
  import { spawn } from "child_process";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getBaseName,
4
4
  validateMindName
5
- } from "./chunk-HHTXM4JT.js";
5
+ } from "./chunk-LRCG2JLP.js";
6
6
 
7
7
  // src/lib/isolation.ts
8
8
  import { execFileSync } from "child_process";
@@ -166,6 +166,54 @@ function chownMindDir(dir, name) {
166
166
  }
167
167
  }
168
168
 
169
+ // src/lib/exec.ts
170
+ import { execFile as execFileCb, execFileSync as execFileSync2, spawn } from "child_process";
171
+ async function exec(cmd, args, options) {
172
+ const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
173
+ return new Promise((resolve, reject) => {
174
+ execFileCb(
175
+ wrappedCmd,
176
+ wrappedArgs,
177
+ { cwd: options?.cwd, env: options?.env },
178
+ (err, stdout, stderr) => {
179
+ if (err) {
180
+ err.stderr = stderr;
181
+ err.stdout = stdout;
182
+ reject(err);
183
+ } else {
184
+ resolve(stdout);
185
+ }
186
+ }
187
+ );
188
+ });
189
+ }
190
+ function gitExec(args, options) {
191
+ const fullArgs = process.env.VOLUTE_ISOLATION === "user" ? ["-c", "safe.directory=*", ...args] : args;
192
+ return exec("git", fullArgs, options);
193
+ }
194
+ function resolveVoluteBin() {
195
+ try {
196
+ return execFileSync2("which", ["volute"], { encoding: "utf-8" }).trim();
197
+ } catch {
198
+ throw new Error("Could not find volute binary on PATH");
199
+ }
200
+ }
201
+ async function execInherit(cmd, args, options) {
202
+ const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
203
+ return new Promise((resolve, reject) => {
204
+ const child = spawn(wrappedCmd, wrappedArgs, {
205
+ cwd: options?.cwd,
206
+ env: options?.env,
207
+ stdio: "inherit"
208
+ });
209
+ child.on("error", reject);
210
+ child.on("close", (code) => {
211
+ if (code === 0) resolve();
212
+ else reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
213
+ });
214
+ });
215
+ }
216
+
169
217
  export {
170
218
  isIsolationEnabled,
171
219
  mindUserName,
@@ -173,5 +221,9 @@ export {
173
221
  createMindUser,
174
222
  deleteMindUser,
175
223
  wrapForIsolation,
176
- chownMindDir
224
+ chownMindDir,
225
+ exec,
226
+ gitExec,
227
+ resolveVoluteBin,
228
+ execInherit
177
229
  };
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ minds,
4
+ schema_exports
5
+ } from "./chunk-RPZZSXV3.js";
6
+
7
+ // src/lib/db.ts
8
+ import { chmodSync, existsSync } from "fs";
9
+ import { dirname as dirname2, resolve as resolve2 } from "path";
10
+ import { fileURLToPath as fileURLToPath2 } from "url";
11
+ import { drizzle } from "drizzle-orm/libsql";
12
+ import { migrate } from "drizzle-orm/libsql/migrator";
13
+
14
+ // src/lib/registry.ts
15
+ import { mkdirSync } from "fs";
16
+ import { homedir } from "os";
17
+ import { dirname, resolve } from "path";
18
+ import { fileURLToPath } from "url";
19
+ import { and, eq, isNull } from "drizzle-orm";
20
+ function voluteHome() {
21
+ if (process.env.VOLUTE_HOME) return process.env.VOLUTE_HOME;
22
+ const dir = dirname(fileURLToPath(import.meta.url));
23
+ if (dir.endsWith("/src/lib")) {
24
+ throw new Error(
25
+ 'VOLUTE_HOME must be set when running from source. For tests, run via "npm test" or add "--import ./test/setup.ts".'
26
+ );
27
+ }
28
+ return resolve(homedir(), ".volute");
29
+ }
30
+ function voluteUserHome() {
31
+ if (process.env.VOLUTE_USER_HOME) return process.env.VOLUTE_USER_HOME;
32
+ return resolve(homedir(), ".volute");
33
+ }
34
+ function voluteSystemDir() {
35
+ return resolve(voluteHome(), "system");
36
+ }
37
+ function ensureSystemDir() {
38
+ mkdirSync(voluteSystemDir(), { recursive: true });
39
+ }
40
+ function ensureVoluteHome() {
41
+ const mindsBase = process.env.VOLUTE_MINDS_DIR ?? resolve(voluteHome(), "minds");
42
+ mkdirSync(mindsBase, { recursive: true });
43
+ ensureSystemDir();
44
+ }
45
+ function rowToEntry(row) {
46
+ return {
47
+ name: row.name,
48
+ port: row.port,
49
+ created: row.created_at,
50
+ running: row.running === 1,
51
+ stage: row.stage ?? (row.parent ? void 0 : "sprouted"),
52
+ template: row.template ?? void 0,
53
+ templateHash: row.template_hash ?? void 0,
54
+ parent: row.parent ?? void 0,
55
+ dir: row.dir ?? void 0,
56
+ branch: row.branch ?? void 0,
57
+ mindType: row.mind_type ?? "mind",
58
+ createdBy: row.created_by ?? void 0
59
+ };
60
+ }
61
+ async function readRegistry() {
62
+ const db2 = await getDb();
63
+ const rows = await db2.select().from(minds).where(and(isNull(minds.parent), eq(minds.mind_type, "mind")));
64
+ return rows.map(rowToEntry);
65
+ }
66
+ async function readAllMinds() {
67
+ const db2 = await getDb();
68
+ const rows = await db2.select().from(minds);
69
+ return rows.map(rowToEntry);
70
+ }
71
+ var MIND_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
72
+ var MIND_NAME_MAX = 64;
73
+ var RESERVED_NAMES = /* @__PURE__ */ new Set(["volute", "system"]);
74
+ function validateMindName(name) {
75
+ if (!name) return "Mind name is required";
76
+ if (name.length > MIND_NAME_MAX) return `Mind name must be at most ${MIND_NAME_MAX} characters`;
77
+ if (!MIND_NAME_RE.test(name)) {
78
+ return "Mind name must start with alphanumeric and contain only alphanumeric, dots, dashes, or underscores";
79
+ }
80
+ if (RESERVED_NAMES.has(name.toLowerCase())) {
81
+ return `"${name}" is a reserved name`;
82
+ }
83
+ return null;
84
+ }
85
+ async function addMind(name, port, stage, template, createdBy) {
86
+ const err = validateMindName(name);
87
+ if (err) throw new Error(err);
88
+ const db2 = await getDb();
89
+ await db2.insert(minds).values({
90
+ name,
91
+ port,
92
+ stage: stage ?? null,
93
+ template: template ?? null,
94
+ created_by: createdBy ?? null
95
+ }).onConflictDoUpdate({
96
+ target: minds.name,
97
+ set: {
98
+ port,
99
+ stage: stage ?? null,
100
+ template: template ?? null,
101
+ created_by: createdBy ?? null
102
+ }
103
+ });
104
+ }
105
+ async function addSpirit(name, port, template, dir) {
106
+ const db2 = await getDb();
107
+ await db2.insert(minds).values({
108
+ name,
109
+ port,
110
+ template,
111
+ dir,
112
+ mind_type: "spirit",
113
+ stage: "sprouted"
114
+ }).onConflictDoUpdate({
115
+ target: minds.name,
116
+ set: { port, template, dir, mind_type: "spirit" }
117
+ });
118
+ }
119
+ async function readSpirits() {
120
+ const db2 = await getDb();
121
+ const rows = await db2.select().from(minds).where(eq(minds.mind_type, "spirit"));
122
+ return rows.map(rowToEntry);
123
+ }
124
+ async function addVariant(name, parent, port, dir, branch) {
125
+ const err = validateMindName(name);
126
+ if (err) throw new Error(err);
127
+ const db2 = await getDb();
128
+ await db2.insert(minds).values({ name, port, parent, dir, branch }).onConflictDoUpdate({
129
+ target: minds.name,
130
+ set: { port, parent, dir, branch }
131
+ });
132
+ }
133
+ async function removeMind(name) {
134
+ const db2 = await getDb();
135
+ await db2.delete(minds).where(eq(minds.name, name));
136
+ }
137
+ async function setMindRunning(name, running) {
138
+ const db2 = await getDb();
139
+ await db2.update(minds).set({ running: running ? 1 : 0 }).where(eq(minds.name, name));
140
+ }
141
+ async function setMindStage(name, stage) {
142
+ const db2 = await getDb();
143
+ await db2.update(minds).set({ stage }).where(eq(minds.name, name));
144
+ }
145
+ async function setMindTemplateHash(name, hash) {
146
+ const db2 = await getDb();
147
+ await db2.update(minds).set({ template_hash: hash }).where(eq(minds.name, name));
148
+ }
149
+ async function findMind(name) {
150
+ const db2 = await getDb();
151
+ const rows = await db2.select().from(minds).where(eq(minds.name, name));
152
+ if (rows.length === 0) return void 0;
153
+ return rowToEntry(rows[0]);
154
+ }
155
+ async function findVariants(parent) {
156
+ const db2 = await getDb();
157
+ const rows = await db2.select().from(minds).where(eq(minds.parent, parent));
158
+ return rows.map(rowToEntry);
159
+ }
160
+ async function getBaseName(name) {
161
+ const entry = await findMind(name);
162
+ return entry?.parent ?? name;
163
+ }
164
+ function mindDir(name) {
165
+ if (process.env.VOLUTE_MINDS_DIR) {
166
+ return resolve(process.env.VOLUTE_MINDS_DIR, name);
167
+ }
168
+ return resolve(voluteHome(), "minds", name);
169
+ }
170
+ async function resolveMindDir(name) {
171
+ const entry = await findMind(name);
172
+ return entry?.dir ?? mindDir(name);
173
+ }
174
+ function stateDir(name) {
175
+ return resolve(voluteSystemDir(), "state", name);
176
+ }
177
+ async function nextPort() {
178
+ const db2 = await getDb();
179
+ const rows = await db2.select({ port: minds.port }).from(minds);
180
+ const usedPorts = new Set(rows.map((r) => r.port));
181
+ const basePort = parseInt(process.env.VOLUTE_BASE_PORT || "4100", 10);
182
+ let port = basePort;
183
+ while (usedPorts.has(port)) port++;
184
+ if (port > 65535) throw new Error("No available ports \u2014 all ports 4100-65535 are allocated");
185
+ return port;
186
+ }
187
+ function daemonLoopback() {
188
+ const host = process.env.VOLUTE_DAEMON_HOSTNAME || "127.0.0.1";
189
+ if (host === "0.0.0.0") return "127.0.0.1";
190
+ if (host === "::") return "[::1]";
191
+ return host;
192
+ }
193
+
194
+ // src/lib/db.ts
195
+ var __dirname = dirname2(fileURLToPath2(import.meta.url));
196
+ var migrationsFolder = existsSync(resolve2(__dirname, "../drizzle")) ? resolve2(__dirname, "../drizzle") : resolve2(__dirname, "../../drizzle");
197
+ var db = null;
198
+ var dbPromise = null;
199
+ async function getDb() {
200
+ if (db) return db;
201
+ if (dbPromise) return dbPromise;
202
+ dbPromise = (async () => {
203
+ try {
204
+ const dbPath = process.env.VOLUTE_DB_PATH || resolve2(voluteSystemDir(), "volute.db");
205
+ const instance = drizzle({ connection: { url: `file:${dbPath}` }, schema: schema_exports });
206
+ await migrate(instance, { migrationsFolder });
207
+ try {
208
+ chmodSync(dbPath, 384);
209
+ } catch (err) {
210
+ console.error(
211
+ `[volute] WARNING: Failed to restrict database file permissions on ${dbPath}:`,
212
+ err
213
+ );
214
+ }
215
+ db = instance;
216
+ return instance;
217
+ } catch (err) {
218
+ dbPromise = null;
219
+ throw err;
220
+ }
221
+ })();
222
+ return dbPromise;
223
+ }
224
+
225
+ export {
226
+ getDb,
227
+ voluteHome,
228
+ voluteUserHome,
229
+ voluteSystemDir,
230
+ ensureSystemDir,
231
+ ensureVoluteHome,
232
+ readRegistry,
233
+ readAllMinds,
234
+ validateMindName,
235
+ addMind,
236
+ addSpirit,
237
+ readSpirits,
238
+ addVariant,
239
+ removeMind,
240
+ setMindRunning,
241
+ setMindStage,
242
+ setMindTemplateHash,
243
+ findMind,
244
+ findVariants,
245
+ getBaseName,
246
+ mindDir,
247
+ resolveMindDir,
248
+ stateDir,
249
+ nextPort,
250
+ daemonLoopback
251
+ };
@@ -2,11 +2,10 @@
2
2
  import {
3
3
  exec,
4
4
  execInherit
5
- } from "./chunk-CVH6Y2YG.js";
5
+ } from "./chunk-LGB6JBHI.js";
6
6
  import {
7
- voluteHome,
8
7
  voluteSystemDir
9
- } from "./chunk-HHTXM4JT.js";
8
+ } from "./chunk-LRCG2JLP.js";
10
9
 
11
10
  // src/lib/service-mode.ts
12
11
  import { execFileSync } from "child_process";
@@ -170,9 +169,7 @@ async function restartService(mode) {
170
169
  }
171
170
  }
172
171
  function readDaemonConfig() {
173
- const newPath = resolve(voluteSystemDir(), "daemon.json");
174
- const legacyPath = resolve(voluteHome(), "daemon.json");
175
- const configPath = existsSync(newPath) ? newPath : legacyPath;
172
+ const configPath = resolve(voluteSystemDir(), "daemon.json");
176
173
  if (!existsSync(configPath)) return { hostname: "127.0.0.1", port: 1618 };
177
174
  try {
178
175
  const config = JSON.parse(readFileSync(configPath, "utf-8"));
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteSystemDir
4
- } from "./chunk-HHTXM4JT.js";
4
+ } from "./chunk-LRCG2JLP.js";
5
5
 
6
6
  // src/lib/update-check.ts
7
7
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+
3
+ // packages/extensions/pages/src/db.ts
4
+ function initDb(db) {
5
+ db.exec(`
6
+ CREATE TABLE IF NOT EXISTS published_pages (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8
+ mind TEXT NOT NULL,
9
+ file TEXT NOT NULL,
10
+ published_at TEXT NOT NULL DEFAULT (datetime('now')),
11
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
12
+ );
13
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_pp_mind_file ON published_pages(mind, file);
14
+ CREATE INDEX IF NOT EXISTS idx_pp_updated_at ON published_pages(updated_at);
15
+ `);
16
+ }
17
+ function getPublishedPages(db, mind) {
18
+ return db.prepare(
19
+ "SELECT file, published_at, updated_at FROM published_pages WHERE mind = ? ORDER BY file"
20
+ ).all(mind);
21
+ }
22
+ function getRecentPages(db, opts) {
23
+ const limit = opts?.limit ?? 10;
24
+ if (opts?.mind) {
25
+ return db.prepare(
26
+ "SELECT mind, file, updated_at FROM published_pages WHERE mind = ? ORDER BY updated_at DESC LIMIT ?"
27
+ ).all(opts.mind, limit);
28
+ }
29
+ return db.prepare("SELECT mind, file, updated_at FROM published_pages ORDER BY updated_at DESC LIMIT ?").all(limit);
30
+ }
31
+ function getAllSites(db) {
32
+ const rows = db.prepare("SELECT mind, file, updated_at FROM published_pages ORDER BY mind, file").all();
33
+ const siteMap = /* @__PURE__ */ new Map();
34
+ for (const row of rows) {
35
+ let files = siteMap.get(row.mind);
36
+ if (!files) {
37
+ files = [];
38
+ siteMap.set(row.mind, files);
39
+ }
40
+ files.push({ file: row.file, updated_at: row.updated_at });
41
+ }
42
+ return Array.from(siteMap.entries()).map(([mind, files]) => ({ mind, files }));
43
+ }
44
+ function syncPublishedPages(db, mind, htmlFiles) {
45
+ const existing = new Map(
46
+ db.prepare("SELECT file, updated_at FROM published_pages WHERE mind = ?").all(mind).map((r) => [r.file, r.updated_at])
47
+ );
48
+ const newSet = new Set(htmlFiles);
49
+ const added = [];
50
+ const updated = [];
51
+ const removed = [];
52
+ db.exec("BEGIN");
53
+ try {
54
+ for (const file of htmlFiles) {
55
+ if (existing.has(file)) {
56
+ db.prepare(
57
+ "UPDATE published_pages SET updated_at = datetime('now') WHERE mind = ? AND file = ?"
58
+ ).run(mind, file);
59
+ updated.push(file);
60
+ } else {
61
+ db.prepare("INSERT INTO published_pages (mind, file) VALUES (?, ?)").run(mind, file);
62
+ added.push(file);
63
+ }
64
+ }
65
+ for (const [file] of existing) {
66
+ if (!newSet.has(file)) {
67
+ db.prepare("DELETE FROM published_pages WHERE mind = ? AND file = ?").run(mind, file);
68
+ removed.push(file);
69
+ }
70
+ }
71
+ db.exec("COMMIT");
72
+ } catch (err) {
73
+ db.exec("ROLLBACK");
74
+ throw err;
75
+ }
76
+ return { added, removed, updated };
77
+ }
78
+
79
+ export {
80
+ initDb,
81
+ getPublishedPages,
82
+ getRecentPages,
83
+ getAllSites,
84
+ syncPublishedPages
85
+ };
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ composeTemplate,
4
+ findTemplatesRoot,
5
+ listFiles
6
+ } from "./chunk-G53F3JA4.js";
7
+
8
+ // src/lib/template-hash.ts
9
+ import { createHash } from "crypto";
10
+ import { existsSync, readFileSync, rmSync } from "fs";
11
+ import { resolve } from "path";
12
+ var hashCache = /* @__PURE__ */ new Map();
13
+ function computeTemplateHash(templateName) {
14
+ const cached = hashCache.get(templateName);
15
+ if (cached) return cached;
16
+ const templatesRoot = findTemplatesRoot();
17
+ const baseDir = resolve(templatesRoot, "_base");
18
+ const templateDir = resolve(templatesRoot, templateName);
19
+ if (!existsSync(baseDir)) throw new Error(`Base template not found: ${baseDir}`);
20
+ if (!existsSync(templateDir)) throw new Error(`Template not found: ${templateName}`);
21
+ const { composedDir } = composeTemplate(templatesRoot, templateName);
22
+ try {
23
+ const files = listFiles(composedDir).filter((f) => !f.startsWith(".init/") && !f.startsWith(".init\\")).sort();
24
+ const hash = createHash("sha256");
25
+ for (const file of files) {
26
+ const content = readFileSync(resolve(composedDir, file));
27
+ hash.update(file);
28
+ hash.update("\0");
29
+ hash.update(content);
30
+ }
31
+ const result = hash.digest("hex");
32
+ hashCache.set(templateName, result);
33
+ return result;
34
+ } finally {
35
+ rmSync(composedDir, { recursive: true, force: true });
36
+ }
37
+ }
38
+
39
+ export {
40
+ computeTemplateHash
41
+ };