volute 0.16.0 → 0.18.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.
- package/dist/chunk-AYB7XAWO.js +812 -0
- package/dist/{chunk-3FD4ZZUL.js → chunk-FW5API7X.js} +116 -10
- package/dist/{chunk-3FC42ZBM.js → chunk-GK4E7LM7.js} +3 -0
- package/dist/cli.js +18 -6
- package/dist/connectors/discord.js +1 -1
- package/dist/connectors/slack.js +1 -1
- package/dist/connectors/telegram.js +1 -1
- package/dist/{daemon-restart-MS5FI44G.js → daemon-restart-2HVTHZAT.js} +1 -1
- package/dist/daemon.js +1443 -592
- package/dist/history-YUEKTJ2N.js +108 -0
- package/dist/{mind-manager-PN5SUDJ4.js → mind-manager-Z7O7PN2O.js} +1 -1
- package/dist/{package-3QGV3KX6.js → package-OKLFO7UY.js} +8 -9
- package/dist/{send-KBBZNYG6.js → send-BNDTLUPM.js} +41 -9
- package/dist/skill-2Y42P4JY.js +287 -0
- package/dist/{up-GZLWZAQE.js → up-7B3BWF2U.js} +1 -1
- package/dist/web-assets/assets/index-CtiimdWK.css +1 -0
- package/dist/web-assets/assets/index-kt1_EcuO.js +63 -0
- package/dist/web-assets/index.html +2 -1
- package/drizzle/0006_mind_history.sql +20 -0
- package/drizzle/0007_system_prompts.sql +5 -0
- package/drizzle/0008_volute_channels.sql +24 -0
- package/drizzle/0009_shared_skills.sql +9 -0
- package/drizzle/meta/0006_snapshot.json +7 -0
- package/drizzle/meta/0007_snapshot.json +7 -0
- package/drizzle/meta/0008_snapshot.json +7 -0
- package/drizzle/meta/0009_snapshot.json +7 -0
- package/drizzle/meta/_journal.json +28 -0
- package/package.json +8 -9
- package/templates/_base/.init/.config/prompts.json +5 -0
- package/templates/_base/_skills/volute-mind/SKILL.md +19 -5
- package/templates/_base/src/lib/daemon-client.ts +45 -0
- package/templates/_base/src/lib/logger.ts +19 -0
- package/templates/_base/src/lib/router.ts +48 -41
- package/templates/_base/src/lib/routing.ts +5 -8
- package/templates/_base/src/lib/startup.ts +43 -0
- package/templates/_base/src/lib/transparency.ts +89 -0
- package/templates/_base/src/lib/types.ts +0 -1
- package/templates/_base/src/lib/volute-server.ts +3 -35
- package/templates/claude/src/agent.ts +9 -22
- package/templates/claude/src/lib/hooks/reply-instructions.ts +6 -9
- package/templates/claude/src/lib/stream-consumer.ts +39 -12
- package/templates/pi/src/agent.ts +9 -22
- package/templates/pi/src/lib/event-handler.ts +58 -7
- package/templates/pi/src/lib/reply-instructions-extension.ts +6 -9
- package/dist/chunk-J52CJCVI.js +0 -447
- package/dist/history-LKCJJMUV.js +0 -50
- package/dist/web-assets/assets/index-B1XIIGCh.js +0 -307
- package/templates/_base/src/lib/auto-reply.ts +0 -38
- /package/dist/{chunk-LLBBVTEY.js → chunk-6DVBMLVN.js} +0 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
slugify,
|
|
6
6
|
splitMessage,
|
|
7
7
|
writeChannelEntry
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-GK4E7LM7.js";
|
|
9
9
|
import {
|
|
10
10
|
voluteHome
|
|
11
11
|
} from "./chunk-M77QBTEH.js";
|
|
@@ -50,9 +50,34 @@ async function read(env, channelSlug, limit) {
|
|
|
50
50
|
const messages = await res.json();
|
|
51
51
|
return messages.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
|
|
52
52
|
}
|
|
53
|
-
async function send(env, channelSlug, message) {
|
|
53
|
+
async function send(env, channelSlug, message, images) {
|
|
54
54
|
const token = requireToken(env);
|
|
55
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
|
+
}
|
|
56
81
|
const chunks = splitMessage(message, DISCORD_MAX_LENGTH);
|
|
57
82
|
for (let i = 0; i < chunks.length; i++) {
|
|
58
83
|
const res = await fetch(`${API_BASE}/channels/${channelId}/messages`, {
|
|
@@ -196,9 +221,41 @@ async function read2(env, channelSlug, limit) {
|
|
|
196
221
|
});
|
|
197
222
|
return data.messages.reverse().map((m) => `${m.user ?? m.bot_id ?? "unknown"}: ${m.text}`).join("\n");
|
|
198
223
|
}
|
|
199
|
-
async function send2(env, channelSlug, message) {
|
|
224
|
+
async function send2(env, channelSlug, message, images) {
|
|
200
225
|
const token = requireToken2(env);
|
|
201
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
|
+
}
|
|
202
259
|
const chunks = splitMessage(message, SLACK_MAX_LENGTH);
|
|
203
260
|
for (let i = 0; i < chunks.length; i++) {
|
|
204
261
|
try {
|
|
@@ -335,9 +392,51 @@ async function read3(_env, _channelSlug, _limit) {
|
|
|
335
392
|
"Telegram Bot API does not support reading chat history. Use volute send instead."
|
|
336
393
|
);
|
|
337
394
|
}
|
|
338
|
-
async function send3(env, channelSlug, message) {
|
|
395
|
+
async function send3(env, channelSlug, message, images) {
|
|
339
396
|
const token = requireToken3(env);
|
|
340
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
|
+
}
|
|
341
440
|
const chunks = splitMessage(message, TELEGRAM_MAX_LENGTH);
|
|
342
441
|
for (let i = 0; i < chunks.length; i++) {
|
|
343
442
|
const res = await fetch(`${API_BASE3}/bot${token}/sendMessage`, {
|
|
@@ -418,7 +517,7 @@ async function read4(env, channelSlug, limit) {
|
|
|
418
517
|
return `${m.sender_name ?? m.role}: ${text}`;
|
|
419
518
|
}).join("\n");
|
|
420
519
|
}
|
|
421
|
-
async function send4(env, channelSlug, message) {
|
|
520
|
+
async function send4(env, channelSlug, message, images) {
|
|
422
521
|
const mindName = env.VOLUTE_MIND;
|
|
423
522
|
if (!mindName) throw new Error("VOLUTE_MIND not set");
|
|
424
523
|
const conversationId = resolveChannelId2(env, channelSlug);
|
|
@@ -431,7 +530,12 @@ async function send4(env, channelSlug, message) {
|
|
|
431
530
|
const res = await fetch(`${url}/api/minds/${encodeURIComponent(mindName)}/chat`, {
|
|
432
531
|
method: "POST",
|
|
433
532
|
headers,
|
|
434
|
-
body: JSON.stringify({
|
|
533
|
+
body: JSON.stringify({
|
|
534
|
+
message,
|
|
535
|
+
conversationId,
|
|
536
|
+
sender: env.VOLUTE_SENDER ?? mindName,
|
|
537
|
+
images
|
|
538
|
+
})
|
|
435
539
|
});
|
|
436
540
|
if (!res.ok) {
|
|
437
541
|
const data = await res.json().catch(() => ({}));
|
|
@@ -467,18 +571,20 @@ async function listConversations4(env) {
|
|
|
467
571
|
} catch (err) {
|
|
468
572
|
console.error(`[volute] failed to fetch participants for ${conv.id}:`, err);
|
|
469
573
|
}
|
|
470
|
-
const isDM = participants.length === 2;
|
|
471
574
|
const slug = buildVoluteSlug({
|
|
472
575
|
participants,
|
|
473
576
|
mindUsername: mindName,
|
|
474
577
|
convTitle: conv.title,
|
|
475
|
-
conversationId: conv.id
|
|
578
|
+
conversationId: conv.id,
|
|
579
|
+
convType: conv.type,
|
|
580
|
+
convName: conv.name
|
|
476
581
|
});
|
|
582
|
+
const convType = conv.type === "channel" ? "channel" : participants.length === 2 ? "dm" : "group";
|
|
477
583
|
results.push({
|
|
478
584
|
id: slug,
|
|
479
585
|
platformId: conv.id,
|
|
480
|
-
name: conv.title ?? "(untitled)",
|
|
481
|
-
type:
|
|
586
|
+
name: conv.type === "channel" ? `#${conv.name}` : conv.title ?? "(untitled)",
|
|
587
|
+
type: convType,
|
|
482
588
|
participantCount: participants.length
|
|
483
589
|
});
|
|
484
590
|
}
|
|
@@ -8,6 +8,9 @@ function slugify(text) {
|
|
|
8
8
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
9
9
|
}
|
|
10
10
|
function buildVoluteSlug(opts) {
|
|
11
|
+
if (opts.convType === "channel" && opts.convName) {
|
|
12
|
+
return `volute:#${opts.convName}`;
|
|
13
|
+
}
|
|
11
14
|
const isDM = opts.participants.length === 2;
|
|
12
15
|
if (isDM) {
|
|
13
16
|
const other = opts.participants.find((p) => p.username !== opts.mindUsername);
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ 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-
|
|
12
|
+
const { default: pkg } = await import("./package-OKLFO7UY.js");
|
|
13
13
|
console.log(pkg.version);
|
|
14
14
|
process.exit(0);
|
|
15
15
|
}
|
|
@@ -18,10 +18,10 @@ switch (command) {
|
|
|
18
18
|
await import("./mind-OJN6RBZW.js").then((m) => m.run(args));
|
|
19
19
|
break;
|
|
20
20
|
case "send":
|
|
21
|
-
await import("./send-
|
|
21
|
+
await import("./send-BNDTLUPM.js").then((m) => m.run(args));
|
|
22
22
|
break;
|
|
23
23
|
case "history":
|
|
24
|
-
await import("./history-
|
|
24
|
+
await import("./history-YUEKTJ2N.js").then((m) => m.run(args));
|
|
25
25
|
break;
|
|
26
26
|
case "variant":
|
|
27
27
|
await import("./variant-X5QFG6KK.js").then((m) => m.run(args));
|
|
@@ -35,17 +35,20 @@ switch (command) {
|
|
|
35
35
|
case "schedule":
|
|
36
36
|
await import("./schedule-AGYLDMNS.js").then((m) => m.run(args));
|
|
37
37
|
break;
|
|
38
|
+
case "skill":
|
|
39
|
+
await import("./skill-2Y42P4JY.js").then((m) => m.run(args));
|
|
40
|
+
break;
|
|
38
41
|
case "env":
|
|
39
42
|
await import("./env-6IDWGBUH.js").then((m) => m.run(args));
|
|
40
43
|
break;
|
|
41
44
|
case "up":
|
|
42
|
-
await import("./up-
|
|
45
|
+
await import("./up-7B3BWF2U.js").then((m) => m.run(args));
|
|
43
46
|
break;
|
|
44
47
|
case "down":
|
|
45
48
|
await import("./down-A56B5JLK.js").then((m) => m.run(args));
|
|
46
49
|
break;
|
|
47
50
|
case "restart":
|
|
48
|
-
await import("./daemon-restart-
|
|
51
|
+
await import("./daemon-restart-2HVTHZAT.js").then((m) => m.run(args));
|
|
49
52
|
break;
|
|
50
53
|
case "setup":
|
|
51
54
|
await import("./setup-DJKIZKGW.js").then((m) => m.run(args));
|
|
@@ -114,6 +117,15 @@ Commands:
|
|
|
114
117
|
volute schedule add ... Add a cron schedule
|
|
115
118
|
volute schedule remove ... Remove a schedule
|
|
116
119
|
|
|
120
|
+
volute skill list List shared skills
|
|
121
|
+
volute skill list --mind <name> List installed skills for a mind
|
|
122
|
+
volute skill info <name> Show details of a shared skill
|
|
123
|
+
volute skill install <name> --mind Install a shared skill into a mind
|
|
124
|
+
volute skill update <name> --mind Update an installed skill
|
|
125
|
+
volute skill publish <name> --mind Publish a mind's skill to shared repo
|
|
126
|
+
volute skill remove <name> Remove a shared skill
|
|
127
|
+
volute skill uninstall <name> --mind Uninstall a skill from a mind
|
|
128
|
+
|
|
117
129
|
volute env <set|get|list|remove> Manage environment variables
|
|
118
130
|
|
|
119
131
|
volute up [--port N] Start the daemon (default: 4200)
|
|
@@ -143,7 +155,7 @@ Options:
|
|
|
143
155
|
--version, -v Show version number
|
|
144
156
|
--help, -h Show this help message
|
|
145
157
|
|
|
146
|
-
Mind-scoped commands (send, history, variant, connector, schedule, channel, pages)
|
|
158
|
+
Mind-scoped commands (send, history, variant, connector, schedule, channel, skill, pages)
|
|
147
159
|
use --mind <name> or VOLUTE_MIND env var to identify the mind.`);
|
|
148
160
|
break;
|
|
149
161
|
default:
|
package/dist/connectors/slack.js
CHANGED