volute 0.19.0 → 0.21.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 (104) hide show
  1. package/README.md +68 -68
  2. package/dist/activity-events-3WHHCOBB.js +15 -0
  3. package/dist/{archive-ZCFOSTKB.js → archive-4ZQYK5MN.js} +4 -2
  4. package/dist/auth-HM2RSPY7.js +37 -0
  5. package/dist/{channel-PUQKGSQM.js → channel-BOOMFULW.js} +2 -2
  6. package/dist/{chunk-OTWLI7F4.js → chunk-5462YKWP.js} +12 -9
  7. package/dist/{chunk-2TJGRJ4O.js → chunk-7LPTHFIL.js} +64 -59
  8. package/dist/chunk-A4S7H6G6.js +56 -0
  9. package/dist/chunk-AKPFNL7L.js +148 -0
  10. package/dist/{chunk-EBGCNDMM.js → chunk-B2CPS4QU.js} +128 -114
  11. package/dist/{chunk-FCDU5BFX.js → chunk-HFCBO2GL.js} +2 -2
  12. package/dist/chunk-HGCDWKSP.js +97 -0
  13. package/dist/{chunk-DYZGP3EW.js → chunk-IPJXU366.js} +1 -1
  14. package/dist/{chunk-VE4D3GOP.js → chunk-J5A3DF2U.js} +2 -2
  15. package/dist/{chunk-WC6ZHVRL.js → chunk-KFI7TQJ6.js} +2 -2
  16. package/dist/{chunk-AW7P4EVV.js → chunk-KTJGZ7M7.js} +55 -7
  17. package/dist/{chunk-4KPUF5JD.js → chunk-L3LHXZD7.js} +18 -5
  18. package/dist/{chunk-OGXOMR65.js → chunk-NWPT4ASZ.js} +1 -1
  19. package/dist/{chunk-FGV2H4TX.js → chunk-OGZYB5GL.js} +312 -268
  20. package/dist/{chunk-SCUDS4US.js → chunk-ON3FF5JA.js} +1 -1
  21. package/dist/{chunk-EMQSAY3B.js → chunk-PC6R6UUW.js} +6 -5
  22. package/dist/{chunk-VDWCHYTS.js → chunk-PHU4DEAJ.js} +1 -1
  23. package/dist/{chunk-7NO7EV5Z.js → chunk-Q7AITQ44.js} +2 -2
  24. package/dist/{chunk-32VR2EOH.js → chunk-QUJUKM4U.js} +2 -2
  25. package/dist/{chunk-VQWDC6UK.js → chunk-SGPEZ32F.js} +46 -1
  26. package/dist/{chunk-RHEGSQFJ.js → chunk-WSLPZF72.js} +1 -1
  27. package/dist/cli.js +59 -111
  28. package/dist/{connector-JBVNZ7VK.js → connector-PYT5UOTZ.js} +6 -6
  29. package/dist/connectors/discord.js +2 -2
  30. package/dist/connectors/slack.js +2 -2
  31. package/dist/connectors/telegram.js +2 -2
  32. package/dist/{create-HP4OVVHF.js → create-WIDA3M4C.js} +1 -1
  33. package/dist/{daemon-client-ITWUCNFO.js → daemon-client-ZHCDL4RS.js} +2 -2
  34. package/dist/{daemon-restart-JMZM3QY4.js → daemon-restart-BH67ZOTE.js} +8 -8
  35. package/dist/daemon.js +2872 -1301
  36. package/dist/{delete-BSU7K3RY.js → delete-LOIANQGD.js} +1 -1
  37. package/dist/down-LIOQ5JDH.js +14 -0
  38. package/dist/{env-A3LMO777.js → env-4PHIHTF4.js} +2 -2
  39. package/dist/{export-GCDNQCF3.js → export-XD6PJBQP.js} +19 -8
  40. package/dist/file-X4L5TTOL.js +204 -0
  41. package/dist/{history-WNK3DFUM.js → history-HTEKRNID.js} +2 -2
  42. package/dist/{import-M63VIUJ5.js → import-E433B4KG.js} +3 -3
  43. package/dist/{log-PPPZDVEF.js → log-SRO5Q6AD.js} +2 -2
  44. package/dist/{login-HNH3EUQV.js → login-UO6AOVEA.js} +4 -4
  45. package/dist/{logout-I5CB5UZS.js → logout-UKD5LA37.js} +2 -2
  46. package/dist/{logs-SF2IMJN4.js → logs-HNTNNBDW.js} +2 -2
  47. package/dist/{merge-33C237A4.js → merge-B6SYTGI7.js} +2 -2
  48. package/dist/{mind-PQ5NCPSU.js → mind-BIDOF65R.js} +27 -11
  49. package/dist/mind-activity-tracker-PGC3DBJ7.js +18 -0
  50. package/dist/{mind-manager-RVCFROAY.js → mind-manager-3V2NXX4I.js} +5 -6
  51. package/dist/{package-MYE2ZJLV.js → package-HQR52XSG.js} +1 -1
  52. package/dist/{pages-AXCOSY3P.js → pages-KQBR5TAZ.js} +6 -6
  53. package/dist/{publish-YB377JB7.js → publish-OJ4QMXVZ.js} +12 -9
  54. package/dist/{pull-XAEWQJ47.js → pull-GRQAXM2E.js} +2 -2
  55. package/dist/{register-VSPCMHKX.js → register-U2UO6TC4.js} +5 -5
  56. package/dist/registry-D2BSQ2X5.js +42 -0
  57. package/dist/{restart-IQKMCK5M.js → restart-CIDAKGG2.js} +3 -6
  58. package/dist/{schedule-LMX7GAQZ.js → schedule-NLR3LZLY.js} +27 -7
  59. package/dist/{seed-J43YDKXG.js → seed-3H2MRREW.js} +2 -2
  60. package/dist/{send-KVIZIGCE.js → send-RP2TA7SG.js} +132 -36
  61. package/dist/{service-LUR7WDO7.js → service-TVNEORO7.js} +31 -13
  62. package/dist/{setup-OH3PJUJO.js → setup-OZDYCKDI.js} +25 -34
  63. package/dist/{shared-KO35ZM44.js → shared-DCQ2UXOM.js} +4 -4
  64. package/dist/{skill-BCVNI6TV.js → skill-Q2Y6PQ3L.js} +2 -2
  65. package/dist/skills/orientation/SKILL.md +2 -2
  66. package/dist/skills/volute-mind/SKILL.md +38 -8
  67. package/dist/{sprout-VBEX63LX.js → sprout-6Z6C42YM.js} +34 -30
  68. package/dist/{start-I5JYB65M.js → start-JR6CUUWF.js} +3 -6
  69. package/dist/{status-D7E5HHBV.js → status-5XDGYHKP.js} +2 -2
  70. package/dist/{status-JCJAOXTW.js → status-LV34BG6G.js} +6 -5
  71. package/dist/{status-4ESFLGH4.js → status-Z7NAFMBI.js} +5 -5
  72. package/dist/{stop-NBVKEFQQ.js → stop-VKPGK25U.js} +2 -5
  73. package/dist/template-hash-BIMA4ILT.js +8 -0
  74. package/dist/{up-WG65SWJU.js → up-7BGDMFRT.js} +5 -5
  75. package/dist/{update-FJIHDJKM.js → update-4WT7VWHW.js} +5 -5
  76. package/dist/{update-check-MWE5AH4U.js → update-check-F5Z3ALXX.js} +2 -2
  77. package/dist/{upgrade-AIT24B5I.js → upgrade-ZEC2GGFO.js} +1 -1
  78. package/dist/{variant-63ZWO2W7.js → variant-A4I7PHXS.js} +16 -24
  79. package/dist/version-notify-TFS2U5CF.js +173 -0
  80. package/dist/web-assets/assets/index-BR3gtK3E.css +1 -0
  81. package/dist/web-assets/assets/index-CWmrZRQd.js +64 -0
  82. package/dist/web-assets/index.html +2 -2
  83. package/drizzle/0012_activity.sql +11 -0
  84. package/drizzle/meta/0012_snapshot.json +7 -0
  85. package/drizzle/meta/_journal.json +7 -0
  86. package/package.json +1 -1
  87. package/templates/_base/home/.config/routes.json +2 -2
  88. package/templates/_base/home/VOLUTE.md +1 -1
  89. package/templates/_base/src/lib/daemon-client.ts +22 -0
  90. package/templates/_base/src/lib/transparency.ts +1 -1
  91. package/templates/claude/.init/.config/routes.json +7 -1
  92. package/templates/pi/.init/.config/routes.json +7 -1
  93. package/templates/pi/src/agent.ts +11 -5
  94. package/templates/pi/src/lib/session-context-extension.ts +6 -4
  95. package/templates/pi/src/server.ts +2 -0
  96. package/dist/chunk-UJ6GHNR7.js +0 -675
  97. package/dist/chunk-Z524RFCJ.js +0 -36
  98. package/dist/db-5ZVC6MQF.js +0 -10
  99. package/dist/delivery-manager-ISTJMZDW.js +0 -16
  100. package/dist/down-ZY35KMHR.js +0 -14
  101. package/dist/schema-5BW7DFZI.js +0 -24
  102. package/dist/variants-JAGWGBXG.js +0 -26
  103. package/dist/web-assets/assets/index-BAbuRsVF.css +0 -1
  104. package/dist/web-assets/assets/index-CiQhSKi_.js +0 -63
@@ -1,675 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- buildVoluteSlug,
4
- resolveChannelId,
5
- slugify,
6
- splitMessage,
7
- writeChannelEntry
8
- } from "./chunk-RHEGSQFJ.js";
9
- import {
10
- voluteHome
11
- } from "./chunk-EBGCNDMM.js";
12
- import {
13
- __export
14
- } from "./chunk-K3NQKI34.js";
15
-
16
- // src/lib/channels/discord.ts
17
- var discord_exports = {};
18
- __export(discord_exports, {
19
- createConversation: () => createConversation,
20
- listConversations: () => listConversations,
21
- listUsers: () => listUsers,
22
- read: () => read,
23
- send: () => send
24
- });
25
- var DISCORD_MAX_LENGTH = 2e3;
26
- var API_BASE = "https://discord.com/api/v10";
27
- function requireToken(env) {
28
- const token = env.DISCORD_TOKEN;
29
- if (!token) throw new Error("DISCORD_TOKEN not set");
30
- return token;
31
- }
32
- async function discordGet(token, path) {
33
- const res = await fetch(`${API_BASE}${path}`, {
34
- headers: { Authorization: `Bot ${token}` }
35
- });
36
- if (!res.ok) {
37
- throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
38
- }
39
- return res.json();
40
- }
41
- async function read(env, channelSlug, limit) {
42
- const token = requireToken(env);
43
- const channelId = resolveChannelId2(env, channelSlug);
44
- const res = await fetch(`${API_BASE}/channels/${channelId}/messages?limit=${limit}`, {
45
- headers: { Authorization: `Bot ${token}` }
46
- });
47
- if (!res.ok) {
48
- throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
49
- }
50
- const messages = await res.json();
51
- return messages.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
52
- }
53
- async function send(env, channelSlug, message, images) {
54
- const token = requireToken(env);
55
- const channelId = resolveChannelId2(env, channelSlug);
56
- if (images?.length) {
57
- for (let i = 0; i < images.length; i++) {
58
- const img = images[i];
59
- const ext = img.media_type.split("/")[1] || "png";
60
- const form = new FormData();
61
- const content = i === 0 ? message.slice(0, DISCORD_MAX_LENGTH) : "";
62
- form.append("payload_json", JSON.stringify({ content }));
63
- form.append(
64
- "files[0]",
65
- new Blob([Buffer.from(img.data, "base64")], { type: img.media_type }),
66
- `image.${ext}`
67
- );
68
- const res = await fetch(`${API_BASE}/channels/${channelId}/messages`, {
69
- method: "POST",
70
- headers: { Authorization: `Bot ${token}` },
71
- body: form
72
- });
73
- if (!res.ok) {
74
- const body = await res.text().catch(() => "");
75
- const partial = i > 0 ? ` (${i}/${images.length} images were already sent)` : "";
76
- throw new Error(`Discord API error: ${res.status} ${body || res.statusText}${partial}`);
77
- }
78
- }
79
- return;
80
- }
81
- const chunks = splitMessage(message, DISCORD_MAX_LENGTH);
82
- for (let i = 0; i < chunks.length; i++) {
83
- const res = await fetch(`${API_BASE}/channels/${channelId}/messages`, {
84
- method: "POST",
85
- headers: {
86
- Authorization: `Bot ${token}`,
87
- "Content-Type": "application/json"
88
- },
89
- body: JSON.stringify({ content: chunks[i] })
90
- });
91
- if (!res.ok) {
92
- const partial = i > 0 ? ` (${i}/${chunks.length} chunks were already sent)` : "";
93
- throw new Error(`Discord API error: ${res.status} ${res.statusText}${partial}`);
94
- }
95
- }
96
- }
97
- async function listConversations(env) {
98
- const token = requireToken(env);
99
- const results = [];
100
- const guilds = await discordGet(token, "/users/@me/guilds");
101
- for (const guild of guilds) {
102
- const channels = await discordGet(token, `/guilds/${guild.id}/channels`);
103
- for (const ch of channels) {
104
- if (ch.type !== 0) continue;
105
- results.push({
106
- id: `discord:${slugify(guild.name)}/${slugify(ch.name)}`,
107
- platformId: ch.id,
108
- name: `#${ch.name}`,
109
- type: "channel"
110
- });
111
- }
112
- }
113
- const dms = await discordGet(token, "/users/@me/channels");
114
- for (const dm of dms) {
115
- const recipients = dm.recipients?.map((r) => r.username) ?? [];
116
- const slug = recipients.length === 0 ? `discord:${dm.id}` : recipients.length === 1 ? `discord:@${slugify(recipients[0])}` : `discord:@${recipients.map(slugify).sort().join(",")}`;
117
- results.push({
118
- id: slug,
119
- platformId: dm.id,
120
- name: recipients.join(", ") || "DM",
121
- type: dm.type === 1 ? "dm" : "group"
122
- });
123
- }
124
- return results;
125
- }
126
- async function listUsers(env) {
127
- const token = requireToken(env);
128
- const seen = /* @__PURE__ */ new Map();
129
- const guilds = await discordGet(token, "/users/@me/guilds");
130
- for (const guild of guilds) {
131
- const members = await discordGet(token, `/guilds/${guild.id}/members?limit=1000`);
132
- for (const m of members) {
133
- if (!seen.has(m.user.id)) {
134
- seen.set(m.user.id, {
135
- id: m.user.id,
136
- username: m.user.username,
137
- type: m.user.bot ? "bot" : "human"
138
- });
139
- }
140
- }
141
- }
142
- return [...seen.values()];
143
- }
144
- async function createConversation(env, participants, _name) {
145
- const token = requireToken(env);
146
- if (participants.length !== 1) {
147
- throw new Error(
148
- "Discord group creation not supported via bot \u2014 use threads in an existing channel"
149
- );
150
- }
151
- const allUsers = await listUsers(env);
152
- const target = allUsers.find((u) => u.username.toLowerCase() === participants[0].toLowerCase());
153
- if (!target) {
154
- throw new Error(`User not found: ${participants[0]}`);
155
- }
156
- const res = await fetch(`${API_BASE}/users/@me/channels`, {
157
- method: "POST",
158
- headers: {
159
- Authorization: `Bot ${token}`,
160
- "Content-Type": "application/json"
161
- },
162
- body: JSON.stringify({ recipient_id: target.id })
163
- });
164
- if (!res.ok) {
165
- throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
166
- }
167
- const dm = await res.json();
168
- const slug = `discord:@${slugify(participants[0])}`;
169
- const mindName = env.VOLUTE_MIND;
170
- if (mindName) {
171
- writeChannelEntry(mindName, slug, {
172
- platformId: dm.id,
173
- platform: "discord",
174
- name: participants[0],
175
- type: "dm"
176
- });
177
- }
178
- return slug;
179
- }
180
-
181
- // src/lib/channels/slack.ts
182
- var slack_exports = {};
183
- __export(slack_exports, {
184
- createConversation: () => createConversation2,
185
- listConversations: () => listConversations2,
186
- listUsers: () => listUsers2,
187
- read: () => read2,
188
- send: () => send2
189
- });
190
- var SLACK_MAX_LENGTH = 4e3;
191
- var API_BASE2 = "https://slack.com/api";
192
- function requireToken2(env) {
193
- const token = env.SLACK_BOT_TOKEN;
194
- if (!token) throw new Error("SLACK_BOT_TOKEN not set");
195
- return token;
196
- }
197
- async function slackApi(token, method, body) {
198
- const res = await fetch(`${API_BASE2}/${method}`, {
199
- method: "POST",
200
- headers: {
201
- Authorization: `Bearer ${token}`,
202
- "Content-Type": "application/json"
203
- },
204
- body: JSON.stringify(body)
205
- });
206
- if (!res.ok) {
207
- throw new Error(`Slack API HTTP error: ${res.status} ${res.statusText}`);
208
- }
209
- const data = await res.json();
210
- if (!data.ok) {
211
- throw new Error(`Slack API error: ${data.error}`);
212
- }
213
- return data;
214
- }
215
- async function read2(env, channelSlug, limit) {
216
- const token = requireToken2(env);
217
- const channelId = resolveChannelId2(env, channelSlug);
218
- const data = await slackApi(token, "conversations.history", {
219
- channel: channelId,
220
- limit
221
- });
222
- return data.messages.reverse().map((m) => `${m.user ?? m.bot_id ?? "unknown"}: ${m.text}`).join("\n");
223
- }
224
- async function send2(env, channelSlug, message, images) {
225
- const token = requireToken2(env);
226
- const channelId = resolveChannelId2(env, channelSlug);
227
- if (images?.length) {
228
- for (const img of images) {
229
- const ext = img.media_type.split("/")[1] || "png";
230
- const filename = `image.${ext}`;
231
- const binary = Buffer.from(img.data, "base64");
232
- const uploadData = await slackApi(token, "files.getUploadURLExternal", {
233
- filename,
234
- length: binary.length
235
- });
236
- const uploadRes = await fetch(uploadData.upload_url, {
237
- method: "POST",
238
- body: binary
239
- });
240
- if (!uploadRes.ok) {
241
- throw new Error(`Slack file upload failed: ${uploadRes.status} ${uploadRes.statusText}`);
242
- }
243
- await slackApi(token, "files.completeUploadExternal", {
244
- files: [{ id: uploadData.file_id }],
245
- channel_id: channelId
246
- });
247
- }
248
- if (message) {
249
- const chunks2 = splitMessage(message, SLACK_MAX_LENGTH);
250
- for (const chunk of chunks2) {
251
- await slackApi(token, "chat.postMessage", {
252
- channel: channelId,
253
- text: chunk
254
- });
255
- }
256
- }
257
- return;
258
- }
259
- const chunks = splitMessage(message, SLACK_MAX_LENGTH);
260
- for (let i = 0; i < chunks.length; i++) {
261
- try {
262
- await slackApi(token, "chat.postMessage", {
263
- channel: channelId,
264
- text: chunks[i]
265
- });
266
- } catch (err) {
267
- const partial = i > 0 ? ` (${i}/${chunks.length} chunks were already sent)` : "";
268
- throw new Error(`${err instanceof Error ? err.message : err}${partial}`);
269
- }
270
- }
271
- }
272
- async function listConversations2(env) {
273
- const token = requireToken2(env);
274
- const authData = await slackApi(token, "auth.test", {});
275
- const teamName = authData.team ?? "workspace";
276
- const data = await slackApi(token, "conversations.list", {
277
- types: "public_channel,private_channel,mpim,im",
278
- limit: 1e3
279
- });
280
- const userMap = /* @__PURE__ */ new Map();
281
- const imChannels = data.channels.filter((ch) => ch.is_im && ch.user);
282
- if (imChannels.length > 0) {
283
- const users = await listUsers2(env);
284
- for (const u of users) {
285
- userMap.set(u.id, u.username);
286
- }
287
- }
288
- return data.channels.map((ch) => {
289
- let type = "channel";
290
- if (ch.is_im) type = "dm";
291
- else if (ch.is_mpim) type = "group";
292
- let slug;
293
- let name;
294
- if (ch.is_im && ch.user) {
295
- const username = userMap.get(ch.user) ?? ch.user;
296
- slug = `slack:@${slugify(username)}`;
297
- name = username;
298
- } else if (ch.name) {
299
- slug = `slack:${slugify(teamName)}/${slugify(ch.name)}`;
300
- name = ch.name;
301
- } else {
302
- slug = `slack:${ch.id}`;
303
- name = ch.id;
304
- }
305
- return {
306
- id: slug,
307
- platformId: ch.id,
308
- name,
309
- type,
310
- participantCount: ch.num_members
311
- };
312
- });
313
- }
314
- async function listUsers2(env) {
315
- const token = requireToken2(env);
316
- const data = await slackApi(token, "users.list", {});
317
- return data.members.filter((m) => !m.deleted).map((m) => ({
318
- id: m.id,
319
- username: m.name,
320
- type: m.is_bot ? "bot" : "human"
321
- }));
322
- }
323
- async function createConversation2(env, participants, name) {
324
- const token = requireToken2(env);
325
- const allUsers = await listUsers2(env);
326
- const ids = [];
327
- for (const p of participants) {
328
- const user = allUsers.find((u) => u.username.toLowerCase() === p.toLowerCase());
329
- if (!user) throw new Error(`User not found: ${p}`);
330
- ids.push(user.id);
331
- }
332
- const mindName = env.VOLUTE_MIND;
333
- if (name) {
334
- const createData = await slackApi(token, "conversations.create", {
335
- name,
336
- is_private: true
337
- });
338
- const channelId = createData.channel.id;
339
- for (const userId of ids) {
340
- await slackApi(token, "conversations.invite", {
341
- channel: channelId,
342
- users: userId
343
- });
344
- }
345
- const authData = await slackApi(token, "auth.test", {});
346
- const teamName = authData.team ?? "workspace";
347
- const slug2 = `slack:${slugify(teamName)}/${slugify(name)}`;
348
- if (mindName) {
349
- writeChannelEntry(mindName, slug2, {
350
- platformId: channelId,
351
- platform: "slack",
352
- name,
353
- type: "channel"
354
- });
355
- }
356
- return slug2;
357
- }
358
- const openData = await slackApi(token, "conversations.open", {
359
- users: ids.join(",")
360
- });
361
- const platformId = openData.channel.id;
362
- const slug = participants.length === 1 ? `slack:@${slugify(participants[0])}` : `slack:@${participants.map(slugify).sort().join(",")}`;
363
- if (mindName) {
364
- writeChannelEntry(mindName, slug, {
365
- platformId,
366
- platform: "slack",
367
- name: participants.join(", "),
368
- type: participants.length === 1 ? "dm" : "group"
369
- });
370
- }
371
- return slug;
372
- }
373
-
374
- // src/lib/channels/telegram.ts
375
- var telegram_exports = {};
376
- __export(telegram_exports, {
377
- createConversation: () => createConversation3,
378
- listConversations: () => listConversations3,
379
- listUsers: () => listUsers3,
380
- read: () => read3,
381
- send: () => send3
382
- });
383
- var TELEGRAM_MAX_LENGTH = 4096;
384
- var API_BASE3 = "https://api.telegram.org";
385
- function requireToken3(env) {
386
- const token = env.TELEGRAM_BOT_TOKEN;
387
- if (!token) throw new Error("TELEGRAM_BOT_TOKEN not set");
388
- return token;
389
- }
390
- async function read3(_env, _channelSlug, _limit) {
391
- throw new Error(
392
- "Telegram Bot API does not support reading chat history. Use volute send instead."
393
- );
394
- }
395
- async function send3(env, channelSlug, message, images) {
396
- const token = requireToken3(env);
397
- const chatId = resolveChannelId2(env, channelSlug);
398
- if (images?.length) {
399
- const CAPTION_MAX = 1024;
400
- for (let i = 0; i < images.length; i++) {
401
- const img = images[i];
402
- const ext = img.media_type.split("/")[1] || "png";
403
- const form = new FormData();
404
- form.append("chat_id", chatId);
405
- form.append(
406
- "photo",
407
- new Blob([Buffer.from(img.data, "base64")], { type: img.media_type }),
408
- `image.${ext}`
409
- );
410
- if (i === 0 && message) {
411
- form.append("caption", message.slice(0, CAPTION_MAX));
412
- }
413
- const res = await fetch(`${API_BASE3}/bot${token}/sendPhoto`, {
414
- method: "POST",
415
- body: form
416
- });
417
- if (!res.ok) {
418
- const body = await res.text().catch(() => "");
419
- const partial = i > 0 ? ` (${i}/${images.length} images were already sent)` : "";
420
- throw new Error(`Telegram API error: ${res.status} ${body}${partial}`);
421
- }
422
- }
423
- if (message && message.length > CAPTION_MAX) {
424
- const remaining = message.slice(CAPTION_MAX);
425
- const chunks2 = splitMessage(remaining, TELEGRAM_MAX_LENGTH);
426
- for (const chunk of chunks2) {
427
- const res = await fetch(`${API_BASE3}/bot${token}/sendMessage`, {
428
- method: "POST",
429
- headers: { "Content-Type": "application/json" },
430
- body: JSON.stringify({ chat_id: chatId, text: chunk })
431
- });
432
- if (!res.ok) {
433
- const body = await res.text().catch(() => "");
434
- throw new Error(`Telegram API error: ${res.status} ${body}`);
435
- }
436
- }
437
- }
438
- return;
439
- }
440
- const chunks = splitMessage(message, TELEGRAM_MAX_LENGTH);
441
- for (let i = 0; i < chunks.length; i++) {
442
- const res = await fetch(`${API_BASE3}/bot${token}/sendMessage`, {
443
- method: "POST",
444
- headers: { "Content-Type": "application/json" },
445
- body: JSON.stringify({ chat_id: chatId, text: chunks[i] })
446
- });
447
- if (!res.ok) {
448
- const body = await res.text().catch(() => "");
449
- const partial = i > 0 ? ` (${i}/${chunks.length} chunks were already sent)` : "";
450
- throw new Error(`Telegram API error: ${res.status} ${body}${partial}`);
451
- }
452
- }
453
- }
454
- async function listConversations3() {
455
- throw new Error(
456
- "Telegram Bot API does not support listing conversations. Users must message the bot first."
457
- );
458
- }
459
- async function listUsers3() {
460
- throw new Error(
461
- "Telegram Bot API does not support listing users. Users must message the bot first."
462
- );
463
- }
464
- async function createConversation3() {
465
- throw new Error(
466
- "Telegram Bot API does not support creating conversations. Users must message the bot first."
467
- );
468
- }
469
-
470
- // src/lib/channels/volute.ts
471
- var volute_exports = {};
472
- __export(volute_exports, {
473
- createConversation: () => createConversation4,
474
- listConversations: () => listConversations4,
475
- listUsers: () => listUsers4,
476
- read: () => read4,
477
- send: () => send4
478
- });
479
- import { existsSync, readFileSync } from "fs";
480
- import { resolve } from "path";
481
- function getDaemonConfig() {
482
- const configPath = resolve(voluteHome(), "daemon.json");
483
- if (!existsSync(configPath)) {
484
- throw new Error("Volute daemon is not running");
485
- }
486
- let config;
487
- try {
488
- config = JSON.parse(readFileSync(configPath, "utf-8"));
489
- } catch (err) {
490
- throw new Error(`Failed to parse ${configPath}: ${err}`);
491
- }
492
- if (typeof config.port !== "number") {
493
- throw new Error(`Invalid or missing port in ${configPath}`);
494
- }
495
- const url = new URL("http://localhost");
496
- url.hostname = config.hostname || "localhost";
497
- url.port = String(config.port);
498
- return { url: url.origin, token: config.token };
499
- }
500
- async function read4(env, channelSlug, limit) {
501
- const mindName = env.VOLUTE_MIND;
502
- if (!mindName) throw new Error("VOLUTE_MIND not set");
503
- const conversationId = resolveChannelId2(env, channelSlug);
504
- const { url, token } = getDaemonConfig();
505
- const headers = { Origin: url };
506
- if (token) headers.Authorization = `Bearer ${token}`;
507
- const res = await fetch(
508
- `${url}/api/minds/${encodeURIComponent(mindName)}/conversations/${encodeURIComponent(conversationId)}/messages`,
509
- { headers }
510
- );
511
- if (!res.ok) {
512
- throw new Error(`Failed to read conversation: ${res.status} ${res.statusText}`);
513
- }
514
- const messages = await res.json();
515
- return messages.slice(-limit).map((m) => {
516
- const text = Array.isArray(m.content) ? m.content.filter((b) => b.type === "text").map((b) => b.text).join("") : m.content;
517
- return `${m.sender_name ?? m.role}: ${text}`;
518
- }).join("\n");
519
- }
520
- async function send4(env, channelSlug, message, images) {
521
- const mindName = env.VOLUTE_MIND;
522
- if (!mindName) throw new Error("VOLUTE_MIND not set");
523
- const conversationId = resolveChannelId2(env, channelSlug);
524
- const { url, token } = getDaemonConfig();
525
- const headers = {
526
- "Content-Type": "application/json",
527
- Origin: url
528
- };
529
- if (token) headers.Authorization = `Bearer ${token}`;
530
- const res = await fetch(`${url}/api/minds/${encodeURIComponent(mindName)}/chat`, {
531
- method: "POST",
532
- headers,
533
- body: JSON.stringify({
534
- message,
535
- conversationId,
536
- sender: env.VOLUTE_SENDER ?? mindName,
537
- images
538
- })
539
- });
540
- if (!res.ok) {
541
- const data = await res.json().catch(() => ({}));
542
- throw new Error(data.error ?? `Failed to send: ${res.status}`);
543
- }
544
- }
545
- async function listConversations4(env) {
546
- const mindName = env.VOLUTE_MIND;
547
- if (!mindName) throw new Error("VOLUTE_MIND not set");
548
- const { url, token } = getDaemonConfig();
549
- const headers = { Origin: url };
550
- if (token) headers.Authorization = `Bearer ${token}`;
551
- const res = await fetch(`${url}/api/minds/${encodeURIComponent(mindName)}/conversations`, {
552
- headers
553
- });
554
- if (!res.ok) {
555
- throw new Error(`Failed to list conversations: ${res.status} ${res.statusText}`);
556
- }
557
- const convs = await res.json();
558
- const results = [];
559
- for (const conv of convs) {
560
- let participants = [];
561
- try {
562
- const pRes = await fetch(
563
- `${url}/api/minds/${encodeURIComponent(mindName)}/conversations/${encodeURIComponent(conv.id)}/participants`,
564
- { headers }
565
- );
566
- if (pRes.ok) {
567
- participants = await pRes.json();
568
- } else {
569
- console.error(`[volute] failed to fetch participants for ${conv.id}: HTTP ${pRes.status}`);
570
- }
571
- } catch (err) {
572
- console.error(`[volute] failed to fetch participants for ${conv.id}:`, err);
573
- }
574
- const slug = buildVoluteSlug({
575
- participants,
576
- mindUsername: mindName,
577
- convTitle: conv.title,
578
- conversationId: conv.id,
579
- convType: conv.type,
580
- convName: conv.name
581
- });
582
- const convType = conv.type === "channel" ? "channel" : participants.length === 2 ? "dm" : "group";
583
- results.push({
584
- id: slug,
585
- platformId: conv.id,
586
- name: conv.type === "channel" ? `#${conv.name}` : conv.title ?? "(untitled)",
587
- type: convType,
588
- participantCount: participants.length
589
- });
590
- }
591
- return results;
592
- }
593
- async function listUsers4(_env) {
594
- const { url, token } = getDaemonConfig();
595
- const headers = { Origin: url };
596
- if (token) headers.Authorization = `Bearer ${token}`;
597
- const res = await fetch(`${url}/api/auth/users`, { headers });
598
- if (!res.ok) {
599
- throw new Error(`Failed to list users: ${res.status} ${res.statusText}`);
600
- }
601
- const data = await res.json();
602
- return data.map((u) => ({
603
- id: String(u.id),
604
- username: u.username,
605
- type: u.user_type
606
- }));
607
- }
608
- async function createConversation4(env, participants, name) {
609
- const mindName = env.VOLUTE_MIND;
610
- if (!mindName) throw new Error("VOLUTE_MIND not set");
611
- const { url, token } = getDaemonConfig();
612
- const headers = {
613
- "Content-Type": "application/json",
614
- Origin: url
615
- };
616
- if (token) headers.Authorization = `Bearer ${token}`;
617
- const res = await fetch(`${url}/api/minds/${encodeURIComponent(mindName)}/conversations`, {
618
- method: "POST",
619
- headers,
620
- body: JSON.stringify({ participantNames: participants, title: name })
621
- });
622
- if (!res.ok) {
623
- const data = await res.json().catch(() => ({}));
624
- throw new Error(data.error ?? `Failed to create conversation: ${res.status}`);
625
- }
626
- const conv = await res.json();
627
- return `volute:${conv.id}`;
628
- }
629
-
630
- // src/lib/channels.ts
631
- var CHANNELS = {
632
- volute: {
633
- name: "volute",
634
- displayName: "Volute",
635
- showToolCalls: true,
636
- builtIn: true,
637
- driver: volute_exports
638
- },
639
- discord: {
640
- name: "discord",
641
- displayName: "Discord",
642
- showToolCalls: false,
643
- driver: discord_exports
644
- },
645
- slack: {
646
- name: "slack",
647
- displayName: "Slack",
648
- showToolCalls: false,
649
- driver: slack_exports
650
- },
651
- telegram: {
652
- name: "telegram",
653
- displayName: "Telegram",
654
- showToolCalls: false,
655
- driver: telegram_exports
656
- },
657
- mail: { name: "mail", displayName: "Email", showToolCalls: false },
658
- system: { name: "system", displayName: "System", showToolCalls: false }
659
- };
660
- function getChannelDriver(platform) {
661
- return CHANNELS[platform]?.driver ?? null;
662
- }
663
- function resolveChannelId2(env, slug) {
664
- const mindName = env.VOLUTE_MIND;
665
- if (!mindName) {
666
- const colonIdx = slug.indexOf(":");
667
- return colonIdx !== -1 ? slug.slice(colonIdx + 1) : slug;
668
- }
669
- return resolveChannelId(mindName, slug);
670
- }
671
-
672
- export {
673
- CHANNELS,
674
- getChannelDriver
675
- };
@@ -1,36 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- schema_exports
4
- } from "./chunk-VQWDC6UK.js";
5
- import {
6
- voluteHome
7
- } from "./chunk-EBGCNDMM.js";
8
-
9
- // src/lib/db.ts
10
- import { chmodSync, existsSync } from "fs";
11
- import { dirname, resolve } from "path";
12
- import { fileURLToPath } from "url";
13
- import { drizzle } from "drizzle-orm/libsql";
14
- import { migrate } from "drizzle-orm/libsql/migrator";
15
- var __dirname = dirname(fileURLToPath(import.meta.url));
16
- var migrationsFolder = existsSync(resolve(__dirname, "../drizzle")) ? resolve(__dirname, "../drizzle") : resolve(__dirname, "../../drizzle");
17
- var db = null;
18
- async function getDb() {
19
- if (db) return db;
20
- const dbPath = process.env.VOLUTE_DB_PATH || resolve(voluteHome(), "volute.db");
21
- db = drizzle({ connection: { url: `file:${dbPath}` }, schema: schema_exports });
22
- await migrate(db, { migrationsFolder });
23
- try {
24
- chmodSync(dbPath, 384);
25
- } catch (err) {
26
- console.error(
27
- `[volute] WARNING: Failed to restrict database file permissions on ${dbPath}:`,
28
- err
29
- );
30
- }
31
- return db;
32
- }
33
-
34
- export {
35
- getDb
36
- };