volute 0.8.0 → 0.8.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/README.md
CHANGED
|
@@ -242,9 +242,11 @@ Or manually:
|
|
|
242
242
|
|
|
243
243
|
```sh
|
|
244
244
|
npm install -g volute
|
|
245
|
-
sudo volute setup --host 0.0.0.0
|
|
245
|
+
sudo $(which volute) setup --host 0.0.0.0
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
+
> **Note:** If you installed Node via nvm, `sudo volute` won't find the binary. Use `sudo $(which volute)` or `sudo su` then run `volute setup` directly.
|
|
249
|
+
|
|
248
250
|
This installs a system-level systemd service with data at `/var/lib/volute` and user isolation enabled. Check status with `systemctl status volute`. Uninstall with `sudo volute setup uninstall --force`.
|
|
249
251
|
|
|
250
252
|
### Auto-start (user-level)
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ 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-RJSONENE.js");
|
|
13
13
|
console.log(pkg.version);
|
|
14
14
|
process.exit(0);
|
|
15
15
|
}
|
|
@@ -48,10 +48,10 @@ switch (command) {
|
|
|
48
48
|
await import("./daemon-restart-CPBLMMRI.js").then((m) => m.run(args));
|
|
49
49
|
break;
|
|
50
50
|
case "setup":
|
|
51
|
-
await import("./setup-
|
|
51
|
+
await import("./setup-32KH5KLN.js").then((m) => m.run(args));
|
|
52
52
|
break;
|
|
53
53
|
case "service":
|
|
54
|
-
await import("./service-
|
|
54
|
+
await import("./service-XCADRKIS.js").then((m) => m.run(args));
|
|
55
55
|
break;
|
|
56
56
|
case "update":
|
|
57
57
|
await import("./update-EUCZ7XGG.js").then((m) => m.run(args));
|
|
@@ -4,7 +4,7 @@ import "./chunk-K3NQKI34.js";
|
|
|
4
4
|
// package.json
|
|
5
5
|
var package_default = {
|
|
6
6
|
name: "volute",
|
|
7
|
-
version: "0.8.
|
|
7
|
+
version: "0.8.1",
|
|
8
8
|
description: "CLI for creating and managing self-modifying AI agents powered by the Claude Agent SDK",
|
|
9
9
|
type: "module",
|
|
10
10
|
license: "MIT",
|
|
@@ -82,6 +82,13 @@ async function install(port, host) {
|
|
|
82
82
|
await execFileAsync("launchctl", ["load", path]);
|
|
83
83
|
console.log("Service installed and loaded. Volute daemon will start on login.");
|
|
84
84
|
} else if (platform === "linux") {
|
|
85
|
+
if (process.getuid?.() === 0) {
|
|
86
|
+
console.error(
|
|
87
|
+
"Error: `volute service install` uses systemd user services, which don't work as root."
|
|
88
|
+
);
|
|
89
|
+
console.error("Use `volute setup` instead to install a system-level service.");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
85
92
|
const path = unitPath();
|
|
86
93
|
mkdirSync(resolve(homedir(), ".config", "systemd", "user"), { recursive: true });
|
|
87
94
|
writeFileSync(path, generateUnit(voluteBin, port, host));
|
|
@@ -14,6 +14,8 @@ import "./chunk-K3NQKI34.js";
|
|
|
14
14
|
// src/commands/setup.ts
|
|
15
15
|
import { execFileSync } from "child_process";
|
|
16
16
|
import { existsSync, mkdirSync, rmSync, unlinkSync, writeFileSync } from "fs";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import { dirname } from "path";
|
|
17
19
|
var SERVICE_NAME = "volute.service";
|
|
18
20
|
var SERVICE_PATH = `/etc/systemd/system/${SERVICE_NAME}`;
|
|
19
21
|
var DATA_DIR = "/var/lib/volute";
|
|
@@ -24,31 +26,51 @@ function validateHost(host) {
|
|
|
24
26
|
throw new Error(`Invalid host: ${host}`);
|
|
25
27
|
}
|
|
26
28
|
}
|
|
29
|
+
function buildServicePath(voluteBin) {
|
|
30
|
+
const binDir = dirname(voluteBin);
|
|
31
|
+
const standardPaths = [
|
|
32
|
+
"/usr/local/sbin",
|
|
33
|
+
"/usr/local/bin",
|
|
34
|
+
"/usr/sbin",
|
|
35
|
+
"/usr/bin",
|
|
36
|
+
"/sbin",
|
|
37
|
+
"/bin"
|
|
38
|
+
];
|
|
39
|
+
const parts = standardPaths.includes(binDir) ? standardPaths : [binDir, ...standardPaths];
|
|
40
|
+
return parts.join(":");
|
|
41
|
+
}
|
|
27
42
|
function generateUnit(voluteBin, port, host) {
|
|
28
43
|
const args = ["up", "--foreground"];
|
|
29
44
|
if (port != null) args.push("--port", String(port));
|
|
30
45
|
if (host) args.push("--host", host);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
const home = homedir();
|
|
47
|
+
const binUnderHome = voluteBin.startsWith(`${home}/`);
|
|
48
|
+
const lines = [
|
|
49
|
+
"[Unit]",
|
|
50
|
+
"Description=Volute Agent Manager",
|
|
51
|
+
"After=network.target",
|
|
52
|
+
"",
|
|
53
|
+
"[Service]",
|
|
54
|
+
"Type=exec",
|
|
55
|
+
`ExecStart=${voluteBin} ${args.join(" ")}`,
|
|
56
|
+
`Environment=PATH=${buildServicePath(voluteBin)}`,
|
|
57
|
+
`Environment=VOLUTE_HOME=${DATA_DIR}`,
|
|
58
|
+
`Environment=VOLUTE_AGENTS_DIR=${AGENTS_DIR}`,
|
|
59
|
+
"Environment=VOLUTE_ISOLATION=user",
|
|
60
|
+
"Restart=on-failure",
|
|
61
|
+
"RestartSec=5",
|
|
62
|
+
"ProtectSystem=strict",
|
|
63
|
+
`ReadWritePaths=${DATA_DIR} ${AGENTS_DIR}`,
|
|
64
|
+
"PrivateTmp=yes"
|
|
65
|
+
];
|
|
66
|
+
if (!binUnderHome) {
|
|
67
|
+
lines.push("ProtectHome=yes");
|
|
68
|
+
} else {
|
|
69
|
+
console.warn(`Warning: ProtectHome=yes omitted because volute binary is under ${home}.`);
|
|
70
|
+
console.warn("Consider installing Node.js system-wide for stronger sandboxing.");
|
|
71
|
+
}
|
|
72
|
+
lines.push("RestrictSUIDSGID=yes", "", "[Install]", "WantedBy=multi-user.target", "");
|
|
73
|
+
return lines.join("\n");
|
|
52
74
|
}
|
|
53
75
|
function install(port, host) {
|
|
54
76
|
if (host) validateHost(host);
|
|
@@ -73,12 +95,30 @@ function install(port, host) {
|
|
|
73
95
|
console.log("Set permissions on directories");
|
|
74
96
|
writeFileSync(SERVICE_PATH, generateUnit(voluteBin, port, host ?? "0.0.0.0"));
|
|
75
97
|
console.log(`Wrote ${SERVICE_PATH}`);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
98
|
+
try {
|
|
99
|
+
execFileSync("systemctl", ["daemon-reload"]);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const e = err;
|
|
102
|
+
console.error(`Failed to reload systemd after writing ${SERVICE_PATH}.`);
|
|
103
|
+
if (e.stderr) console.error(e.stderr.toString().trim());
|
|
104
|
+
console.error(
|
|
105
|
+
"Try running `systemctl daemon-reload` manually, then `systemctl enable --now volute`."
|
|
106
|
+
);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
execFileSync("systemctl", ["enable", "--now", SERVICE_NAME]);
|
|
111
|
+
console.log("Service installed, enabled, and started.");
|
|
112
|
+
console.log(`
|
|
80
113
|
Volute daemon is running. Data directory: ${DATA_DIR}`);
|
|
81
|
-
|
|
114
|
+
console.log("Use `systemctl status volute` to check status.");
|
|
115
|
+
} catch (err) {
|
|
116
|
+
const e = err;
|
|
117
|
+
console.error("Service installed but failed to start.");
|
|
118
|
+
if (e.stderr) console.error(e.stderr.toString().trim());
|
|
119
|
+
console.error("Check `journalctl -xeu volute.service` for details.");
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
82
122
|
}
|
|
83
123
|
function uninstall(force) {
|
|
84
124
|
if (process.getuid?.() !== 0) {
|