volute 0.5.0 → 0.7.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 (62) hide show
  1. package/dist/{agent-Z2B6EFEQ.js → agent-7JF7MT73.js} +13 -9
  2. package/dist/{agent-manager-PXBKA2GK.js → agent-manager-IMZ7ZMBF.js} +4 -4
  3. package/dist/channel-SMCNOIVQ.js +262 -0
  4. package/dist/{chunk-MW2KFO3B.js → chunk-62X577Y7.js} +10 -8
  5. package/dist/chunk-7ACDT3P2.js +265 -0
  6. package/dist/{chunk-MXUCNIBG.js → chunk-BX7KI4S3.js} +68 -3
  7. package/dist/{up-7ILD7GU7.js → chunk-EG45HBSJ.js} +16 -4
  8. package/dist/{chunk-HE67X4T6.js → chunk-H7AMDUIA.js} +1 -1
  9. package/dist/{chunk-7L4AN5D4.js → chunk-JR4UXCTO.js} +1 -1
  10. package/dist/{down-O7IFZLVJ.js → chunk-LLJNZPCU.js} +48 -13
  11. package/dist/{chunk-5X7HGB6L.js → chunk-NKXULRSW.js} +2 -1
  12. package/dist/{chunk-UX25Z2ND.js → chunk-UWHWAPGO.js} +7 -0
  13. package/dist/{chunk-UAVD2AHX.js → chunk-W76KWE23.js} +1 -1
  14. package/dist/chunk-ZZOOTYXK.js +583 -0
  15. package/dist/cli.js +22 -21
  16. package/dist/{connector-LYEMXQEV.js → connector-Y7JPNROO.js} +3 -3
  17. package/dist/connectors/discord.js +38 -7
  18. package/dist/connectors/slack.js +22 -3
  19. package/dist/connectors/telegram.js +34 -4
  20. package/dist/{create-RVCZN6HE.js → create-G525LWEA.js} +2 -2
  21. package/dist/{daemon-client-ZY6UUN2M.js → daemon-client-442IV43D.js} +2 -2
  22. package/dist/daemon-restart-4HVEKYFY.js +23 -0
  23. package/dist/daemon.js +1042 -809
  24. package/dist/{delete-3QH7VYIN.js → delete-UOU4AFQN.js} +7 -3
  25. package/dist/down-AZVH5TCD.js +11 -0
  26. package/dist/{env-4D4REPJF.js → env-7GLUJCWS.js} +2 -2
  27. package/dist/{history-OEONB53Z.js → history-H72ZUIBN.js} +2 -2
  28. package/dist/{import-MXJB2EII.js → import-AVKQJDYC.js} +2 -2
  29. package/dist/{logs-DF342W4M.js → logs-EDGK26AK.js} +1 -1
  30. package/dist/{message-ADHWFHSI.js → message-SCOQDR3P.js} +2 -2
  31. package/dist/{package-VQOE7JNH.js → package-T2WAVJOU.js} +1 -1
  32. package/dist/restart-O4ETYLJF.js +29 -0
  33. package/dist/{schedule-NAG6F463.js → schedule-S6QVC5ON.js} +2 -2
  34. package/dist/send-G7PE4DOJ.js +72 -0
  35. package/dist/{setup-RPRRGG2F.js → setup-F4TCWVSP.js} +2 -2
  36. package/dist/{start-TUOXDSFL.js → start-VHQ7LNWM.js} +2 -2
  37. package/dist/{status-A36EHRO4.js → status-QAJWXKMZ.js} +2 -2
  38. package/dist/{stop-AOJZLQ5X.js → stop-CAGCT5NI.js} +2 -2
  39. package/dist/up-RWZF6MLT.js +12 -0
  40. package/dist/{update-LPSIAWQ2.js → update-F7QWV2LB.js} +2 -2
  41. package/dist/{update-check-Y33QDCFL.js → update-check-B4J6IEQ4.js} +2 -2
  42. package/dist/{upgrade-FX2TKJ2S.js → upgrade-YXKPWDRU.js} +2 -2
  43. package/dist/{variant-LAB67OC2.js → variant-4Z6W3PP6.js} +2 -2
  44. package/dist/web-assets/assets/index-B1CqjUYD.js +308 -0
  45. package/dist/web-assets/index.html +1 -1
  46. package/package.json +1 -1
  47. package/templates/_base/.init/.config/scripts/session-reader.ts +59 -0
  48. package/templates/_base/_skills/sessions/SKILL.md +49 -0
  49. package/templates/_base/_skills/volute-agent/SKILL.md +13 -9
  50. package/templates/_base/src/lib/format-prefix.ts +6 -0
  51. package/templates/_base/src/lib/router.ts +30 -3
  52. package/templates/_base/src/lib/session-monitor.ts +400 -0
  53. package/templates/_base/src/lib/types.ts +2 -0
  54. package/templates/agent-sdk/src/agent.ts +16 -0
  55. package/templates/agent-sdk/src/lib/hooks/session-context.ts +32 -0
  56. package/templates/pi/src/agent.ts +7 -1
  57. package/templates/pi/src/lib/session-context-extension.ts +33 -0
  58. package/dist/channel-MK5OK2SI.js +0 -113
  59. package/dist/chunk-SMISE4SV.js +0 -226
  60. package/dist/conversation-ERXEQZTY.js +0 -163
  61. package/dist/send-66QMKRUH.js +0 -75
  62. package/dist/web-assets/assets/index-BbRmoxoA.js +0 -308
@@ -0,0 +1,583 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ resolveChannelId,
4
+ slugify,
5
+ writeChannelEntry
6
+ } from "./chunk-BX7KI4S3.js";
7
+ import {
8
+ voluteHome
9
+ } from "./chunk-UWHWAPGO.js";
10
+ import {
11
+ __export
12
+ } from "./chunk-K3NQKI34.js";
13
+
14
+ // src/lib/channels/discord.ts
15
+ var discord_exports = {};
16
+ __export(discord_exports, {
17
+ createConversation: () => createConversation,
18
+ listConversations: () => listConversations,
19
+ listUsers: () => listUsers,
20
+ read: () => read,
21
+ send: () => send
22
+ });
23
+ var API_BASE = "https://discord.com/api/v10";
24
+ function requireToken(env) {
25
+ const token = env.DISCORD_TOKEN;
26
+ if (!token) throw new Error("DISCORD_TOKEN not set");
27
+ return token;
28
+ }
29
+ async function discordGet(token, path) {
30
+ const res = await fetch(`${API_BASE}${path}`, {
31
+ headers: { Authorization: `Bot ${token}` }
32
+ });
33
+ if (!res.ok) {
34
+ throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
35
+ }
36
+ return res.json();
37
+ }
38
+ async function read(env, channelSlug, limit) {
39
+ const token = requireToken(env);
40
+ const channelId = resolveChannelId2(env, channelSlug);
41
+ const res = await fetch(`${API_BASE}/channels/${channelId}/messages?limit=${limit}`, {
42
+ headers: { Authorization: `Bot ${token}` }
43
+ });
44
+ if (!res.ok) {
45
+ throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
46
+ }
47
+ const messages = await res.json();
48
+ return messages.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
49
+ }
50
+ async function send(env, channelSlug, message) {
51
+ const token = requireToken(env);
52
+ const channelId = resolveChannelId2(env, channelSlug);
53
+ const res = await fetch(`${API_BASE}/channels/${channelId}/messages`, {
54
+ method: "POST",
55
+ headers: {
56
+ Authorization: `Bot ${token}`,
57
+ "Content-Type": "application/json"
58
+ },
59
+ body: JSON.stringify({ content: message })
60
+ });
61
+ if (!res.ok) {
62
+ throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
63
+ }
64
+ }
65
+ async function listConversations(env) {
66
+ const token = requireToken(env);
67
+ const results = [];
68
+ const guilds = await discordGet(token, "/users/@me/guilds");
69
+ for (const guild of guilds) {
70
+ const channels = await discordGet(token, `/guilds/${guild.id}/channels`);
71
+ for (const ch of channels) {
72
+ if (ch.type !== 0) continue;
73
+ results.push({
74
+ id: `discord:${slugify(guild.name)}/${slugify(ch.name)}`,
75
+ platformId: ch.id,
76
+ name: `#${ch.name}`,
77
+ type: "channel"
78
+ });
79
+ }
80
+ }
81
+ const dms = await discordGet(token, "/users/@me/channels");
82
+ for (const dm of dms) {
83
+ const recipients = dm.recipients?.map((r) => r.username) ?? [];
84
+ const slug = recipients.length === 0 ? `discord:${dm.id}` : recipients.length === 1 ? `discord:@${slugify(recipients[0])}` : `discord:@${recipients.map(slugify).sort().join(",")}`;
85
+ results.push({
86
+ id: slug,
87
+ platformId: dm.id,
88
+ name: recipients.join(", ") || "DM",
89
+ type: dm.type === 1 ? "dm" : "group"
90
+ });
91
+ }
92
+ return results;
93
+ }
94
+ async function listUsers(env) {
95
+ const token = requireToken(env);
96
+ const seen = /* @__PURE__ */ new Map();
97
+ const guilds = await discordGet(token, "/users/@me/guilds");
98
+ for (const guild of guilds) {
99
+ const members = await discordGet(token, `/guilds/${guild.id}/members?limit=1000`);
100
+ for (const m of members) {
101
+ if (!seen.has(m.user.id)) {
102
+ seen.set(m.user.id, {
103
+ id: m.user.id,
104
+ username: m.user.username,
105
+ type: m.user.bot ? "bot" : "human"
106
+ });
107
+ }
108
+ }
109
+ }
110
+ return [...seen.values()];
111
+ }
112
+ async function createConversation(env, participants, _name) {
113
+ const token = requireToken(env);
114
+ if (participants.length !== 1) {
115
+ throw new Error(
116
+ "Discord group creation not supported via bot \u2014 use threads in an existing channel"
117
+ );
118
+ }
119
+ const allUsers = await listUsers(env);
120
+ const target = allUsers.find((u) => u.username.toLowerCase() === participants[0].toLowerCase());
121
+ if (!target) {
122
+ throw new Error(`User not found: ${participants[0]}`);
123
+ }
124
+ const res = await fetch(`${API_BASE}/users/@me/channels`, {
125
+ method: "POST",
126
+ headers: {
127
+ Authorization: `Bot ${token}`,
128
+ "Content-Type": "application/json"
129
+ },
130
+ body: JSON.stringify({ recipient_id: target.id })
131
+ });
132
+ if (!res.ok) {
133
+ throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
134
+ }
135
+ const dm = await res.json();
136
+ const slug = `discord:@${slugify(participants[0])}`;
137
+ const agentDir = env.VOLUTE_AGENT_DIR;
138
+ if (agentDir) {
139
+ writeChannelEntry(agentDir, slug, {
140
+ platformId: dm.id,
141
+ platform: "discord",
142
+ name: participants[0],
143
+ type: "dm"
144
+ });
145
+ }
146
+ return slug;
147
+ }
148
+
149
+ // src/lib/channels/slack.ts
150
+ var slack_exports = {};
151
+ __export(slack_exports, {
152
+ createConversation: () => createConversation2,
153
+ listConversations: () => listConversations2,
154
+ listUsers: () => listUsers2,
155
+ read: () => read2,
156
+ send: () => send2
157
+ });
158
+ var API_BASE2 = "https://slack.com/api";
159
+ function requireToken2(env) {
160
+ const token = env.SLACK_BOT_TOKEN;
161
+ if (!token) throw new Error("SLACK_BOT_TOKEN not set");
162
+ return token;
163
+ }
164
+ async function slackApi(token, method, body) {
165
+ const res = await fetch(`${API_BASE2}/${method}`, {
166
+ method: "POST",
167
+ headers: {
168
+ Authorization: `Bearer ${token}`,
169
+ "Content-Type": "application/json"
170
+ },
171
+ body: JSON.stringify(body)
172
+ });
173
+ if (!res.ok) {
174
+ throw new Error(`Slack API HTTP error: ${res.status} ${res.statusText}`);
175
+ }
176
+ const data = await res.json();
177
+ if (!data.ok) {
178
+ throw new Error(`Slack API error: ${data.error}`);
179
+ }
180
+ return data;
181
+ }
182
+ async function read2(env, channelSlug, limit) {
183
+ const token = requireToken2(env);
184
+ const channelId = resolveChannelId2(env, channelSlug);
185
+ const data = await slackApi(token, "conversations.history", {
186
+ channel: channelId,
187
+ limit
188
+ });
189
+ return data.messages.reverse().map((m) => `${m.user ?? m.bot_id ?? "unknown"}: ${m.text}`).join("\n");
190
+ }
191
+ async function send2(env, channelSlug, message) {
192
+ const token = requireToken2(env);
193
+ const channelId = resolveChannelId2(env, channelSlug);
194
+ await slackApi(token, "chat.postMessage", {
195
+ channel: channelId,
196
+ text: message
197
+ });
198
+ }
199
+ async function listConversations2(env) {
200
+ const token = requireToken2(env);
201
+ const authData = await slackApi(token, "auth.test", {});
202
+ const teamName = authData.team ?? "workspace";
203
+ const data = await slackApi(token, "conversations.list", {
204
+ types: "public_channel,private_channel,mpim,im",
205
+ limit: 1e3
206
+ });
207
+ const userMap = /* @__PURE__ */ new Map();
208
+ const imChannels = data.channels.filter((ch) => ch.is_im && ch.user);
209
+ if (imChannels.length > 0) {
210
+ const users = await listUsers2(env);
211
+ for (const u of users) {
212
+ userMap.set(u.id, u.username);
213
+ }
214
+ }
215
+ return data.channels.map((ch) => {
216
+ let type = "channel";
217
+ if (ch.is_im) type = "dm";
218
+ else if (ch.is_mpim) type = "group";
219
+ let slug;
220
+ let name;
221
+ if (ch.is_im && ch.user) {
222
+ const username = userMap.get(ch.user) ?? ch.user;
223
+ slug = `slack:@${slugify(username)}`;
224
+ name = username;
225
+ } else if (ch.name) {
226
+ slug = `slack:${slugify(teamName)}/${slugify(ch.name)}`;
227
+ name = ch.name;
228
+ } else {
229
+ slug = `slack:${ch.id}`;
230
+ name = ch.id;
231
+ }
232
+ return {
233
+ id: slug,
234
+ platformId: ch.id,
235
+ name,
236
+ type,
237
+ participantCount: ch.num_members
238
+ };
239
+ });
240
+ }
241
+ async function listUsers2(env) {
242
+ const token = requireToken2(env);
243
+ const data = await slackApi(token, "users.list", {});
244
+ return data.members.filter((m) => !m.deleted).map((m) => ({
245
+ id: m.id,
246
+ username: m.name,
247
+ type: m.is_bot ? "bot" : "human"
248
+ }));
249
+ }
250
+ async function createConversation2(env, participants, name) {
251
+ const token = requireToken2(env);
252
+ const allUsers = await listUsers2(env);
253
+ const ids = [];
254
+ for (const p of participants) {
255
+ const user = allUsers.find((u) => u.username.toLowerCase() === p.toLowerCase());
256
+ if (!user) throw new Error(`User not found: ${p}`);
257
+ ids.push(user.id);
258
+ }
259
+ const agentDir = env.VOLUTE_AGENT_DIR;
260
+ if (name) {
261
+ const createData = await slackApi(token, "conversations.create", {
262
+ name,
263
+ is_private: true
264
+ });
265
+ const channelId = createData.channel.id;
266
+ for (const userId of ids) {
267
+ await slackApi(token, "conversations.invite", {
268
+ channel: channelId,
269
+ users: userId
270
+ });
271
+ }
272
+ const authData = await slackApi(token, "auth.test", {});
273
+ const teamName = authData.team ?? "workspace";
274
+ const slug2 = `slack:${slugify(teamName)}/${slugify(name)}`;
275
+ if (agentDir) {
276
+ writeChannelEntry(agentDir, slug2, {
277
+ platformId: channelId,
278
+ platform: "slack",
279
+ name,
280
+ type: "channel"
281
+ });
282
+ }
283
+ return slug2;
284
+ }
285
+ const openData = await slackApi(token, "conversations.open", {
286
+ users: ids.join(",")
287
+ });
288
+ const platformId = openData.channel.id;
289
+ const slug = participants.length === 1 ? `slack:@${slugify(participants[0])}` : `slack:@${participants.map(slugify).sort().join(",")}`;
290
+ if (agentDir) {
291
+ writeChannelEntry(agentDir, slug, {
292
+ platformId,
293
+ platform: "slack",
294
+ name: participants.join(", "),
295
+ type: participants.length === 1 ? "dm" : "group"
296
+ });
297
+ }
298
+ return slug;
299
+ }
300
+
301
+ // src/lib/channels/telegram.ts
302
+ var telegram_exports = {};
303
+ __export(telegram_exports, {
304
+ createConversation: () => createConversation3,
305
+ listConversations: () => listConversations3,
306
+ listUsers: () => listUsers3,
307
+ read: () => read3,
308
+ send: () => send3
309
+ });
310
+ var API_BASE3 = "https://api.telegram.org";
311
+ function requireToken3(env) {
312
+ const token = env.TELEGRAM_BOT_TOKEN;
313
+ if (!token) throw new Error("TELEGRAM_BOT_TOKEN not set");
314
+ return token;
315
+ }
316
+ async function read3(_env, _channelSlug, _limit) {
317
+ throw new Error(
318
+ "Telegram Bot API does not support reading chat history. Use volute channel send instead."
319
+ );
320
+ }
321
+ async function send3(env, channelSlug, message) {
322
+ const token = requireToken3(env);
323
+ const chatId = resolveChannelId2(env, channelSlug);
324
+ const res = await fetch(`${API_BASE3}/bot${token}/sendMessage`, {
325
+ method: "POST",
326
+ headers: { "Content-Type": "application/json" },
327
+ body: JSON.stringify({ chat_id: chatId, text: message })
328
+ });
329
+ if (!res.ok) {
330
+ const body = await res.text().catch(() => "");
331
+ throw new Error(`Telegram API error: ${res.status} ${body}`);
332
+ }
333
+ }
334
+ async function listConversations3() {
335
+ throw new Error(
336
+ "Telegram Bot API does not support listing conversations. Users must message the bot first."
337
+ );
338
+ }
339
+ async function listUsers3() {
340
+ throw new Error(
341
+ "Telegram Bot API does not support listing users. Users must message the bot first."
342
+ );
343
+ }
344
+ async function createConversation3() {
345
+ throw new Error(
346
+ "Telegram Bot API does not support creating conversations. Users must message the bot first."
347
+ );
348
+ }
349
+
350
+ // src/lib/channels/volute.ts
351
+ var volute_exports = {};
352
+ __export(volute_exports, {
353
+ createConversation: () => createConversation4,
354
+ listConversations: () => listConversations4,
355
+ listUsers: () => listUsers4,
356
+ read: () => read4,
357
+ send: () => send4,
358
+ sendAndStream: () => sendAndStream
359
+ });
360
+ import { existsSync, readFileSync } from "fs";
361
+ import { resolve } from "path";
362
+ function getDaemonConfig() {
363
+ const configPath = resolve(voluteHome(), "daemon.json");
364
+ if (!existsSync(configPath)) {
365
+ throw new Error("Volute daemon is not running");
366
+ }
367
+ let config;
368
+ try {
369
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
370
+ } catch (err) {
371
+ throw new Error(`Failed to parse ${configPath}: ${err}`);
372
+ }
373
+ if (typeof config.port !== "number") {
374
+ throw new Error(`Invalid or missing port in ${configPath}`);
375
+ }
376
+ const url = new URL("http://localhost");
377
+ url.hostname = config.hostname || "localhost";
378
+ url.port = String(config.port);
379
+ return { url: url.origin, token: config.token };
380
+ }
381
+ async function read4(env, channelSlug, limit) {
382
+ const agentName = env.VOLUTE_AGENT;
383
+ if (!agentName) throw new Error("VOLUTE_AGENT not set");
384
+ const conversationId = resolveChannelId2(env, channelSlug);
385
+ const { url, token } = getDaemonConfig();
386
+ const headers = { Origin: url };
387
+ if (token) headers.Authorization = `Bearer ${token}`;
388
+ const res = await fetch(
389
+ `${url}/api/agents/${encodeURIComponent(agentName)}/conversations/${encodeURIComponent(conversationId)}/messages`,
390
+ { headers }
391
+ );
392
+ if (!res.ok) {
393
+ throw new Error(`Failed to read conversation: ${res.status} ${res.statusText}`);
394
+ }
395
+ const messages = await res.json();
396
+ return messages.slice(-limit).map((m) => {
397
+ const text = Array.isArray(m.content) ? m.content.filter((b) => b.type === "text").map((b) => b.text).join("") : m.content;
398
+ return `${m.sender_name ?? m.role}: ${text}`;
399
+ }).join("\n");
400
+ }
401
+ async function* sendAndStream(env, channelSlug, message) {
402
+ const agentName = env.VOLUTE_AGENT;
403
+ if (!agentName) throw new Error("VOLUTE_AGENT not set");
404
+ const conversationId = resolveChannelId2(env, channelSlug);
405
+ const { url, token } = getDaemonConfig();
406
+ const headers = {
407
+ "Content-Type": "application/json",
408
+ Origin: url
409
+ };
410
+ if (token) headers.Authorization = `Bearer ${token}`;
411
+ const res = await fetch(`${url}/api/agents/${encodeURIComponent(agentName)}/chat`, {
412
+ method: "POST",
413
+ headers,
414
+ body: JSON.stringify({ message, conversationId, sender: env.VOLUTE_SENDER ?? agentName })
415
+ });
416
+ if (!res.ok) {
417
+ const data = await res.json().catch(() => ({}));
418
+ throw new Error(data.error ?? `Failed to send: ${res.status}`);
419
+ }
420
+ if (!res.body) return;
421
+ const reader = res.body.getReader();
422
+ const decoder = new TextDecoder();
423
+ let buffer = "";
424
+ try {
425
+ while (true) {
426
+ const { done, value } = await reader.read();
427
+ if (done) break;
428
+ buffer += decoder.decode(value, { stream: true });
429
+ const lines = buffer.split("\n");
430
+ buffer = lines.pop() ?? "";
431
+ for (const line of lines) {
432
+ if (!line.startsWith("data:")) continue;
433
+ const data = line.slice(5).trim();
434
+ if (!data) continue;
435
+ try {
436
+ const event = JSON.parse(data);
437
+ yield event;
438
+ if (event.type === "done") return;
439
+ } catch (err) {
440
+ console.error(`[volute] failed to parse SSE data: ${data}`, err);
441
+ }
442
+ }
443
+ }
444
+ } finally {
445
+ reader.releaseLock();
446
+ }
447
+ }
448
+ async function send4(env, channelSlug, message) {
449
+ for await (const event of sendAndStream(env, channelSlug, message)) {
450
+ if (event.type === "done") break;
451
+ }
452
+ }
453
+ async function listConversations4(env) {
454
+ const agentName = env.VOLUTE_AGENT;
455
+ if (!agentName) throw new Error("VOLUTE_AGENT not set");
456
+ const { url, token } = getDaemonConfig();
457
+ const headers = { Origin: url };
458
+ if (token) headers.Authorization = `Bearer ${token}`;
459
+ const res = await fetch(`${url}/api/agents/${encodeURIComponent(agentName)}/conversations`, {
460
+ headers
461
+ });
462
+ if (!res.ok) {
463
+ throw new Error(`Failed to list conversations: ${res.status} ${res.statusText}`);
464
+ }
465
+ const convs = await res.json();
466
+ const results = [];
467
+ for (const conv of convs) {
468
+ let participantCount;
469
+ try {
470
+ const pRes = await fetch(
471
+ `${url}/api/agents/${encodeURIComponent(agentName)}/conversations/${encodeURIComponent(conv.id)}/participants`,
472
+ { headers }
473
+ );
474
+ if (pRes.ok) {
475
+ const participants = await pRes.json();
476
+ participantCount = participants.length;
477
+ }
478
+ } catch (err) {
479
+ console.error(`[volute] failed to fetch participants for ${conv.id}:`, err);
480
+ }
481
+ const slug = conv.title ? `volute:${slugify(conv.title)}` : `volute:${conv.id}`;
482
+ results.push({
483
+ id: slug,
484
+ platformId: conv.id,
485
+ name: conv.title ?? "(untitled)",
486
+ type: participantCount === 2 ? "dm" : "group",
487
+ participantCount
488
+ });
489
+ }
490
+ return results;
491
+ }
492
+ async function listUsers4(_env) {
493
+ const { url, token } = getDaemonConfig();
494
+ const headers = { Origin: url };
495
+ if (token) headers.Authorization = `Bearer ${token}`;
496
+ const res = await fetch(`${url}/api/auth/users`, { headers });
497
+ if (!res.ok) {
498
+ throw new Error(`Failed to list users: ${res.status} ${res.statusText}`);
499
+ }
500
+ const data = await res.json();
501
+ return data.map((u) => ({
502
+ id: String(u.id),
503
+ username: u.username,
504
+ type: u.user_type
505
+ }));
506
+ }
507
+ async function createConversation4(env, participants, name) {
508
+ const agentName = env.VOLUTE_AGENT;
509
+ if (!agentName) throw new Error("VOLUTE_AGENT not set");
510
+ const { url, token } = getDaemonConfig();
511
+ const headers = {
512
+ "Content-Type": "application/json",
513
+ Origin: url
514
+ };
515
+ if (token) headers.Authorization = `Bearer ${token}`;
516
+ const res = await fetch(`${url}/api/agents/${encodeURIComponent(agentName)}/conversations`, {
517
+ method: "POST",
518
+ headers,
519
+ body: JSON.stringify({ participantNames: participants, title: name })
520
+ });
521
+ if (!res.ok) {
522
+ const data = await res.json().catch(() => ({}));
523
+ throw new Error(data.error ?? `Failed to create conversation: ${res.status}`);
524
+ }
525
+ const conv = await res.json();
526
+ const slug = name ? `volute:${slugify(name)}` : `volute:${conv.id}`;
527
+ const agentDir = env.VOLUTE_AGENT_DIR;
528
+ if (agentDir) {
529
+ writeChannelEntry(agentDir, slug, {
530
+ platformId: conv.id,
531
+ platform: "volute",
532
+ name: name ?? participants.join(", "),
533
+ type: participants.length <= 1 ? "dm" : "group"
534
+ });
535
+ }
536
+ return slug;
537
+ }
538
+
539
+ // src/lib/channels.ts
540
+ var CHANNELS = {
541
+ volute: {
542
+ name: "volute",
543
+ displayName: "Volute",
544
+ showToolCalls: true,
545
+ builtIn: true,
546
+ driver: volute_exports
547
+ },
548
+ discord: {
549
+ name: "discord",
550
+ displayName: "Discord",
551
+ showToolCalls: false,
552
+ driver: discord_exports
553
+ },
554
+ slack: {
555
+ name: "slack",
556
+ displayName: "Slack",
557
+ showToolCalls: false,
558
+ driver: slack_exports
559
+ },
560
+ telegram: {
561
+ name: "telegram",
562
+ displayName: "Telegram",
563
+ showToolCalls: false,
564
+ driver: telegram_exports
565
+ },
566
+ system: { name: "system", displayName: "System", showToolCalls: false }
567
+ };
568
+ function getChannelDriver(platform) {
569
+ return CHANNELS[platform]?.driver ?? null;
570
+ }
571
+ function resolveChannelId2(env, slug) {
572
+ const agentDir = env.VOLUTE_AGENT_DIR;
573
+ if (!agentDir) {
574
+ const colonIdx = slug.indexOf(":");
575
+ return colonIdx !== -1 ? slug.slice(colonIdx + 1) : slug;
576
+ }
577
+ return resolveChannelId(agentDir, slug);
578
+ }
579
+
580
+ export {
581
+ CHANNELS,
582
+ getChannelDriver
583
+ };
package/dist/cli.js CHANGED
@@ -9,49 +9,49 @@ if (!process.env.VOLUTE_HOME) {
9
9
  var command = process.argv[2];
10
10
  var args = process.argv.slice(3);
11
11
  if (command === "--version" || command === "-v") {
12
- const { default: pkg } = await import("./package-VQOE7JNH.js");
12
+ const { default: pkg } = await import("./package-T2WAVJOU.js");
13
13
  console.log(pkg.version);
14
14
  process.exit(0);
15
15
  }
16
16
  switch (command) {
17
17
  case "agent":
18
- await import("./agent-Z2B6EFEQ.js").then((m) => m.run(args));
18
+ await import("./agent-7JF7MT73.js").then((m) => m.run(args));
19
19
  break;
20
20
  case "message":
21
- await import("./message-ADHWFHSI.js").then((m) => m.run(args));
21
+ await import("./message-SCOQDR3P.js").then((m) => m.run(args));
22
22
  break;
23
23
  case "variant":
24
- await import("./variant-LAB67OC2.js").then((m) => m.run(args));
24
+ await import("./variant-4Z6W3PP6.js").then((m) => m.run(args));
25
25
  break;
26
26
  case "connector":
27
- await import("./connector-LYEMXQEV.js").then((m) => m.run(args));
27
+ await import("./connector-Y7JPNROO.js").then((m) => m.run(args));
28
28
  break;
29
29
  case "channel":
30
- await import("./channel-MK5OK2SI.js").then((m) => m.run(args));
30
+ await import("./channel-SMCNOIVQ.js").then((m) => m.run(args));
31
31
  break;
32
32
  case "schedule":
33
- await import("./schedule-NAG6F463.js").then((m) => m.run(args));
34
- break;
35
- case "conversation":
36
- await import("./conversation-ERXEQZTY.js").then((m) => m.run(args));
33
+ await import("./schedule-S6QVC5ON.js").then((m) => m.run(args));
37
34
  break;
38
35
  case "env":
39
- await import("./env-4D4REPJF.js").then((m) => m.run(args));
36
+ await import("./env-7GLUJCWS.js").then((m) => m.run(args));
40
37
  break;
41
38
  case "up":
42
- await import("./up-7ILD7GU7.js").then((m) => m.run(args));
39
+ await import("./up-RWZF6MLT.js").then((m) => m.run(args));
43
40
  break;
44
41
  case "down":
45
- await import("./down-O7IFZLVJ.js").then((m) => m.run(args));
42
+ await import("./down-AZVH5TCD.js").then((m) => m.run(args));
43
+ break;
44
+ case "restart":
45
+ await import("./daemon-restart-4HVEKYFY.js").then((m) => m.run(args));
46
46
  break;
47
47
  case "setup":
48
- await import("./setup-RPRRGG2F.js").then((m) => m.run(args));
48
+ await import("./setup-F4TCWVSP.js").then((m) => m.run(args));
49
49
  break;
50
50
  case "service":
51
51
  await import("./service-HZNIDNJF.js").then((m) => m.run(args));
52
52
  break;
53
53
  case "update":
54
- await import("./update-LPSIAWQ2.js").then((m) => m.run(args));
54
+ await import("./update-F7QWV2LB.js").then((m) => m.run(args));
55
55
  break;
56
56
  case "--help":
57
57
  case "-h":
@@ -62,6 +62,7 @@ Commands:
62
62
  volute agent create <name> Create a new agent
63
63
  volute agent start <name> Start an agent (daemonized)
64
64
  volute agent stop <name> Stop an agent
65
+ volute agent restart <name> Restart an agent
65
66
  volute agent delete <name> [--force] Delete an agent (--force removes files)
66
67
  volute agent list List all agents
67
68
  volute agent status <name> Check agent status
@@ -82,19 +83,19 @@ Commands:
82
83
 
83
84
  volute channel read <uri> Read recent messages from a channel
84
85
  volute channel send <uri> "<msg>" Send a message to a channel
86
+ volute channel list [<platform>] List conversations on a platform
87
+ volute channel users <platform> List users on a platform
88
+ volute channel create <platform> ... Create a conversation on a platform
85
89
 
86
90
  volute schedule list List schedules for an agent
87
91
  volute schedule add ... Add a cron schedule
88
92
  volute schedule remove ... Remove a schedule
89
93
 
90
- volute conversation create ... Create a group conversation
91
- volute conversation list List conversations
92
- volute conversation send <id> "<msg>" Send a message to a conversation
93
-
94
94
  volute env <set|get|list|remove> Manage environment variables
95
95
 
96
96
  volute up [--port N] Start the daemon (default: 4200)
97
97
  volute down Stop the daemon
98
+ volute restart [--port N] Restart the daemon
98
99
 
99
100
  volute service install [--port N] Install as system service (auto-start)
100
101
  volute service uninstall Remove system service
@@ -108,7 +109,7 @@ Options:
108
109
  --version, -v Show version number
109
110
  --help, -h Show this help message
110
111
 
111
- Agent-scoped commands (variant, connector, schedule, channel, conversation, message history)
112
+ Agent-scoped commands (variant, connector, schedule, channel, message history)
112
113
  use --agent <name> or VOLUTE_AGENT env var to identify the agent.`);
113
114
  break;
114
115
  default:
@@ -117,7 +118,7 @@ Run 'volute --help' for usage.`);
117
118
  process.exit(1);
118
119
  }
119
120
  if (command !== "update") {
120
- import("./update-check-Y33QDCFL.js").then((m) => m.checkForUpdate()).then((result) => {
121
+ import("./update-check-B4J6IEQ4.js").then((m) => m.checkForUpdate()).then((result) => {
121
122
  if (result.updateAvailable) {
122
123
  console.error(`
123
124
  Update available: ${result.current} \u2192 ${result.latest}`);