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.
- package/dist/{agent-Z2B6EFEQ.js → agent-7JF7MT73.js} +13 -9
- package/dist/{agent-manager-PXBKA2GK.js → agent-manager-IMZ7ZMBF.js} +4 -4
- package/dist/channel-SMCNOIVQ.js +262 -0
- package/dist/{chunk-MW2KFO3B.js → chunk-62X577Y7.js} +10 -8
- package/dist/chunk-7ACDT3P2.js +265 -0
- package/dist/{chunk-MXUCNIBG.js → chunk-BX7KI4S3.js} +68 -3
- package/dist/{up-7ILD7GU7.js → chunk-EG45HBSJ.js} +16 -4
- package/dist/{chunk-HE67X4T6.js → chunk-H7AMDUIA.js} +1 -1
- package/dist/{chunk-7L4AN5D4.js → chunk-JR4UXCTO.js} +1 -1
- package/dist/{down-O7IFZLVJ.js → chunk-LLJNZPCU.js} +48 -13
- package/dist/{chunk-5X7HGB6L.js → chunk-NKXULRSW.js} +2 -1
- package/dist/{chunk-UX25Z2ND.js → chunk-UWHWAPGO.js} +7 -0
- package/dist/{chunk-UAVD2AHX.js → chunk-W76KWE23.js} +1 -1
- package/dist/chunk-ZZOOTYXK.js +583 -0
- package/dist/cli.js +22 -21
- package/dist/{connector-LYEMXQEV.js → connector-Y7JPNROO.js} +3 -3
- package/dist/connectors/discord.js +38 -7
- package/dist/connectors/slack.js +22 -3
- package/dist/connectors/telegram.js +34 -4
- package/dist/{create-RVCZN6HE.js → create-G525LWEA.js} +2 -2
- package/dist/{daemon-client-ZY6UUN2M.js → daemon-client-442IV43D.js} +2 -2
- package/dist/daemon-restart-4HVEKYFY.js +23 -0
- package/dist/daemon.js +1042 -809
- package/dist/{delete-3QH7VYIN.js → delete-UOU4AFQN.js} +7 -3
- package/dist/down-AZVH5TCD.js +11 -0
- package/dist/{env-4D4REPJF.js → env-7GLUJCWS.js} +2 -2
- package/dist/{history-OEONB53Z.js → history-H72ZUIBN.js} +2 -2
- package/dist/{import-MXJB2EII.js → import-AVKQJDYC.js} +2 -2
- package/dist/{logs-DF342W4M.js → logs-EDGK26AK.js} +1 -1
- package/dist/{message-ADHWFHSI.js → message-SCOQDR3P.js} +2 -2
- package/dist/{package-VQOE7JNH.js → package-T2WAVJOU.js} +1 -1
- package/dist/restart-O4ETYLJF.js +29 -0
- package/dist/{schedule-NAG6F463.js → schedule-S6QVC5ON.js} +2 -2
- package/dist/send-G7PE4DOJ.js +72 -0
- package/dist/{setup-RPRRGG2F.js → setup-F4TCWVSP.js} +2 -2
- package/dist/{start-TUOXDSFL.js → start-VHQ7LNWM.js} +2 -2
- package/dist/{status-A36EHRO4.js → status-QAJWXKMZ.js} +2 -2
- package/dist/{stop-AOJZLQ5X.js → stop-CAGCT5NI.js} +2 -2
- package/dist/up-RWZF6MLT.js +12 -0
- package/dist/{update-LPSIAWQ2.js → update-F7QWV2LB.js} +2 -2
- package/dist/{update-check-Y33QDCFL.js → update-check-B4J6IEQ4.js} +2 -2
- package/dist/{upgrade-FX2TKJ2S.js → upgrade-YXKPWDRU.js} +2 -2
- package/dist/{variant-LAB67OC2.js → variant-4Z6W3PP6.js} +2 -2
- package/dist/web-assets/assets/index-B1CqjUYD.js +308 -0
- package/dist/web-assets/index.html +1 -1
- package/package.json +1 -1
- package/templates/_base/.init/.config/scripts/session-reader.ts +59 -0
- package/templates/_base/_skills/sessions/SKILL.md +49 -0
- package/templates/_base/_skills/volute-agent/SKILL.md +13 -9
- package/templates/_base/src/lib/format-prefix.ts +6 -0
- package/templates/_base/src/lib/router.ts +30 -3
- package/templates/_base/src/lib/session-monitor.ts +400 -0
- package/templates/_base/src/lib/types.ts +2 -0
- package/templates/agent-sdk/src/agent.ts +16 -0
- package/templates/agent-sdk/src/lib/hooks/session-context.ts +32 -0
- package/templates/pi/src/agent.ts +7 -1
- package/templates/pi/src/lib/session-context-extension.ts +33 -0
- package/dist/channel-MK5OK2SI.js +0 -113
- package/dist/chunk-SMISE4SV.js +0 -226
- package/dist/conversation-ERXEQZTY.js +0 -163
- package/dist/send-66QMKRUH.js +0 -75
- package/dist/web-assets/assets/index-BbRmoxoA.js +0 -308
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/lib/slugify.ts
|
|
4
|
+
function slugify(text) {
|
|
5
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
6
|
+
}
|
|
7
|
+
|
|
3
8
|
// src/connectors/sdk.ts
|
|
4
|
-
import { existsSync, readFileSync } from "fs";
|
|
5
|
-
import { resolve } from "path";
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
10
|
+
import { join, resolve } from "path";
|
|
6
11
|
function loadEnv() {
|
|
7
12
|
const agentPort = process.env.VOLUTE_AGENT_PORT;
|
|
8
13
|
const agentName = process.env.VOLUTE_AGENT_NAME;
|
|
@@ -92,6 +97,15 @@ function onShutdown(cleanup) {
|
|
|
92
97
|
process.on("SIGINT", handler);
|
|
93
98
|
process.on("SIGTERM", handler);
|
|
94
99
|
}
|
|
100
|
+
function reportTyping(env, channel, sender, active) {
|
|
101
|
+
fetch(`${env.baseUrl}/typing`, {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: getHeaders(env),
|
|
104
|
+
body: JSON.stringify({ channel, sender, active })
|
|
105
|
+
}).catch((err) => {
|
|
106
|
+
console.warn(`[typing] failed to report for ${sender} on ${channel}: ${err}`);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
95
109
|
async function fireAndForget(env, payload) {
|
|
96
110
|
try {
|
|
97
111
|
const res = await fetch(`${env.baseUrl}/message`, {
|
|
@@ -157,12 +171,63 @@ async function handleAgentMessage(env, payload, handlers) {
|
|
|
157
171
|
await handlers.onError(errMsg);
|
|
158
172
|
}
|
|
159
173
|
}
|
|
174
|
+
function buildChannelSlug(platform, meta) {
|
|
175
|
+
if (meta.isDM) {
|
|
176
|
+
if (meta.recipients && meta.recipients.length > 0) {
|
|
177
|
+
const sorted = meta.recipients.map(slugify).sort();
|
|
178
|
+
return `${platform}:@${sorted.join(",")}`;
|
|
179
|
+
}
|
|
180
|
+
if (meta.senderName) {
|
|
181
|
+
return `${platform}:@${slugify(meta.senderName)}`;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (meta.channelName && meta.serverName) {
|
|
185
|
+
return `${platform}:${slugify(meta.serverName)}/${slugify(meta.channelName)}`;
|
|
186
|
+
}
|
|
187
|
+
if (meta.channelName) {
|
|
188
|
+
return `${platform}:${slugify(meta.channelName)}`;
|
|
189
|
+
}
|
|
190
|
+
if (meta.platformId) {
|
|
191
|
+
return `${platform}:${meta.platformId}`;
|
|
192
|
+
}
|
|
193
|
+
return `${platform}:unknown`;
|
|
194
|
+
}
|
|
195
|
+
function readChannelMap(agentDir) {
|
|
196
|
+
const filePath = join(agentDir, ".volute", "channels.json");
|
|
197
|
+
if (!existsSync(filePath)) return {};
|
|
198
|
+
try {
|
|
199
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
200
|
+
} catch {
|
|
201
|
+
return {};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function writeChannelEntry(agentDir, slug, entry) {
|
|
205
|
+
const voluteDir = join(agentDir, ".volute");
|
|
206
|
+
mkdirSync(voluteDir, { recursive: true });
|
|
207
|
+
const filePath = join(voluteDir, "channels.json");
|
|
208
|
+
const map = readChannelMap(agentDir);
|
|
209
|
+
map[slug] = entry;
|
|
210
|
+
writeFileSync(filePath, JSON.stringify(map, null, 2) + "\n");
|
|
211
|
+
}
|
|
212
|
+
function resolveChannelId(agentDir, slug) {
|
|
213
|
+
const map = readChannelMap(agentDir);
|
|
214
|
+
if (map[slug]) {
|
|
215
|
+
return map[slug].platformId;
|
|
216
|
+
}
|
|
217
|
+
const colonIndex = slug.indexOf(":");
|
|
218
|
+
return colonIndex >= 0 ? slug.slice(colonIndex + 1) : slug;
|
|
219
|
+
}
|
|
160
220
|
|
|
161
221
|
export {
|
|
222
|
+
slugify,
|
|
162
223
|
loadEnv,
|
|
163
224
|
loadFollowedChannels,
|
|
164
225
|
splitMessage,
|
|
165
226
|
onShutdown,
|
|
227
|
+
reportTyping,
|
|
166
228
|
fireAndForget,
|
|
167
|
-
handleAgentMessage
|
|
229
|
+
handleAgentMessage,
|
|
230
|
+
buildChannelSlug,
|
|
231
|
+
writeChannelEntry,
|
|
232
|
+
resolveChannelId
|
|
168
233
|
};
|
|
@@ -4,21 +4,31 @@ import {
|
|
|
4
4
|
} from "./chunk-D424ZQGI.js";
|
|
5
5
|
import {
|
|
6
6
|
voluteHome
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-K3NQKI34.js";
|
|
7
|
+
} from "./chunk-UWHWAPGO.js";
|
|
9
8
|
|
|
10
9
|
// src/commands/up.ts
|
|
11
10
|
import { spawn } from "child_process";
|
|
12
11
|
import { existsSync, mkdirSync, openSync, readFileSync } from "fs";
|
|
13
12
|
import { dirname, resolve } from "path";
|
|
13
|
+
function readGlobalConfig() {
|
|
14
|
+
const configPath = resolve(voluteHome(), "config.json");
|
|
15
|
+
if (!existsSync(configPath)) return {};
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error(`Invalid config file ${configPath}: ${err instanceof Error ? err.message : err}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
14
23
|
async function run(args) {
|
|
15
24
|
const { flags } = parseArgs(args, {
|
|
16
25
|
port: { type: "number" },
|
|
17
26
|
host: { type: "string" },
|
|
18
27
|
foreground: { type: "boolean" }
|
|
19
28
|
});
|
|
20
|
-
const
|
|
21
|
-
const
|
|
29
|
+
const config = readGlobalConfig();
|
|
30
|
+
const port = flags.port ?? config.port ?? 4200;
|
|
31
|
+
const hostname = flags.host ?? config.hostname ?? "127.0.0.1";
|
|
22
32
|
const home = voluteHome();
|
|
23
33
|
const pidPath = resolve(home, "daemon.pid");
|
|
24
34
|
if (existsSync(pidPath)) {
|
|
@@ -92,6 +102,8 @@ async function run(args) {
|
|
|
92
102
|
console.error(`Check logs: ${logFile}`);
|
|
93
103
|
process.exit(1);
|
|
94
104
|
}
|
|
105
|
+
|
|
95
106
|
export {
|
|
107
|
+
readGlobalConfig,
|
|
96
108
|
run
|
|
97
109
|
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
voluteHome
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-K3NQKI34.js";
|
|
4
|
+
} from "./chunk-UWHWAPGO.js";
|
|
6
5
|
|
|
7
6
|
// src/commands/down.ts
|
|
8
7
|
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
9
8
|
import { resolve } from "path";
|
|
10
|
-
async function
|
|
9
|
+
async function stopDaemon() {
|
|
11
10
|
const home = voluteHome();
|
|
12
11
|
const pidPath = resolve(home, "daemon.pid");
|
|
13
12
|
if (!existsSync(pidPath)) {
|
|
@@ -28,16 +27,21 @@ async function run(_args) {
|
|
|
28
27
|
url.port = String(port);
|
|
29
28
|
const res = await fetch(`${url.origin}/api/health`);
|
|
30
29
|
if (res.ok) {
|
|
31
|
-
|
|
32
|
-
console.error(`Kill the process manually: lsof -ti :${port} | xargs kill`);
|
|
33
|
-
process.exit(1);
|
|
30
|
+
return { stopped: false, reason: "orphan", port };
|
|
34
31
|
}
|
|
35
32
|
} catch {
|
|
36
33
|
}
|
|
37
|
-
|
|
38
|
-
process.exit(1);
|
|
34
|
+
return { stopped: false, reason: "not-running" };
|
|
39
35
|
}
|
|
40
36
|
const pid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
|
|
37
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
38
|
+
console.error(`Stale or corrupt PID file (${pidPath}), removing.`);
|
|
39
|
+
try {
|
|
40
|
+
unlinkSync(pidPath);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
return { stopped: false, reason: "not-running" };
|
|
44
|
+
}
|
|
41
45
|
try {
|
|
42
46
|
process.kill(pid, 0);
|
|
43
47
|
} catch {
|
|
@@ -46,21 +50,28 @@ async function run(_args) {
|
|
|
46
50
|
} catch {
|
|
47
51
|
}
|
|
48
52
|
console.log("Daemon was not running (cleaned up stale PID file).");
|
|
49
|
-
return;
|
|
53
|
+
return { stopped: false, reason: "not-running" };
|
|
50
54
|
}
|
|
51
55
|
try {
|
|
52
56
|
process.kill(-pid, "SIGTERM");
|
|
53
57
|
console.log(`Sent SIGTERM to daemon group (pid ${pid})`);
|
|
54
58
|
} catch {
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
try {
|
|
60
|
+
process.kill(pid, "SIGTERM");
|
|
61
|
+
console.log(`Sent SIGTERM to daemon (pid ${pid})`);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.error(
|
|
64
|
+
`Failed to send SIGTERM to daemon (pid ${pid}): ${e instanceof Error ? e.message : e}`
|
|
65
|
+
);
|
|
66
|
+
return { stopped: false, reason: "kill-failed", port: pid };
|
|
67
|
+
}
|
|
57
68
|
}
|
|
58
69
|
const maxWait = 1e4;
|
|
59
70
|
const start = Date.now();
|
|
60
71
|
while (Date.now() - start < maxWait) {
|
|
61
72
|
if (!existsSync(pidPath)) {
|
|
62
73
|
console.log("Daemon stopped.");
|
|
63
|
-
return;
|
|
74
|
+
return { stopped: true, clean: true };
|
|
64
75
|
}
|
|
65
76
|
await new Promise((r) => setTimeout(r, 200));
|
|
66
77
|
}
|
|
@@ -69,11 +80,35 @@ async function run(_args) {
|
|
|
69
80
|
} catch {
|
|
70
81
|
try {
|
|
71
82
|
process.kill(pid, "SIGKILL");
|
|
72
|
-
} catch {
|
|
83
|
+
} catch (e) {
|
|
84
|
+
console.error(
|
|
85
|
+
`Failed to force-kill daemon (pid ${pid}): ${e instanceof Error ? e.message : e}`
|
|
86
|
+
);
|
|
87
|
+
console.error(`The daemon may still be running. Kill it manually: kill -9 ${pid}`);
|
|
88
|
+
return { stopped: false, reason: "kill-failed" };
|
|
73
89
|
}
|
|
74
90
|
}
|
|
91
|
+
try {
|
|
92
|
+
unlinkSync(pidPath);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
75
96
|
console.error("Daemon did not exit cleanly, sent SIGKILL.");
|
|
97
|
+
return { stopped: true, clean: false };
|
|
76
98
|
}
|
|
99
|
+
async function run(_args) {
|
|
100
|
+
const result = await stopDaemon();
|
|
101
|
+
if (result.stopped) return;
|
|
102
|
+
if (result.reason === "orphan") {
|
|
103
|
+
console.error(`Daemon appears to be running on port ${result.port} but PID file is missing.`);
|
|
104
|
+
console.error(`Kill the process manually: lsof -ti :${result.port} | xargs kill`);
|
|
105
|
+
} else if (result.reason === "not-running") {
|
|
106
|
+
console.error("Daemon is not running (no PID file found).");
|
|
107
|
+
}
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
77
111
|
export {
|
|
112
|
+
stopDaemon,
|
|
78
113
|
run
|
|
79
114
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
voluteHome
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UWHWAPGO.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/update-check.ts
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -28,6 +28,7 @@ function writeCache(latest) {
|
|
|
28
28
|
function getCurrentVersion() {
|
|
29
29
|
const thisDir = new URL(".", import.meta.url).pathname;
|
|
30
30
|
const candidates = [
|
|
31
|
+
resolve(thisDir, "../package.json"),
|
|
31
32
|
resolve(thisDir, "../../package.json"),
|
|
32
33
|
resolve(thisDir, "../../../package.json")
|
|
33
34
|
];
|
|
@@ -90,6 +90,12 @@ function nextPort() {
|
|
|
90
90
|
if (port > 65535) throw new Error("No available ports \u2014 all ports 4100-65535 are allocated");
|
|
91
91
|
return port;
|
|
92
92
|
}
|
|
93
|
+
function daemonLoopback() {
|
|
94
|
+
const host = process.env.VOLUTE_DAEMON_HOSTNAME || "127.0.0.1";
|
|
95
|
+
if (host === "0.0.0.0") return "127.0.0.1";
|
|
96
|
+
if (host === "::") return "[::1]";
|
|
97
|
+
return host;
|
|
98
|
+
}
|
|
93
99
|
function resolveAgent(name) {
|
|
94
100
|
const [baseName, variantName] = name.split("@", 2);
|
|
95
101
|
const entry = findAgent(baseName);
|
|
@@ -227,5 +233,6 @@ export {
|
|
|
227
233
|
findAgent,
|
|
228
234
|
agentDir,
|
|
229
235
|
nextPort,
|
|
236
|
+
daemonLoopback,
|
|
230
237
|
resolveAgent
|
|
231
238
|
};
|