volute 0.8.2 → 0.8.3

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.
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-DP2DX4WV.js";
8
8
 
9
9
  // src/commands/up.ts
10
- import { spawn } from "child_process";
10
+ import { execFileSync, spawn } from "child_process";
11
11
  import { existsSync, mkdirSync, openSync, readFileSync } from "fs";
12
12
  import { dirname, resolve } from "path";
13
13
  function readGlobalConfig() {
@@ -20,12 +20,27 @@ function readGlobalConfig() {
20
20
  process.exit(1);
21
21
  }
22
22
  }
23
+ function isSystemdServiceEnabled() {
24
+ try {
25
+ execFileSync("systemctl", ["is-enabled", "--quiet", "volute"]);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
23
31
  async function run(args) {
24
32
  const { flags } = parseArgs(args, {
25
33
  port: { type: "number" },
26
34
  host: { type: "string" },
27
35
  foreground: { type: "boolean" }
28
36
  });
37
+ if (!flags.foreground && isSystemdServiceEnabled()) {
38
+ console.error("Volute is managed by a systemd service.");
39
+ console.error("Use: sudo systemctl start volute");
40
+ console.error(" sudo systemctl restart volute");
41
+ console.error(" systemctl status volute");
42
+ process.exit(1);
43
+ }
29
44
  const config = readGlobalConfig();
30
45
  const port = flags.port ?? config.port ?? 4200;
31
46
  const hostname = flags.host ?? config.hostname ?? "127.0.0.1";
@@ -4,9 +4,21 @@ import {
4
4
  } from "./chunk-DP2DX4WV.js";
5
5
 
6
6
  // src/commands/down.ts
7
+ import { execFileSync } from "child_process";
7
8
  import { existsSync, readFileSync, unlinkSync } from "fs";
8
9
  import { resolve } from "path";
10
+ function isSystemdServiceEnabled() {
11
+ try {
12
+ execFileSync("systemctl", ["is-enabled", "--quiet", "volute"]);
13
+ return true;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
9
18
  async function stopDaemon() {
19
+ if (isSystemdServiceEnabled()) {
20
+ return { stopped: false, reason: "systemd" };
21
+ }
10
22
  const home = voluteHome();
11
23
  const pidPath = resolve(home, "daemon.pid");
12
24
  if (!existsSync(pidPath)) {
@@ -99,7 +111,10 @@ async function stopDaemon() {
99
111
  async function run(_args) {
100
112
  const result = await stopDaemon();
101
113
  if (result.stopped) return;
102
- if (result.reason === "orphan") {
114
+ if (result.reason === "systemd") {
115
+ console.error("Volute is managed by a systemd service.");
116
+ console.error("Use: sudo systemctl stop volute");
117
+ } else if (result.reason === "orphan") {
103
118
  console.error(`Daemon appears to be running on port ${result.port} but PID file is missing.`);
104
119
  console.error(`Kill the process manually: lsof -ti :${result.port} | xargs kill`);
105
120
  } else if (result.reason === "not-running") {
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-TWWKVRRL.js");
12
+ const { default: pkg } = await import("./package-2S7APQBC.js");
13
13
  console.log(pkg.version);
14
14
  process.exit(0);
15
15
  }
@@ -39,22 +39,22 @@ switch (command) {
39
39
  await import("./env-KMNYGVZ2.js").then((m) => m.run(args));
40
40
  break;
41
41
  case "up":
42
- await import("./up-V6EAA7OZ.js").then((m) => m.run(args));
42
+ await import("./up-RZJMSVQS.js").then((m) => m.run(args));
43
43
  break;
44
44
  case "down":
45
- await import("./down-O4EWZTVA.js").then((m) => m.run(args));
45
+ await import("./down-4DGRZRJU.js").then((m) => m.run(args));
46
46
  break;
47
47
  case "restart":
48
- await import("./daemon-restart-CPBLMMRI.js").then((m) => m.run(args));
48
+ await import("./daemon-restart-IMNCBWFV.js").then((m) => m.run(args));
49
49
  break;
50
50
  case "setup":
51
- await import("./setup-2JDBGU7Y.js").then((m) => m.run(args));
51
+ await import("./setup-7SPMWF2O.js").then((m) => m.run(args));
52
52
  break;
53
53
  case "service":
54
- await import("./service-XCADRKIS.js").then((m) => m.run(args));
54
+ await import("./service-56CY4S6Z.js").then((m) => m.run(args));
55
55
  break;
56
56
  case "update":
57
- await import("./update-EUCZ7XGG.js").then((m) => m.run(args));
57
+ await import("./update-3TGXUTO2.js").then((m) => m.run(args));
58
58
  break;
59
59
  case "--help":
60
60
  case "-h":
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "./chunk-XSJ27WEM.js";
4
+ } from "./chunk-6RDCTVQK.js";
5
5
  import {
6
6
  stopDaemon
7
- } from "./chunk-HZ5LTOEJ.js";
7
+ } from "./chunk-YNNK4QN2.js";
8
8
  import "./chunk-D424ZQGI.js";
9
9
  import "./chunk-DP2DX4WV.js";
10
10
  import "./chunk-K3NQKI34.js";
@@ -12,6 +12,11 @@ import "./chunk-K3NQKI34.js";
12
12
  // src/commands/daemon-restart.ts
13
13
  async function run2(args) {
14
14
  const result = await stopDaemon();
15
+ if (!result.stopped && result.reason === "systemd") {
16
+ console.error("Volute is managed by a systemd service.");
17
+ console.error("Use: sudo systemctl restart volute");
18
+ process.exit(1);
19
+ }
15
20
  if (!result.stopped && result.reason === "kill-failed") {
16
21
  console.error("Cannot restart: failed to stop the running daemon.");
17
22
  process.exit(1);
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  run,
4
4
  stopDaemon
5
- } from "./chunk-HZ5LTOEJ.js";
5
+ } from "./chunk-YNNK4QN2.js";
6
6
  import "./chunk-DP2DX4WV.js";
7
7
  import "./chunk-K3NQKI34.js";
8
8
  export {
@@ -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.2",
7
+ version: "0.8.3",
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",
@@ -15,6 +15,7 @@ import { resolve } from "path";
15
15
  import { promisify } from "util";
16
16
  var execFileAsync = promisify(execFile);
17
17
  var HOST_RE = /^[a-zA-Z0-9.:_-]+$/;
18
+ var SYSTEM_SERVICE_PATH = "/etc/systemd/system/volute.service";
18
19
  function validateHost(host) {
19
20
  if (!HOST_RE.test(host)) {
20
21
  throw new Error(`Invalid host: ${host}`);
@@ -82,6 +83,12 @@ async function install(port, host) {
82
83
  await execFileAsync("launchctl", ["load", path]);
83
84
  console.log("Service installed and loaded. Volute daemon will start on login.");
84
85
  } else if (platform === "linux") {
86
+ if (existsSync(SYSTEM_SERVICE_PATH)) {
87
+ console.error("A system-level Volute service is already installed (via `volute setup`).");
88
+ console.error("Use `systemctl status volute` to check its status.");
89
+ console.error("To remove it first: sudo volute setup uninstall");
90
+ process.exit(1);
91
+ }
85
92
  if (process.getuid?.() === 0) {
86
93
  console.error(
87
94
  "Error: `volute service install` uses systemd user services, which don't work as root."
@@ -147,6 +154,23 @@ async function status() {
147
154
  console.log("Service installed but not currently loaded.");
148
155
  }
149
156
  } else if (platform === "linux") {
157
+ if (existsSync(SYSTEM_SERVICE_PATH)) {
158
+ try {
159
+ const { stdout } = await execFileAsync("systemctl", ["status", "volute", "--no-pager"]);
160
+ console.log(stdout);
161
+ } catch (err) {
162
+ const e = err;
163
+ if (e.stdout) {
164
+ console.log(e.stdout);
165
+ } else {
166
+ console.error("System service installed but could not retrieve status.");
167
+ if (e.stderr) console.error(e.stderr);
168
+ else if (e.message) console.error(e.message);
169
+ console.error("Try running: systemctl status volute");
170
+ }
171
+ }
172
+ return;
173
+ }
150
174
  if (!existsSync(unitPath())) {
151
175
  console.log("Service not installed.");
152
176
  return;
@@ -15,7 +15,7 @@ import "./chunk-K3NQKI34.js";
15
15
  import { execFileSync } from "child_process";
16
16
  import { existsSync, mkdirSync, rmSync, unlinkSync, writeFileSync } from "fs";
17
17
  import { homedir } from "os";
18
- import { dirname, resolve } from "path";
18
+ import { dirname } from "path";
19
19
  var SERVICE_NAME = "volute.service";
20
20
  var SERVICE_PATH = `/etc/systemd/system/${SERVICE_NAME}`;
21
21
  var PROFILE_PATH = "/etc/profile.d/volute.sh";
@@ -104,9 +104,11 @@ export VOLUTE_AGENTS_DIR=${AGENTS_DIR}
104
104
  console.log(`Wrote ${PROFILE_PATH}`);
105
105
  const binDir = dirname(voluteBin);
106
106
  if (voluteBin !== WRAPPER_PATH && !voluteBin.startsWith("/usr/bin")) {
107
- const nodeBin = resolve(binDir, "node");
108
107
  const wrapper = `#!/bin/sh
109
- exec "${nodeBin}" "${voluteBin}" "$@"
108
+ export PATH="${binDir}:$PATH"
109
+ export VOLUTE_HOME="${DATA_DIR}"
110
+ export VOLUTE_AGENTS_DIR="${AGENTS_DIR}"
111
+ exec "${voluteBin}" "$@"
110
112
  `;
111
113
  writeFileSync(WRAPPER_PATH, wrapper, { mode: 493 });
112
114
  console.log(`Wrote ${WRAPPER_PATH} (wrapper for ${voluteBin})`);
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  readGlobalConfig,
4
4
  run
5
- } from "./chunk-XSJ27WEM.js";
5
+ } from "./chunk-6RDCTVQK.js";
6
6
  import "./chunk-D424ZQGI.js";
7
7
  import "./chunk-DP2DX4WV.js";
8
8
  import "./chunk-K3NQKI34.js";
@@ -12,9 +12,19 @@ import {
12
12
  import "./chunk-K3NQKI34.js";
13
13
 
14
14
  // src/commands/update.ts
15
+ import { execFileSync } from "child_process";
15
16
  import { existsSync, readFileSync, unlinkSync } from "fs";
16
17
  import { resolve } from "path";
17
18
  async function run(_args) {
19
+ try {
20
+ execFileSync("systemctl", ["is-enabled", "--quiet", "volute"]);
21
+ console.error("Volute is managed by a systemd service.");
22
+ console.error("To update, run:");
23
+ console.error(" sudo npm install -g volute@latest");
24
+ console.error(" sudo systemctl restart volute");
25
+ process.exit(1);
26
+ } catch {
27
+ }
18
28
  const result = await checkForUpdate();
19
29
  if (result.checkFailed) {
20
30
  console.error("Could not reach npm registry. Check your network connection and try again.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volute",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "CLI for creating and managing self-modifying AI agents powered by the Claude Agent SDK",
5
5
  "type": "module",
6
6
  "license": "MIT",