volute 0.11.4 → 0.13.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-2AQPI3QV.js → agent-HYX2ZTFM.js} +10 -10
- package/dist/{agent-manager-AZUDAKCP.js → agent-manager-HHBAAL2D.js} +4 -4
- package/dist/{channel-2WHBRDTD.js → channel-72YET5JC.js} +2 -2
- package/dist/{chunk-QF22MYDJ.js → chunk-BJDLYTPS.js} +1 -1
- package/dist/{chunk-46S7YHUB.js → chunk-EN3NHRQC.js} +12 -19
- package/dist/{chunk-DP2DX4WV.js → chunk-ESTOWEG2.js} +12 -3
- package/dist/{chunk-N6MLQ26B.js → chunk-GPZCPGV3.js} +11 -1
- package/dist/{chunk-ZKNBD5P3.js → chunk-IELXXS7E.js} +2 -2
- package/dist/{chunk-STOEJOJO.js → chunk-J3IHIXDB.js} +1 -1
- package/dist/{chunk-R3VB7NF5.js → chunk-KYHC7LHS.js} +2 -2
- package/dist/{chunk-YY2QX2J6.js → chunk-LG4ROCHN.js} +1 -1
- package/dist/{chunk-YTOPX4PB.js → chunk-NQKKTRET.js} +28 -37
- package/dist/{chunk-D5EVQTGJ.js → chunk-PEQQ7MRI.js} +2 -2
- package/dist/{chunk-LIPPXNIE.js → chunk-PTK3GBCG.js} +17 -18
- package/dist/{chunk-RGWADNLT.js → chunk-RH3XLDY2.js} +2 -2
- package/dist/{chunk-WTJI3JVR.js → chunk-VRAOTXDF.js} +9 -6
- package/dist/chunk-XUA3JUFK.js +121 -0
- package/dist/cli.js +26 -17
- package/dist/{connector-L2HBLZBW.js → connector-Z5KYVTZ5.js} +2 -2
- package/dist/connectors/discord.js +2 -2
- package/dist/connectors/slack.js +2 -2
- package/dist/connectors/telegram.js +2 -2
- package/dist/{create-VBZZNJOG.js → create-AAI52BC2.js} +1 -1
- package/dist/{daemon-client-P44NU3KU.js → daemon-client-WXN43USO.js} +2 -2
- package/dist/{daemon-restart-5W5AGBZ2.js → daemon-restart-4OXIGWV6.js} +6 -5
- package/dist/daemon.js +533 -557
- package/dist/{delete-BOTVU4YO.js → delete-BJ3LNU2I.js} +1 -1
- package/dist/down-IMZE7V42.js +14 -0
- package/dist/{env-CGORIKVF.js → env-EOO2C7L7.js} +2 -2
- package/dist/{history-NI5QP27M.js → history-J7TURCZS.js} +2 -2
- package/dist/{import-2BZUWT23.js → import-TKF67X4R.js} +3 -3
- package/dist/{logs-APWVWGNX.js → logs-KHBOS6IZ.js} +2 -2
- package/dist/{package-KVUXPTEW.js → package-TPXKJXLG.js} +1 -1
- package/dist/{restart-CCYM3MEC.js → restart-CMP63H6A.js} +2 -2
- package/dist/{schedule-E4MFGYSA.js → schedule-YMAJZ52M.js} +2 -2
- package/dist/seed-M6QPHFTV.js +68 -0
- package/dist/{send-X6OQGSD6.js → send-EZYEIVMA.js} +18 -6
- package/dist/{service-UL3OCODG.js → service-DDFZA7Q3.js} +4 -3
- package/dist/{setup-7N4KYOYN.js → setup-DCZWBBD7.js} +7 -7
- package/dist/sprout-EAWETTWZ.js +89 -0
- package/dist/{start-6YRS6FF6.js → start-54JXJ7VV.js} +2 -2
- package/dist/{status-DFWM342I.js → status-PMMS4XUH.js} +7 -5
- package/dist/{stop-UQSNF4CG.js → stop-WXU42NWP.js} +2 -2
- package/dist/{up-365HL7UT.js → up-N2OWDCT6.js} +5 -4
- package/dist/{update-PV3XM6DX.js → update-FXLGIIWH.js} +5 -4
- package/dist/{update-check-YPGH5X4E.js → update-check-NBHTSTHK.js} +2 -2
- package/dist/{upgrade-RSE4CZNE.js → upgrade-TEI7N6CQ.js} +1 -1
- package/dist/{variant-7IZF6OWO.js → variant-ZCZS3JAP.js} +4 -4
- package/dist/web-assets/assets/index-TqXd1QOX.js +307 -0
- package/dist/web-assets/index.html +1 -1
- package/package.json +1 -1
- package/templates/_base/_skills/orientation/SKILL.md +58 -0
- package/templates/_base/home/.config/config.json.tmpl +3 -0
- package/templates/_base/src/lib/startup.ts +8 -4
- package/templates/agent-sdk/volute-template.json +1 -1
- package/templates/pi/home/.config/config.json.tmpl +3 -0
- package/templates/pi/volute-template.json +1 -1
- package/dist/down-O2EQJ5DO.js +0 -13
- package/dist/web-assets/assets/index-D-3zx6vs.js +0 -307
- package/templates/_base/home/.config/volute.json.tmpl +0 -3
- package/templates/pi/home/.config/volute.json.tmpl +0 -3
|
@@ -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-AAI52BC2.js").then((m) => m.run(args.slice(1)));
|
|
10
10
|
break;
|
|
11
11
|
case "start":
|
|
12
|
-
await import("./start-
|
|
12
|
+
await import("./start-54JXJ7VV.js").then((m) => m.run(args.slice(1)));
|
|
13
13
|
break;
|
|
14
14
|
case "stop":
|
|
15
|
-
await import("./stop-
|
|
15
|
+
await import("./stop-WXU42NWP.js").then((m) => m.run(args.slice(1)));
|
|
16
16
|
break;
|
|
17
17
|
case "restart":
|
|
18
|
-
await import("./restart-
|
|
18
|
+
await import("./restart-CMP63H6A.js").then((m) => m.run(args.slice(1)));
|
|
19
19
|
break;
|
|
20
20
|
case "delete":
|
|
21
|
-
await import("./delete-
|
|
21
|
+
await import("./delete-BJ3LNU2I.js").then((m) => m.run(args.slice(1)));
|
|
22
22
|
break;
|
|
23
23
|
case "list":
|
|
24
|
-
await import("./status-
|
|
24
|
+
await import("./status-PMMS4XUH.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-PMMS4XUH.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-KHBOS6IZ.js").then((m) => m.run(logsArgs));
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
40
40
|
case "upgrade":
|
|
41
|
-
await import("./upgrade-
|
|
41
|
+
await import("./upgrade-TEI7N6CQ.js").then((m) => m.run(args.slice(1)));
|
|
42
42
|
break;
|
|
43
43
|
case "import":
|
|
44
|
-
await import("./import-
|
|
44
|
+
await import("./import-TKF67X4R.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-NQKKTRET.js";
|
|
7
|
+
import "./chunk-BJDLYTPS.js";
|
|
8
|
+
import "./chunk-EN3NHRQC.js";
|
|
9
|
+
import "./chunk-ESTOWEG2.js";
|
|
10
10
|
import "./chunk-K3NQKI34.js";
|
|
11
11
|
export {
|
|
12
12
|
AgentManager,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
validateAgentName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ESTOWEG2.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/isolation.ts
|
|
7
|
-
import {
|
|
8
|
-
import { promisify } from "util";
|
|
9
|
-
var execFileAsync = promisify(execFile);
|
|
7
|
+
import { execFileSync } from "child_process";
|
|
10
8
|
function isIsolationEnabled() {
|
|
11
9
|
return process.env.VOLUTE_ISOLATION === "user";
|
|
12
10
|
}
|
|
@@ -29,7 +27,7 @@ function ensureVoluteGroup(opts) {
|
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
|
-
function createAgentUser(name) {
|
|
30
|
+
function createAgentUser(name, homeDir) {
|
|
33
31
|
if (!isIsolationEnabled()) return;
|
|
34
32
|
const user = agentUserName(name);
|
|
35
33
|
try {
|
|
@@ -38,7 +36,10 @@ function createAgentUser(name) {
|
|
|
38
36
|
} catch {
|
|
39
37
|
}
|
|
40
38
|
try {
|
|
41
|
-
|
|
39
|
+
const args = ["-r", "-M", "-G", "volute", "-s", "/usr/sbin/nologin"];
|
|
40
|
+
if (homeDir) args.push("-d", homeDir);
|
|
41
|
+
args.push(user);
|
|
42
|
+
execFileSync("useradd", args, {
|
|
42
43
|
stdio: ["ignore", "ignore", "pipe"]
|
|
43
44
|
});
|
|
44
45
|
} catch (err) {
|
|
@@ -54,18 +55,11 @@ function deleteAgentUser(name) {
|
|
|
54
55
|
} catch {
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const { stdout: uidStr } = await execFileAsync("id", ["-u", user]);
|
|
60
|
-
const { stdout: gidStr } = await execFileAsync("id", ["-g", user]);
|
|
61
|
-
return { uid: parseInt(uidStr.trim(), 10), gid: parseInt(gidStr.trim(), 10) };
|
|
62
|
-
}
|
|
63
|
-
async function applyIsolation(spawnOpts, agentName) {
|
|
64
|
-
if (!isIsolationEnabled()) return;
|
|
58
|
+
function wrapForIsolation(cmd, args, agentName) {
|
|
59
|
+
if (!isIsolationEnabled()) return [cmd, args];
|
|
65
60
|
const baseName = agentName.split("@", 2)[0];
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
spawnOpts.gid = gid;
|
|
61
|
+
const user = agentUserName(baseName);
|
|
62
|
+
return ["runuser", ["-u", user, "--", cmd, ...args]];
|
|
69
63
|
}
|
|
70
64
|
function chownAgentDir(dir, name) {
|
|
71
65
|
if (!isIsolationEnabled()) return;
|
|
@@ -89,7 +83,6 @@ export {
|
|
|
89
83
|
ensureVoluteGroup,
|
|
90
84
|
createAgentUser,
|
|
91
85
|
deleteAgentUser,
|
|
92
|
-
|
|
93
|
-
applyIsolation,
|
|
86
|
+
wrapForIsolation,
|
|
94
87
|
chownAgentDir
|
|
95
88
|
};
|
|
@@ -28,7 +28,7 @@ function readRegistry() {
|
|
|
28
28
|
if (!existsSync(registryPath)) return [];
|
|
29
29
|
try {
|
|
30
30
|
const entries = JSON.parse(readFileSync(registryPath, "utf-8"));
|
|
31
|
-
return entries.map((e) => ({ ...e, running: e.running ?? false }));
|
|
31
|
+
return entries.map((e) => ({ ...e, running: e.running ?? false, stage: e.stage ?? "mind" }));
|
|
32
32
|
} catch {
|
|
33
33
|
return [];
|
|
34
34
|
}
|
|
@@ -52,12 +52,12 @@ function validateAgentName(name) {
|
|
|
52
52
|
}
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
|
-
function addAgent(name, port) {
|
|
55
|
+
function addAgent(name, port, stage) {
|
|
56
56
|
const err = validateAgentName(name);
|
|
57
57
|
if (err) throw new Error(err);
|
|
58
58
|
const entries = readRegistry();
|
|
59
59
|
const filtered = entries.filter((e) => e.name !== name);
|
|
60
|
-
filtered.push({ name, port, created: (/* @__PURE__ */ new Date()).toISOString(), running: false });
|
|
60
|
+
filtered.push({ name, port, created: (/* @__PURE__ */ new Date()).toISOString(), running: false, stage });
|
|
61
61
|
writeRegistry(filtered);
|
|
62
62
|
}
|
|
63
63
|
function removeAgent(name) {
|
|
@@ -72,6 +72,14 @@ function setAgentRunning(name, running) {
|
|
|
72
72
|
writeRegistry(entries);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
+
function setAgentStage(name, stage) {
|
|
76
|
+
const entries = readRegistry();
|
|
77
|
+
const entry = entries.find((e) => e.name === name);
|
|
78
|
+
if (entry) {
|
|
79
|
+
entry.stage = stage;
|
|
80
|
+
writeRegistry(entries);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
75
83
|
function findAgent(name) {
|
|
76
84
|
return readRegistry().find((e) => e.name === name);
|
|
77
85
|
}
|
|
@@ -237,6 +245,7 @@ export {
|
|
|
237
245
|
addAgent,
|
|
238
246
|
removeAgent,
|
|
239
247
|
setAgentRunning,
|
|
248
|
+
setAgentStage,
|
|
240
249
|
findAgent,
|
|
241
250
|
agentDir,
|
|
242
251
|
stateDir,
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
stateDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ESTOWEG2.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/slugify.ts
|
|
7
7
|
function slugify(text) {
|
|
8
8
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
9
9
|
}
|
|
10
|
+
function buildVoluteSlug(opts) {
|
|
11
|
+
const isDM = opts.participants.length === 2;
|
|
12
|
+
if (isDM) {
|
|
13
|
+
const other = opts.participants.find((p) => p.username !== opts.agentUsername);
|
|
14
|
+
const otherSlug = other ? slugify(other.username) : "";
|
|
15
|
+
return otherSlug ? `volute:@${otherSlug}` : `volute:${opts.conversationId}`;
|
|
16
|
+
}
|
|
17
|
+
return opts.convTitle ? `volute:${slugify(opts.convTitle)}` : `volute:${opts.conversationId}`;
|
|
18
|
+
}
|
|
10
19
|
|
|
11
20
|
// src/connectors/sdk.ts
|
|
12
21
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -148,6 +157,7 @@ function resolveChannelId(agentName, slug) {
|
|
|
148
157
|
|
|
149
158
|
export {
|
|
150
159
|
slugify,
|
|
160
|
+
buildVoluteSlug,
|
|
151
161
|
loadEnv,
|
|
152
162
|
loadFollowedChannels,
|
|
153
163
|
splitMessage,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
execInherit
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VRAOTXDF.js";
|
|
5
5
|
import {
|
|
6
6
|
voluteHome
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ESTOWEG2.js";
|
|
8
8
|
|
|
9
9
|
// src/lib/service-mode.ts
|
|
10
10
|
import { execFileSync } from "child_process";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
agentEnvPath,
|
|
4
4
|
readEnv,
|
|
5
5
|
writeEnv
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BJDLYTPS.js";
|
|
7
7
|
import {
|
|
8
8
|
parseArgs
|
|
9
9
|
} from "./chunk-D424ZQGI.js";
|
|
@@ -43,7 +43,7 @@ async function run(args) {
|
|
|
43
43
|
template: { type: "string" }
|
|
44
44
|
});
|
|
45
45
|
const wsDir = resolveWorkspace(positional[0]);
|
|
46
|
-
const { daemonFetch } = await import("./daemon-client-
|
|
46
|
+
const { daemonFetch } = await import("./daemon-client-WXN43USO.js");
|
|
47
47
|
const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
|
|
48
48
|
const client = getClient();
|
|
49
49
|
const res = await daemonFetch(urlOf(client.api.agents.import.$url()), {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
applyIsolation,
|
|
4
|
-
chownAgentDir,
|
|
5
|
-
isIsolationEnabled
|
|
6
|
-
} from "./chunk-46S7YHUB.js";
|
|
7
2
|
import {
|
|
8
3
|
loadMergedEnv
|
|
9
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BJDLYTPS.js";
|
|
5
|
+
import {
|
|
6
|
+
chownAgentDir,
|
|
7
|
+
isIsolationEnabled,
|
|
8
|
+
wrapForIsolation
|
|
9
|
+
} from "./chunk-EN3NHRQC.js";
|
|
10
10
|
import {
|
|
11
11
|
agentDir,
|
|
12
12
|
findAgent,
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
setVariantRunning,
|
|
16
16
|
stateDir,
|
|
17
17
|
voluteHome
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-ESTOWEG2.js";
|
|
19
19
|
|
|
20
20
|
// src/lib/agent-manager.ts
|
|
21
21
|
import { execFile, spawn } from "child_process";
|
|
22
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
22
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
23
23
|
import { resolve } from "path";
|
|
24
24
|
import { promisify } from "util";
|
|
25
25
|
|
|
@@ -188,7 +188,13 @@ var AgentManager = class {
|
|
|
188
188
|
const logsDir = resolve(agentStateDir, "logs");
|
|
189
189
|
mkdirSync(logsDir, { recursive: true });
|
|
190
190
|
if (isIsolationEnabled()) {
|
|
191
|
-
|
|
191
|
+
try {
|
|
192
|
+
chownAgentDir(agentStateDir, baseName);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Cannot start agent ${name}: failed to set ownership on state directory ${agentStateDir}: ${err instanceof Error ? err.message : err}`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
192
198
|
}
|
|
193
199
|
const logStream = new RotatingLog(resolve(logsDir, "agent.log"));
|
|
194
200
|
const agentEnv = loadMergedEnv(name);
|
|
@@ -200,43 +206,24 @@ var AgentManager = class {
|
|
|
200
206
|
VOLUTE_AGENT_DIR: dir,
|
|
201
207
|
VOLUTE_AGENT_PORT: String(port)
|
|
202
208
|
};
|
|
203
|
-
if (isIsolationEnabled()
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
`Cannot start agent ${name}: failed to create config directory at ${agentClaudeDir}: ${err instanceof Error ? err.message : err}`
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
const sharedCreds = resolve(process.env.CLAUDE_CONFIG_DIR, ".credentials.json");
|
|
213
|
-
const agentCreds = resolve(agentClaudeDir, ".credentials.json");
|
|
214
|
-
if (existsSync3(sharedCreds)) {
|
|
215
|
-
try {
|
|
216
|
-
writeFileSync2(agentCreds, readFileSync2(sharedCreds));
|
|
217
|
-
} catch (err) {
|
|
218
|
-
throw new Error(
|
|
219
|
-
`Cannot start agent ${name}: failed to copy credentials to ${agentClaudeDir}: ${err instanceof Error ? err.message : err}`
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
console.warn(
|
|
224
|
-
`[daemon] shared credentials not found at ${sharedCreds} for agent ${name}. Copy ~/.claude/.credentials.json to ${process.env.CLAUDE_CONFIG_DIR}/.credentials.json or set ANTHROPIC_API_KEY in the agent's environment.`
|
|
225
|
-
);
|
|
209
|
+
if (isIsolationEnabled()) {
|
|
210
|
+
env.HOME = resolve(dir, "home");
|
|
211
|
+
if (process.env.CLAUDE_CONFIG_DIR) {
|
|
212
|
+
const projectsDir = resolve(process.env.CLAUDE_CONFIG_DIR, "projects");
|
|
213
|
+
mkdirSync(projectsDir, { recursive: true });
|
|
214
|
+
chmodSync(projectsDir, 1528);
|
|
226
215
|
}
|
|
227
|
-
const baseName2 = name.split("@", 2)[0];
|
|
228
|
-
chownAgentDir(agentClaudeDir, baseName2);
|
|
229
|
-
env.CLAUDE_CONFIG_DIR = agentClaudeDir;
|
|
230
216
|
}
|
|
231
217
|
const tsxBin = resolve(dir, "node_modules", ".bin", "tsx");
|
|
218
|
+
const tsxArgs = ["src/server.ts", "--port", String(port)];
|
|
219
|
+
const [spawnCmd, spawnArgs] = wrapForIsolation(tsxBin, tsxArgs, name);
|
|
232
220
|
const spawnOpts = {
|
|
233
221
|
cwd: dir,
|
|
234
222
|
stdio: ["ignore", "pipe", "pipe"],
|
|
235
223
|
detached: true,
|
|
236
224
|
env
|
|
237
225
|
};
|
|
238
|
-
|
|
239
|
-
const child = spawn(tsxBin, ["src/server.ts", "--port", String(port)], spawnOpts);
|
|
226
|
+
const child = spawn(spawnCmd, spawnArgs, spawnOpts);
|
|
240
227
|
this.agents.set(name, { child, port });
|
|
241
228
|
child.stdout?.pipe(logStream);
|
|
242
229
|
child.stderr?.pipe(logStream);
|
|
@@ -299,6 +286,10 @@ var AgentManager = class {
|
|
|
299
286
|
const parts = [];
|
|
300
287
|
if (context.type === "merge" || context.type === "merged") {
|
|
301
288
|
parts.push(`[system] Variant "${context.name}" has been merged and you have been restarted.`);
|
|
289
|
+
} else if (context.type === "sprouted") {
|
|
290
|
+
parts.push(
|
|
291
|
+
"[system] You've sprouted. You now have full agent capabilities \u2014 connectors, schedules, variants, and the complete volute CLI. Check your new skills for details."
|
|
292
|
+
);
|
|
302
293
|
} else {
|
|
303
294
|
parts.push("[system] You have been restarted.");
|
|
304
295
|
}
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
modeLabel,
|
|
5
5
|
pollHealth,
|
|
6
6
|
startService
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IELXXS7E.js";
|
|
8
8
|
import {
|
|
9
9
|
parseArgs
|
|
10
10
|
} from "./chunk-D424ZQGI.js";
|
|
11
11
|
import {
|
|
12
12
|
voluteHome
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ESTOWEG2.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/up.ts
|
|
16
16
|
import { spawn } from "child_process";
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
buildVoluteSlug,
|
|
3
4
|
resolveChannelId,
|
|
4
5
|
slugify,
|
|
5
6
|
splitMessage,
|
|
6
7
|
writeChannelEntry
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-GPZCPGV3.js";
|
|
8
9
|
import {
|
|
9
10
|
voluteHome
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ESTOWEG2.js";
|
|
11
12
|
import {
|
|
12
13
|
__export
|
|
13
14
|
} from "./chunk-K3NQKI34.js";
|
|
@@ -452,26 +453,33 @@ async function listConversations4(env) {
|
|
|
452
453
|
const convs = await res.json();
|
|
453
454
|
const results = [];
|
|
454
455
|
for (const conv of convs) {
|
|
455
|
-
let
|
|
456
|
+
let participants = [];
|
|
456
457
|
try {
|
|
457
458
|
const pRes = await fetch(
|
|
458
459
|
`${url}/api/agents/${encodeURIComponent(agentName)}/conversations/${encodeURIComponent(conv.id)}/participants`,
|
|
459
460
|
{ headers }
|
|
460
461
|
);
|
|
461
462
|
if (pRes.ok) {
|
|
462
|
-
|
|
463
|
-
|
|
463
|
+
participants = await pRes.json();
|
|
464
|
+
} else {
|
|
465
|
+
console.error(`[volute] failed to fetch participants for ${conv.id}: HTTP ${pRes.status}`);
|
|
464
466
|
}
|
|
465
467
|
} catch (err) {
|
|
466
468
|
console.error(`[volute] failed to fetch participants for ${conv.id}:`, err);
|
|
467
469
|
}
|
|
468
|
-
const
|
|
470
|
+
const isDM = participants.length === 2;
|
|
471
|
+
const slug = buildVoluteSlug({
|
|
472
|
+
participants,
|
|
473
|
+
agentUsername: agentName,
|
|
474
|
+
convTitle: conv.title,
|
|
475
|
+
conversationId: conv.id
|
|
476
|
+
});
|
|
469
477
|
results.push({
|
|
470
478
|
id: slug,
|
|
471
479
|
platformId: conv.id,
|
|
472
480
|
name: conv.title ?? "(untitled)",
|
|
473
|
-
type:
|
|
474
|
-
participantCount
|
|
481
|
+
type: isDM ? "dm" : "group",
|
|
482
|
+
participantCount: participants.length
|
|
475
483
|
});
|
|
476
484
|
}
|
|
477
485
|
return results;
|
|
@@ -510,16 +518,7 @@ async function createConversation4(env, participants, name) {
|
|
|
510
518
|
throw new Error(data.error ?? `Failed to create conversation: ${res.status}`);
|
|
511
519
|
}
|
|
512
520
|
const conv = await res.json();
|
|
513
|
-
|
|
514
|
-
if (agentName) {
|
|
515
|
-
writeChannelEntry(agentName, slug, {
|
|
516
|
-
platformId: conv.id,
|
|
517
|
-
platform: "volute",
|
|
518
|
-
name: name ?? participants.join(", "),
|
|
519
|
-
type: participants.length <= 1 ? "dm" : "group"
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
return slug;
|
|
521
|
+
return `volute:${conv.id}`;
|
|
523
522
|
}
|
|
524
523
|
|
|
525
524
|
// src/lib/channels.ts
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
pollHealthDown,
|
|
6
6
|
readDaemonConfig,
|
|
7
7
|
stopService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-IELXXS7E.js";
|
|
9
9
|
import {
|
|
10
10
|
voluteHome
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ESTOWEG2.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/down.ts
|
|
14
14
|
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
wrapForIsolation
|
|
4
|
+
} from "./chunk-EN3NHRQC.js";
|
|
2
5
|
|
|
3
6
|
// src/lib/exec.ts
|
|
4
7
|
import { execFile as execFileCb, execFileSync, spawn } from "child_process";
|
|
5
8
|
function exec(cmd, args, options) {
|
|
9
|
+
const [wrappedCmd, wrappedArgs] = options?.agentName ? wrapForIsolation(cmd, args, options.agentName) : [cmd, args];
|
|
6
10
|
return new Promise((resolve, reject) => {
|
|
7
11
|
execFileCb(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
{ cwd: options?.cwd,
|
|
12
|
+
wrappedCmd,
|
|
13
|
+
wrappedArgs,
|
|
14
|
+
{ cwd: options?.cwd, env: options?.env },
|
|
11
15
|
(err, stdout, stderr) => {
|
|
12
16
|
if (err) {
|
|
13
17
|
err.stderr = stderr;
|
|
@@ -31,11 +35,10 @@ function resolveVoluteBin() {
|
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
function execInherit(cmd, args, options) {
|
|
38
|
+
const [wrappedCmd, wrappedArgs] = options?.agentName ? wrapForIsolation(cmd, args, options.agentName) : [cmd, args];
|
|
34
39
|
return new Promise((resolve, reject) => {
|
|
35
|
-
const child = spawn(
|
|
40
|
+
const child = spawn(wrappedCmd, wrappedArgs, {
|
|
36
41
|
cwd: options?.cwd,
|
|
37
|
-
uid: options?.uid,
|
|
38
|
-
gid: options?.gid,
|
|
39
42
|
env: options?.env,
|
|
40
43
|
stdio: "inherit"
|
|
41
44
|
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/template.ts
|
|
4
|
+
import {
|
|
5
|
+
cpSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
rmSync,
|
|
12
|
+
statSync,
|
|
13
|
+
writeFileSync
|
|
14
|
+
} from "fs";
|
|
15
|
+
import { tmpdir } from "os";
|
|
16
|
+
import { dirname, join, relative, resolve } from "path";
|
|
17
|
+
function findTemplatesRoot() {
|
|
18
|
+
let dir = dirname(new URL(import.meta.url).pathname);
|
|
19
|
+
for (let i = 0; i < 5; i++) {
|
|
20
|
+
const candidate = resolve(dir, "templates");
|
|
21
|
+
if (existsSync(resolve(candidate, "_base"))) return candidate;
|
|
22
|
+
dir = dirname(dir);
|
|
23
|
+
}
|
|
24
|
+
console.error(
|
|
25
|
+
"Templates directory not found. Searched up from:",
|
|
26
|
+
dirname(new URL(import.meta.url).pathname)
|
|
27
|
+
);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
function composeTemplate(templatesRoot, templateName) {
|
|
31
|
+
const baseDir = resolve(templatesRoot, "_base");
|
|
32
|
+
const templateDir = resolve(templatesRoot, templateName);
|
|
33
|
+
if (!existsSync(baseDir)) {
|
|
34
|
+
console.error("Base template not found:", baseDir);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
if (!existsSync(templateDir)) {
|
|
38
|
+
console.error(`Template not found: ${templateName}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const composedDir = resolve(tmpdir(), `volute-template-${Date.now()}`);
|
|
42
|
+
mkdirSync(composedDir, { recursive: true });
|
|
43
|
+
cpSync(baseDir, composedDir, { recursive: true });
|
|
44
|
+
for (const file of listFiles(templateDir)) {
|
|
45
|
+
const src = resolve(templateDir, file);
|
|
46
|
+
const dest = resolve(composedDir, file);
|
|
47
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
48
|
+
cpSync(src, dest);
|
|
49
|
+
}
|
|
50
|
+
const manifestPath = resolve(composedDir, "volute-template.json");
|
|
51
|
+
if (!existsSync(manifestPath)) {
|
|
52
|
+
rmSync(composedDir, { recursive: true, force: true });
|
|
53
|
+
console.error(`Template manifest not found: ${templateName}/volute-template.json`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
57
|
+
const skillsSrc = resolve(composedDir, "_skills");
|
|
58
|
+
if (existsSync(skillsSrc)) {
|
|
59
|
+
const skillsDest = resolve(composedDir, manifest.skillsDir);
|
|
60
|
+
mkdirSync(skillsDest, { recursive: true });
|
|
61
|
+
cpSync(skillsSrc, skillsDest, { recursive: true });
|
|
62
|
+
rmSync(skillsSrc, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
rmSync(manifestPath);
|
|
65
|
+
return { composedDir, manifest };
|
|
66
|
+
}
|
|
67
|
+
function copyTemplateToDir(composedDir, destDir, agentName, manifest) {
|
|
68
|
+
cpSync(composedDir, destDir, { recursive: true });
|
|
69
|
+
for (const [from, to] of Object.entries(manifest.rename)) {
|
|
70
|
+
const fromPath = resolve(destDir, from);
|
|
71
|
+
if (existsSync(fromPath)) {
|
|
72
|
+
renameSync(fromPath, resolve(destDir, to));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const file of manifest.substitute) {
|
|
76
|
+
const path = resolve(destDir, file);
|
|
77
|
+
if (existsSync(path)) {
|
|
78
|
+
const content = readFileSync(path, "utf-8");
|
|
79
|
+
writeFileSync(path, content.replaceAll("{{name}}", agentName));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function applyInitFiles(destDir) {
|
|
84
|
+
const initDir = resolve(destDir, ".init");
|
|
85
|
+
if (!existsSync(initDir)) return;
|
|
86
|
+
const homeDir = resolve(destDir, "home");
|
|
87
|
+
for (const file of listFiles(initDir)) {
|
|
88
|
+
const src = resolve(initDir, file);
|
|
89
|
+
const dest = resolve(homeDir, file);
|
|
90
|
+
const parent = dirname(dest);
|
|
91
|
+
if (!existsSync(parent)) {
|
|
92
|
+
mkdirSync(parent, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
cpSync(src, dest);
|
|
95
|
+
}
|
|
96
|
+
rmSync(initDir, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
function listFiles(dir) {
|
|
99
|
+
const results = [];
|
|
100
|
+
function walk(current) {
|
|
101
|
+
for (const entry of readdirSync(current)) {
|
|
102
|
+
const full = join(current, entry);
|
|
103
|
+
if (statSync(full).isDirectory()) {
|
|
104
|
+
if (entry === ".git") continue;
|
|
105
|
+
walk(full);
|
|
106
|
+
} else {
|
|
107
|
+
results.push(relative(dir, full));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
walk(dir);
|
|
112
|
+
return results;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
findTemplatesRoot,
|
|
117
|
+
composeTemplate,
|
|
118
|
+
copyTemplateToDir,
|
|
119
|
+
applyInitFiles,
|
|
120
|
+
listFiles
|
|
121
|
+
};
|