volute 0.31.0 → 0.33.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 (195) hide show
  1. package/README.md +31 -22
  2. package/dist/{accept-GAKQ3MEH.js → accept-D5VBM7JW.js} +5 -4
  3. package/dist/{activity-events-T5ZRCVAL.js → activity-events-XJO3P4RR.js} +3 -2
  4. package/dist/{ai-service-UWUPM4T6.js → ai-service-SBY2WG7O.js} +18 -5
  5. package/dist/api.d.ts +703 -1068
  6. package/dist/{archive-YBNSJYZZ.js → archive-INXYFVCW.js} +3 -2
  7. package/dist/{auth-T5AW2USD.js → auth-GKCDSO4T.js} +4 -3
  8. package/dist/{bridge-4AJ3EY26.js → bridge-TXWWPPOJ.js} +5 -4
  9. package/dist/{chat-7YLT7FI3.js → chat-U5ZOME3O.js} +8 -8
  10. package/dist/{chunk-NV3TYNWX.js → chunk-2NGTS5UU.js} +1 -1
  11. package/dist/{chunk-BWKIHH7B.js → chunk-3Z2DPESO.js} +662 -508
  12. package/dist/chunk-6LXAAQ43.js +22 -0
  13. package/dist/chunk-7J3HEVR7.js +220 -0
  14. package/dist/{chunk-NOWVQ7AL.js → chunk-A2A4KLFE.js} +351 -301
  15. package/dist/{chunk-LX6T3GKQ.js → chunk-ALEF47VT.js} +1 -1
  16. package/dist/{chunk-S2TZLSDH.js → chunk-C7I35G4R.js} +163 -15
  17. package/dist/{chunk-VGWJSNHS.js → chunk-G53F3JA4.js} +1 -35
  18. package/dist/{chunk-A6TUJJ3L.js → chunk-G6BSYHPK.js} +2 -2
  19. package/dist/{chunk-DAXJKPHZ.js → chunk-GY5HBI7A.js} +2 -2
  20. package/dist/{chunk-BC3P3QCK.js → chunk-I5KY25PQ.js} +1 -9
  21. package/dist/{chunk-BNC43CSY.js → chunk-JUKK7FPS.js} +2 -2
  22. package/dist/{chunk-R5QJBZZG.js → chunk-JYVGHWEJ.js} +21 -11
  23. package/dist/chunk-KIEPMIM5.js +59 -0
  24. package/dist/{chunk-EKDWA7E4.js → chunk-KVK2DLWI.js} +5 -2
  25. package/dist/{chunk-AAO77TZX.js → chunk-LOEJ4HPQ.js} +1 -1
  26. package/dist/chunk-LRCG2JLP.js +251 -0
  27. package/dist/{chunk-EMPFLFTG.js → chunk-M7UL5S3Q.js} +1 -1
  28. package/dist/{chunk-6QIUN46C.js → chunk-N432I7QH.js} +20 -3
  29. package/dist/{chunk-SNVPRRT7.js → chunk-NNB4WIG7.js} +2 -2
  30. package/dist/{chunk-HDKY4TWU.js → chunk-NPKSDYA2.js} +3 -3
  31. package/dist/chunk-OYAKCAVY.js +29 -0
  32. package/dist/chunk-PB65JZK2.js +85 -0
  33. package/dist/chunk-PVY5W6QN.js +41 -0
  34. package/dist/{chunk-PNQCXLSV.js → chunk-QTUVYI7W.js} +58 -1
  35. package/dist/{chunk-X62AXPR7.js → chunk-RPZZSXV3.js} +8 -196
  36. package/dist/{chunk-WRS3B556.js → chunk-RSX4OPZY.js} +5 -5
  37. package/dist/{chunk-FAHDKPEH.js → chunk-RVGLDGMI.js} +5 -3
  38. package/dist/chunk-SKLSMHXO.js +208 -0
  39. package/dist/{chunk-4OUOFS23.js → chunk-UKVWJRKN.js} +1 -1
  40. package/dist/{chunk-57OKQMP3.js → chunk-VH33ZWMW.js} +5 -55
  41. package/dist/cli.js +49 -23
  42. package/dist/{clock-LJCG426D.js → clock-BVH3V6E3.js} +7 -6
  43. package/dist/{cloud-sync-O3LXIRN6.js → cloud-sync-4NWLMFVH.js} +20 -14
  44. package/dist/config-H2H4UIF7.js +72 -0
  45. package/dist/connectors/discord-bridge.js +1 -1
  46. package/dist/connectors/slack-bridge.js +1 -1
  47. package/dist/connectors/telegram-bridge.js +1 -1
  48. package/dist/{conversations-RKKGP5IA.js → conversations-AWI5SZW2.js} +4 -3
  49. package/dist/{create-TL623TFC.js → create-2FK7Z46Y.js} +6 -2
  50. package/dist/{create-WUTIIRI2.js → create-YWD2TIP4.js} +6 -5
  51. package/dist/{daemon-client-CVGM25DM.js → daemon-client-6QXHZ7US.js} +3 -2
  52. package/dist/{daemon-restart-EZP7XH3V.js → daemon-restart-GOBUKLX7.js} +8 -6
  53. package/dist/daemon.js +1918 -1472
  54. package/dist/{db-SW5PL6QA.js → db-F34YLV7D.js} +2 -1
  55. package/dist/db-RA45JBFG.js +16 -0
  56. package/dist/{delete-Z6HAG35F.js → delete-QTGWEDBI.js} +1 -1
  57. package/dist/delivery-manager-PFAKEJTC.js +32 -0
  58. package/dist/delivery-router-FL45JL7N.js +21 -0
  59. package/dist/down-FWWTEKXM.js +15 -0
  60. package/dist/{env-NHESNNSP.js → env-JCOF2222.js} +5 -4
  61. package/dist/{export-EVMP7GWY.js → export-SUYRLI5Q.js} +4 -3
  62. package/dist/{extension-LR7EW3JF.js → extension-OBTGKQQD.js} +5 -3
  63. package/dist/{extensions-NGEJI7JH.js → extensions-KYNTVTMO.js} +10 -7
  64. package/dist/{files-3SM7V33S.js → files-65PMW5IK.js} +6 -5
  65. package/dist/{history-PQD3LXEP.js → history-DKCDI3JO.js} +9 -4
  66. package/dist/{import-PR2OCGQJ.js → import-DDUFE7AY.js} +4 -3
  67. package/dist/isolation-LLAYQYDY.js +22 -0
  68. package/dist/{join-R4EN5CWQ.js → join-I5QEE3LG.js} +1 -1
  69. package/dist/{list-B4XNUOFO.js → list-JQ463EDA.js} +5 -4
  70. package/dist/{login-62JVY6A2.js → login-D7ETSU4R.js} +5 -4
  71. package/dist/{login-URWP6S2N.js → login-RIJF2F4G.js} +3 -2
  72. package/dist/{logout-NXJQJDLI.js → logout-5MLHZALK.js} +3 -2
  73. package/dist/{logout-ZK2N62T3.js → logout-UZJRGY4Z.js} +3 -2
  74. package/dist/message-delivery-DFF5SJRM.js +42 -0
  75. package/dist/{mind-E2ZV2WRX.js → mind-IOJFLEM5.js} +25 -19
  76. package/dist/{mind-activity-tracker-ASNZBMLC.js → mind-activity-tracker-F6O4Q2SL.js} +4 -3
  77. package/dist/{mind-list-BEI7E5WY.js → mind-list-WUPMQDYQ.js} +3 -2
  78. package/dist/mind-manager-NBJF5D26.js +32 -0
  79. package/dist/mind-profile-P67FEHOY.js +47 -0
  80. package/dist/mind-service-2MQ6UK5N.js +38 -0
  81. package/dist/{mind-sleep-CANABWJI.js → mind-sleep-WW2IX7JT.js} +5 -4
  82. package/dist/{mind-status-6WKZVUOP.js → mind-status-L3EFFRPR.js} +3 -2
  83. package/dist/{mind-wake-RZKLH2IN.js → mind-wake-VSSGW465.js} +5 -4
  84. package/dist/{package-NU4CA7OU.js → package-U3VFO273.js} +2 -1
  85. package/dist/{read-THL362EI.js → read-EBY56C33.js} +5 -4
  86. package/dist/read-stdin-HQJ7774D.js +8 -0
  87. package/dist/{register-QAQELAS6.js → register-HD74C4TT.js} +5 -4
  88. package/dist/{registry-ASXCQCNH.js → registry-PJ4S5PHQ.js} +8 -1
  89. package/dist/{reject-AYPBNPNL.js → reject-UJKFBHRO.js} +5 -4
  90. package/dist/{restart-6SKPV3T2.js → restart-3UCMRUVC.js} +3 -2
  91. package/dist/{sandbox-6ZEWQDVU.js → sandbox-GJOK4QLQ.js} +4 -3
  92. package/dist/scheduler-ZZ7XGQG6.js +32 -0
  93. package/dist/schema-PA3M5ZKH.js +32 -0
  94. package/dist/seed-QDYVLG74.js +11 -0
  95. package/dist/seed-check-S2IX25RL.js +32 -0
  96. package/dist/seed-cmd-DKOUFEAU.js +36 -0
  97. package/dist/{seed-OWX2AW75.js → seed-create-4XBBOLRH.js} +27 -10
  98. package/dist/{sprout-FDVI2CGN.js → seed-sprout-GQEIIQRT.js} +24 -9
  99. package/dist/{send-ZO4BTWXK.js → send-QIV2INHB.js} +92 -101
  100. package/dist/{setup-7CFITEQN.js → setup-TISPCO22.js} +7 -2
  101. package/dist/{setup-ZXBXG7E4.js → setup-XMCBE3LF.js} +11 -7
  102. package/dist/{skill-YFXP67A2.js → skill-PSQGRRJX.js} +5 -4
  103. package/dist/skills/dreaming/SKILL.md +6 -4
  104. package/dist/skills/dreaming/references/INSTALL.md +2 -2
  105. package/dist/skills/dreaming/scripts/dream.ts +2 -2
  106. package/dist/skills/dreaming/scripts/wake-context-dreams.sh +1 -1
  107. package/dist/skills/imagegen/SKILL.md +16 -11
  108. package/dist/skills/imagegen/references/INSTALL.md +1 -1
  109. package/dist/skills/imagegen/scripts/imagegen.ts +146 -25
  110. package/dist/skills/orientation/SKILL.md +9 -2
  111. package/dist/skills/resonance/SKILL.md +4 -1
  112. package/dist/skills/resonance/references/INSTALL.md +2 -2
  113. package/dist/skills/resonance/scripts/resonance-hook.sh +2 -0
  114. package/dist/skills/resonance/scripts/resonance.ts +35 -5
  115. package/dist/skills/seed-nurture/SKILL.md +42 -0
  116. package/dist/skills/volute-admin/SKILL.md +83 -0
  117. package/dist/skills/volute-mind/SKILL.md +15 -11
  118. package/dist/skills-7FV7EJTE.js +62 -0
  119. package/dist/sleep-manager-JTXSN7NV.js +36 -0
  120. package/dist/spirit-VRONKFMF.js +23 -0
  121. package/dist/{split-MI62KJUU.js → split-STOROBYJ.js} +1 -1
  122. package/dist/sprout-WKLZXUIQ.js +11 -0
  123. package/dist/{start-D64BRKPH.js → start-K2NCUUCG.js} +3 -2
  124. package/dist/{status-ZZWBYFGE.js → status-3JBTFSMI.js} +6 -4
  125. package/dist/{stop-OP2CTXCO.js → stop-H26JZDXF.js} +3 -2
  126. package/dist/system-chat-JAPOJ3KE.js +36 -0
  127. package/dist/{systems-EQPPT4B7.js → systems-XRI52VCH.js} +6 -5
  128. package/dist/{tailscale-6DJKUMNF.js → tailscale-XHQBZROW.js} +2 -1
  129. package/dist/{template-hash-3HOR4UAJ.js → template-hash-A6VVKOXJ.js} +2 -1
  130. package/dist/up-M5AS6SBV.js +18 -0
  131. package/dist/{update-KUJXATRS.js → update-UD543CXX.js} +6 -4
  132. package/dist/{update-check-5WVSU37T.js → update-check-ZD6OOIYQ.js} +3 -2
  133. package/dist/{upgrade-KBHCWX6T.js → upgrade-O4Q7WJM3.js} +12 -14
  134. package/dist/{version-notify-75ELVKPV.js → version-notify-NBI2MTJO.js} +22 -16
  135. package/dist/volute-config-HD7WWUQC.js +10 -0
  136. package/dist/web-assets/assets/index-CWJrVveV.css +1 -0
  137. package/dist/web-assets/assets/index-DJt14FRI.js +75 -0
  138. package/dist/web-assets/ext-theme.css +93 -0
  139. package/dist/web-assets/index.html +2 -2
  140. package/drizzle/0004_spirits.sql +5 -0
  141. package/drizzle/meta/0004_snapshot.json +7 -0
  142. package/drizzle/meta/_journal.json +7 -0
  143. package/package.json +2 -1
  144. package/packages/extensions/notes/dist/ui/assets/index-8jWEv9SA.js +61 -0
  145. package/packages/extensions/notes/dist/ui/assets/index-DkaB7Ytd.css +1 -0
  146. package/packages/extensions/notes/dist/ui/index.html +2 -2
  147. package/packages/extensions/pages/skills/pages/SKILL.md +16 -46
  148. package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +40 -0
  149. package/templates/_base/.init/{.config → .local}/bin/volute +1 -1
  150. package/templates/_base/.init/.local/hooks/pre-prompt/session-activity.ts +40 -0
  151. package/templates/_base/.init/.local/hooks/startup-context.ts +58 -0
  152. package/templates/_base/home/.config/routes.json +1 -1
  153. package/templates/_base/src/lib/daemon-client.ts +21 -13
  154. package/templates/_base/src/lib/format-prefix.ts +1 -0
  155. package/templates/_base/src/lib/hook-loader.ts +155 -0
  156. package/templates/_base/src/lib/startup.ts +11 -4
  157. package/templates/_base/src/lib/transparency.ts +2 -2
  158. package/templates/claude/.init/.claude/settings.json +1 -1
  159. package/templates/claude/.init/.config/routes.json +2 -2
  160. package/templates/claude/src/agent.ts +95 -13
  161. package/templates/claude/src/lib/message-channel.ts +7 -2
  162. package/templates/claude/src/lib/stream-consumer.ts +38 -0
  163. package/templates/codex/.init/.config/routes.json +11 -0
  164. package/templates/codex/.init/AGENTS.md +29 -0
  165. package/templates/codex/home/.config/config.json.tmpl +7 -0
  166. package/templates/codex/package.json.tmpl +20 -0
  167. package/templates/codex/src/agent.ts +554 -0
  168. package/templates/codex/src/lib/content.ts +16 -0
  169. package/templates/codex/src/lib/session-store.ts +56 -0
  170. package/templates/codex/src/server.ts +59 -0
  171. package/templates/codex/volute-template.json +8 -0
  172. package/templates/pi/.init/.config/routes.json +2 -2
  173. package/templates/pi/src/agent.ts +62 -8
  174. package/templates/pi/src/lib/event-handler.ts +1 -1
  175. package/templates/pi/src/lib/reply-instructions-extension.ts +32 -11
  176. package/dist/chunk-HR5JKIDG.js +0 -222
  177. package/dist/down-TS4XQBA4.js +0 -13
  178. package/dist/message-delivery-UJHCLVU4.js +0 -30
  179. package/dist/mind-manager-IPA6DZXD.js +0 -26
  180. package/dist/pages-watcher-72OVPRMH.js +0 -22
  181. package/dist/skills/sessions/SKILL.md +0 -49
  182. package/dist/sleep-manager-TPS6OGCA.js +0 -30
  183. package/dist/system-chat-B43GIXQU.js +0 -30
  184. package/dist/up-TDXEP3VA.js +0 -16
  185. package/dist/web-assets/assets/index-BM1cTzBg.js +0 -72
  186. package/dist/web-assets/assets/index-BfJkKTPF.css +0 -1
  187. package/packages/extensions/notes/dist/ui/assets/index-B8GdTnXs.css +0 -1
  188. package/packages/extensions/notes/dist/ui/assets/index-CDpGTCWb.js +0 -2
  189. package/packages/extensions/pages/skills/pages/scripts/pages.mjs +0 -58
  190. package/templates/_base/.init/.config/hooks/startup-context.sh +0 -46
  191. package/templates/_base/.init/.config/scripts/session-reader.ts +0 -59
  192. package/templates/_base/src/lib/session-monitor.ts +0 -400
  193. package/templates/claude/src/lib/hooks/session-context.ts +0 -32
  194. package/templates/pi/src/lib/session-context-extension.ts +0 -35
  195. /package/templates/_base/.init/{.config → .local}/hooks/wake-context.sh +0 -0
@@ -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
+ };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteSystemDir
4
- } from "./chunk-X62AXPR7.js";
4
+ } from "./chunk-LRCG2JLP.js";
5
5
 
6
6
  // src/lib/update-check.ts
7
7
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteSystemDir
4
- } from "./chunk-X62AXPR7.js";
4
+ } from "./chunk-LRCG2JLP.js";
5
5
 
6
6
  // src/lib/setup.ts
7
7
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -27,12 +27,29 @@ function writeGlobalConfig(config) {
27
27
  }
28
28
  function isSetupComplete() {
29
29
  const config = readGlobalConfig();
30
- return config.setup != null;
30
+ return config.setupCompleted === true;
31
+ }
32
+ function isImagegenEnabled() {
33
+ const config = readGlobalConfig();
34
+ const ig = config.imagegen;
35
+ if (!ig) return false;
36
+ if (ig.enabled === true) return true;
37
+ if (ig.providers && Object.keys(ig.providers).length > 0) return true;
38
+ return false;
39
+ }
40
+ function migrateSetupCompleted() {
41
+ const config = readGlobalConfig();
42
+ if (config.setup != null && config.setupCompleted == null) {
43
+ config.setupCompleted = true;
44
+ writeGlobalConfig(config);
45
+ }
31
46
  }
32
47
 
33
48
  export {
34
49
  configPath,
35
50
  readGlobalConfig,
36
51
  writeGlobalConfig,
37
- isSetupComplete
52
+ isSetupComplete,
53
+ isImagegenEnabled,
54
+ migrateSetupCompleted
38
55
  };
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  exec,
4
4
  execInherit
5
- } from "./chunk-57OKQMP3.js";
5
+ } from "./chunk-KIEPMIM5.js";
6
6
  import {
7
7
  voluteSystemDir
8
- } from "./chunk-X62AXPR7.js";
8
+ } from "./chunk-LRCG2JLP.js";
9
9
 
10
10
  // src/lib/service-mode.ts
11
11
  import { execFileSync } from "child_process";
@@ -4,17 +4,17 @@ import {
4
4
  modeLabel,
5
5
  pollHealth,
6
6
  startService
7
- } from "./chunk-SNVPRRT7.js";
7
+ } from "./chunk-NNB4WIG7.js";
8
8
  import {
9
9
  readGlobalConfig
10
- } from "./chunk-6QIUN46C.js";
10
+ } from "./chunk-N432I7QH.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-X62AXPR7.js";
17
+ } from "./chunk-LRCG2JLP.js";
18
18
 
19
19
  // src/commands/up.ts
20
20
  import { spawn } from "child_process";
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/volute-config.ts
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
+ import { dirname, resolve } from "path";
6
+ function readJson(path) {
7
+ if (!existsSync(path)) return null;
8
+ try {
9
+ return JSON.parse(readFileSync(path, "utf-8"));
10
+ } catch (err) {
11
+ console.error(`[volute-config] failed to parse ${path}: ${err}`);
12
+ return null;
13
+ }
14
+ }
15
+ function readVoluteConfig(mindDir) {
16
+ const path = resolve(mindDir, "home/.config/volute.json");
17
+ return readJson(path);
18
+ }
19
+ function writeVoluteConfig(mindDir, config) {
20
+ const path = resolve(mindDir, "home/.config/volute.json");
21
+ mkdirSync(dirname(path), { recursive: true });
22
+ writeFileSync(path, `${JSON.stringify(config, null, 2)}
23
+ `);
24
+ }
25
+
26
+ export {
27
+ readVoluteConfig,
28
+ writeVoluteConfig
29
+ };
@@ -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
+ };
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  readGlobalConfig,
7
7
  writeGlobalConfig
8
- } from "./chunk-6QIUN46C.js";
8
+ } from "./chunk-N432I7QH.js";
9
9
 
10
10
  // src/lib/ai-service.ts
11
11
  import { complete, getEnvApiKey, getModel, getModels, getProviders } from "@mariozechner/pi-ai";
@@ -57,6 +57,47 @@ function getConfiguredProviders() {
57
57
  function isAiConfigured() {
58
58
  return getEnabledModels().length > 0;
59
59
  }
60
+ function getUtilityModel() {
61
+ const ai = getAiConfig();
62
+ return ai?.utilityModel;
63
+ }
64
+ function setUtilityModel(modelId) {
65
+ const ai = getAiConfig() ?? { providers: {} };
66
+ ai.utilityModel = modelId;
67
+ const config = readGlobalConfig();
68
+ writeGlobalConfig({ ...config, ai });
69
+ }
70
+ async function aiCompleteUtility(systemPrompt, userMessage) {
71
+ const utilityModel = getUtilityModel();
72
+ return aiComplete(systemPrompt, userMessage, utilityModel);
73
+ }
74
+ function templateForProvider(provider) {
75
+ if (provider === "anthropic") return "claude";
76
+ if (provider === "openai-codex") return "codex";
77
+ return "pi";
78
+ }
79
+ function resolveTemplate(modelId) {
80
+ if (!modelId) {
81
+ const enabled = getEnabledModels();
82
+ if (enabled.length > 0) {
83
+ const model2 = findModel(enabled[0]);
84
+ if (model2) return templateForProvider(model2.provider);
85
+ }
86
+ const providers = getConfiguredProviders();
87
+ if (providers.length === 1) return templateForProvider(providers[0]);
88
+ if (providers.length > 0 && !providers.includes("anthropic")) {
89
+ return templateForProvider(providers[0]);
90
+ }
91
+ return "claude";
92
+ }
93
+ if (modelId.includes(":")) {
94
+ const provider = modelId.split(":")[0];
95
+ return templateForProvider(provider);
96
+ }
97
+ const model = findModel(modelId);
98
+ if (model) return templateForProvider(model.provider);
99
+ return "claude";
100
+ }
60
101
  function getEnabledModels() {
61
102
  const ai = getAiConfig();
62
103
  return ai?.models ?? [];
@@ -100,6 +141,16 @@ async function resolveApiKey(providerId) {
100
141
  if (providerConfig?.apiKey) return providerConfig.apiKey;
101
142
  return getEnvApiKey(providerId) ?? void 0;
102
143
  }
144
+ function qualifyModelId(modelId) {
145
+ if (modelId.includes(":")) return modelId;
146
+ const model = findModel(modelId);
147
+ if (model) return `${model.provider}:${model.id}`;
148
+ return modelId;
149
+ }
150
+ function unqualifyModelId(modelId) {
151
+ const idx = modelId.indexOf(":");
152
+ return idx >= 0 ? modelId.slice(idx + 1) : modelId;
153
+ }
103
154
  function findModel(modelId) {
104
155
  const providers = getConfiguredProviders();
105
156
  for (const provider of providers) {
@@ -161,9 +212,15 @@ export {
161
212
  removeAiConfig,
162
213
  getConfiguredProviders,
163
214
  isAiConfigured,
215
+ getUtilityModel,
216
+ setUtilityModel,
217
+ aiCompleteUtility,
218
+ resolveTemplate,
164
219
  getEnabledModels,
165
220
  setEnabledModels,
166
221
  getAvailableModels,
167
222
  resolveApiKey,
223
+ qualifyModelId,
224
+ unqualifyModelId,
168
225
  aiComplete
169
226
  };