volute 0.30.1 → 0.31.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 (169) hide show
  1. package/dist/{accept-E3PAH3QJ.js → accept-GAKQ3MEH.js} +4 -4
  2. package/dist/{activity-events-BKBPPUBP.js → activity-events-T5ZRCVAL.js} +2 -2
  3. package/dist/{ai-service-VAJT5UBS.js → ai-service-UWUPM4T6.js} +5 -3
  4. package/dist/api.d.ts +238 -111
  5. package/dist/{archive-WWDBWYN2.js → archive-YBNSJYZZ.js} +2 -2
  6. package/dist/auth-T5AW2USD.js +43 -0
  7. package/dist/{bridge-RO37CUFM.js → bridge-4AJ3EY26.js} +4 -4
  8. package/dist/{chat-TCUNPFGO.js → chat-7YLT7FI3.js} +8 -8
  9. package/dist/{chunk-EFVHR7KH.js → chunk-4OUOFS23.js} +24 -5
  10. package/dist/{chunk-G3GBKZGG.js → chunk-57OKQMP3.js} +54 -2
  11. package/dist/chunk-6QIUN46C.js +38 -0
  12. package/dist/{chunk-FSM45XD5.js → chunk-AAO77TZX.js} +1 -1
  13. package/dist/{chunk-DTC6EH5I.js → chunk-BC3P3QCK.js} +1 -1
  14. package/dist/{chunk-P7VFDSSG.js → chunk-BNC43CSY.js} +2 -2
  15. package/dist/{chunk-QVAQ5454.js → chunk-BWKIHH7B.js} +3080 -2099
  16. package/dist/{chunk-IKHDUZRH.js → chunk-DAXJKPHZ.js} +2 -2
  17. package/dist/{chunk-P27RV5WM.js → chunk-EKDWA7E4.js} +3 -1
  18. package/dist/{chunk-S5LR3XYJ.js → chunk-EMPFLFTG.js} +1 -1
  19. package/dist/{chunk-2C2VXEBB.js → chunk-FAHDKPEH.js} +18 -56
  20. package/dist/{chunk-W5OOPLNP.js → chunk-HDKY4TWU.js} +3 -3
  21. package/dist/{chunk-EFP3PE6C.js → chunk-HR5JKIDG.js} +2 -12
  22. package/dist/{chunk-JGFRDMR6.js → chunk-LX6T3GKQ.js} +1 -1
  23. package/dist/{chunk-2NDZC3S7.js → chunk-NOWVQ7AL.js} +447 -299
  24. package/dist/{chunk-ZWKTUQEL.js → chunk-NV3TYNWX.js} +1 -1
  25. package/dist/{chunk-NSBFETWP.js → chunk-PNQCXLSV.js} +7 -26
  26. package/dist/chunk-R5QJBZZG.js +175 -0
  27. package/dist/{chunk-MDPCSXZ4.js → chunk-S2TZLSDH.js} +4 -4
  28. package/dist/{chunk-FXHXHI2A.js → chunk-SNVPRRT7.js} +3 -6
  29. package/dist/{chunk-UPA6COHU.js → chunk-WRS3B556.js} +5 -5
  30. package/dist/{chunk-HHTXM4JT.js → chunk-X62AXPR7.js} +36 -4
  31. package/dist/cli.js +74 -23
  32. package/dist/{clock-G3ALCMLJ.js → clock-LJCG426D.js} +10 -8
  33. package/dist/{cloud-sync-JV4LJOK3.js → cloud-sync-O3LXIRN6.js} +13 -13
  34. package/dist/{conversations-7KVQV7EZ.js → conversations-RKKGP5IA.js} +7 -7
  35. package/dist/{create-VQSQHJQW.js → create-TL623TFC.js} +1 -1
  36. package/dist/{create-JTLS7GX3.js → create-WUTIIRI2.js} +4 -4
  37. package/dist/daemon-client-CVGM25DM.js +11 -0
  38. package/dist/{daemon-restart-4JGBHEJ4.js → daemon-restart-EZP7XH3V.js} +6 -7
  39. package/dist/daemon.js +1879 -1727
  40. package/dist/{db-HMFPIRO2.js → db-SW5PL6QA.js} +1 -1
  41. package/dist/{delete-JESHKE7F.js → delete-Z6HAG35F.js} +1 -1
  42. package/dist/down-TS4XQBA4.js +13 -0
  43. package/dist/{env-CLXXT7M2.js → env-NHESNNSP.js} +4 -4
  44. package/dist/{export-EGA5M5PB.js → export-EVMP7GWY.js} +3 -3
  45. package/dist/{extension-WZ4SUPJB.js → extension-LR7EW3JF.js} +5 -6
  46. package/dist/{extensions-ECO4RPFQ.js → extensions-NGEJI7JH.js} +7 -7
  47. package/dist/{files-4VEJDASH.js → files-3SM7V33S.js} +5 -5
  48. package/dist/{history-EJMMLXDO.js → history-PQD3LXEP.js} +4 -4
  49. package/dist/{import-YCGPMBSI.js → import-PR2OCGQJ.js} +3 -3
  50. package/dist/{join-2GBJKZEN.js → join-R4EN5CWQ.js} +1 -1
  51. package/dist/{list-Q6O7FGAN.js → list-B4XNUOFO.js} +4 -4
  52. package/dist/{login-RL6AU2SM.js → login-62JVY6A2.js} +4 -4
  53. package/dist/{login-RET5WESK.js → login-URWP6S2N.js} +2 -2
  54. package/dist/{logout-CGAGJN3L.js → logout-NXJQJDLI.js} +2 -2
  55. package/dist/{logout-JRPBEMMR.js → logout-ZK2N62T3.js} +2 -2
  56. package/dist/message-delivery-UJHCLVU4.js +30 -0
  57. package/dist/{mind-LUWRQUQ5.js → mind-E2ZV2WRX.js} +17 -17
  58. package/dist/{mind-activity-tracker-VYN2ZZ2M.js → mind-activity-tracker-ASNZBMLC.js} +3 -3
  59. package/dist/{mind-list-V5WW5DUA.js → mind-list-BEI7E5WY.js} +2 -2
  60. package/dist/mind-manager-IPA6DZXD.js +26 -0
  61. package/dist/{mind-sleep-R6PTNNW4.js → mind-sleep-CANABWJI.js} +4 -4
  62. package/dist/{mind-status-I4ISFJ6I.js → mind-status-6WKZVUOP.js} +2 -2
  63. package/dist/{mind-wake-67ZQEWAV.js → mind-wake-RZKLH2IN.js} +4 -4
  64. package/dist/{package-OYUD4ZJ4.js → package-NU4CA7OU.js} +3 -3
  65. package/dist/{pages-watcher-Z3PKNROC.js → pages-watcher-72OVPRMH.js} +4 -3
  66. package/dist/read-THL362EI.js +74 -0
  67. package/dist/{register-NZDSTLP3.js → register-QAQELAS6.js} +4 -4
  68. package/dist/{registry-ODSALQQL.js → registry-ASXCQCNH.js} +1 -1
  69. package/dist/{reject-2HZOJEIJ.js → reject-AYPBNPNL.js} +4 -4
  70. package/dist/{restart-QHS3NT64.js → restart-6SKPV3T2.js} +4 -4
  71. package/dist/{sandbox-O5FUSF43.js → sandbox-6ZEWQDVU.js} +3 -3
  72. package/dist/{seed-WUQMPLDM.js → seed-OWX2AW75.js} +36 -12
  73. package/dist/{send-OAN3RYYY.js → send-ZO4BTWXK.js} +5 -5
  74. package/dist/{setup-QMDK5RZX.js → setup-7CFITEQN.js} +2 -4
  75. package/dist/{setup-XJH3E7YM.js → setup-ZXBXG7E4.js} +6 -8
  76. package/dist/{skill-FZIN4W4Q.js → skill-YFXP67A2.js} +4 -4
  77. package/dist/skills/dreaming/references/INSTALL.md +2 -3
  78. package/dist/skills/dreaming/scripts/dream.ts +3 -25
  79. package/dist/skills/volute-mind/SKILL.md +1 -1
  80. package/dist/sleep-manager-TPS6OGCA.js +30 -0
  81. package/dist/{split-EXYGGGQN.js → split-MI62KJUU.js} +1 -1
  82. package/dist/{sprout-AXQ6H5DB.js → sprout-FDVI2CGN.js} +5 -6
  83. package/dist/{start-MTOVL6SY.js → start-D64BRKPH.js} +4 -4
  84. package/dist/{status-ZRO37MWR.js → status-ZZWBYFGE.js} +4 -5
  85. package/dist/{stop-OK5WEPVC.js → stop-OP2CTXCO.js} +4 -4
  86. package/dist/system-chat-B43GIXQU.js +30 -0
  87. package/dist/{systems-W3BBMSOZ.js → systems-EQPPT4B7.js} +5 -5
  88. package/dist/{tailscale-BM72RXCJ.js → tailscale-6DJKUMNF.js} +1 -1
  89. package/dist/up-TDXEP3VA.js +16 -0
  90. package/dist/{update-PLPHMMZ2.js → update-KUJXATRS.js} +4 -5
  91. package/dist/{update-check-CVCN7MF6.js → update-check-5WVSU37T.js} +2 -2
  92. package/dist/{upgrade-I6NPCYUU.js → upgrade-KBHCWX6T.js} +1 -1
  93. package/dist/{version-notify-2NTWVEHL.js → version-notify-75ELVKPV.js} +17 -21
  94. package/dist/web-assets/assets/index-BM1cTzBg.js +72 -0
  95. package/dist/web-assets/assets/index-BfJkKTPF.css +1 -0
  96. package/dist/web-assets/ext-theme.css +1 -0
  97. package/dist/web-assets/index.html +2 -2
  98. package/drizzle/0000_baseline.sql +152 -0
  99. package/drizzle/0001_add_conversation_private.sql +1 -0
  100. package/drizzle/0002_turns.sql +21 -0
  101. package/drizzle/0003_turn_feed_links.sql +11 -0
  102. package/drizzle/meta/0000_snapshot.json +3 -223
  103. package/drizzle/meta/0001_snapshot.json +3 -294
  104. package/drizzle/meta/0002_snapshot.json +3 -335
  105. package/drizzle/meta/0003_snapshot.json +3 -413
  106. package/drizzle/meta/_journal.json +8 -106
  107. package/package.json +3 -3
  108. package/packages/extensions/notes/dist/ui/assets/{index-DgawVO5g.css → index-B8GdTnXs.css} +1 -1
  109. package/packages/extensions/notes/dist/ui/assets/index-CDpGTCWb.js +2 -0
  110. package/packages/extensions/notes/dist/ui/index.html +2 -2
  111. package/packages/extensions/notes/skills/notes/SKILL.md +8 -8
  112. package/packages/extensions/pages/skills/pages/SKILL.md +7 -4
  113. package/packages/extensions/pages/skills/pages/scripts/pages.mjs +58 -0
  114. package/templates/_base/.init/.config/bin/volute +27 -0
  115. package/templates/_base/src/lib/auto-commit.ts +82 -43
  116. package/templates/_base/src/lib/daemon-client.ts +19 -23
  117. package/templates/_base/src/lib/router.ts +17 -1
  118. package/templates/_base/src/lib/startup.ts +6 -8
  119. package/templates/_base/src/lib/volute-server.ts +2 -5
  120. package/templates/claude/src/agent.ts +2 -1
  121. package/templates/claude/src/lib/hooks/auto-commit.ts +7 -3
  122. package/templates/claude/src/server.ts +0 -9
  123. package/templates/pi/package.json.tmpl +1 -1
  124. package/templates/pi/src/agent.ts +1 -1
  125. package/templates/pi/src/lib/event-handler.ts +5 -3
  126. package/dist/chunk-7D47T4RB.js +0 -84
  127. package/dist/chunk-CVH6Y2YG.js +0 -59
  128. package/dist/chunk-LIRWLNAK.js +0 -729
  129. package/dist/daemon-client-BCTFGVCZ.js +0 -9
  130. package/dist/down-NGBMGORS.js +0 -14
  131. package/dist/message-delivery-6YMVNOEC.js +0 -28
  132. package/dist/migrate-registry-to-db-FK35IPEH.js +0 -110
  133. package/dist/mind-manager-YFCOIAAX.js +0 -18
  134. package/dist/read-WQMPTSN2.js +0 -46
  135. package/dist/sleep-manager-O7YQFCV5.js +0 -30
  136. package/dist/up-BXUAIDXB.js +0 -17
  137. package/dist/web-assets/assets/index--kREqKl9.js +0 -72
  138. package/dist/web-assets/assets/index-BXYTG0nJ.css +0 -1
  139. package/drizzle/0000_flaky_mariko_yashida.sql +0 -34
  140. package/drizzle/0001_careless_warpath.sql +0 -12
  141. package/drizzle/0002_wealthy_the_call.sql +0 -6
  142. package/drizzle/0003_clean_ego.sql +0 -12
  143. package/drizzle/0004_magical_silverclaw.sql +0 -1
  144. package/drizzle/0005_rename_agents_to_minds.sql +0 -11
  145. package/drizzle/0006_mind_history.sql +0 -20
  146. package/drizzle/0007_system_prompts.sql +0 -5
  147. package/drizzle/0008_volute_channels.sql +0 -24
  148. package/drizzle/0009_shared_skills.sql +0 -9
  149. package/drizzle/0010_delivery_queue.sql +0 -12
  150. package/drizzle/0011_rename_human_to_brain.sql +0 -1
  151. package/drizzle/0012_activity.sql +0 -11
  152. package/drizzle/0013_user_profiles.sql +0 -3
  153. package/drizzle/0014_conversation_reads.sql +0 -7
  154. package/drizzle/0015_notes.sql +0 -23
  155. package/drizzle/0016_note_reactions_and_replies.sql +0 -15
  156. package/drizzle/0017_minds.sql +0 -16
  157. package/drizzle/meta/0004_snapshot.json +0 -410
  158. package/drizzle/meta/0005_snapshot.json +0 -410
  159. package/drizzle/meta/0006_snapshot.json +0 -7
  160. package/drizzle/meta/0007_snapshot.json +0 -7
  161. package/drizzle/meta/0008_snapshot.json +0 -7
  162. package/drizzle/meta/0009_snapshot.json +0 -7
  163. package/drizzle/meta/0010_snapshot.json +0 -7
  164. package/drizzle/meta/0011_snapshot.json +0 -7
  165. package/drizzle/meta/0012_snapshot.json +0 -7
  166. package/drizzle/meta/0013_snapshot.json +0 -7
  167. package/packages/extensions/notes/dist/ui/assets/index-qUWoeC4c.js +0 -2
  168. package/packages/extensions/notes/skills/notes/scripts/notes.mjs +0 -185
  169. package/templates/_base/home/public/.gitkeep +0 -0
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  stateDir,
4
4
  voluteSystemDir
5
- } from "./chunk-HHTXM4JT.js";
5
+ } from "./chunk-X62AXPR7.js";
6
6
 
7
7
  // src/lib/env.ts
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -5,36 +5,15 @@ import {
5
5
  import {
6
6
  readGlobalConfig,
7
7
  writeGlobalConfig
8
- } from "./chunk-7D47T4RB.js";
8
+ } from "./chunk-6QIUN46C.js";
9
9
 
10
10
  // src/lib/ai-service.ts
11
- import {
12
- complete,
13
- getEnvApiKey,
14
- getModel,
15
- getModels,
16
- getOAuthApiKey,
17
- getProviders
18
- } from "@mariozechner/pi-ai";
11
+ import { complete, getEnvApiKey, getModel, getModels, getProviders } from "@mariozechner/pi-ai";
12
+ import { getOAuthApiKey } from "@mariozechner/pi-ai/oauth";
19
13
  var aiLog = logger_default.child("ai-service");
20
14
  function getAiConfig() {
21
15
  const config = readGlobalConfig();
22
- if (!config.ai) return null;
23
- const ai = config.ai;
24
- if ("provider" in ai && !("providers" in ai)) {
25
- const old = ai;
26
- const migrated = {
27
- providers: {
28
- [old.provider]: {
29
- ...old.apiKey ? { apiKey: old.apiKey } : {},
30
- ...old.oauth ? { oauth: old.oauth } : {}
31
- }
32
- }
33
- };
34
- writeGlobalConfig({ ...config, ai: migrated });
35
- return migrated;
36
- }
37
- return config.ai;
16
+ return config.ai ?? null;
38
17
  }
39
18
  function saveProviderConfig(providerId, providerConfig) {
40
19
  const ai = getAiConfig() ?? { providers: {} };
@@ -95,7 +74,7 @@ function getAvailableModels() {
95
74
  try {
96
75
  models.push(...getModels(provider));
97
76
  } catch (err) {
98
- aiLog.debug(`no models for provider ${provider}`, logger_default.errorData(err));
77
+ aiLog.warn(`failed to load models for provider ${provider}`, logger_default.errorData(err));
99
78
  }
100
79
  }
101
80
  return models;
@@ -154,6 +133,7 @@ async function aiComplete(systemPrompt, userMessage, modelId) {
154
133
  const model = modelId ? findModel(modelId) : autoSelectModel();
155
134
  if (!model) {
156
135
  if (modelId) aiLog.warn(`model not found: ${modelId}`);
136
+ else aiLog.debug("no enabled model available for auto-selection");
157
137
  return null;
158
138
  }
159
139
  try {
@@ -184,5 +164,6 @@ export {
184
164
  getEnabledModels,
185
165
  setEnabledModels,
186
166
  getAvailableModels,
167
+ resolveApiKey,
187
168
  aiComplete
188
169
  };
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ broadcast
4
+ } from "./chunk-EKDWA7E4.js";
5
+ import {
6
+ getDb,
7
+ users
8
+ } from "./chunk-X62AXPR7.js";
9
+
10
+ // src/lib/auth.ts
11
+ import { compareSync, hashSync } from "bcryptjs";
12
+ import { and, count, eq } from "drizzle-orm";
13
+ var userSelectFields = {
14
+ id: users.id,
15
+ username: users.username,
16
+ role: users.role,
17
+ user_type: users.user_type,
18
+ display_name: users.display_name,
19
+ description: users.description,
20
+ avatar: users.avatar,
21
+ created_at: users.created_at
22
+ };
23
+ async function createUser(username, password) {
24
+ const db = await getDb();
25
+ const hash = hashSync(password, 10);
26
+ const [{ value }] = await db.select({ value: count() }).from(users).where(eq(users.user_type, "brain"));
27
+ const role = value === 0 ? "admin" : "pending";
28
+ const [result] = await db.insert(users).values({ username, password_hash: hash, role }).returning(userSelectFields);
29
+ return result;
30
+ }
31
+ async function verifyUser(username, password) {
32
+ const db = await getDb();
33
+ const row = await db.select().from(users).where(eq(users.username, username)).get();
34
+ if (!row) return null;
35
+ if (row.user_type === "mind" || row.user_type === "system") return null;
36
+ if (!compareSync(password, row.password_hash)) return null;
37
+ const { password_hash: _, ...user } = row;
38
+ return user;
39
+ }
40
+ async function getUser(id) {
41
+ const db = await getDb();
42
+ const row = await db.select(userSelectFields).from(users).where(eq(users.id, id)).get();
43
+ return row ?? null;
44
+ }
45
+ async function getUserByUsername(username) {
46
+ const db = await getDb();
47
+ const row = await db.select(userSelectFields).from(users).where(eq(users.username, username)).get();
48
+ return row ?? null;
49
+ }
50
+ async function listUsers() {
51
+ const db = await getDb();
52
+ return db.select(userSelectFields).from(users).orderBy(users.created_at).all();
53
+ }
54
+ async function listPendingUsers() {
55
+ const db = await getDb();
56
+ return db.select(userSelectFields).from(users).where(eq(users.role, "pending")).orderBy(users.created_at).all();
57
+ }
58
+ async function listUsersByType(userType) {
59
+ const db = await getDb();
60
+ return db.select(userSelectFields).from(users).where(eq(users.user_type, userType)).orderBy(users.created_at).all();
61
+ }
62
+ async function getOrCreateMindUser(mindName) {
63
+ const db = await getDb();
64
+ const existing = await db.select(userSelectFields).from(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind"))).get();
65
+ if (existing) return existing;
66
+ try {
67
+ const [result] = await db.insert(users).values({
68
+ username: mindName,
69
+ password_hash: "!mind",
70
+ role: "user",
71
+ user_type: "mind"
72
+ }).returning(userSelectFields);
73
+ return result;
74
+ } catch (err) {
75
+ if (err instanceof Error && err.message.includes("UNIQUE constraint")) {
76
+ const retried = await db.select(userSelectFields).from(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind"))).get();
77
+ if (retried) return retried;
78
+ }
79
+ throw err;
80
+ }
81
+ }
82
+ async function getOrCreateSystemUser() {
83
+ const db = await getDb();
84
+ const existing = await db.select(userSelectFields).from(users).where(and(eq(users.username, "volute"), eq(users.user_type, "system"))).get();
85
+ if (existing) return existing;
86
+ try {
87
+ const [result] = await db.insert(users).values({
88
+ username: "volute",
89
+ password_hash: "!system",
90
+ role: "user",
91
+ user_type: "system",
92
+ display_name: "Volute"
93
+ }).returning(userSelectFields);
94
+ return result;
95
+ } catch (err) {
96
+ if (err instanceof Error && err.message.includes("UNIQUE constraint")) {
97
+ const retried = await db.select(userSelectFields).from(users).where(and(eq(users.username, "volute"), eq(users.user_type, "system"))).get();
98
+ if (retried) return retried;
99
+ }
100
+ throw err;
101
+ }
102
+ }
103
+ async function deleteMindUser(mindName) {
104
+ const db = await getDb();
105
+ await db.delete(users).where(and(eq(users.username, mindName), eq(users.user_type, "mind")));
106
+ }
107
+ async function changePassword(userId, currentPassword, newPassword) {
108
+ const db = await getDb();
109
+ const row = await db.select().from(users).where(eq(users.id, userId)).get();
110
+ if (!row) return false;
111
+ if (!compareSync(currentPassword, row.password_hash)) return false;
112
+ const hash = hashSync(newPassword, 10);
113
+ await db.update(users).set({ password_hash: hash }).where(eq(users.id, userId));
114
+ return true;
115
+ }
116
+ async function approveUser(id) {
117
+ const db = await getDb();
118
+ await db.update(users).set({ role: "user" }).where(and(eq(users.id, id), eq(users.role, "pending")));
119
+ }
120
+ async function countAdmins() {
121
+ const db = await getDb();
122
+ const [{ value }] = await db.select({ value: count() }).from(users).where(eq(users.role, "admin"));
123
+ return value;
124
+ }
125
+ async function setUserRole(id, role) {
126
+ const db = await getDb();
127
+ const target = await db.select({ id: users.id }).from(users).where(eq(users.id, id)).get();
128
+ if (!target) throw new Error("User not found");
129
+ await db.update(users).set({ role }).where(eq(users.id, id));
130
+ }
131
+ async function deleteUser(id) {
132
+ const db = await getDb();
133
+ const target = await db.select({ id: users.id }).from(users).where(and(eq(users.id, id), eq(users.user_type, "brain"))).get();
134
+ if (!target) throw new Error("User not found");
135
+ await db.delete(users).where(and(eq(users.id, id), eq(users.user_type, "brain")));
136
+ }
137
+ async function updateUserProfile(userId, profile) {
138
+ const db = await getDb();
139
+ const target = await db.select({ id: users.id }).from(users).where(eq(users.id, userId)).get();
140
+ if (!target) throw new Error("User not found");
141
+ await db.update(users).set(profile).where(eq(users.id, userId));
142
+ }
143
+ async function syncMindProfile(mindName, config) {
144
+ const user = await getOrCreateMindUser(mindName);
145
+ const newProfile = {
146
+ display_name: config.displayName ?? null,
147
+ description: config.description ?? null,
148
+ avatar: config.avatar ?? null
149
+ };
150
+ const changed = user.display_name !== newProfile.display_name || user.description !== newProfile.description || user.avatar !== newProfile.avatar;
151
+ if (!changed) return;
152
+ const db = await getDb();
153
+ await db.update(users).set(newProfile).where(eq(users.id, user.id));
154
+ broadcast({ type: "profile_updated", mind: mindName, summary: `${mindName} profile updated` });
155
+ }
156
+
157
+ export {
158
+ createUser,
159
+ verifyUser,
160
+ getUser,
161
+ getUserByUsername,
162
+ listUsers,
163
+ listPendingUsers,
164
+ listUsersByType,
165
+ getOrCreateMindUser,
166
+ getOrCreateSystemUser,
167
+ deleteMindUser,
168
+ changePassword,
169
+ approveUser,
170
+ countAdmins,
171
+ setUserRole,
172
+ deleteUser,
173
+ updateUserProfile,
174
+ syncMindProfile
175
+ };
@@ -5,16 +5,16 @@ import {
5
5
  import {
6
6
  exec,
7
7
  gitExec
8
- } from "./chunk-CVH6Y2YG.js";
8
+ } from "./chunk-57OKQMP3.js";
9
9
  import {
10
10
  readGlobalConfig,
11
11
  writeGlobalConfig
12
- } from "./chunk-7D47T4RB.js";
12
+ } from "./chunk-6QIUN46C.js";
13
13
  import {
14
14
  getDb,
15
15
  sharedSkills,
16
16
  voluteHome
17
- } from "./chunk-HHTXM4JT.js";
17
+ } from "./chunk-X62AXPR7.js";
18
18
 
19
19
  // src/lib/skills.ts
20
20
  import { createHash } from "crypto";
@@ -42,7 +42,7 @@ async function initDefaultSkills() {
42
42
  const config = readGlobalConfig();
43
43
  let extensionSkills = [];
44
44
  try {
45
- const { getExtensionStandardSkills } = await import("./extensions-ECO4RPFQ.js");
45
+ const { getExtensionStandardSkills } = await import("./extensions-NGEJI7JH.js");
46
46
  extensionSkills = getExtensionStandardSkills();
47
47
  } catch (err) {
48
48
  logger_default.warn("failed to load extension standard skills during init", logger_default.errorData(err));
@@ -2,11 +2,10 @@
2
2
  import {
3
3
  exec,
4
4
  execInherit
5
- } from "./chunk-CVH6Y2YG.js";
5
+ } from "./chunk-57OKQMP3.js";
6
6
  import {
7
- voluteHome,
8
7
  voluteSystemDir
9
- } from "./chunk-HHTXM4JT.js";
8
+ } from "./chunk-X62AXPR7.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"));
@@ -3,7 +3,7 @@ import {
3
3
  readEnv,
4
4
  sharedEnvPath,
5
5
  writeEnv
6
- } from "./chunk-ZWKTUQEL.js";
6
+ } from "./chunk-NV3TYNWX.js";
7
7
  import {
8
8
  logger_default
9
9
  } from "./chunk-YUIHSKR6.js";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-D424ZQGI.js";
13
13
  import {
14
14
  voluteSystemDir
15
- } from "./chunk-HHTXM4JT.js";
15
+ } from "./chunk-X62AXPR7.js";
16
16
 
17
17
  // src/commands/import.ts
18
18
  import {
@@ -112,7 +112,7 @@ async function run(args) {
112
112
  return;
113
113
  }
114
114
  const wsDir = resolveWorkspace(inputPath);
115
- const { daemonFetch } = await import("./daemon-client-BCTFGVCZ.js");
115
+ const { daemonFetch } = await import("./daemon-client-CVGM25DM.js");
116
116
  const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
117
117
  const client = getClient();
118
118
  const res = await daemonFetch(urlOf(client.api.minds.import.$url()), {
@@ -152,7 +152,7 @@ async function importArchive(archivePath, nameOverride) {
152
152
  console.error(`File not found: ${archivePath}`);
153
153
  process.exit(1);
154
154
  }
155
- const { extractArchive } = await import("./archive-WWDBWYN2.js");
155
+ const { extractArchive } = await import("./archive-YBNSJYZZ.js");
156
156
  const tempDir = resolve2(tmpdir(), `volute-import-${Date.now()}`);
157
157
  mkdirSync(tempDir, { recursive: true });
158
158
  let extracted;
@@ -164,7 +164,7 @@ async function importArchive(archivePath, nameOverride) {
164
164
  process.exit(1);
165
165
  }
166
166
  try {
167
- const { daemonFetch } = await import("./daemon-client-BCTFGVCZ.js");
167
+ const { daemonFetch } = await import("./daemon-client-CVGM25DM.js");
168
168
  const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
169
169
  const client = getClient();
170
170
  const res = await daemonFetch(urlOf(client.api.minds.import.$url()), {
@@ -31,6 +31,7 @@ __export(schema_exports, {
31
31
  sessions: () => sessions,
32
32
  sharedSkills: () => sharedSkills,
33
33
  systemPrompts: () => systemPrompts,
34
+ turns: () => turns,
34
35
  users: () => users
35
36
  });
36
37
  import { sql } from "drizzle-orm";
@@ -72,6 +73,7 @@ var conversations = sqliteTable(
72
73
  name: text("name"),
73
74
  user_id: integer("user_id").references(() => users.id),
74
75
  title: text("title"),
76
+ private: integer("private").notNull().default(0),
75
77
  created_at: text("created_at").notNull().default(sql`(datetime('now'))`),
76
78
  updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
77
79
  },
@@ -82,6 +84,22 @@ var conversations = sqliteTable(
82
84
  uniqueIndex("idx_conversations_name").on(table.name)
83
85
  ]
84
86
  );
87
+ var turns = sqliteTable(
88
+ "turns",
89
+ {
90
+ id: text("id").primaryKey(),
91
+ mind: text("mind").notNull(),
92
+ session: text("session"),
93
+ trigger_event_id: integer("trigger_event_id"),
94
+ summary_event_id: integer("summary_event_id"),
95
+ status: text("status").notNull().default("active"),
96
+ created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
97
+ },
98
+ (table) => [
99
+ index("idx_turns_mind").on(table.mind),
100
+ index("idx_turns_mind_status").on(table.mind, table.status)
101
+ ]
102
+ );
85
103
  var mindHistory = sqliteTable(
86
104
  "mind_history",
87
105
  {
@@ -94,12 +112,14 @@ var mindHistory = sqliteTable(
94
112
  type: text("type").notNull(),
95
113
  content: text("content"),
96
114
  metadata: text("metadata"),
115
+ turn_id: text("turn_id"),
97
116
  created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
98
117
  },
99
118
  (table) => [
100
119
  index("idx_mind_history_mind").on(table.mind),
101
120
  index("idx_mind_history_mind_channel").on(table.mind, table.channel),
102
- index("idx_mind_history_mind_type").on(table.mind, table.type)
121
+ index("idx_mind_history_mind_type").on(table.mind, table.type),
122
+ index("idx_mind_history_turn_id").on(table.turn_id)
103
123
  ]
104
124
  );
105
125
  var conversationParticipants = sqliteTable(
@@ -159,11 +179,14 @@ var activity = sqliteTable(
159
179
  mind: text("mind").notNull(),
160
180
  summary: text("summary").notNull(),
161
181
  metadata: text("metadata"),
182
+ turn_id: text("turn_id"),
183
+ source_event_id: integer("source_event_id"),
162
184
  created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
163
185
  },
164
186
  (table) => [
165
187
  index("idx_activity_created_at").on(table.created_at),
166
- index("idx_activity_mind").on(table.mind)
188
+ index("idx_activity_mind").on(table.mind),
189
+ index("idx_activity_turn_id").on(table.turn_id)
167
190
  ]
168
191
  );
169
192
  var conversationReads = sqliteTable(
@@ -185,9 +208,14 @@ var messages = sqliteTable(
185
208
  role: text("role").notNull(),
186
209
  sender_name: text("sender_name"),
187
210
  content: text("content").notNull(),
211
+ source_event_id: integer("source_event_id"),
212
+ turn_id: text("turn_id"),
188
213
  created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
189
214
  },
190
- (table) => [index("idx_messages_conversation_id").on(table.conversation_id)]
215
+ (table) => [
216
+ index("idx_messages_conversation_id").on(table.conversation_id),
217
+ index("idx_messages_turn_id").on(table.turn_id)
218
+ ]
191
219
  );
192
220
 
193
221
  // src/lib/registry.ts
@@ -242,12 +270,16 @@ async function readAllMinds() {
242
270
  }
243
271
  var MIND_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
244
272
  var MIND_NAME_MAX = 64;
273
+ var RESERVED_NAMES = /* @__PURE__ */ new Set(["volute", "system"]);
245
274
  function validateMindName(name) {
246
275
  if (!name) return "Mind name is required";
247
276
  if (name.length > MIND_NAME_MAX) return `Mind name must be at most ${MIND_NAME_MAX} characters`;
248
277
  if (!MIND_NAME_RE.test(name)) {
249
278
  return "Mind name must start with alphanumeric and contain only alphanumeric, dots, dashes, or underscores";
250
279
  }
280
+ if (RESERVED_NAMES.has(name.toLowerCase())) {
281
+ return `"${name}" is a reserved name`;
282
+ }
251
283
  return null;
252
284
  }
253
285
  async function addMind(name, port, stage, template) {
@@ -346,9 +378,9 @@ async function getDb() {
346
378
  }
347
379
 
348
380
  export {
349
- minds,
350
381
  users,
351
382
  conversations,
383
+ turns,
352
384
  mindHistory,
353
385
  conversationParticipants,
354
386
  sessions,
package/dist/cli.js CHANGED
@@ -10,14 +10,13 @@ if (!process.env.VOLUTE_HOME) {
10
10
  var command = process.argv[2];
11
11
  var args = process.argv.slice(3);
12
12
  if (command === "--version" || command === "-v") {
13
- const { default: pkg } = await import("./package-OYUD4ZJ4.js");
13
+ const { default: pkg } = await import("./package-NU4CA7OU.js");
14
14
  console.log(pkg.version);
15
15
  process.exit(0);
16
16
  }
17
17
  var ungatedCommands = /* @__PURE__ */ new Set(["setup", "--help", "-h", "--version", "-v", "update", void 0]);
18
18
  if (!ungatedCommands.has(command)) {
19
- const { isSetupComplete, migrateSetupConfig } = await import("./setup-QMDK5RZX.js");
20
- migrateSetupConfig();
19
+ const { isSetupComplete } = await import("./setup-7CFITEQN.js");
21
20
  if (!isSetupComplete()) {
22
21
  console.error("Volute is not set up. Run `volute setup` first.");
23
22
  process.exit(1);
@@ -25,55 +24,52 @@ if (!ungatedCommands.has(command)) {
25
24
  }
26
25
  switch (command) {
27
26
  case "setup":
28
- await import("./setup-XJH3E7YM.js").then((m) => m.run(args));
27
+ await import("./setup-ZXBXG7E4.js").then((m) => m.run(args));
29
28
  break;
30
29
  case "mind":
31
- await import("./mind-LUWRQUQ5.js").then((m) => m.run(args));
30
+ await import("./mind-E2ZV2WRX.js").then((m) => m.run(args));
32
31
  break;
33
32
  case "chat":
34
- await import("./chat-TCUNPFGO.js").then((m) => m.run(args));
33
+ await import("./chat-7YLT7FI3.js").then((m) => m.run(args));
35
34
  break;
36
35
  case "variant":
37
36
  await import("./variant-7TGZHOU3.js").then((m) => m.run(args));
38
37
  break;
39
38
  case "clock":
40
- await import("./clock-G3ALCMLJ.js").then((m) => m.run(args));
41
- break;
42
- case "schedule":
43
- await import("./clock-G3ALCMLJ.js").then((m) => m.run(args));
39
+ await import("./clock-LJCG426D.js").then((m) => m.run(args));
44
40
  break;
45
41
  case "skill":
46
- await import("./skill-FZIN4W4Q.js").then((m) => m.run(args));
42
+ await import("./skill-YFXP67A2.js").then((m) => m.run(args));
47
43
  break;
48
44
  case "env":
49
- await import("./env-CLXXT7M2.js").then((m) => m.run(args));
45
+ await import("./env-NHESNNSP.js").then((m) => m.run(args));
50
46
  break;
51
47
  case "up":
52
- await import("./up-BXUAIDXB.js").then((m) => m.run(args));
48
+ await import("./up-TDXEP3VA.js").then((m) => m.run(args));
53
49
  break;
54
50
  case "down":
55
- await import("./down-NGBMGORS.js").then((m) => m.run(args));
51
+ await import("./down-TS4XQBA4.js").then((m) => m.run(args));
56
52
  break;
57
53
  case "restart":
58
- await import("./daemon-restart-4JGBHEJ4.js").then((m) => m.run(args));
54
+ await import("./daemon-restart-EZP7XH3V.js").then((m) => m.run(args));
59
55
  break;
60
56
  case "update":
61
- await import("./update-PLPHMMZ2.js").then((m) => m.run(args));
57
+ await import("./update-KUJXATRS.js").then((m) => m.run(args));
62
58
  break;
63
59
  case "status":
64
- await import("./status-ZRO37MWR.js").then((m) => m.run(args));
60
+ await import("./status-ZZWBYFGE.js").then((m) => m.run(args));
65
61
  break;
66
62
  case "extension":
67
- await import("./extension-WZ4SUPJB.js").then((m) => m.run(args));
63
+ await import("./extension-LR7EW3JF.js").then((m) => m.run(args));
68
64
  break;
69
65
  case "systems":
70
- await import("./systems-W3BBMSOZ.js").then((m) => m.run(args));
66
+ await import("./systems-EQPPT4B7.js").then((m) => m.run(args));
71
67
  break;
72
68
  case "login":
73
- await import("./login-RET5WESK.js").then((m) => m.run(args));
69
+ await import("./login-URWP6S2N.js").then((m) => m.run(args));
74
70
  break;
75
71
  case "logout":
76
- await import("./logout-CGAGJN3L.js").then((m) => m.run(args));
72
+ await import("./logout-NXJQJDLI.js").then((m) => m.run(args));
77
73
  break;
78
74
  case "--help":
79
75
  case "-h":
@@ -111,6 +107,10 @@ System:
111
107
  update Update volute
112
108
  systems register/login/logout volute.systems account
113
109
 
110
+ Extensions:
111
+ notes write/list/read/... Manage notes
112
+ pages notify Notify page updates
113
+
114
114
  Options:
115
115
  --version, -v Show version number
116
116
  --help, -h Show this help message
@@ -120,13 +120,64 @@ Run 'volute <command> --help' for details.
120
120
  Mind-scoped commands (chat, clock, skill)
121
121
  use --mind <name> or VOLUTE_MIND env var to identify the mind.`);
122
122
  break;
123
- default:
123
+ default: {
124
+ let isExtensionCommand = false;
125
+ try {
126
+ const { daemonFetch } = await import("./daemon-client-CVGM25DM.js");
127
+ const res = await daemonFetch("/api/extensions/commands");
128
+ if (res.ok) {
129
+ const extCommands = await res.json();
130
+ if (command && command in extCommands) {
131
+ isExtensionCommand = true;
132
+ const subcommand = args[0];
133
+ const ext = extCommands[command];
134
+ if (!subcommand || !(subcommand in ext.commands)) {
135
+ console.log(`volute ${command} \u2014 ${Object.keys(ext.commands).join(", ")}
136
+ `);
137
+ for (const [name, meta] of Object.entries(ext.commands)) {
138
+ console.log(` ${name.padEnd(12)} ${meta.description}`);
139
+ }
140
+ process.exit(0);
141
+ }
142
+ const cmdArgs = args.slice(1);
143
+ let mind = process.env.VOLUTE_MIND;
144
+ const mindIdx = cmdArgs.indexOf("--mind");
145
+ if (mindIdx !== -1 && cmdArgs[mindIdx + 1]) {
146
+ mind = cmdArgs[mindIdx + 1];
147
+ cmdArgs.splice(mindIdx, 2);
148
+ }
149
+ const cmdRes = await daemonFetch(`/api/ext/${command}/commands/${subcommand}`, {
150
+ method: "POST",
151
+ headers: { "Content-Type": "application/json" },
152
+ body: JSON.stringify({ args: cmdArgs, mind })
153
+ });
154
+ if (!cmdRes.ok) {
155
+ const text = await cmdRes.text().catch(() => "");
156
+ console.error(`Extension command failed (HTTP ${cmdRes.status}): ${text}`);
157
+ process.exit(1);
158
+ }
159
+ const result = await cmdRes.json();
160
+ if (result.error) {
161
+ console.error(result.error);
162
+ process.exit(1);
163
+ }
164
+ if (result.output) console.log(result.output);
165
+ break;
166
+ }
167
+ }
168
+ } catch (err) {
169
+ if (isExtensionCommand) {
170
+ console.error(`Extension command failed: ${err instanceof Error ? err.message : err}`);
171
+ process.exit(1);
172
+ }
173
+ }
124
174
  console.error(`Unknown command: ${command}
125
175
  Run 'volute --help' for usage.`);
126
176
  process.exit(1);
177
+ }
127
178
  }
128
179
  if (command !== "update") {
129
- import("./update-check-CVCN7MF6.js").then((m) => m.checkForUpdate()).then((result) => {
180
+ import("./update-check-5WVSU37T.js").then((m) => m.checkForUpdate()).then((result) => {
130
181
  if (result.updateAvailable) {
131
182
  console.error(`
132
183
  Update available: ${result.current} \u2192 ${result.latest}`);