volute 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-2AQPI3QV.js → agent-IUSETOXJ.js} +10 -10
- package/dist/{agent-manager-RMWXST3T.js → agent-manager-BLAMP6YV.js} +4 -4
- package/dist/{channel-2WHBRDTD.js → channel-JZJJRRWT.js} +2 -2
- package/dist/{chunk-RGWADNLT.js → chunk-6BQHEIDO.js} +2 -2
- package/dist/{chunk-POLMWJFC.js → chunk-AA5TDLXB.js} +1 -1
- package/dist/{chunk-WKPMR5B2.js → chunk-AOSGW3MX.js} +2 -2
- package/dist/{chunk-IZEQ47HW.js → chunk-DCZZFBCX.js} +2 -2
- package/dist/{chunk-R3VB7NF5.js → chunk-FE5O5RSL.js} +2 -2
- package/dist/{chunk-PHJCXAWJ.js → chunk-K2ZT3YQ3.js} +19 -37
- package/dist/{chunk-WTJI3JVR.js → chunk-KRJ6KCBI.js} +9 -6
- package/dist/{chunk-YY2QX2J6.js → chunk-KXOFPDO6.js} +1 -1
- package/dist/{chunk-DP2DX4WV.js → chunk-LGSW7T7K.js} +14 -4
- package/dist/{chunk-46S7YHUB.js → chunk-NXT67PPK.js} +12 -19
- package/dist/{chunk-QF22MYDJ.js → chunk-O4BN3ZIY.js} +1 -1
- package/dist/{chunk-STOEJOJO.js → chunk-VQIJUR43.js} +1 -1
- package/dist/chunk-XUA3JUFK.js +121 -0
- package/dist/{chunk-ZKNBD5P3.js → chunk-YYUSXARD.js} +2 -2
- package/dist/cli.js +26 -17
- package/dist/{connector-L2HBLZBW.js → connector-WFT5KK67.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-HT47ZH5T.js} +1 -1
- package/dist/{daemon-client-P44NU3KU.js → daemon-client-DEF7IFEJ.js} +2 -2
- package/dist/{daemon-restart-IVJ7X4PF.js → daemon-restart-43RQBPF2.js} +6 -5
- package/dist/daemon.js +502 -540
- package/dist/{delete-BOTVU4YO.js → delete-YG3RVURA.js} +1 -1
- package/dist/down-36YCOZ7V.js +14 -0
- package/dist/{env-CGORIKVF.js → env-BQYYF4YL.js} +2 -2
- package/dist/{history-NI5QP27M.js → history-I4KIKIUX.js} +2 -2
- package/dist/{import-2BZUWT23.js → import-UHCK6PRC.js} +3 -3
- package/dist/{logs-APWVWGNX.js → logs-2DWFES6A.js} +2 -2
- package/dist/{package-5LQNWBH7.js → package-GUQVVO3V.js} +1 -1
- package/dist/{restart-CCYM3MEC.js → restart-6PE3GWYZ.js} +2 -2
- package/dist/{schedule-E4MFGYSA.js → schedule-5AYTQM3N.js} +2 -2
- package/dist/seed-3QQVFMBU.js +68 -0
- package/dist/{send-V5ESR5O2.js → send-FPFW7J5Q.js} +18 -6
- package/dist/{service-UL3OCODG.js → service-5X5EKPVM.js} +4 -3
- package/dist/{setup-7N4KYOYN.js → setup-Y6P7RFQ4.js} +8 -8
- package/dist/sprout-VOUJ4Y3I.js +89 -0
- package/dist/{start-6YRS6FF6.js → start-ICPSQ2ZK.js} +2 -2
- package/dist/{status-DFWM342I.js → status-JBT7ENQN.js} +7 -5
- package/dist/{stop-UQSNF4CG.js → stop-IXJGAG4T.js} +2 -2
- package/dist/{up-A6XT6AFX.js → up-PR7SGODX.js} +5 -4
- package/dist/{update-PV3XM6DX.js → update-GU6JYDSN.js} +5 -4
- package/dist/{update-check-YPGH5X4E.js → update-check-MUPZYTW4.js} +2 -2
- package/dist/{upgrade-RSE4CZNE.js → upgrade-275LKIEG.js} +1 -1
- package/dist/{variant-7IZF6OWO.js → variant-RE45F2IY.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-HT47ZH5T.js").then((m) => m.run(args.slice(1)));
|
|
10
10
|
break;
|
|
11
11
|
case "start":
|
|
12
|
-
await import("./start-
|
|
12
|
+
await import("./start-ICPSQ2ZK.js").then((m) => m.run(args.slice(1)));
|
|
13
13
|
break;
|
|
14
14
|
case "stop":
|
|
15
|
-
await import("./stop-
|
|
15
|
+
await import("./stop-IXJGAG4T.js").then((m) => m.run(args.slice(1)));
|
|
16
16
|
break;
|
|
17
17
|
case "restart":
|
|
18
|
-
await import("./restart-
|
|
18
|
+
await import("./restart-6PE3GWYZ.js").then((m) => m.run(args.slice(1)));
|
|
19
19
|
break;
|
|
20
20
|
case "delete":
|
|
21
|
-
await import("./delete-
|
|
21
|
+
await import("./delete-YG3RVURA.js").then((m) => m.run(args.slice(1)));
|
|
22
22
|
break;
|
|
23
23
|
case "list":
|
|
24
|
-
await import("./status-
|
|
24
|
+
await import("./status-JBT7ENQN.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-JBT7ENQN.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-2DWFES6A.js").then((m) => m.run(logsArgs));
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
40
40
|
case "upgrade":
|
|
41
|
-
await import("./upgrade-
|
|
41
|
+
await import("./upgrade-275LKIEG.js").then((m) => m.run(args.slice(1)));
|
|
42
42
|
break;
|
|
43
43
|
case "import":
|
|
44
|
-
await import("./import-
|
|
44
|
+
await import("./import-UHCK6PRC.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-K2ZT3YQ3.js";
|
|
7
|
+
import "./chunk-O4BN3ZIY.js";
|
|
8
|
+
import "./chunk-NXT67PPK.js";
|
|
9
|
+
import "./chunk-LGSW7T7K.js";
|
|
10
10
|
import "./chunk-K3NQKI34.js";
|
|
11
11
|
export {
|
|
12
12
|
AgentManager,
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
pollHealthDown,
|
|
6
6
|
readDaemonConfig,
|
|
7
7
|
stopService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YYUSXARD.js";
|
|
9
9
|
import {
|
|
10
10
|
voluteHome
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LGSW7T7K.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/down.ts
|
|
14
14
|
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
slugify,
|
|
6
6
|
splitMessage,
|
|
7
7
|
writeChannelEntry
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AA5TDLXB.js";
|
|
9
9
|
import {
|
|
10
10
|
voluteHome
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LGSW7T7K.js";
|
|
12
12
|
import {
|
|
13
13
|
__export
|
|
14
14
|
} from "./chunk-K3NQKI34.js";
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
modeLabel,
|
|
5
5
|
pollHealth,
|
|
6
6
|
startService
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YYUSXARD.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-LGSW7T7K.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/up.ts
|
|
16
16
|
import { spawn } from "child_process";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
agentEnvPath,
|
|
4
4
|
readEnv,
|
|
5
5
|
writeEnv
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-O4BN3ZIY.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-DEF7IFEJ.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-O4BN3ZIY.js";
|
|
5
|
+
import {
|
|
6
|
+
chownAgentDir,
|
|
7
|
+
isIsolationEnabled,
|
|
8
|
+
wrapForIsolation
|
|
9
|
+
} from "./chunk-NXT67PPK.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-LGSW7T7K.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
|
|
|
@@ -208,44 +208,22 @@ var AgentManager = class {
|
|
|
208
208
|
};
|
|
209
209
|
if (isIsolationEnabled()) {
|
|
210
210
|
env.HOME = resolve(dir, "home");
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
mkdirSync(agentClaudeDir, { recursive: true });
|
|
216
|
-
} catch (err) {
|
|
217
|
-
throw new Error(
|
|
218
|
-
`Cannot start agent ${name}: failed to create config directory at ${agentClaudeDir}: ${err instanceof Error ? err.message : err}`
|
|
219
|
-
);
|
|
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);
|
|
220
215
|
}
|
|
221
|
-
const sharedCreds = resolve(process.env.CLAUDE_CONFIG_DIR, ".credentials.json");
|
|
222
|
-
const agentCreds = resolve(agentClaudeDir, ".credentials.json");
|
|
223
|
-
if (existsSync3(sharedCreds)) {
|
|
224
|
-
try {
|
|
225
|
-
writeFileSync2(agentCreds, readFileSync2(sharedCreds));
|
|
226
|
-
} catch (err) {
|
|
227
|
-
throw new Error(
|
|
228
|
-
`Cannot start agent ${name}: failed to copy credentials to ${agentClaudeDir}: ${err instanceof Error ? err.message : err}`
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
console.warn(
|
|
233
|
-
`[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.`
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
const baseName2 = name.split("@", 2)[0];
|
|
237
|
-
chownAgentDir(agentClaudeDir, baseName2);
|
|
238
|
-
env.CLAUDE_CONFIG_DIR = agentClaudeDir;
|
|
239
216
|
}
|
|
240
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);
|
|
241
220
|
const spawnOpts = {
|
|
242
221
|
cwd: dir,
|
|
243
222
|
stdio: ["ignore", "pipe", "pipe"],
|
|
244
223
|
detached: true,
|
|
245
224
|
env
|
|
246
225
|
};
|
|
247
|
-
|
|
248
|
-
const child = spawn(tsxBin, ["src/server.ts", "--port", String(port)], spawnOpts);
|
|
226
|
+
const child = spawn(spawnCmd, spawnArgs, spawnOpts);
|
|
249
227
|
this.agents.set(name, { child, port });
|
|
250
228
|
child.stdout?.pipe(logStream);
|
|
251
229
|
child.stderr?.pipe(logStream);
|
|
@@ -308,6 +286,10 @@ var AgentManager = class {
|
|
|
308
286
|
const parts = [];
|
|
309
287
|
if (context.type === "merge" || context.type === "merged") {
|
|
310
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
|
+
);
|
|
311
293
|
} else {
|
|
312
294
|
parts.push("[system] You have been restarted.");
|
|
313
295
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
wrapForIsolation
|
|
4
|
+
} from "./chunk-NXT67PPK.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
|
});
|
|
@@ -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
|
}
|
|
@@ -92,7 +100,8 @@ function nextPort() {
|
|
|
92
100
|
if (v.port) usedPorts.add(v.port);
|
|
93
101
|
}
|
|
94
102
|
}
|
|
95
|
-
|
|
103
|
+
const basePort = parseInt(process.env.VOLUTE_BASE_PORT || "4100", 10);
|
|
104
|
+
let port = basePort;
|
|
96
105
|
while (usedPorts.has(port)) port++;
|
|
97
106
|
if (port > 65535) throw new Error("No available ports \u2014 all ports 4100-65535 are allocated");
|
|
98
107
|
return port;
|
|
@@ -237,6 +246,7 @@ export {
|
|
|
237
246
|
addAgent,
|
|
238
247
|
removeAgent,
|
|
239
248
|
setAgentRunning,
|
|
249
|
+
setAgentStage,
|
|
240
250
|
findAgent,
|
|
241
251
|
agentDir,
|
|
242
252
|
stateDir,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
validateAgentName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LGSW7T7K.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
|
};
|
|
@@ -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
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
execInherit
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KRJ6KCBI.js";
|
|
5
5
|
import {
|
|
6
6
|
voluteHome
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LGSW7T7K.js";
|
|
8
8
|
|
|
9
9
|
// src/lib/service-mode.ts
|
|
10
10
|
import { execFileSync } from "child_process";
|
package/dist/cli.js
CHANGED
|
@@ -9,55 +9,61 @@ if (!process.env.VOLUTE_HOME) {
|
|
|
9
9
|
var command = process.argv[2];
|
|
10
10
|
var args = process.argv.slice(3);
|
|
11
11
|
if (command === "--version" || command === "-v") {
|
|
12
|
-
const { default: pkg } = await import("./package-
|
|
12
|
+
const { default: pkg } = await import("./package-GUQVVO3V.js");
|
|
13
13
|
console.log(pkg.version);
|
|
14
14
|
process.exit(0);
|
|
15
15
|
}
|
|
16
16
|
switch (command) {
|
|
17
17
|
case "agent":
|
|
18
|
-
await import("./agent-
|
|
18
|
+
await import("./agent-IUSETOXJ.js").then((m) => m.run(args));
|
|
19
19
|
break;
|
|
20
20
|
case "send":
|
|
21
|
-
await import("./send-
|
|
21
|
+
await import("./send-FPFW7J5Q.js").then((m) => m.run(args));
|
|
22
22
|
break;
|
|
23
23
|
case "history":
|
|
24
|
-
await import("./history-
|
|
24
|
+
await import("./history-I4KIKIUX.js").then((m) => m.run(args));
|
|
25
25
|
break;
|
|
26
26
|
case "variant":
|
|
27
|
-
await import("./variant-
|
|
27
|
+
await import("./variant-RE45F2IY.js").then((m) => m.run(args));
|
|
28
28
|
break;
|
|
29
29
|
case "connector":
|
|
30
|
-
await import("./connector-
|
|
30
|
+
await import("./connector-WFT5KK67.js").then((m) => m.run(args));
|
|
31
31
|
break;
|
|
32
32
|
case "channel":
|
|
33
|
-
await import("./channel-
|
|
33
|
+
await import("./channel-JZJJRRWT.js").then((m) => m.run(args));
|
|
34
34
|
break;
|
|
35
35
|
case "schedule":
|
|
36
|
-
await import("./schedule-
|
|
36
|
+
await import("./schedule-5AYTQM3N.js").then((m) => m.run(args));
|
|
37
37
|
break;
|
|
38
38
|
case "env":
|
|
39
|
-
await import("./env-
|
|
39
|
+
await import("./env-BQYYF4YL.js").then((m) => m.run(args));
|
|
40
40
|
break;
|
|
41
41
|
case "up":
|
|
42
|
-
await import("./up-
|
|
42
|
+
await import("./up-PR7SGODX.js").then((m) => m.run(args));
|
|
43
43
|
break;
|
|
44
44
|
case "down":
|
|
45
|
-
await import("./down-
|
|
45
|
+
await import("./down-36YCOZ7V.js").then((m) => m.run(args));
|
|
46
46
|
break;
|
|
47
47
|
case "restart":
|
|
48
|
-
await import("./daemon-restart-
|
|
48
|
+
await import("./daemon-restart-43RQBPF2.js").then((m) => m.run(args));
|
|
49
49
|
break;
|
|
50
50
|
case "setup":
|
|
51
|
-
await import("./setup-
|
|
51
|
+
await import("./setup-Y6P7RFQ4.js").then((m) => m.run(args));
|
|
52
52
|
break;
|
|
53
53
|
case "service":
|
|
54
|
-
await import("./service-
|
|
54
|
+
await import("./service-5X5EKPVM.js").then((m) => m.run(args));
|
|
55
55
|
break;
|
|
56
56
|
case "update":
|
|
57
|
-
await import("./update-
|
|
57
|
+
await import("./update-GU6JYDSN.js").then((m) => m.run(args));
|
|
58
58
|
break;
|
|
59
59
|
case "status":
|
|
60
|
-
await import("./status-
|
|
60
|
+
await import("./status-JBT7ENQN.js").then((m) => m.run(args));
|
|
61
|
+
break;
|
|
62
|
+
case "seed":
|
|
63
|
+
await import("./seed-3QQVFMBU.js").then((m) => m.run(args));
|
|
64
|
+
break;
|
|
65
|
+
case "sprout":
|
|
66
|
+
await import("./sprout-VOUJ4Y3I.js").then((m) => m.run(args));
|
|
61
67
|
break;
|
|
62
68
|
case "--help":
|
|
63
69
|
case "-h":
|
|
@@ -108,6 +114,9 @@ Commands:
|
|
|
108
114
|
volute setup [--port N] [--host H] Install system service with user isolation
|
|
109
115
|
volute setup uninstall [--force] Remove system service + isolation
|
|
110
116
|
|
|
117
|
+
volute seed <name> Plant a seed agent (orientation mode)
|
|
118
|
+
volute sprout Complete orientation, become a full agent
|
|
119
|
+
|
|
111
120
|
volute update Update to latest version
|
|
112
121
|
volute status Show daemon status and agents
|
|
113
122
|
|
|
@@ -124,7 +133,7 @@ Run 'volute --help' for usage.`);
|
|
|
124
133
|
process.exit(1);
|
|
125
134
|
}
|
|
126
135
|
if (command !== "update") {
|
|
127
|
-
import("./update-check-
|
|
136
|
+
import("./update-check-MUPZYTW4.js").then((m) => m.checkForUpdate()).then((result) => {
|
|
128
137
|
if (result.updateAvailable) {
|
|
129
138
|
console.error(`
|
|
130
139
|
Update available: ${result.current} \u2192 ${result.latest}`);
|