volute 0.7.0 → 0.8.1
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/README.md +16 -14
- package/dist/{agent-7JF7MT73.js → agent-YORVRB6I.js} +10 -10
- package/dist/{agent-manager-IMZ7ZMBF.js → agent-manager-CMMH5KQQ.js} +4 -4
- package/dist/{channel-SMCNOIVQ.js → channel-RDGHBFSI.js} +16 -56
- package/dist/{chunk-JR4UXCTO.js → chunk-23L3MKEV.js} +1 -1
- package/dist/{chunk-5SKQ6J7T.js → chunk-5C5JWR2L.js} +15 -7
- package/dist/{chunk-UWHWAPGO.js → chunk-DP2DX4WV.js} +9 -1
- package/dist/{chunk-7ACDT3P2.js → chunk-ECPQXRLB.js} +1 -2
- package/dist/{chunk-LLJNZPCU.js → chunk-HZ5LTOEJ.js} +1 -1
- package/dist/{chunk-W76KWE23.js → chunk-IQXBMFZG.js} +6 -4
- package/dist/{chunk-ZZOOTYXK.js → chunk-LIPPXNIE.js} +60 -74
- package/dist/{chunk-BX7KI4S3.js → chunk-N6MLQ26B.js} +23 -96
- package/dist/{chunk-H7AMDUIA.js → chunk-QF22MYDJ.js} +6 -5
- package/dist/{chunk-NKXULRSW.js → chunk-RT6Y7AR3.js} +1 -1
- package/dist/{chunk-62X577Y7.js → chunk-W6TMWYU3.js} +126 -73
- package/dist/{chunk-EG45HBSJ.js → chunk-XSJ27WEM.js} +1 -1
- package/dist/cli.js +22 -20
- package/dist/{connector-Y7JPNROO.js → connector-ZP6MEFF4.js} +3 -3
- package/dist/connectors/discord.js +18 -59
- package/dist/connectors/slack.js +21 -38
- package/dist/connectors/telegram.js +31 -49
- package/dist/{create-G525LWEA.js → create-HGJHLABX.js} +22 -17
- package/dist/{daemon-client-442IV43D.js → daemon-client-54J3EIZD.js} +2 -2
- package/dist/{daemon-restart-4HVEKYFY.js → daemon-restart-CPBLMMRI.js} +3 -3
- package/dist/daemon.js +342 -402
- package/dist/{delete-UOU4AFQN.js → delete-45TGQC4N.js} +10 -5
- package/dist/{down-AZVH5TCD.js → down-O4EWZTVA.js} +2 -2
- package/dist/{env-7GLUJCWS.js → env-KMNYGVZ2.js} +7 -9
- package/dist/{history-H72ZUIBN.js → history-PXJVYLVY.js} +2 -2
- package/dist/{import-AVKQJDYC.js → import-CNEDF3TD.js} +6 -6
- package/dist/{logs-EDGK26AK.js → logs-TZB3MTLZ.js} +5 -4
- package/dist/{package-T2WAVJOU.js → package-RJSONENE.js} +1 -1
- package/dist/{restart-O4ETYLJF.js → restart-KVH3TK5N.js} +2 -2
- package/dist/{schedule-S6QVC5ON.js → schedule-HCUCBNQI.js} +2 -2
- package/dist/send-BNC2S5BY.js +162 -0
- package/dist/{service-HZNIDNJF.js → service-XCADRKIS.js} +8 -1
- package/dist/{setup-F4TCWVSP.js → setup-32KH5KLN.js} +85 -26
- package/dist/{start-VHQ7LNWM.js → start-QU73YTJW.js} +2 -2
- package/dist/{status-QAJWXKMZ.js → status-Q6ZQJXNI.js} +2 -2
- package/dist/{stop-CAGCT5NI.js → stop-N7U5N6A7.js} +2 -2
- package/dist/{up-RWZF6MLT.js → up-V6EAA7OZ.js} +2 -2
- package/dist/{update-F7QWV2LB.js → update-EUCZ7XGG.js} +3 -3
- package/dist/{update-check-B4J6IEQ4.js → update-check-SM4244SU.js} +2 -2
- package/dist/{upgrade-YXKPWDRU.js → upgrade-CZF6PN7Y.js} +4 -4
- package/dist/{variant-4Z6W3PP6.js → variant-RKXPN5DH.js} +20 -46
- package/dist/web-assets/assets/index-D-3zx6vs.js +307 -0
- package/dist/web-assets/index.html +1 -1
- package/drizzle/0004_magical_silverclaw.sql +1 -0
- package/drizzle/meta/0004_snapshot.json +410 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/templates/_base/_skills/volute-agent/SKILL.md +32 -16
- package/templates/_base/home/.config/routes.json +4 -8
- package/templates/_base/home/VOLUTE.md +16 -14
- package/templates/_base/src/lib/auto-reply.ts +38 -0
- package/templates/_base/src/lib/daemon-client.ts +53 -0
- package/templates/_base/src/lib/router.ts +66 -14
- package/templates/_base/src/lib/routing.ts +48 -9
- package/templates/_base/src/lib/startup.ts +1 -25
- package/templates/_base/src/lib/types.ts +2 -1
- package/templates/_base/src/lib/volute-server.ts +29 -14
- package/templates/agent-sdk/src/agent.ts +53 -111
- package/templates/agent-sdk/src/lib/content.ts +41 -0
- package/templates/agent-sdk/src/lib/session-store.ts +43 -0
- package/templates/agent-sdk/src/lib/stream-consumer.ts +66 -0
- package/templates/agent-sdk/src/server.ts +5 -13
- package/templates/pi/.init/AGENTS.md +5 -5
- package/templates/pi/src/agent.ts +32 -84
- package/templates/pi/src/lib/content.ts +15 -0
- package/templates/pi/src/lib/event-handler.ts +74 -0
- package/templates/pi/src/lib/resolve-model.ts +21 -0
- package/templates/pi/src/server.ts +3 -7
- package/dist/chunk-B3R6L2GW.js +0 -24
- package/dist/chunk-ZYGKG6VC.js +0 -22
- package/dist/message-SCOQDR3P.js +0 -32
- package/dist/send-G7PE4DOJ.js +0 -72
- package/dist/web-assets/assets/index-B1CqjUYD.js +0 -308
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
resolveChannelId,
|
|
4
4
|
slugify,
|
|
5
|
+
splitMessage,
|
|
5
6
|
writeChannelEntry
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-N6MLQ26B.js";
|
|
7
8
|
import {
|
|
8
9
|
voluteHome
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DP2DX4WV.js";
|
|
10
11
|
import {
|
|
11
12
|
__export
|
|
12
13
|
} from "./chunk-K3NQKI34.js";
|
|
@@ -20,6 +21,7 @@ __export(discord_exports, {
|
|
|
20
21
|
read: () => read,
|
|
21
22
|
send: () => send
|
|
22
23
|
});
|
|
24
|
+
var DISCORD_MAX_LENGTH = 2e3;
|
|
23
25
|
var API_BASE = "https://discord.com/api/v10";
|
|
24
26
|
function requireToken(env) {
|
|
25
27
|
const token = env.DISCORD_TOKEN;
|
|
@@ -50,16 +52,20 @@ async function read(env, channelSlug, limit) {
|
|
|
50
52
|
async function send(env, channelSlug, message) {
|
|
51
53
|
const token = requireToken(env);
|
|
52
54
|
const channelId = resolveChannelId2(env, channelSlug);
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
const chunks = splitMessage(message, DISCORD_MAX_LENGTH);
|
|
56
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
57
|
+
const res = await fetch(`${API_BASE}/channels/${channelId}/messages`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: {
|
|
60
|
+
Authorization: `Bot ${token}`,
|
|
61
|
+
"Content-Type": "application/json"
|
|
62
|
+
},
|
|
63
|
+
body: JSON.stringify({ content: chunks[i] })
|
|
64
|
+
});
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
const partial = i > 0 ? ` (${i}/${chunks.length} chunks were already sent)` : "";
|
|
67
|
+
throw new Error(`Discord API error: ${res.status} ${res.statusText}${partial}`);
|
|
68
|
+
}
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
async function listConversations(env) {
|
|
@@ -134,9 +140,9 @@ async function createConversation(env, participants, _name) {
|
|
|
134
140
|
}
|
|
135
141
|
const dm = await res.json();
|
|
136
142
|
const slug = `discord:@${slugify(participants[0])}`;
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
139
|
-
writeChannelEntry(
|
|
143
|
+
const agentName = env.VOLUTE_AGENT;
|
|
144
|
+
if (agentName) {
|
|
145
|
+
writeChannelEntry(agentName, slug, {
|
|
140
146
|
platformId: dm.id,
|
|
141
147
|
platform: "discord",
|
|
142
148
|
name: participants[0],
|
|
@@ -155,6 +161,7 @@ __export(slack_exports, {
|
|
|
155
161
|
read: () => read2,
|
|
156
162
|
send: () => send2
|
|
157
163
|
});
|
|
164
|
+
var SLACK_MAX_LENGTH = 4e3;
|
|
158
165
|
var API_BASE2 = "https://slack.com/api";
|
|
159
166
|
function requireToken2(env) {
|
|
160
167
|
const token = env.SLACK_BOT_TOKEN;
|
|
@@ -191,10 +198,18 @@ async function read2(env, channelSlug, limit) {
|
|
|
191
198
|
async function send2(env, channelSlug, message) {
|
|
192
199
|
const token = requireToken2(env);
|
|
193
200
|
const channelId = resolveChannelId2(env, channelSlug);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
const chunks = splitMessage(message, SLACK_MAX_LENGTH);
|
|
202
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
203
|
+
try {
|
|
204
|
+
await slackApi(token, "chat.postMessage", {
|
|
205
|
+
channel: channelId,
|
|
206
|
+
text: chunks[i]
|
|
207
|
+
});
|
|
208
|
+
} catch (err) {
|
|
209
|
+
const partial = i > 0 ? ` (${i}/${chunks.length} chunks were already sent)` : "";
|
|
210
|
+
throw new Error(`${err instanceof Error ? err.message : err}${partial}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
198
213
|
}
|
|
199
214
|
async function listConversations2(env) {
|
|
200
215
|
const token = requireToken2(env);
|
|
@@ -256,7 +271,7 @@ async function createConversation2(env, participants, name) {
|
|
|
256
271
|
if (!user) throw new Error(`User not found: ${p}`);
|
|
257
272
|
ids.push(user.id);
|
|
258
273
|
}
|
|
259
|
-
const
|
|
274
|
+
const agentName = env.VOLUTE_AGENT;
|
|
260
275
|
if (name) {
|
|
261
276
|
const createData = await slackApi(token, "conversations.create", {
|
|
262
277
|
name,
|
|
@@ -272,8 +287,8 @@ async function createConversation2(env, participants, name) {
|
|
|
272
287
|
const authData = await slackApi(token, "auth.test", {});
|
|
273
288
|
const teamName = authData.team ?? "workspace";
|
|
274
289
|
const slug2 = `slack:${slugify(teamName)}/${slugify(name)}`;
|
|
275
|
-
if (
|
|
276
|
-
writeChannelEntry(
|
|
290
|
+
if (agentName) {
|
|
291
|
+
writeChannelEntry(agentName, slug2, {
|
|
277
292
|
platformId: channelId,
|
|
278
293
|
platform: "slack",
|
|
279
294
|
name,
|
|
@@ -287,8 +302,8 @@ async function createConversation2(env, participants, name) {
|
|
|
287
302
|
});
|
|
288
303
|
const platformId = openData.channel.id;
|
|
289
304
|
const slug = participants.length === 1 ? `slack:@${slugify(participants[0])}` : `slack:@${participants.map(slugify).sort().join(",")}`;
|
|
290
|
-
if (
|
|
291
|
-
writeChannelEntry(
|
|
305
|
+
if (agentName) {
|
|
306
|
+
writeChannelEntry(agentName, slug, {
|
|
292
307
|
platformId,
|
|
293
308
|
platform: "slack",
|
|
294
309
|
name: participants.join(", "),
|
|
@@ -307,6 +322,7 @@ __export(telegram_exports, {
|
|
|
307
322
|
read: () => read3,
|
|
308
323
|
send: () => send3
|
|
309
324
|
});
|
|
325
|
+
var TELEGRAM_MAX_LENGTH = 4096;
|
|
310
326
|
var API_BASE3 = "https://api.telegram.org";
|
|
311
327
|
function requireToken3(env) {
|
|
312
328
|
const token = env.TELEGRAM_BOT_TOKEN;
|
|
@@ -315,20 +331,24 @@ function requireToken3(env) {
|
|
|
315
331
|
}
|
|
316
332
|
async function read3(_env, _channelSlug, _limit) {
|
|
317
333
|
throw new Error(
|
|
318
|
-
"Telegram Bot API does not support reading chat history. Use volute
|
|
334
|
+
"Telegram Bot API does not support reading chat history. Use volute send instead."
|
|
319
335
|
);
|
|
320
336
|
}
|
|
321
337
|
async function send3(env, channelSlug, message) {
|
|
322
338
|
const token = requireToken3(env);
|
|
323
339
|
const chatId = resolveChannelId2(env, channelSlug);
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
340
|
+
const chunks = splitMessage(message, TELEGRAM_MAX_LENGTH);
|
|
341
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
342
|
+
const res = await fetch(`${API_BASE3}/bot${token}/sendMessage`, {
|
|
343
|
+
method: "POST",
|
|
344
|
+
headers: { "Content-Type": "application/json" },
|
|
345
|
+
body: JSON.stringify({ chat_id: chatId, text: chunks[i] })
|
|
346
|
+
});
|
|
347
|
+
if (!res.ok) {
|
|
348
|
+
const body = await res.text().catch(() => "");
|
|
349
|
+
const partial = i > 0 ? ` (${i}/${chunks.length} chunks were already sent)` : "";
|
|
350
|
+
throw new Error(`Telegram API error: ${res.status} ${body}${partial}`);
|
|
351
|
+
}
|
|
332
352
|
}
|
|
333
353
|
}
|
|
334
354
|
async function listConversations3() {
|
|
@@ -354,8 +374,7 @@ __export(volute_exports, {
|
|
|
354
374
|
listConversations: () => listConversations4,
|
|
355
375
|
listUsers: () => listUsers4,
|
|
356
376
|
read: () => read4,
|
|
357
|
-
send: () => send4
|
|
358
|
-
sendAndStream: () => sendAndStream
|
|
377
|
+
send: () => send4
|
|
359
378
|
});
|
|
360
379
|
import { existsSync, readFileSync } from "fs";
|
|
361
380
|
import { resolve } from "path";
|
|
@@ -398,7 +417,7 @@ async function read4(env, channelSlug, limit) {
|
|
|
398
417
|
return `${m.sender_name ?? m.role}: ${text}`;
|
|
399
418
|
}).join("\n");
|
|
400
419
|
}
|
|
401
|
-
async function
|
|
420
|
+
async function send4(env, channelSlug, message) {
|
|
402
421
|
const agentName = env.VOLUTE_AGENT;
|
|
403
422
|
if (!agentName) throw new Error("VOLUTE_AGENT not set");
|
|
404
423
|
const conversationId = resolveChannelId2(env, channelSlug);
|
|
@@ -417,38 +436,6 @@ async function* sendAndStream(env, channelSlug, message) {
|
|
|
417
436
|
const data = await res.json().catch(() => ({}));
|
|
418
437
|
throw new Error(data.error ?? `Failed to send: ${res.status}`);
|
|
419
438
|
}
|
|
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
439
|
}
|
|
453
440
|
async function listConversations4(env) {
|
|
454
441
|
const agentName = env.VOLUTE_AGENT;
|
|
@@ -524,9 +511,8 @@ async function createConversation4(env, participants, name) {
|
|
|
524
511
|
}
|
|
525
512
|
const conv = await res.json();
|
|
526
513
|
const slug = name ? `volute:${slugify(name)}` : `volute:${conv.id}`;
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
writeChannelEntry(agentDir, slug, {
|
|
514
|
+
if (agentName) {
|
|
515
|
+
writeChannelEntry(agentName, slug, {
|
|
530
516
|
platformId: conv.id,
|
|
531
517
|
platform: "volute",
|
|
532
518
|
name: name ?? participants.join(", "),
|
|
@@ -569,12 +555,12 @@ function getChannelDriver(platform) {
|
|
|
569
555
|
return CHANNELS[platform]?.driver ?? null;
|
|
570
556
|
}
|
|
571
557
|
function resolveChannelId2(env, slug) {
|
|
572
|
-
const
|
|
573
|
-
if (!
|
|
558
|
+
const agentName = env.VOLUTE_AGENT;
|
|
559
|
+
if (!agentName) {
|
|
574
560
|
const colonIdx = slug.indexOf(":");
|
|
575
561
|
return colonIdx !== -1 ? slug.slice(colonIdx + 1) : slug;
|
|
576
562
|
}
|
|
577
|
-
return resolveChannelId(
|
|
563
|
+
return resolveChannelId(agentName, slug);
|
|
578
564
|
}
|
|
579
565
|
|
|
580
566
|
export {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
stateDir
|
|
4
|
+
} from "./chunk-DP2DX4WV.js";
|
|
2
5
|
|
|
3
6
|
// src/lib/slugify.ts
|
|
4
7
|
function slugify(text) {
|
|
@@ -45,37 +48,6 @@ function splitMessage(text, maxLength) {
|
|
|
45
48
|
if (text) chunks.push(text);
|
|
46
49
|
return chunks;
|
|
47
50
|
}
|
|
48
|
-
async function* readNdjson(body) {
|
|
49
|
-
const reader = body.getReader();
|
|
50
|
-
const decoder = new TextDecoder();
|
|
51
|
-
let buffer = "";
|
|
52
|
-
try {
|
|
53
|
-
while (true) {
|
|
54
|
-
const { done, value } = await reader.read();
|
|
55
|
-
if (done) break;
|
|
56
|
-
buffer += decoder.decode(value, { stream: true });
|
|
57
|
-
const lines = buffer.split("\n");
|
|
58
|
-
buffer = lines.pop() || "";
|
|
59
|
-
for (const line of lines) {
|
|
60
|
-
if (!line.trim()) continue;
|
|
61
|
-
try {
|
|
62
|
-
yield JSON.parse(line);
|
|
63
|
-
} catch {
|
|
64
|
-
console.warn(`ndjson: skipping invalid line: ${line.slice(0, 100)}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (buffer.trim()) {
|
|
69
|
-
try {
|
|
70
|
-
yield JSON.parse(buffer);
|
|
71
|
-
} catch {
|
|
72
|
-
console.warn(`ndjson: skipping invalid line: ${buffer.slice(0, 100)}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
} finally {
|
|
76
|
-
reader.releaseLock();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
51
|
function getHeaders(env) {
|
|
80
52
|
const headers = { "Content-Type": "application/json" };
|
|
81
53
|
if (env.daemonUrl && env.daemonToken) {
|
|
@@ -106,28 +78,10 @@ function reportTyping(env, channel, sender, active) {
|
|
|
106
78
|
console.warn(`[typing] failed to report for ${sender} on ${channel}: ${err}`);
|
|
107
79
|
});
|
|
108
80
|
}
|
|
109
|
-
async function
|
|
110
|
-
|
|
111
|
-
const res = await fetch(`${env.baseUrl}/message`, {
|
|
112
|
-
method: "POST",
|
|
113
|
-
headers: getHeaders(env),
|
|
114
|
-
body: JSON.stringify(payload)
|
|
115
|
-
});
|
|
116
|
-
if (!res.ok) {
|
|
117
|
-
console.error(`fireAndForget: agent returned ${res.status}`);
|
|
118
|
-
}
|
|
119
|
-
if (res.body) {
|
|
120
|
-
const reader = res.body.getReader();
|
|
121
|
-
while (!(await reader.read()).done) {
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
} catch (err) {
|
|
125
|
-
console.error(`Failed to forward message: ${err}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async function handleAgentMessage(env, payload, handlers) {
|
|
81
|
+
async function sendToAgent(env, payload) {
|
|
82
|
+
const url = `${env.baseUrl}/message`;
|
|
129
83
|
try {
|
|
130
|
-
const res = await fetch(
|
|
84
|
+
const res = await fetch(url, {
|
|
131
85
|
method: "POST",
|
|
132
86
|
headers: getHeaders(env),
|
|
133
87
|
body: JSON.stringify(payload)
|
|
@@ -135,40 +89,13 @@ async function handleAgentMessage(env, payload, handlers) {
|
|
|
135
89
|
if (!res.ok) {
|
|
136
90
|
const body = await res.text().catch(() => "");
|
|
137
91
|
console.error(`Agent returned ${res.status}: ${body}`);
|
|
138
|
-
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if (!res.body) {
|
|
142
|
-
await handlers.onError("Error: no response from agent");
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
let accumulated = "";
|
|
146
|
-
const pendingImages = [];
|
|
147
|
-
for await (const event of readNdjson(res.body)) {
|
|
148
|
-
if (event.type === "text") {
|
|
149
|
-
accumulated += event.content;
|
|
150
|
-
} else if (event.type === "image") {
|
|
151
|
-
pendingImages.push({ data: event.data, media_type: event.media_type });
|
|
152
|
-
} else if (event.type === "tool_use") {
|
|
153
|
-
const text2 = accumulated.trim();
|
|
154
|
-
accumulated = "";
|
|
155
|
-
const images2 = pendingImages.splice(0);
|
|
156
|
-
if (text2 || images2.length > 0) {
|
|
157
|
-
await handlers.onFlush(text2, images2);
|
|
158
|
-
}
|
|
159
|
-
} else if (event.type === "done") {
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
const text = accumulated.trim();
|
|
164
|
-
const images = pendingImages.splice(0);
|
|
165
|
-
if (text || images.length > 0) {
|
|
166
|
-
await handlers.onFlush(text, images);
|
|
92
|
+
return { ok: false, error: `Agent returned ${res.status}` };
|
|
167
93
|
}
|
|
94
|
+
return { ok: true };
|
|
168
95
|
} catch (err) {
|
|
169
|
-
console.error(`Failed to
|
|
170
|
-
const
|
|
171
|
-
|
|
96
|
+
console.error(`Failed to forward message: ${err}`);
|
|
97
|
+
const isConnRefused = err instanceof TypeError && err.cause?.code === "ECONNREFUSED";
|
|
98
|
+
return { ok: false, error: isConnRefused ? "Agent is not running" : "Failed to reach agent" };
|
|
172
99
|
}
|
|
173
100
|
}
|
|
174
101
|
function buildChannelSlug(platform, meta) {
|
|
@@ -192,25 +119,26 @@ function buildChannelSlug(platform, meta) {
|
|
|
192
119
|
}
|
|
193
120
|
return `${platform}:unknown`;
|
|
194
121
|
}
|
|
195
|
-
function readChannelMap(
|
|
196
|
-
const filePath = join(
|
|
122
|
+
function readChannelMap(agentName) {
|
|
123
|
+
const filePath = join(stateDir(agentName), "channels.json");
|
|
197
124
|
if (!existsSync(filePath)) return {};
|
|
198
125
|
try {
|
|
199
126
|
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
200
|
-
} catch {
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error(`[sdk] failed to parse ${filePath}:`, err);
|
|
201
129
|
return {};
|
|
202
130
|
}
|
|
203
131
|
}
|
|
204
|
-
function writeChannelEntry(
|
|
205
|
-
const
|
|
206
|
-
mkdirSync(
|
|
207
|
-
const filePath = join(
|
|
208
|
-
const map = readChannelMap(
|
|
132
|
+
function writeChannelEntry(agentName, slug, entry) {
|
|
133
|
+
const dir = stateDir(agentName);
|
|
134
|
+
mkdirSync(dir, { recursive: true });
|
|
135
|
+
const filePath = join(dir, "channels.json");
|
|
136
|
+
const map = readChannelMap(agentName);
|
|
209
137
|
map[slug] = entry;
|
|
210
138
|
writeFileSync(filePath, JSON.stringify(map, null, 2) + "\n");
|
|
211
139
|
}
|
|
212
|
-
function resolveChannelId(
|
|
213
|
-
const map = readChannelMap(
|
|
140
|
+
function resolveChannelId(agentName, slug) {
|
|
141
|
+
const map = readChannelMap(agentName);
|
|
214
142
|
if (map[slug]) {
|
|
215
143
|
return map[slug].platformId;
|
|
216
144
|
}
|
|
@@ -225,8 +153,7 @@ export {
|
|
|
225
153
|
splitMessage,
|
|
226
154
|
onShutdown,
|
|
227
155
|
reportTyping,
|
|
228
|
-
|
|
229
|
-
handleAgentMessage,
|
|
156
|
+
sendToAgent,
|
|
230
157
|
buildChannelSlug,
|
|
231
158
|
writeChannelEntry,
|
|
232
159
|
resolveChannelId
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
stateDir,
|
|
3
4
|
voluteHome
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DP2DX4WV.js";
|
|
5
6
|
|
|
6
7
|
// src/lib/env.ts
|
|
7
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -9,8 +10,8 @@ import { dirname, resolve } from "path";
|
|
|
9
10
|
function sharedEnvPath() {
|
|
10
11
|
return resolve(voluteHome(), "env.json");
|
|
11
12
|
}
|
|
12
|
-
function agentEnvPath(
|
|
13
|
-
return resolve(
|
|
13
|
+
function agentEnvPath(agentName) {
|
|
14
|
+
return resolve(stateDir(agentName), "env.json");
|
|
14
15
|
}
|
|
15
16
|
function readEnv(path) {
|
|
16
17
|
if (!existsSync(path)) return {};
|
|
@@ -25,9 +26,9 @@ function writeEnv(path, env) {
|
|
|
25
26
|
writeFileSync(path, `${JSON.stringify(env, null, 2)}
|
|
26
27
|
`, { mode: 384 });
|
|
27
28
|
}
|
|
28
|
-
function loadMergedEnv(
|
|
29
|
+
function loadMergedEnv(agentName) {
|
|
29
30
|
const shared = readEnv(sharedEnvPath());
|
|
30
|
-
const agent = readEnv(agentEnvPath(
|
|
31
|
+
const agent = readEnv(agentEnvPath(agentName));
|
|
31
32
|
return { ...shared, ...agent };
|
|
32
33
|
}
|
|
33
34
|
|