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
|
@@ -6,39 +6,42 @@ async function run(args) {
|
|
|
6
6
|
const subcommand = args[0];
|
|
7
7
|
switch (subcommand) {
|
|
8
8
|
case "create":
|
|
9
|
-
await import("./create-
|
|
9
|
+
await import("./create-G525LWEA.js").then((m) => m.run(args.slice(1)));
|
|
10
10
|
break;
|
|
11
11
|
case "start":
|
|
12
|
-
await import("./start-
|
|
12
|
+
await import("./start-VHQ7LNWM.js").then((m) => m.run(args.slice(1)));
|
|
13
13
|
break;
|
|
14
14
|
case "stop":
|
|
15
|
-
await import("./stop-
|
|
15
|
+
await import("./stop-CAGCT5NI.js").then((m) => m.run(args.slice(1)));
|
|
16
|
+
break;
|
|
17
|
+
case "restart":
|
|
18
|
+
await import("./restart-O4ETYLJF.js").then((m) => m.run(args.slice(1)));
|
|
16
19
|
break;
|
|
17
20
|
case "delete":
|
|
18
|
-
await import("./delete-
|
|
21
|
+
await import("./delete-UOU4AFQN.js").then((m) => m.run(args.slice(1)));
|
|
19
22
|
break;
|
|
20
23
|
case "list":
|
|
21
|
-
await import("./status-
|
|
24
|
+
await import("./status-QAJWXKMZ.js").then((m) => m.run(args.slice(1)));
|
|
22
25
|
break;
|
|
23
26
|
case "status": {
|
|
24
27
|
const rest = args.slice(1);
|
|
25
28
|
if (!rest[0] && process.env.VOLUTE_AGENT) {
|
|
26
29
|
rest.unshift(process.env.VOLUTE_AGENT);
|
|
27
30
|
}
|
|
28
|
-
await import("./status-
|
|
31
|
+
await import("./status-QAJWXKMZ.js").then((m) => m.run(rest));
|
|
29
32
|
break;
|
|
30
33
|
}
|
|
31
34
|
case "logs": {
|
|
32
35
|
const rest = args.slice(1);
|
|
33
36
|
const logsArgs = transformAgentFlag(rest);
|
|
34
|
-
await import("./logs-
|
|
37
|
+
await import("./logs-EDGK26AK.js").then((m) => m.run(logsArgs));
|
|
35
38
|
break;
|
|
36
39
|
}
|
|
37
40
|
case "upgrade":
|
|
38
|
-
await import("./upgrade-
|
|
41
|
+
await import("./upgrade-YXKPWDRU.js").then((m) => m.run(args.slice(1)));
|
|
39
42
|
break;
|
|
40
43
|
case "import":
|
|
41
|
-
await import("./import-
|
|
44
|
+
await import("./import-AVKQJDYC.js").then((m) => m.run(args.slice(1)));
|
|
42
45
|
break;
|
|
43
46
|
case "--help":
|
|
44
47
|
case "-h":
|
|
@@ -61,6 +64,7 @@ function printUsage() {
|
|
|
61
64
|
volute agent create <name> [--template <name>]
|
|
62
65
|
volute agent start <name>
|
|
63
66
|
volute agent stop [name]
|
|
67
|
+
volute agent restart [name]
|
|
64
68
|
volute agent delete [name] [--force]
|
|
65
69
|
volute agent list
|
|
66
70
|
volute agent status [name]
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
AgentManager,
|
|
4
4
|
getAgentManager,
|
|
5
5
|
initAgentManager
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-62X577Y7.js";
|
|
7
|
+
import "./chunk-H7AMDUIA.js";
|
|
8
|
+
import "./chunk-W76KWE23.js";
|
|
9
|
+
import "./chunk-UWHWAPGO.js";
|
|
10
10
|
import "./chunk-K3NQKI34.js";
|
|
11
11
|
export {
|
|
12
12
|
AgentManager,
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
readStdin
|
|
4
|
+
} from "./chunk-ZYGKG6VC.js";
|
|
5
|
+
import {
|
|
6
|
+
resolveAgentName
|
|
7
|
+
} from "./chunk-AZEL2IEK.js";
|
|
8
|
+
import {
|
|
9
|
+
CHANNELS,
|
|
10
|
+
getChannelDriver
|
|
11
|
+
} from "./chunk-ZZOOTYXK.js";
|
|
12
|
+
import {
|
|
13
|
+
loadMergedEnv
|
|
14
|
+
} from "./chunk-H7AMDUIA.js";
|
|
15
|
+
import {
|
|
16
|
+
writeChannelEntry
|
|
17
|
+
} from "./chunk-BX7KI4S3.js";
|
|
18
|
+
import {
|
|
19
|
+
parseArgs
|
|
20
|
+
} from "./chunk-D424ZQGI.js";
|
|
21
|
+
import {
|
|
22
|
+
daemonFetch
|
|
23
|
+
} from "./chunk-JR4UXCTO.js";
|
|
24
|
+
import {
|
|
25
|
+
resolveAgent
|
|
26
|
+
} from "./chunk-UWHWAPGO.js";
|
|
27
|
+
import "./chunk-K3NQKI34.js";
|
|
28
|
+
|
|
29
|
+
// src/commands/channel.ts
|
|
30
|
+
async function run(args) {
|
|
31
|
+
const subcommand = args[0];
|
|
32
|
+
switch (subcommand) {
|
|
33
|
+
case "read":
|
|
34
|
+
await readChannel(args.slice(1));
|
|
35
|
+
break;
|
|
36
|
+
case "send":
|
|
37
|
+
await sendChannel(args.slice(1));
|
|
38
|
+
break;
|
|
39
|
+
case "list":
|
|
40
|
+
await listChannels(args.slice(1));
|
|
41
|
+
break;
|
|
42
|
+
case "users":
|
|
43
|
+
await listUsers(args.slice(1));
|
|
44
|
+
break;
|
|
45
|
+
case "create":
|
|
46
|
+
await createChannel(args.slice(1));
|
|
47
|
+
break;
|
|
48
|
+
case "typing":
|
|
49
|
+
await typingChannel(args.slice(1));
|
|
50
|
+
break;
|
|
51
|
+
case "--help":
|
|
52
|
+
case "-h":
|
|
53
|
+
case void 0:
|
|
54
|
+
printUsage();
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
printUsage();
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function printUsage() {
|
|
62
|
+
console.log(`Usage:
|
|
63
|
+
volute channel read <channel-uri> [--limit N] [--agent <name>]
|
|
64
|
+
volute channel send <channel-uri> "<message>" [--agent <name>]
|
|
65
|
+
volute channel list [<platform>] [--agent <name>]
|
|
66
|
+
volute channel users <platform> [--agent <name>]
|
|
67
|
+
volute channel create <platform> --participants user1,user2 [--name "..."] [--agent <name>]
|
|
68
|
+
volute channel typing <channel-uri> [--agent <name>]
|
|
69
|
+
echo "message" | volute channel send <channel-uri> [--agent <name>]`);
|
|
70
|
+
}
|
|
71
|
+
async function readChannel(args) {
|
|
72
|
+
const { positional, flags } = parseArgs(args, {
|
|
73
|
+
agent: { type: "string" },
|
|
74
|
+
limit: { type: "number" }
|
|
75
|
+
});
|
|
76
|
+
const uri = positional[0];
|
|
77
|
+
if (!uri) {
|
|
78
|
+
console.error("Usage: volute channel read <channel-uri> [--limit N] [--agent <name>]");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const agentName = resolveAgentName(flags);
|
|
82
|
+
const { platform } = parseUri(uri);
|
|
83
|
+
const driver = requireDriver(platform);
|
|
84
|
+
const { dir } = resolveAgent(agentName);
|
|
85
|
+
const env = { ...loadMergedEnv(dir), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
86
|
+
try {
|
|
87
|
+
const limit = flags.limit ?? 20;
|
|
88
|
+
const output = await driver.read(env, uri, limit);
|
|
89
|
+
console.log(output);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function sendChannel(args) {
|
|
96
|
+
const { positional, flags } = parseArgs(args, {
|
|
97
|
+
agent: { type: "string" }
|
|
98
|
+
});
|
|
99
|
+
const uri = positional[0];
|
|
100
|
+
const message = positional[1] ?? await readStdin();
|
|
101
|
+
if (!uri || !message) {
|
|
102
|
+
console.error('Usage: volute channel send <channel-uri> "<message>" [--agent <name>]');
|
|
103
|
+
console.error(' echo "message" | volute channel send <channel-uri> [--agent <name>]');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
const agentName = resolveAgentName(flags);
|
|
107
|
+
const { platform } = parseUri(uri);
|
|
108
|
+
const driver = requireDriver(platform);
|
|
109
|
+
const { dir } = resolveAgent(agentName);
|
|
110
|
+
const env = { ...loadMergedEnv(dir), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
111
|
+
try {
|
|
112
|
+
await driver.send(env, uri, message);
|
|
113
|
+
try {
|
|
114
|
+
await daemonFetch(`/api/agents/${encodeURIComponent(agentName)}/history`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: { "Content-Type": "application/json" },
|
|
117
|
+
body: JSON.stringify({ channel: uri, content: message })
|
|
118
|
+
});
|
|
119
|
+
} catch (err) {
|
|
120
|
+
console.error(`Failed to persist to history: ${err instanceof Error ? err.message : err}`);
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function listChannels(args) {
|
|
128
|
+
const { positional, flags } = parseArgs(args, {
|
|
129
|
+
agent: { type: "string" }
|
|
130
|
+
});
|
|
131
|
+
const platform = positional[0];
|
|
132
|
+
const agentName = resolveAgentName(flags);
|
|
133
|
+
const { dir } = resolveAgent(agentName);
|
|
134
|
+
const env = { ...loadMergedEnv(dir), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
135
|
+
const platforms = platform ? [platform] : Object.keys(CHANNELS);
|
|
136
|
+
for (const p of platforms) {
|
|
137
|
+
const driver = getChannelDriver(p);
|
|
138
|
+
if (!driver?.listConversations) continue;
|
|
139
|
+
try {
|
|
140
|
+
const convs = await driver.listConversations(env);
|
|
141
|
+
for (const conv of convs) {
|
|
142
|
+
writeChannelEntry(dir, conv.id, {
|
|
143
|
+
platformId: conv.platformId,
|
|
144
|
+
platform: p,
|
|
145
|
+
name: conv.name,
|
|
146
|
+
type: conv.type
|
|
147
|
+
});
|
|
148
|
+
const parts = [conv.id.padEnd(24), conv.name.padEnd(28), conv.type];
|
|
149
|
+
if (conv.participantCount != null) {
|
|
150
|
+
parts.push(String(conv.participantCount));
|
|
151
|
+
}
|
|
152
|
+
console.log(parts.join(" "));
|
|
153
|
+
}
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.error(`${p}: ${err instanceof Error ? err.message : String(err)}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function listUsers(args) {
|
|
160
|
+
const { positional, flags } = parseArgs(args, {
|
|
161
|
+
agent: { type: "string" }
|
|
162
|
+
});
|
|
163
|
+
const platform = positional[0];
|
|
164
|
+
if (!platform) {
|
|
165
|
+
console.error("Usage: volute channel users <platform> [--agent <name>]");
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
const driver = requireDriver(platform);
|
|
169
|
+
if (!driver.listUsers) {
|
|
170
|
+
console.error(`Platform ${platform} does not support listing users`);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
const agentName = resolveAgentName(flags);
|
|
174
|
+
const { dir } = resolveAgent(agentName);
|
|
175
|
+
const env = { ...loadMergedEnv(dir), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
176
|
+
try {
|
|
177
|
+
const users = await driver.listUsers(env);
|
|
178
|
+
for (const user of users) {
|
|
179
|
+
console.log(`${user.username.padEnd(20)} ${user.id.padEnd(20)} ${user.type ?? ""}`);
|
|
180
|
+
}
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function createChannel(args) {
|
|
187
|
+
const { positional, flags } = parseArgs(args, {
|
|
188
|
+
agent: { type: "string" },
|
|
189
|
+
participants: { type: "string" },
|
|
190
|
+
name: { type: "string" }
|
|
191
|
+
});
|
|
192
|
+
const platform = positional[0];
|
|
193
|
+
if (!platform || !flags.participants) {
|
|
194
|
+
console.error(
|
|
195
|
+
'Usage: volute channel create <platform> --participants user1,user2 [--name "..."] [--agent <name>]'
|
|
196
|
+
);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
const driver = requireDriver(platform);
|
|
200
|
+
if (!driver.createConversation) {
|
|
201
|
+
console.error(`Platform ${platform} does not support creating conversations`);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
const agentName = resolveAgentName(flags);
|
|
205
|
+
const { dir } = resolveAgent(agentName);
|
|
206
|
+
const env = { ...loadMergedEnv(dir), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
207
|
+
const participants = flags.participants.split(",").map((s) => s.trim());
|
|
208
|
+
try {
|
|
209
|
+
const slug = await driver.createConversation(env, participants, flags.name);
|
|
210
|
+
console.log(slug);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function typingChannel(args) {
|
|
217
|
+
const { positional, flags } = parseArgs(args, {
|
|
218
|
+
agent: { type: "string" }
|
|
219
|
+
});
|
|
220
|
+
const uri = positional[0];
|
|
221
|
+
if (!uri) {
|
|
222
|
+
console.error("Usage: volute channel typing <channel-uri> [--agent <name>]");
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
const agentName = resolveAgentName(flags);
|
|
226
|
+
try {
|
|
227
|
+
const res = await daemonFetch(
|
|
228
|
+
`/api/agents/${encodeURIComponent(agentName)}/typing?channel=${encodeURIComponent(uri)}`
|
|
229
|
+
);
|
|
230
|
+
if (!res.ok) {
|
|
231
|
+
const body = await res.json().catch(() => ({}));
|
|
232
|
+
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
const data = await res.json();
|
|
236
|
+
if (data.typing.length > 0) {
|
|
237
|
+
console.log(data.typing.join(", "));
|
|
238
|
+
}
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function parseUri(uri) {
|
|
245
|
+
const colonIdx = uri.indexOf(":");
|
|
246
|
+
if (colonIdx === -1) {
|
|
247
|
+
console.error(`Invalid channel URI: ${uri} (expected format: platform:id)`);
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
return { platform: uri.slice(0, colonIdx), channelId: uri.slice(colonIdx + 1) };
|
|
251
|
+
}
|
|
252
|
+
function requireDriver(platform) {
|
|
253
|
+
const driver = getChannelDriver(platform);
|
|
254
|
+
if (!driver) {
|
|
255
|
+
console.error(`No channel driver for platform: ${platform}`);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
return driver;
|
|
259
|
+
}
|
|
260
|
+
export {
|
|
261
|
+
run
|
|
262
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadMergedEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-H7AMDUIA.js";
|
|
5
5
|
import {
|
|
6
6
|
applyIsolation
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-W76KWE23.js";
|
|
8
8
|
import {
|
|
9
9
|
agentDir,
|
|
10
10
|
findAgent,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
setVariantRunning,
|
|
14
14
|
validateBranchName,
|
|
15
15
|
voluteHome
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-UWHWAPGO.js";
|
|
17
17
|
|
|
18
18
|
// src/lib/agent-manager.ts
|
|
19
19
|
import { execFile, spawn } from "child_process";
|
|
@@ -285,11 +285,13 @@ var AgentManager = class {
|
|
|
285
285
|
});
|
|
286
286
|
this.stopping.delete(name);
|
|
287
287
|
if (this.restartAttempts.delete(name)) this.saveCrashAttempts();
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
if (!this.shuttingDown) {
|
|
289
|
+
const [baseName, variantName] = name.split("@", 2);
|
|
290
|
+
if (variantName) {
|
|
291
|
+
setVariantRunning(baseName, variantName, false);
|
|
292
|
+
} else {
|
|
293
|
+
setAgentRunning(name, false);
|
|
294
|
+
}
|
|
293
295
|
}
|
|
294
296
|
console.error(`[daemon] stopped agent ${name}`);
|
|
295
297
|
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
voluteHome
|
|
4
|
+
} from "./chunk-UWHWAPGO.js";
|
|
5
|
+
import {
|
|
6
|
+
__export
|
|
7
|
+
} from "./chunk-K3NQKI34.js";
|
|
8
|
+
|
|
9
|
+
// src/lib/auth.ts
|
|
10
|
+
import { compareSync, hashSync } from "bcryptjs";
|
|
11
|
+
import { and, count, eq } from "drizzle-orm";
|
|
12
|
+
|
|
13
|
+
// src/lib/db.ts
|
|
14
|
+
import { chmodSync, existsSync } from "fs";
|
|
15
|
+
import { dirname, resolve } from "path";
|
|
16
|
+
import { fileURLToPath } from "url";
|
|
17
|
+
import { drizzle } from "drizzle-orm/libsql";
|
|
18
|
+
import { migrate } from "drizzle-orm/libsql/migrator";
|
|
19
|
+
|
|
20
|
+
// src/lib/schema.ts
|
|
21
|
+
var schema_exports = {};
|
|
22
|
+
__export(schema_exports, {
|
|
23
|
+
agentMessages: () => agentMessages,
|
|
24
|
+
conversationParticipants: () => conversationParticipants,
|
|
25
|
+
conversations: () => conversations,
|
|
26
|
+
messages: () => messages,
|
|
27
|
+
sessions: () => sessions,
|
|
28
|
+
users: () => users
|
|
29
|
+
});
|
|
30
|
+
import { sql } from "drizzle-orm";
|
|
31
|
+
import { index, integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
|
|
32
|
+
var users = sqliteTable("users", {
|
|
33
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
34
|
+
username: text("username").unique().notNull(),
|
|
35
|
+
password_hash: text("password_hash").notNull(),
|
|
36
|
+
role: text("role").notNull().default("pending"),
|
|
37
|
+
user_type: text("user_type").notNull().default("human"),
|
|
38
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
39
|
+
});
|
|
40
|
+
var conversations = sqliteTable(
|
|
41
|
+
"conversations",
|
|
42
|
+
{
|
|
43
|
+
id: text("id").primaryKey(),
|
|
44
|
+
agent_name: text("agent_name").notNull(),
|
|
45
|
+
channel: text("channel").notNull(),
|
|
46
|
+
user_id: integer("user_id").references(() => users.id),
|
|
47
|
+
title: text("title"),
|
|
48
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`),
|
|
49
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
50
|
+
},
|
|
51
|
+
(table) => [
|
|
52
|
+
index("idx_conversations_agent_name").on(table.agent_name),
|
|
53
|
+
index("idx_conversations_user_id").on(table.user_id),
|
|
54
|
+
index("idx_conversations_updated_at").on(table.updated_at)
|
|
55
|
+
]
|
|
56
|
+
);
|
|
57
|
+
var agentMessages = sqliteTable(
|
|
58
|
+
"agent_messages",
|
|
59
|
+
{
|
|
60
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
61
|
+
agent: text("agent").notNull(),
|
|
62
|
+
channel: text("channel").notNull(),
|
|
63
|
+
role: text("role").notNull(),
|
|
64
|
+
sender: text("sender"),
|
|
65
|
+
content: text("content").notNull(),
|
|
66
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
67
|
+
},
|
|
68
|
+
(table) => [
|
|
69
|
+
index("idx_agent_messages_agent").on(table.agent),
|
|
70
|
+
index("idx_agent_messages_channel").on(table.agent, table.channel)
|
|
71
|
+
]
|
|
72
|
+
);
|
|
73
|
+
var conversationParticipants = sqliteTable(
|
|
74
|
+
"conversation_participants",
|
|
75
|
+
{
|
|
76
|
+
conversation_id: text("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
|
|
77
|
+
user_id: integer("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
78
|
+
role: text("role").notNull().default("member"),
|
|
79
|
+
joined_at: text("joined_at").notNull().default(sql`(datetime('now'))`)
|
|
80
|
+
},
|
|
81
|
+
(table) => [
|
|
82
|
+
uniqueIndex("idx_cp_unique").on(table.conversation_id, table.user_id),
|
|
83
|
+
index("idx_cp_user_id").on(table.user_id)
|
|
84
|
+
]
|
|
85
|
+
);
|
|
86
|
+
var sessions = sqliteTable("sessions", {
|
|
87
|
+
id: text("id").primaryKey(),
|
|
88
|
+
userId: integer("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
|
|
89
|
+
createdAt: integer("created_at").notNull()
|
|
90
|
+
});
|
|
91
|
+
var messages = sqliteTable(
|
|
92
|
+
"messages",
|
|
93
|
+
{
|
|
94
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
95
|
+
conversation_id: text("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
|
|
96
|
+
role: text("role").notNull(),
|
|
97
|
+
sender_name: text("sender_name"),
|
|
98
|
+
content: text("content").notNull(),
|
|
99
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
100
|
+
},
|
|
101
|
+
(table) => [index("idx_messages_conversation_id").on(table.conversation_id)]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// src/lib/db.ts
|
|
105
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
106
|
+
var migrationsFolder = existsSync(resolve(__dirname, "../drizzle")) ? resolve(__dirname, "../drizzle") : resolve(__dirname, "../../drizzle");
|
|
107
|
+
var db = null;
|
|
108
|
+
async function getDb() {
|
|
109
|
+
if (db) return db;
|
|
110
|
+
const dbPath = process.env.VOLUTE_DB_PATH || resolve(voluteHome(), "volute.db");
|
|
111
|
+
db = drizzle({ connection: { url: `file:${dbPath}` }, schema: schema_exports });
|
|
112
|
+
await migrate(db, { migrationsFolder });
|
|
113
|
+
try {
|
|
114
|
+
chmodSync(dbPath, 384);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error(
|
|
117
|
+
`[volute] WARNING: Failed to restrict database file permissions on ${dbPath}:`,
|
|
118
|
+
err
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return db;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/lib/auth.ts
|
|
125
|
+
async function createUser(username, password) {
|
|
126
|
+
const db2 = await getDb();
|
|
127
|
+
const hash = hashSync(password, 10);
|
|
128
|
+
const [{ value }] = await db2.select({ value: count() }).from(users).where(eq(users.user_type, "human"));
|
|
129
|
+
const role = value === 0 ? "admin" : "pending";
|
|
130
|
+
const [result] = await db2.insert(users).values({ username, password_hash: hash, role }).returning({
|
|
131
|
+
id: users.id,
|
|
132
|
+
username: users.username,
|
|
133
|
+
role: users.role,
|
|
134
|
+
user_type: users.user_type,
|
|
135
|
+
created_at: users.created_at
|
|
136
|
+
});
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
async function verifyUser(username, password) {
|
|
140
|
+
const db2 = await getDb();
|
|
141
|
+
const row = await db2.select().from(users).where(eq(users.username, username)).get();
|
|
142
|
+
if (!row) return null;
|
|
143
|
+
if (row.user_type === "agent") return null;
|
|
144
|
+
if (!compareSync(password, row.password_hash)) return null;
|
|
145
|
+
const { password_hash: _, ...user } = row;
|
|
146
|
+
return user;
|
|
147
|
+
}
|
|
148
|
+
async function getUser(id) {
|
|
149
|
+
const db2 = await getDb();
|
|
150
|
+
const row = await db2.select({
|
|
151
|
+
id: users.id,
|
|
152
|
+
username: users.username,
|
|
153
|
+
role: users.role,
|
|
154
|
+
user_type: users.user_type,
|
|
155
|
+
created_at: users.created_at
|
|
156
|
+
}).from(users).where(eq(users.id, id)).get();
|
|
157
|
+
return row ?? null;
|
|
158
|
+
}
|
|
159
|
+
async function getUserByUsername(username) {
|
|
160
|
+
const db2 = await getDb();
|
|
161
|
+
const row = await db2.select({
|
|
162
|
+
id: users.id,
|
|
163
|
+
username: users.username,
|
|
164
|
+
role: users.role,
|
|
165
|
+
user_type: users.user_type,
|
|
166
|
+
created_at: users.created_at
|
|
167
|
+
}).from(users).where(eq(users.username, username)).get();
|
|
168
|
+
return row ?? null;
|
|
169
|
+
}
|
|
170
|
+
async function listUsers() {
|
|
171
|
+
const db2 = await getDb();
|
|
172
|
+
return db2.select({
|
|
173
|
+
id: users.id,
|
|
174
|
+
username: users.username,
|
|
175
|
+
role: users.role,
|
|
176
|
+
user_type: users.user_type,
|
|
177
|
+
created_at: users.created_at
|
|
178
|
+
}).from(users).orderBy(users.created_at).all();
|
|
179
|
+
}
|
|
180
|
+
async function listPendingUsers() {
|
|
181
|
+
const db2 = await getDb();
|
|
182
|
+
return db2.select({
|
|
183
|
+
id: users.id,
|
|
184
|
+
username: users.username,
|
|
185
|
+
role: users.role,
|
|
186
|
+
user_type: users.user_type,
|
|
187
|
+
created_at: users.created_at
|
|
188
|
+
}).from(users).where(eq(users.role, "pending")).orderBy(users.created_at).all();
|
|
189
|
+
}
|
|
190
|
+
async function listUsersByType(userType) {
|
|
191
|
+
const db2 = await getDb();
|
|
192
|
+
return db2.select({
|
|
193
|
+
id: users.id,
|
|
194
|
+
username: users.username,
|
|
195
|
+
role: users.role,
|
|
196
|
+
user_type: users.user_type,
|
|
197
|
+
created_at: users.created_at
|
|
198
|
+
}).from(users).where(eq(users.user_type, userType)).orderBy(users.created_at).all();
|
|
199
|
+
}
|
|
200
|
+
async function getOrCreateAgentUser(agentName) {
|
|
201
|
+
const db2 = await getDb();
|
|
202
|
+
const existing = await db2.select({
|
|
203
|
+
id: users.id,
|
|
204
|
+
username: users.username,
|
|
205
|
+
role: users.role,
|
|
206
|
+
user_type: users.user_type,
|
|
207
|
+
created_at: users.created_at
|
|
208
|
+
}).from(users).where(and(eq(users.username, agentName), eq(users.user_type, "agent"))).get();
|
|
209
|
+
if (existing) return existing;
|
|
210
|
+
try {
|
|
211
|
+
const [result] = await db2.insert(users).values({
|
|
212
|
+
username: agentName,
|
|
213
|
+
password_hash: "!agent",
|
|
214
|
+
role: "agent",
|
|
215
|
+
user_type: "agent"
|
|
216
|
+
}).returning({
|
|
217
|
+
id: users.id,
|
|
218
|
+
username: users.username,
|
|
219
|
+
role: users.role,
|
|
220
|
+
user_type: users.user_type,
|
|
221
|
+
created_at: users.created_at
|
|
222
|
+
});
|
|
223
|
+
return result;
|
|
224
|
+
} catch (err) {
|
|
225
|
+
if (err instanceof Error && err.message.includes("UNIQUE constraint")) {
|
|
226
|
+
const retried = await db2.select({
|
|
227
|
+
id: users.id,
|
|
228
|
+
username: users.username,
|
|
229
|
+
role: users.role,
|
|
230
|
+
user_type: users.user_type,
|
|
231
|
+
created_at: users.created_at
|
|
232
|
+
}).from(users).where(and(eq(users.username, agentName), eq(users.user_type, "agent"))).get();
|
|
233
|
+
if (retried) return retried;
|
|
234
|
+
}
|
|
235
|
+
throw err;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async function deleteAgentUser(agentName) {
|
|
239
|
+
const db2 = await getDb();
|
|
240
|
+
await db2.delete(users).where(and(eq(users.username, agentName), eq(users.user_type, "agent")));
|
|
241
|
+
}
|
|
242
|
+
async function approveUser(id) {
|
|
243
|
+
const db2 = await getDb();
|
|
244
|
+
await db2.update(users).set({ role: "user" }).where(and(eq(users.id, id), eq(users.role, "pending")));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export {
|
|
248
|
+
users,
|
|
249
|
+
conversations,
|
|
250
|
+
agentMessages,
|
|
251
|
+
conversationParticipants,
|
|
252
|
+
sessions,
|
|
253
|
+
messages,
|
|
254
|
+
getDb,
|
|
255
|
+
createUser,
|
|
256
|
+
verifyUser,
|
|
257
|
+
getUser,
|
|
258
|
+
getUserByUsername,
|
|
259
|
+
listUsers,
|
|
260
|
+
listPendingUsers,
|
|
261
|
+
listUsersByType,
|
|
262
|
+
getOrCreateAgentUser,
|
|
263
|
+
deleteAgentUser,
|
|
264
|
+
approveUser
|
|
265
|
+
};
|