volute 0.7.0 → 0.8.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/README.md +13 -13
- 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-5UCKNK6J.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-R4MCNBOA.js} +1 -1
- package/dist/{setup-F4TCWVSP.js → setup-JXDCJX7W.js} +25 -6
- 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
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ volute agent create atlas
|
|
|
23
23
|
volute agent start atlas
|
|
24
24
|
|
|
25
25
|
# Talk to it
|
|
26
|
-
volute
|
|
26
|
+
volute send @atlas "hey, what can you do?"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
You now have a running AI agent with persistent memory, auto-committing file changes, and session resume across restarts. Open `http://localhost:4200` for the web dashboard.
|
|
@@ -58,10 +58,10 @@ volute agent delete atlas --force # also delete files
|
|
|
58
58
|
### Sending messages
|
|
59
59
|
|
|
60
60
|
```sh
|
|
61
|
-
volute
|
|
61
|
+
volute send @atlas "what's on your mind?"
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
The agent knows which channel each message came from — CLI, web, Discord, or system — and routes its response back to the source.
|
|
65
65
|
|
|
66
66
|
### Anatomy of an agent
|
|
67
67
|
|
|
@@ -95,7 +95,7 @@ This is the interesting part. Agents can fork themselves into isolated branches,
|
|
|
95
95
|
volute variant create experiment --agent atlas
|
|
96
96
|
|
|
97
97
|
# Talk to the variant directly
|
|
98
|
-
volute
|
|
98
|
+
volute send @atlas@experiment "try a different approach"
|
|
99
99
|
|
|
100
100
|
# List all variants
|
|
101
101
|
volute variant list --agent atlas
|
|
@@ -144,7 +144,7 @@ Read from and write to connector channels directly:
|
|
|
144
144
|
|
|
145
145
|
```sh
|
|
146
146
|
volute channel read discord:123456789 --agent atlas # recent messages
|
|
147
|
-
volute
|
|
147
|
+
volute send discord:123456789 "hello" --agent atlas # send a message
|
|
148
148
|
```
|
|
149
149
|
|
|
150
150
|
## Schedules
|
|
@@ -152,12 +152,12 @@ volute channel send discord:123456789 "hello" --agent atlas # send a message
|
|
|
152
152
|
Cron-based scheduled messages — daily check-ins, periodic tasks, whatever you need.
|
|
153
153
|
|
|
154
154
|
```sh
|
|
155
|
-
volute schedule add atlas \
|
|
155
|
+
volute schedule add --agent atlas \
|
|
156
156
|
--cron "0 9 * * *" \
|
|
157
157
|
--message "good morning — write your daily log"
|
|
158
158
|
|
|
159
|
-
volute schedule list atlas
|
|
160
|
-
volute schedule remove atlas --id <schedule-id>
|
|
159
|
+
volute schedule list --agent atlas
|
|
160
|
+
volute schedule remove --agent atlas --id <schedule-id>
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
## Environment variables
|
|
@@ -191,7 +191,7 @@ volute agent upgrade atlas # creates an "upgrade" variant
|
|
|
191
191
|
# resolve conflicts if needed, then:
|
|
192
192
|
volute agent upgrade atlas --continue
|
|
193
193
|
# test:
|
|
194
|
-
volute
|
|
194
|
+
volute send @atlas@upgrade "are you working?"
|
|
195
195
|
# merge:
|
|
196
196
|
volute variant merge upgrade --agent atlas
|
|
197
197
|
```
|
|
@@ -219,7 +219,7 @@ Set the model via `home/.config/volute.json` in the agent directory, or the `VOL
|
|
|
219
219
|
|
|
220
220
|
```sh
|
|
221
221
|
docker build -t volute .
|
|
222
|
-
docker run -d -p 4200:4200 -v volute-data:/data volute
|
|
222
|
+
docker run -d -p 4200:4200 -v volute-data:/data -v volute-agents:/agents volute
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
Or with docker-compose:
|
|
@@ -252,9 +252,9 @@ This installs a system-level systemd service with data at `/var/lib/volute` and
|
|
|
252
252
|
On macOS or Linux (without root), use the user-level service installer:
|
|
253
253
|
|
|
254
254
|
```sh
|
|
255
|
-
volute service install
|
|
256
|
-
volute service status
|
|
257
|
-
volute service uninstall
|
|
255
|
+
volute service install [--port N] [--host H] # auto-start on login
|
|
256
|
+
volute service status # check status
|
|
257
|
+
volute service uninstall # remove
|
|
258
258
|
```
|
|
259
259
|
|
|
260
260
|
## Development
|
|
@@ -6,42 +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-HGJHLABX.js").then((m) => m.run(args.slice(1)));
|
|
10
10
|
break;
|
|
11
11
|
case "start":
|
|
12
|
-
await import("./start-
|
|
12
|
+
await import("./start-QU73YTJW.js").then((m) => m.run(args.slice(1)));
|
|
13
13
|
break;
|
|
14
14
|
case "stop":
|
|
15
|
-
await import("./stop-
|
|
15
|
+
await import("./stop-N7U5N6A7.js").then((m) => m.run(args.slice(1)));
|
|
16
16
|
break;
|
|
17
17
|
case "restart":
|
|
18
|
-
await import("./restart-
|
|
18
|
+
await import("./restart-KVH3TK5N.js").then((m) => m.run(args.slice(1)));
|
|
19
19
|
break;
|
|
20
20
|
case "delete":
|
|
21
|
-
await import("./delete-
|
|
21
|
+
await import("./delete-45TGQC4N.js").then((m) => m.run(args.slice(1)));
|
|
22
22
|
break;
|
|
23
23
|
case "list":
|
|
24
|
-
await import("./status-
|
|
24
|
+
await import("./status-Q6ZQJXNI.js").then((m) => m.run(args.slice(1)));
|
|
25
25
|
break;
|
|
26
26
|
case "status": {
|
|
27
27
|
const rest = args.slice(1);
|
|
28
28
|
if (!rest[0] && process.env.VOLUTE_AGENT) {
|
|
29
29
|
rest.unshift(process.env.VOLUTE_AGENT);
|
|
30
30
|
}
|
|
31
|
-
await import("./status-
|
|
31
|
+
await import("./status-Q6ZQJXNI.js").then((m) => m.run(rest));
|
|
32
32
|
break;
|
|
33
33
|
}
|
|
34
34
|
case "logs": {
|
|
35
35
|
const rest = args.slice(1);
|
|
36
36
|
const logsArgs = transformAgentFlag(rest);
|
|
37
|
-
await import("./logs-
|
|
37
|
+
await import("./logs-TZB3MTLZ.js").then((m) => m.run(logsArgs));
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
40
40
|
case "upgrade":
|
|
41
|
-
await import("./upgrade-
|
|
41
|
+
await import("./upgrade-CZF6PN7Y.js").then((m) => m.run(args.slice(1)));
|
|
42
42
|
break;
|
|
43
43
|
case "import":
|
|
44
|
-
await import("./import-
|
|
44
|
+
await import("./import-CNEDF3TD.js").then((m) => m.run(args.slice(1)));
|
|
45
45
|
break;
|
|
46
46
|
case "--help":
|
|
47
47
|
case "-h":
|
|
@@ -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-W6TMWYU3.js";
|
|
7
|
+
import "./chunk-QF22MYDJ.js";
|
|
8
|
+
import "./chunk-IQXBMFZG.js";
|
|
9
|
+
import "./chunk-DP2DX4WV.js";
|
|
10
10
|
import "./chunk-K3NQKI34.js";
|
|
11
11
|
export {
|
|
12
12
|
AgentManager,
|
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
readStdin
|
|
4
|
-
} from "./chunk-ZYGKG6VC.js";
|
|
5
2
|
import {
|
|
6
3
|
resolveAgentName
|
|
7
4
|
} from "./chunk-AZEL2IEK.js";
|
|
8
5
|
import {
|
|
9
6
|
CHANNELS,
|
|
10
7
|
getChannelDriver
|
|
11
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LIPPXNIE.js";
|
|
12
9
|
import {
|
|
13
10
|
loadMergedEnv
|
|
14
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-QF22MYDJ.js";
|
|
15
12
|
import {
|
|
16
13
|
writeChannelEntry
|
|
17
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-N6MLQ26B.js";
|
|
18
15
|
import {
|
|
19
16
|
parseArgs
|
|
20
17
|
} from "./chunk-D424ZQGI.js";
|
|
21
18
|
import {
|
|
22
19
|
daemonFetch
|
|
23
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-23L3MKEV.js";
|
|
24
21
|
import {
|
|
25
|
-
|
|
26
|
-
} from "./chunk-
|
|
22
|
+
agentDir
|
|
23
|
+
} from "./chunk-DP2DX4WV.js";
|
|
27
24
|
import "./chunk-K3NQKI34.js";
|
|
28
25
|
|
|
29
26
|
// src/commands/channel.ts
|
|
@@ -33,9 +30,6 @@ async function run(args) {
|
|
|
33
30
|
case "read":
|
|
34
31
|
await readChannel(args.slice(1));
|
|
35
32
|
break;
|
|
36
|
-
case "send":
|
|
37
|
-
await sendChannel(args.slice(1));
|
|
38
|
-
break;
|
|
39
33
|
case "list":
|
|
40
34
|
await listChannels(args.slice(1));
|
|
41
35
|
break;
|
|
@@ -61,12 +55,10 @@ async function run(args) {
|
|
|
61
55
|
function printUsage() {
|
|
62
56
|
console.log(`Usage:
|
|
63
57
|
volute channel read <channel-uri> [--limit N] [--agent <name>]
|
|
64
|
-
volute channel send <channel-uri> "<message>" [--agent <name>]
|
|
65
58
|
volute channel list [<platform>] [--agent <name>]
|
|
66
59
|
volute channel users <platform> [--agent <name>]
|
|
67
60
|
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>]`);
|
|
61
|
+
volute channel typing <channel-uri> [--agent <name>]`);
|
|
70
62
|
}
|
|
71
63
|
async function readChannel(args) {
|
|
72
64
|
const { positional, flags } = parseArgs(args, {
|
|
@@ -81,8 +73,8 @@ async function readChannel(args) {
|
|
|
81
73
|
const agentName = resolveAgentName(flags);
|
|
82
74
|
const { platform } = parseUri(uri);
|
|
83
75
|
const driver = requireDriver(platform);
|
|
84
|
-
const
|
|
85
|
-
const env = { ...loadMergedEnv(
|
|
76
|
+
const dir = agentDir(agentName);
|
|
77
|
+
const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
86
78
|
try {
|
|
87
79
|
const limit = flags.limit ?? 20;
|
|
88
80
|
const output = await driver.read(env, uri, limit);
|
|
@@ -92,46 +84,14 @@ async function readChannel(args) {
|
|
|
92
84
|
process.exit(1);
|
|
93
85
|
}
|
|
94
86
|
}
|
|
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
87
|
async function listChannels(args) {
|
|
128
88
|
const { positional, flags } = parseArgs(args, {
|
|
129
89
|
agent: { type: "string" }
|
|
130
90
|
});
|
|
131
91
|
const platform = positional[0];
|
|
132
92
|
const agentName = resolveAgentName(flags);
|
|
133
|
-
const
|
|
134
|
-
const env = { ...loadMergedEnv(
|
|
93
|
+
const dir = agentDir(agentName);
|
|
94
|
+
const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
135
95
|
const platforms = platform ? [platform] : Object.keys(CHANNELS);
|
|
136
96
|
for (const p of platforms) {
|
|
137
97
|
const driver = getChannelDriver(p);
|
|
@@ -139,7 +99,7 @@ async function listChannels(args) {
|
|
|
139
99
|
try {
|
|
140
100
|
const convs = await driver.listConversations(env);
|
|
141
101
|
for (const conv of convs) {
|
|
142
|
-
writeChannelEntry(
|
|
102
|
+
writeChannelEntry(agentName, conv.id, {
|
|
143
103
|
platformId: conv.platformId,
|
|
144
104
|
platform: p,
|
|
145
105
|
name: conv.name,
|
|
@@ -171,8 +131,8 @@ async function listUsers(args) {
|
|
|
171
131
|
process.exit(1);
|
|
172
132
|
}
|
|
173
133
|
const agentName = resolveAgentName(flags);
|
|
174
|
-
const
|
|
175
|
-
const env = { ...loadMergedEnv(
|
|
134
|
+
const dir = agentDir(agentName);
|
|
135
|
+
const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
176
136
|
try {
|
|
177
137
|
const users = await driver.listUsers(env);
|
|
178
138
|
for (const user of users) {
|
|
@@ -202,8 +162,8 @@ async function createChannel(args) {
|
|
|
202
162
|
process.exit(1);
|
|
203
163
|
}
|
|
204
164
|
const agentName = resolveAgentName(flags);
|
|
205
|
-
const
|
|
206
|
-
const env = { ...loadMergedEnv(
|
|
165
|
+
const dir = agentDir(agentName);
|
|
166
|
+
const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
207
167
|
const participants = flags.participants.split(",").map((s) => s.trim());
|
|
208
168
|
try {
|
|
209
169
|
const slug = await driver.createConversation(env, participants, flags.name);
|
|
@@ -4,14 +4,19 @@
|
|
|
4
4
|
import { execFile as execFileCb, execFileSync, spawn } from "child_process";
|
|
5
5
|
function exec(cmd, args, options) {
|
|
6
6
|
return new Promise((resolve, reject) => {
|
|
7
|
-
execFileCb(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
execFileCb(
|
|
8
|
+
cmd,
|
|
9
|
+
args,
|
|
10
|
+
{ cwd: options?.cwd, uid: options?.uid, gid: options?.gid, env: options?.env },
|
|
11
|
+
(err, stdout, stderr) => {
|
|
12
|
+
if (err) {
|
|
13
|
+
err.stderr = stderr;
|
|
14
|
+
reject(err);
|
|
15
|
+
} else {
|
|
16
|
+
resolve(stdout);
|
|
17
|
+
}
|
|
13
18
|
}
|
|
14
|
-
|
|
19
|
+
);
|
|
15
20
|
});
|
|
16
21
|
}
|
|
17
22
|
function resolveVoluteBin() {
|
|
@@ -25,6 +30,9 @@ function execInherit(cmd, args, options) {
|
|
|
25
30
|
return new Promise((resolve, reject) => {
|
|
26
31
|
const child = spawn(cmd, args, {
|
|
27
32
|
cwd: options?.cwd,
|
|
33
|
+
uid: options?.uid,
|
|
34
|
+
gid: options?.gid,
|
|
35
|
+
env: options?.env,
|
|
28
36
|
stdio: "inherit"
|
|
29
37
|
});
|
|
30
38
|
child.on("error", reject);
|
|
@@ -20,7 +20,8 @@ function voluteHome() {
|
|
|
20
20
|
return resolve(homedir(), ".volute");
|
|
21
21
|
}
|
|
22
22
|
function ensureVoluteHome() {
|
|
23
|
-
|
|
23
|
+
const agentsBase = process.env.VOLUTE_AGENTS_DIR ?? resolve(voluteHome(), "agents");
|
|
24
|
+
mkdirSync(agentsBase, { recursive: true });
|
|
24
25
|
}
|
|
25
26
|
function readRegistry() {
|
|
26
27
|
const registryPath = resolve(voluteHome(), "agents.json");
|
|
@@ -75,8 +76,14 @@ function findAgent(name) {
|
|
|
75
76
|
return readRegistry().find((e) => e.name === name);
|
|
76
77
|
}
|
|
77
78
|
function agentDir(name) {
|
|
79
|
+
if (process.env.VOLUTE_AGENTS_DIR) {
|
|
80
|
+
return resolve(process.env.VOLUTE_AGENTS_DIR, name);
|
|
81
|
+
}
|
|
78
82
|
return resolve(voluteHome(), "agents", name);
|
|
79
83
|
}
|
|
84
|
+
function stateDir(name) {
|
|
85
|
+
return resolve(voluteHome(), "state", name);
|
|
86
|
+
}
|
|
80
87
|
function nextPort() {
|
|
81
88
|
const entries = readRegistry();
|
|
82
89
|
const usedPorts = new Set(entries.map((e) => e.port));
|
|
@@ -232,6 +239,7 @@ export {
|
|
|
232
239
|
setAgentRunning,
|
|
233
240
|
findAgent,
|
|
234
241
|
agentDir,
|
|
242
|
+
stateDir,
|
|
235
243
|
nextPort,
|
|
236
244
|
daemonLoopback,
|
|
237
245
|
resolveAgent
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
voluteHome
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DP2DX4WV.js";
|
|
5
5
|
import {
|
|
6
6
|
__export
|
|
7
7
|
} from "./chunk-K3NQKI34.js";
|
|
@@ -60,7 +60,6 @@ var agentMessages = sqliteTable(
|
|
|
60
60
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
61
61
|
agent: text("agent").notNull(),
|
|
62
62
|
channel: text("channel").notNull(),
|
|
63
|
-
role: text("role").notNull(),
|
|
64
63
|
sender: text("sender"),
|
|
65
64
|
content: text("content").notNull(),
|
|
66
65
|
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
validateAgentName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DP2DX4WV.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/isolation.ts
|
|
7
7
|
import { execFile, execFileSync } from "child_process";
|
|
@@ -13,7 +13,7 @@ function isIsolationEnabled() {
|
|
|
13
13
|
function agentUserName(agentName) {
|
|
14
14
|
const err = validateAgentName(agentName);
|
|
15
15
|
if (err) throw new Error(`Invalid agent name for isolation: ${err}`);
|
|
16
|
-
const prefix = process.env.VOLUTE_USER_PREFIX ?? "
|
|
16
|
+
const prefix = process.env.VOLUTE_USER_PREFIX ?? "agent-";
|
|
17
17
|
return `${prefix}${agentName}`;
|
|
18
18
|
}
|
|
19
19
|
function ensureVoluteGroup(opts) {
|
|
@@ -37,7 +37,7 @@ function createAgentUser(name) {
|
|
|
37
37
|
} catch {
|
|
38
38
|
}
|
|
39
39
|
try {
|
|
40
|
-
execFileSync("useradd", ["-r", "-M", "-
|
|
40
|
+
execFileSync("useradd", ["-r", "-M", "-G", "volute", "-s", "/usr/sbin/nologin", user], {
|
|
41
41
|
stdio: "ignore"
|
|
42
42
|
});
|
|
43
43
|
} catch (err) {
|
|
@@ -68,14 +68,16 @@ async function applyIsolation(spawnOpts, agentName) {
|
|
|
68
68
|
function chownAgentDir(dir, name) {
|
|
69
69
|
if (!isIsolationEnabled()) return;
|
|
70
70
|
const user = agentUserName(name);
|
|
71
|
-
execFileSync("chown", ["-R", `${user}
|
|
71
|
+
execFileSync("chown", ["-R", `${user}:${user}`, dir]);
|
|
72
72
|
execFileSync("chmod", ["700", dir]);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
export {
|
|
76
|
+
isIsolationEnabled,
|
|
76
77
|
ensureVoluteGroup,
|
|
77
78
|
createAgentUser,
|
|
78
79
|
deleteAgentUser,
|
|
80
|
+
getAgentUserIds,
|
|
79
81
|
applyIsolation,
|
|
80
82
|
chownAgentDir
|
|
81
83
|
};
|