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
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger_default
4
+ } from "./chunk-YUIHSKR6.js";
5
+ import {
6
+ mindDir
7
+ } from "./chunk-LRCG2JLP.js";
8
+
9
+ // src/lib/delivery/delivery-router.ts
10
+ import { readFileSync, statSync } from "fs";
11
+ import { resolve } from "path";
12
+ function extractTextContent(content) {
13
+ if (typeof content === "string") return content;
14
+ if (Array.isArray(content)) {
15
+ return content.filter((p) => p.type === "text" && p.text).map((p) => p.text).join("\n");
16
+ }
17
+ return JSON.stringify(content);
18
+ }
19
+ var configCache = /* @__PURE__ */ new Map();
20
+ var statCheckCache = /* @__PURE__ */ new Map();
21
+ var STAT_TTL_MS = 5e3;
22
+ var dlog = logger_default.child("delivery-router");
23
+ var dirOverrides = /* @__PURE__ */ new Map();
24
+ function registerMindDir(name, dir) {
25
+ dirOverrides.set(name, dir);
26
+ }
27
+ function configPath(mindName) {
28
+ const dir = dirOverrides.get(mindName) ?? mindDir(mindName);
29
+ return resolve(dir, "home/.config/routes.json");
30
+ }
31
+ function getRoutingConfig(mindName) {
32
+ const path = configPath(mindName);
33
+ const now = Date.now();
34
+ const statCached = statCheckCache.get(mindName);
35
+ const cached = configCache.get(mindName);
36
+ if (statCached && cached && now - statCached.checkedAt < STAT_TTL_MS) {
37
+ return cached.config;
38
+ }
39
+ let mtime;
40
+ try {
41
+ mtime = statSync(path).mtimeMs;
42
+ } catch {
43
+ configCache.delete(mindName);
44
+ statCheckCache.delete(mindName);
45
+ return {};
46
+ }
47
+ statCheckCache.set(mindName, { mtime, checkedAt: now });
48
+ if (cached && cached.mtime === mtime) {
49
+ return cached.config;
50
+ }
51
+ try {
52
+ const config = JSON.parse(readFileSync(path, "utf-8"));
53
+ configCache.set(mindName, { config, mtime });
54
+ return config;
55
+ } catch (err) {
56
+ dlog.warn(`failed to load routes.json for ${mindName}`, logger_default.errorData(err));
57
+ configCache.delete(mindName);
58
+ return {};
59
+ }
60
+ }
61
+ var globRegexCache = /* @__PURE__ */ new Map();
62
+ function clearConfigCache(mindName) {
63
+ if (mindName) {
64
+ configCache.delete(mindName);
65
+ statCheckCache.delete(mindName);
66
+ } else {
67
+ configCache.clear();
68
+ statCheckCache.clear();
69
+ globRegexCache.clear();
70
+ }
71
+ }
72
+ function globMatch(pattern, value) {
73
+ let regex = globRegexCache.get(pattern);
74
+ if (!regex) {
75
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
76
+ regex = new RegExp(`^${escaped}$`);
77
+ globRegexCache.set(pattern, regex);
78
+ }
79
+ return regex.test(value);
80
+ }
81
+ var GLOB_MATCH_KEYS = /* @__PURE__ */ new Set(["channel", "sender"]);
82
+ var NON_MATCH_KEYS = /* @__PURE__ */ new Set(["session", "destination", "path", "mode", "batch"]);
83
+ function ruleMatches(rule, meta) {
84
+ for (const [key, pattern] of Object.entries(rule)) {
85
+ if (NON_MATCH_KEYS.has(key)) continue;
86
+ if (key === "isDM") {
87
+ if (typeof pattern !== "boolean") return false;
88
+ if ((meta.isDM ?? false) !== pattern) return false;
89
+ continue;
90
+ }
91
+ if (key === "participants") {
92
+ if (typeof pattern !== "number") return false;
93
+ if ((meta.participantCount ?? 0) !== pattern) return false;
94
+ continue;
95
+ }
96
+ if (typeof pattern !== "string") return false;
97
+ if (!GLOB_MATCH_KEYS.has(key)) return false;
98
+ const value = meta[key] ?? "";
99
+ if (!globMatch(pattern, value)) return false;
100
+ }
101
+ return true;
102
+ }
103
+ function expandTemplate(template, meta) {
104
+ return template.replace(/\$\{sender\}/g, meta.sender ?? "unknown").replace(/\$\{channel\}/g, meta.channel ?? "unknown");
105
+ }
106
+ function sanitizeSessionName(name) {
107
+ return name.replace(/\0/g, "").replace(/[/\\]/g, "-").replace(/\.\./g, "-").slice(0, 100);
108
+ }
109
+ function resolveRoute(config, meta) {
110
+ const fallback = config.default ?? "main";
111
+ if (!config.rules) {
112
+ return { destination: "mind", session: fallback, matched: false };
113
+ }
114
+ for (const rule of config.rules) {
115
+ if (ruleMatches(rule, meta)) {
116
+ if (rule.destination === "file") {
117
+ if (!rule.path) {
118
+ dlog.warn("file destination rule missing path \u2014 falling through");
119
+ continue;
120
+ }
121
+ return { destination: "file", path: rule.path, matched: true };
122
+ }
123
+ return {
124
+ destination: "mind",
125
+ session: sanitizeSessionName(expandTemplate(rule.session ?? fallback, meta)),
126
+ matched: true,
127
+ mode: rule.mode,
128
+ rule
129
+ };
130
+ }
131
+ }
132
+ return { destination: "mind", session: fallback, matched: false };
133
+ }
134
+ var DEFAULT_BATCH_DEBOUNCE = 5;
135
+ var DEFAULT_BATCH_MAX_WAIT = 120;
136
+ function normalizeBatchConfig(batch) {
137
+ if (typeof batch === "number") return { maxWait: batch * 60 };
138
+ return batch;
139
+ }
140
+ function resolveDeliveryMode(config, sessionName, rule) {
141
+ const ruleBatch = rule?.batch;
142
+ const defaults = {
143
+ delivery: { mode: "immediate" },
144
+ interrupt: true
145
+ };
146
+ if (!config.sessions) {
147
+ if (ruleBatch != null) {
148
+ const batch = normalizeBatchConfig(ruleBatch);
149
+ return {
150
+ delivery: {
151
+ mode: "batch",
152
+ debounce: batch.debounce ?? DEFAULT_BATCH_DEBOUNCE,
153
+ maxWait: batch.maxWait ?? DEFAULT_BATCH_MAX_WAIT,
154
+ triggers: batch.triggers
155
+ },
156
+ interrupt: true
157
+ };
158
+ }
159
+ return defaults;
160
+ }
161
+ for (const [pattern, sessionConfig] of Object.entries(config.sessions)) {
162
+ if (globMatch(pattern, sessionName)) {
163
+ let delivery;
164
+ if (sessionConfig.delivery == null || sessionConfig.delivery === "immediate") {
165
+ delivery = { mode: "immediate" };
166
+ } else if (sessionConfig.delivery === "batch") {
167
+ delivery = {
168
+ mode: "batch",
169
+ debounce: DEFAULT_BATCH_DEBOUNCE,
170
+ maxWait: DEFAULT_BATCH_MAX_WAIT
171
+ };
172
+ } else {
173
+ delivery = {
174
+ mode: "batch",
175
+ debounce: sessionConfig.delivery.debounce ?? DEFAULT_BATCH_DEBOUNCE,
176
+ maxWait: sessionConfig.delivery.maxWait ?? DEFAULT_BATCH_MAX_WAIT
177
+ };
178
+ }
179
+ return {
180
+ delivery,
181
+ interrupt: true,
182
+ instructions: sessionConfig.instructions
183
+ };
184
+ }
185
+ }
186
+ if (ruleBatch != null) {
187
+ const batch = normalizeBatchConfig(ruleBatch);
188
+ return {
189
+ delivery: {
190
+ mode: "batch",
191
+ debounce: batch.debounce ?? DEFAULT_BATCH_DEBOUNCE,
192
+ maxWait: batch.maxWait ?? DEFAULT_BATCH_MAX_WAIT,
193
+ triggers: batch.triggers
194
+ },
195
+ interrupt: true
196
+ };
197
+ }
198
+ return defaults;
199
+ }
200
+
201
+ export {
202
+ extractTextContent,
203
+ registerMindDir,
204
+ getRoutingConfig,
205
+ clearConfigCache,
206
+ resolveRoute,
207
+ resolveDeliveryMode
208
+ };
@@ -4,14 +4,14 @@ import {
4
4
  } from "./chunk-YUIHSKR6.js";
5
5
  import {
6
6
  readGlobalConfig
7
- } from "./chunk-7D47T4RB.js";
7
+ } from "./chunk-TSXLLQZW.js";
8
8
  import {
9
9
  getBaseName,
10
10
  readRegistry,
11
11
  voluteHome,
12
12
  voluteSystemDir,
13
13
  voluteUserHome
14
- } from "./chunk-HHTXM4JT.js";
14
+ } from "./chunk-LRCG2JLP.js";
15
15
 
16
16
  // src/lib/sandbox.ts
17
17
  import { resolve } from "path";
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ broadcast
4
+ } from "./chunk-QZANELPX.js";
5
+ import {
6
+ getDb
7
+ } from "./chunk-LRCG2JLP.js";
8
+ import {
9
+ users
10
+ } from "./chunk-RPZZSXV3.js";
11
+
12
+ // src/lib/auth.ts
13
+ import { compareSync, hashSync } from "bcryptjs";
14
+ import { and, count, eq, or } from "drizzle-orm";
15
+ var userSelectFields = {
16
+ id: users.id,
17
+ username: users.username,
18
+ role: users.role,
19
+ user_type: users.user_type,
20
+ display_name: users.display_name,
21
+ description: users.description,
22
+ avatar: users.avatar,
23
+ created_at: users.created_at
24
+ };
25
+ async function createUser(username, password) {
26
+ const db = await getDb();
27
+ const hash = hashSync(password, 10);
28
+ const [{ value }] = await db.select({ value: count() }).from(users).where(eq(users.user_type, "brain"));
29
+ const role = value === 0 ? "admin" : "pending";
30
+ const [result] = await db.insert(users).values({ username, password_hash: hash, role }).returning(userSelectFields);
31
+ return result;
32
+ }
33
+ async function verifyUser(username, password) {
34
+ const db = await getDb();
35
+ const row = await db.select().from(users).where(eq(users.username, username)).get();
36
+ if (!row) return null;
37
+ if (row.user_type === "mind" || row.user_type === "system") return null;
38
+ if (!compareSync(password, row.password_hash)) return null;
39
+ const { password_hash: _, ...user } = row;
40
+ return user;
41
+ }
42
+ async function getUser(id) {
43
+ const db = await getDb();
44
+ const row = await db.select(userSelectFields).from(users).where(eq(users.id, id)).get();
45
+ return row ?? null;
46
+ }
47
+ async function getUserByUsername(username) {
48
+ const db = await getDb();
49
+ const row = await db.select(userSelectFields).from(users).where(eq(users.username, username)).get();
50
+ return row ?? null;
51
+ }
52
+ async function listUsers() {
53
+ const db = await getDb();
54
+ return db.select(userSelectFields).from(users).orderBy(users.created_at).all();
55
+ }
56
+ async function listPendingUsers() {
57
+ const db = await getDb();
58
+ return db.select(userSelectFields).from(users).where(eq(users.role, "pending")).orderBy(users.created_at).all();
59
+ }
60
+ async function listUsersByType(userType) {
61
+ const db = await getDb();
62
+ return db.select(userSelectFields).from(users).where(eq(users.user_type, userType)).orderBy(users.created_at).all();
63
+ }
64
+ async function getOrCreateMindUser(mindName) {
65
+ const db = await getDb();
66
+ const existing = await db.select(userSelectFields).from(users).where(
67
+ and(
68
+ eq(users.username, mindName),
69
+ or(eq(users.user_type, "mind"), eq(users.user_type, "system"))
70
+ )
71
+ ).get();
72
+ if (existing) return existing;
73
+ try {
74
+ const [result] = await db.insert(users).values({
75
+ username: mindName,
76
+ password_hash: "!mind",
77
+ role: "user",
78
+ user_type: "mind"
79
+ }).returning(userSelectFields);
80
+ return result;
81
+ } catch (err) {
82
+ const retried = await db.select(userSelectFields).from(users).where(
83
+ and(
84
+ eq(users.username, mindName),
85
+ or(eq(users.user_type, "mind"), eq(users.user_type, "system"))
86
+ )
87
+ ).get();
88
+ if (retried) return retried;
89
+ throw err;
90
+ }
91
+ }
92
+ async function getOrCreateSystemUser() {
93
+ const db = await getDb();
94
+ const existing = await db.select(userSelectFields).from(users).where(and(eq(users.username, "volute"), eq(users.user_type, "system"))).get();
95
+ if (existing) return existing;
96
+ try {
97
+ const [result] = await db.insert(users).values({
98
+ username: "volute",
99
+ password_hash: "!system",
100
+ role: "system",
101
+ user_type: "system",
102
+ display_name: "Volute"
103
+ }).returning(userSelectFields);
104
+ return result;
105
+ } catch (err) {
106
+ if (err instanceof Error && err.message.includes("UNIQUE constraint")) {
107
+ const retried = await db.select(userSelectFields).from(users).where(and(eq(users.username, "volute"), eq(users.user_type, "system"))).get();
108
+ if (retried) return retried;
109
+ }
110
+ throw err;
111
+ }
112
+ }
113
+ async function deleteMindUser(mindName) {
114
+ const db = await getDb();
115
+ await db.delete(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind")));
116
+ }
117
+ async function changePassword(userId, currentPassword, newPassword) {
118
+ const db = await getDb();
119
+ const row = await db.select().from(users).where(eq(users.id, userId)).get();
120
+ if (!row) return false;
121
+ if (!compareSync(currentPassword, row.password_hash)) return false;
122
+ const hash = hashSync(newPassword, 10);
123
+ await db.update(users).set({ password_hash: hash }).where(eq(users.id, userId));
124
+ return true;
125
+ }
126
+ async function approveUser(id) {
127
+ const db = await getDb();
128
+ await db.update(users).set({ role: "user" }).where(and(eq(users.id, id), eq(users.role, "pending")));
129
+ }
130
+ async function countAdmins() {
131
+ const db = await getDb();
132
+ const [{ value }] = await db.select({ value: count() }).from(users).where(eq(users.role, "admin"));
133
+ return value;
134
+ }
135
+ async function setUserRole(id, role) {
136
+ const db = await getDb();
137
+ const target = await db.select({ id: users.id }).from(users).where(eq(users.id, id)).get();
138
+ if (!target) throw new Error("User not found");
139
+ await db.update(users).set({ role }).where(eq(users.id, id));
140
+ }
141
+ async function deleteUser(id) {
142
+ const db = await getDb();
143
+ const target = await db.select({ id: users.id }).from(users).where(and(eq(users.id, id), eq(users.user_type, "brain"))).get();
144
+ if (!target) throw new Error("User not found");
145
+ await db.delete(users).where(and(eq(users.id, id), eq(users.user_type, "brain")));
146
+ }
147
+ async function updateUserProfile(userId, profile) {
148
+ const db = await getDb();
149
+ const target = await db.select({ id: users.id }).from(users).where(eq(users.id, userId)).get();
150
+ if (!target) throw new Error("User not found");
151
+ await db.update(users).set(profile).where(eq(users.id, userId));
152
+ }
153
+ async function syncMindProfile(mindName, config) {
154
+ const user = await getOrCreateMindUser(mindName);
155
+ const newProfile = {
156
+ display_name: config.displayName ?? null,
157
+ description: config.description ?? null,
158
+ avatar: config.avatar ?? null
159
+ };
160
+ const changed = user.display_name !== newProfile.display_name || user.description !== newProfile.description || user.avatar !== newProfile.avatar;
161
+ if (!changed) return;
162
+ const db = await getDb();
163
+ await db.update(users).set(newProfile).where(eq(users.id, user.id));
164
+ broadcast({ type: "profile_updated", mind: mindName, summary: `${mindName} profile updated` });
165
+ }
166
+
167
+ export {
168
+ createUser,
169
+ verifyUser,
170
+ getUser,
171
+ getUserByUsername,
172
+ listUsers,
173
+ listPendingUsers,
174
+ listUsersByType,
175
+ getOrCreateMindUser,
176
+ getOrCreateSystemUser,
177
+ deleteMindUser,
178
+ changePassword,
179
+ approveUser,
180
+ countAdmins,
181
+ setUserRole,
182
+ deleteUser,
183
+ updateUserProfile,
184
+ syncMindProfile
185
+ };
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ voluteSystemDir
4
+ } from "./chunk-LRCG2JLP.js";
5
+
6
+ // src/lib/setup.ts
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
8
+ import { resolve } from "path";
9
+ function configPath() {
10
+ return resolve(voluteSystemDir(), "config.json");
11
+ }
12
+ function readGlobalConfig() {
13
+ const path = configPath();
14
+ if (!existsSync(path)) return {};
15
+ try {
16
+ return JSON.parse(readFileSync(path, "utf-8"));
17
+ } catch (err) {
18
+ console.error(`Failed to parse ${path}: ${err instanceof Error ? err.message : err}`);
19
+ return {};
20
+ }
21
+ }
22
+ function writeGlobalConfig(config) {
23
+ const path = configPath();
24
+ mkdirSync(voluteSystemDir(), { recursive: true });
25
+ writeFileSync(path, `${JSON.stringify(config, null, 2)}
26
+ `);
27
+ }
28
+ function isSetupComplete() {
29
+ const config = readGlobalConfig();
30
+ return config.setupCompleted === true;
31
+ }
32
+ function migrateSetupCompleted() {
33
+ const config = readGlobalConfig();
34
+ if (config.setup != null && config.setupCompleted == null) {
35
+ config.setupCompleted = true;
36
+ writeGlobalConfig(config);
37
+ }
38
+ }
39
+
40
+ export {
41
+ configPath,
42
+ readGlobalConfig,
43
+ writeGlobalConfig,
44
+ isSetupComplete,
45
+ migrateSetupCompleted
46
+ };
@@ -1,13 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- voluteHome,
4
3
  voluteSystemDir,
5
4
  voluteUserHome
6
- } from "./chunk-HHTXM4JT.js";
5
+ } from "./chunk-LRCG2JLP.js";
7
6
 
8
7
  // src/lib/daemon-client.ts
9
8
  import { existsSync, readFileSync } from "fs";
10
9
  import { resolve } from "path";
10
+ function readSessionFile(mindDir) {
11
+ try {
12
+ const p = resolve(mindDir, ".mind", "current-session");
13
+ if (existsSync(p)) return readFileSync(p, "utf-8").trim() || void 0;
14
+ } catch (err) {
15
+ const code = err.code;
16
+ if (code !== "ENOENT") {
17
+ console.error(`[volute] failed to read session file: ${code ?? err}`);
18
+ }
19
+ }
20
+ return void 0;
21
+ }
22
+ function readMindSessionFile() {
23
+ const mindDir = process.env.VOLUTE_MIND_DIR;
24
+ if (!mindDir) return void 0;
25
+ return readSessionFile(mindDir);
26
+ }
11
27
  function readCliSession() {
12
28
  const sessionPath = resolve(voluteUserHome(), "cli-session.json");
13
29
  if (!existsSync(sessionPath)) return null;
@@ -18,9 +34,7 @@ function readCliSession() {
18
34
  }
19
35
  }
20
36
  function readDaemonConfig() {
21
- const newPath = resolve(voluteSystemDir(), "daemon.json");
22
- const legacyPath = resolve(voluteHome(), "daemon.json");
23
- const configPath = existsSync(newPath) ? newPath : legacyPath;
37
+ const configPath = resolve(voluteSystemDir(), "daemon.json");
24
38
  if (!existsSync(configPath)) {
25
39
  if (existsSync("/etc/systemd/system/volute.service") && !process.env.VOLUTE_HOME) {
26
40
  console.error("Volute is running as a system service but VOLUTE_HOME is not set.");
@@ -68,6 +82,10 @@ async function daemonFetch(path, options) {
68
82
  headers.set("Authorization", `Bearer ${cliSession.sessionId}`);
69
83
  }
70
84
  headers.set("Origin", url);
85
+ const voluteSession = process.env.VOLUTE_SESSION ?? readMindSessionFile();
86
+ if (voluteSession) {
87
+ headers.set("X-Volute-Session", voluteSession);
88
+ }
71
89
  try {
72
90
  const res = await fetch(`${url}${path}`, { ...options, headers });
73
91
  if (res.status === 401 && !path.startsWith("/api/auth/")) {
@@ -89,5 +107,6 @@ async function daemonFetch(path, options) {
89
107
  }
90
108
 
91
109
  export {
110
+ readSessionFile,
92
111
  daemonFetch
93
112
  };