volute 0.25.0 → 0.26.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.
Files changed (102) hide show
  1. package/README.md +15 -20
  2. package/dist/{activity-events-4O37J7PD.js → activity-events-ZMBAKLUF.js} +2 -2
  3. package/dist/api.d.ts +477 -6
  4. package/dist/{auth-HM2RSPY7.js → auth-4TV573WE.js} +2 -2
  5. package/dist/{channel-HZOSHGNF.js → channel-ZVZV42UD.js} +3 -3
  6. package/dist/{chunk-SHSWYG2J.js → chunk-2VO7453N.js} +56 -19
  7. package/dist/{chunk-PMX4EIJK.js → chunk-3CFRE2VC.js} +878 -741
  8. package/dist/{chunk-PHHKNGA3.js → chunk-3TV4GLFO.js} +2 -2
  9. package/dist/{chunk-BOTQ25QT.js → chunk-5Y3PBKW6.js} +2 -2
  10. package/dist/{chunk-BFK6SOEJ.js → chunk-J2CO4WEV.js} +1 -1
  11. package/dist/{chunk-ZSH4G2P5.js → chunk-LX22GRG7.js} +10 -13
  12. package/dist/{chunk-E7GOKNOT.js → chunk-NWI2425I.js} +1 -1
  13. package/dist/{chunk-2767L2RZ.js → chunk-OZFKBXD6.js} +1 -1
  14. package/dist/{chunk-RVKR2R7F.js → chunk-SSI47XP2.js} +10 -2
  15. package/dist/chunk-TZKJLDQN.js +78 -0
  16. package/dist/{chunk-DG7TO7EE.js → chunk-USNBKHYG.js} +3 -3
  17. package/dist/chunk-UTL75LP6.js +113 -0
  18. package/dist/{chunk-3AIBT4TW.js → chunk-V63B7DX3.js} +24 -1
  19. package/dist/{chunk-33XAVCS4.js → chunk-WBHMQ5OZ.js} +49 -0
  20. package/dist/{chunk-TRQEV3CD.js → chunk-WGOGUMPO.js} +22 -3
  21. package/dist/chunk-XOXLRRR2.js +176 -0
  22. package/dist/{chunk-JTDFJWI2.js → chunk-YJA7P64S.js} +1 -1
  23. package/dist/chunk-ZYGKG6VC.js +22 -0
  24. package/dist/cli.js +44 -20
  25. package/dist/{cloud-sync-PPBBJDY6.js → cloud-sync-NI2K3C7G.js} +11 -9
  26. package/dist/{connector-M6XFI6GM.js → connector-G722WXAU.js} +4 -4
  27. package/dist/{create-VDQJER52.js → create-4YBRTTJS.js} +1 -1
  28. package/dist/{daemon-client-JOVQZ52X.js → daemon-client-Z7FAJ6JW.js} +1 -1
  29. package/dist/{daemon-restart-FDNOZEAD.js → daemon-restart-BJZ3O4U4.js} +6 -5
  30. package/dist/daemon.js +693 -265
  31. package/dist/{delete-2MRR4JX5.js → delete-27OYNK25.js} +1 -1
  32. package/dist/{down-674SX2IZ.js → down-7UKFMJJZ.js} +4 -4
  33. package/dist/{env-2FPOZK37.js → env-M336ONDP.js} +4 -4
  34. package/dist/{export-IKFAPRAO.js → export-HP4G5DQC.js} +1 -1
  35. package/dist/{file-KT3UIQM3.js → file-HUDKTRAS.js} +3 -3
  36. package/dist/{history-46WZN5CN.js → history-B64GTFTD.js} +3 -3
  37. package/dist/{import-TH26J76F.js → import-XIB7UV4S.js} +1 -1
  38. package/dist/{log-6SGSSR3D.js → log-PBFNILJ4.js} +3 -3
  39. package/dist/{login-UO6AOVEA.js → login-6U7U6BNG.js} +1 -1
  40. package/dist/login-B5E7N7MY.js +46 -0
  41. package/dist/logout-XSJRYS3U.js +39 -0
  42. package/dist/{logs-HRBONI5I.js → logs-3CART7O7.js} +3 -3
  43. package/dist/{merge-KSFJKX6T.js → merge-VK2HSKMA.js} +3 -3
  44. package/dist/{message-delivery-XMGV3FUM.js → message-delivery-MS5JYPZX.js} +10 -8
  45. package/dist/{mind-YVWAHL2A.js → mind-HZ3QSDDJ.js} +17 -17
  46. package/dist/{mind-activity-tracker-NMDDEV3K.js → mind-activity-tracker-4G6FURY2.js} +3 -3
  47. package/dist/{mind-manager-4NDNAYAB.js → mind-manager-VVK67AY3.js} +6 -4
  48. package/dist/{mind-sleep-GHPTSAYN.js → mind-sleep-DTV7L44D.js} +3 -3
  49. package/dist/{mind-wake-BJDJFMDF.js → mind-wake-PFN4FN3T.js} +3 -3
  50. package/dist/notes-37FW2UR2.js +230 -0
  51. package/dist/{package-3HF5MXU2.js → package-VZWLXPHV.js} +2 -1
  52. package/dist/{pages-Y6DRWUOJ.js → pages-DIIT5HMQ.js} +1 -1
  53. package/dist/{publish-EEKTZBHW.js → publish-HQV7YREB.js} +3 -3
  54. package/dist/{pull-D32SPFVU.js → pull-2MB4SK3C.js} +3 -3
  55. package/dist/{register-U2UO6TC4.js → register-EFND67FQ.js} +1 -1
  56. package/dist/{restart-5BMNV7KU.js → restart-CCK7D6TV.js} +3 -3
  57. package/dist/sandbox-EHGFF52K.js +19 -0
  58. package/dist/{schedule-YEFDLVMJ.js → schedule-6F7ELB2M.js} +3 -3
  59. package/dist/{seed-6FEKB3YC.js → seed-E5OQGWX3.js} +1 -1
  60. package/dist/{send-IISDYFCL.js → send-IH6XZKPC.js} +6 -20
  61. package/dist/service-LLBV3R7M.js +122 -0
  62. package/dist/setup-F6TWFYGQ.js +371 -0
  63. package/dist/setup-YGAAIKKZ.js +17 -0
  64. package/dist/{shared-LWMNTTZN.js → shared-UMO4S7CC.js} +4 -4
  65. package/dist/{skill-T3EMR6IR.js → skill-42LGFBQC.js} +3 -3
  66. package/dist/skills/dreaming/SKILL.md +68 -0
  67. package/dist/skills/dreaming/references/INSTALL.md +56 -0
  68. package/dist/skills/dreaming/scripts/dream.ts +289 -0
  69. package/dist/skills/dreaming/scripts/wake-context-dreams.sh +30 -0
  70. package/dist/skills/notes/SKILL.md +34 -0
  71. package/dist/{sleep-manager-RKTFZPD3.js → sleep-manager-EE4NRN2Q.js} +10 -8
  72. package/dist/{sprout-QJVGJDSH.js → sprout-QL74KR2X.js} +5 -5
  73. package/dist/{start-C7XITZ5O.js → start-O5JQASRC.js} +3 -3
  74. package/dist/{status-SIRPLEZC.js → status-FZBEBM7Q.js} +3 -3
  75. package/dist/{status-LYS4NUOZ.js → status-WXD4HXRL.js} +3 -3
  76. package/dist/{stop-CVKBSLXY.js → stop-2SOG5NYF.js} +3 -3
  77. package/dist/up-SDMCSVI3.js +17 -0
  78. package/dist/{update-7XCZMYBT.js → update-5VUDAI3D.js} +6 -6
  79. package/dist/{upgrade-7RUIXGOO.js → upgrade-QCCO33BK.js} +1 -1
  80. package/dist/{variant-UGREB4G5.js → variant-WWLDY6D5.js} +4 -4
  81. package/dist/{version-notify-AZQMC32A.js → version-notify-USFZBWMG.js} +11 -9
  82. package/dist/web-assets/assets/index-CUQ31ieL.js +69 -0
  83. package/dist/web-assets/assets/index-CW8NSl1o.css +1 -0
  84. package/dist/web-assets/index.html +2 -2
  85. package/drizzle/0015_notes.sql +23 -0
  86. package/drizzle/0016_note_reactions_and_replies.sql +15 -0
  87. package/drizzle/meta/_journal.json +14 -0
  88. package/package.json +2 -1
  89. package/templates/_base/.init/.config/hooks/wake-context.sh +7 -0
  90. package/templates/_base/src/lib/startup.ts +8 -0
  91. package/templates/claude/src/agent.ts +51 -1
  92. package/templates/claude/src/server.ts +1 -0
  93. package/templates/pi/package.json.tmpl +1 -0
  94. package/templates/pi/src/agent.ts +48 -1
  95. package/templates/pi/src/lib/subagents.ts +150 -0
  96. package/templates/pi/src/server.ts +1 -0
  97. package/dist/chunk-NWPT4ASZ.js +0 -89
  98. package/dist/service-FASYWLTC.js +0 -247
  99. package/dist/setup-BMLM2UTK.js +0 -230
  100. package/dist/up-CJ26KQLN.js +0 -15
  101. package/dist/web-assets/assets/index-CGPSVu19.js +0 -69
  102. package/dist/web-assets/assets/index-V_rNDsM8.css +0 -1
@@ -1,89 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- validateMindName
4
- } from "./chunk-B2CPS4QU.js";
5
-
6
- // src/lib/isolation.ts
7
- import { execFileSync } from "child_process";
8
- function isIsolationEnabled() {
9
- return process.env.VOLUTE_ISOLATION === "user";
10
- }
11
- function mindUserName(mindName) {
12
- const err = validateMindName(mindName);
13
- if (err) throw new Error(`Invalid mind name for isolation: ${err}`);
14
- const prefix = process.env.VOLUTE_USER_PREFIX ?? "mind-";
15
- return `${prefix}${mindName}`;
16
- }
17
- function ensureVoluteGroup(opts) {
18
- if (!opts?.force && !isIsolationEnabled()) return;
19
- try {
20
- execFileSync("getent", ["group", "volute"], { stdio: "ignore" });
21
- } catch {
22
- try {
23
- execFileSync("groupadd", ["volute"], { stdio: ["ignore", "ignore", "pipe"] });
24
- } catch (err) {
25
- const stderr = err?.stderr?.toString().trim();
26
- throw new Error(`Failed to create volute group${stderr ? `: ${stderr}` : ""}`);
27
- }
28
- }
29
- }
30
- function createMindUser(name, homeDir) {
31
- if (!isIsolationEnabled()) return;
32
- const user = mindUserName(name);
33
- try {
34
- execFileSync("id", [user], { stdio: "ignore" });
35
- return;
36
- } catch {
37
- }
38
- try {
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, {
43
- stdio: ["ignore", "ignore", "pipe"]
44
- });
45
- } catch (err) {
46
- const stderr = err?.stderr?.toString().trim();
47
- throw new Error(`Failed to create user ${user}${stderr ? `: ${stderr}` : ""}`);
48
- }
49
- }
50
- function deleteMindUser(name) {
51
- if (!isIsolationEnabled()) return;
52
- const user = mindUserName(name);
53
- try {
54
- execFileSync("userdel", [user], { stdio: "ignore" });
55
- } catch {
56
- }
57
- }
58
- function wrapForIsolation(cmd, args, mindName) {
59
- if (!isIsolationEnabled()) return [cmd, args];
60
- const baseName = mindName.split("@", 2)[0];
61
- const user = mindUserName(baseName);
62
- return ["runuser", ["-u", user, "--", cmd, ...args]];
63
- }
64
- function chownMindDir(dir, name) {
65
- if (!isIsolationEnabled()) return;
66
- const user = mindUserName(name);
67
- try {
68
- execFileSync("chown", ["-R", `${user}:${user}`, dir], { stdio: ["ignore", "ignore", "pipe"] });
69
- } catch (err) {
70
- const stderr = err?.stderr?.toString().trim();
71
- throw new Error(`Failed to chown ${dir} to ${user}${stderr ? `: ${stderr}` : ""}`);
72
- }
73
- try {
74
- execFileSync("chmod", ["700", dir], { stdio: ["ignore", "ignore", "pipe"] });
75
- } catch (err) {
76
- const stderr = err?.stderr?.toString().trim();
77
- throw new Error(`Failed to chmod ${dir}${stderr ? `: ${stderr}` : ""}`);
78
- }
79
- }
80
-
81
- export {
82
- isIsolationEnabled,
83
- mindUserName,
84
- ensureVoluteGroup,
85
- createMindUser,
86
- deleteMindUser,
87
- wrapForIsolation,
88
- chownMindDir
89
- };
@@ -1,247 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- LAUNCHD_PLIST_LABEL,
4
- LAUNCHD_PLIST_PATH,
5
- SYSTEM_SERVICE_PATH,
6
- USER_SYSTEMD_UNIT
7
- } from "./chunk-3AIBT4TW.js";
8
- import {
9
- parseArgs
10
- } from "./chunk-D424ZQGI.js";
11
- import {
12
- resolveVoluteBin
13
- } from "./chunk-JTDFJWI2.js";
14
- import "./chunk-NWPT4ASZ.js";
15
- import "./chunk-B2CPS4QU.js";
16
- import "./chunk-K3NQKI34.js";
17
-
18
- // src/commands/service.ts
19
- import { execFile } from "child_process";
20
- import { existsSync, mkdirSync, unlinkSync, writeFileSync } from "fs";
21
- import { homedir } from "os";
22
- import { resolve } from "path";
23
- import { promisify } from "util";
24
- var execFileAsync = promisify(execFile);
25
- var HOST_RE = /^[a-zA-Z0-9.:_-]+$/;
26
- function validateHost(host) {
27
- if (!HOST_RE.test(host)) {
28
- throw new Error(`Invalid host: ${host}`);
29
- }
30
- }
31
- function escapeXml(s) {
32
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
33
- }
34
- function generatePlist(voluteBin, port, host) {
35
- const args = ["up", "--foreground"];
36
- if (port != null) args.push("--port", String(port));
37
- if (host) args.push("--host", host);
38
- return `<?xml version="1.0" encoding="UTF-8"?>
39
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
40
- <plist version="1.0">
41
- <dict>
42
- <key>Label</key>
43
- <string>${LAUNCHD_PLIST_LABEL}</string>
44
- <key>ProgramArguments</key>
45
- <array>
46
- ${[voluteBin, ...args].map((a) => `<string>${escapeXml(a)}</string>`).join("\n ")}
47
- </array>
48
- <key>RunAtLoad</key>
49
- <true/>
50
- <key>KeepAlive</key>
51
- <true/>
52
- <key>StandardOutPath</key>
53
- <string>${resolve(homedir(), ".volute", "daemon.log")}</string>
54
- <key>StandardErrorPath</key>
55
- <string>${resolve(homedir(), ".volute", "daemon.log")}</string>
56
- </dict>
57
- </plist>`;
58
- }
59
- function generateUnit(voluteBin, port, host) {
60
- const args = ["up", "--foreground"];
61
- if (port != null) args.push("--port", String(port));
62
- if (host) args.push("--host", host);
63
- return `[Unit]
64
- Description=Volute Daemon
65
- After=network.target
66
-
67
- [Service]
68
- Type=exec
69
- ExecStart=${voluteBin} ${args.join(" ")}
70
- Restart=on-failure
71
- RestartSec=5
72
-
73
- [Install]
74
- WantedBy=default.target
75
- `;
76
- }
77
- async function install(port, host) {
78
- if (host) validateHost(host);
79
- const voluteBin = resolveVoluteBin();
80
- const platform = process.platform;
81
- if (platform === "darwin") {
82
- const path = LAUNCHD_PLIST_PATH;
83
- mkdirSync(resolve(homedir(), "Library", "LaunchAgents"), { recursive: true });
84
- writeFileSync(path, generatePlist(voluteBin, port, host));
85
- console.log(`Wrote ${path}`);
86
- await execFileAsync("launchctl", ["load", path]);
87
- console.log("Service installed and loaded. Volute daemon will start on login.");
88
- } else if (platform === "linux") {
89
- if (existsSync(SYSTEM_SERVICE_PATH)) {
90
- console.error(
91
- "A system-level Volute service is already installed (via `volute service install --system`)."
92
- );
93
- console.error("Use `systemctl status volute` to check its status.");
94
- console.error("To remove it first: sudo volute service uninstall --system");
95
- process.exit(1);
96
- }
97
- if (process.getuid?.() === 0) {
98
- console.error(
99
- "Error: `volute service install` uses systemd user services, which don't work as root."
100
- );
101
- console.error(
102
- "Use `volute service install --system` instead to install a system-level service."
103
- );
104
- process.exit(1);
105
- }
106
- const path = USER_SYSTEMD_UNIT;
107
- mkdirSync(resolve(homedir(), ".config", "systemd", "user"), { recursive: true });
108
- writeFileSync(path, generateUnit(voluteBin, port, host));
109
- console.log(`Wrote ${path}`);
110
- await execFileAsync("systemctl", ["--user", "enable", "--now", "volute"]);
111
- console.log("Service installed and enabled. Volute daemon will start on login.");
112
- } else {
113
- console.error(`Unsupported platform: ${platform}. Only macOS and Linux are supported.`);
114
- process.exit(1);
115
- }
116
- }
117
- async function uninstall() {
118
- const platform = process.platform;
119
- if (platform === "darwin") {
120
- const path = LAUNCHD_PLIST_PATH;
121
- if (existsSync(path)) {
122
- try {
123
- await execFileAsync("launchctl", ["unload", path]);
124
- } catch {
125
- console.warn("Warning: failed to unload service (may already be unloaded)");
126
- }
127
- unlinkSync(path);
128
- console.log("Service uninstalled.");
129
- } else {
130
- console.log("Service not installed.");
131
- }
132
- } else if (platform === "linux") {
133
- const path = USER_SYSTEMD_UNIT;
134
- if (existsSync(path)) {
135
- try {
136
- await execFileAsync("systemctl", ["--user", "disable", "--now", "volute"]);
137
- } catch {
138
- console.warn("Warning: failed to disable service (may already be stopped)");
139
- }
140
- unlinkSync(path);
141
- console.log("Service uninstalled.");
142
- } else {
143
- console.log("Service not installed.");
144
- }
145
- } else {
146
- console.error(`Unsupported platform: ${platform}`);
147
- process.exit(1);
148
- }
149
- }
150
- async function status() {
151
- const platform = process.platform;
152
- if (platform === "darwin") {
153
- if (!existsSync(LAUNCHD_PLIST_PATH)) {
154
- console.log("Service not installed.");
155
- return;
156
- }
157
- try {
158
- const { stdout } = await execFileAsync("launchctl", ["list", LAUNCHD_PLIST_LABEL]);
159
- console.log(stdout);
160
- } catch {
161
- console.log("Service installed but not currently loaded.");
162
- }
163
- } else if (platform === "linux") {
164
- if (existsSync(SYSTEM_SERVICE_PATH)) {
165
- try {
166
- const { stdout } = await execFileAsync("systemctl", ["status", "volute", "--no-pager"]);
167
- console.log(stdout);
168
- } catch (err) {
169
- const e = err;
170
- if (e.stdout) {
171
- console.log(e.stdout);
172
- } else {
173
- console.error("System service installed but could not retrieve status.");
174
- if (e.stderr) console.error(e.stderr);
175
- else if (e.message) console.error(e.message);
176
- console.error("Try running: systemctl status volute");
177
- }
178
- }
179
- return;
180
- }
181
- if (!existsSync(USER_SYSTEMD_UNIT)) {
182
- console.log("Service not installed.");
183
- return;
184
- }
185
- try {
186
- const { stdout } = await execFileAsync("systemctl", [
187
- "--user",
188
- "status",
189
- "volute",
190
- "--no-pager"
191
- ]);
192
- console.log(stdout);
193
- } catch (err) {
194
- const e = err;
195
- if (e.stdout) console.log(e.stdout);
196
- else console.log("Service installed but status unknown.");
197
- }
198
- } else {
199
- console.error(`Unsupported platform: ${platform}`);
200
- process.exit(1);
201
- }
202
- }
203
- async function run(args) {
204
- const { positional, flags } = parseArgs(args, {
205
- port: { type: "number" },
206
- host: { type: "string" },
207
- system: { type: "boolean" },
208
- force: { type: "boolean" }
209
- });
210
- const subcommand = positional[0];
211
- switch (subcommand) {
212
- case "install":
213
- if (flags.system) {
214
- const setup = await import("./setup-BMLM2UTK.js");
215
- setup.install(flags.port, flags.host);
216
- } else {
217
- await install(flags.port, flags.host);
218
- }
219
- break;
220
- case "uninstall":
221
- if (flags.system) {
222
- const setup = await import("./setup-BMLM2UTK.js");
223
- setup.uninstall(!!flags.force);
224
- } else {
225
- await uninstall();
226
- }
227
- break;
228
- case "status":
229
- await status();
230
- break;
231
- default:
232
- console.log(`Usage:
233
- volute service install [--port N] [--host H] Install as user-level service
234
- volute service install --system [--port N] [--host H] Install system service with user isolation
235
- volute service uninstall Remove user-level service
236
- volute service uninstall --system [--force] Remove system service (--force removes data + users)
237
- volute service status Check service status`);
238
- if (subcommand) {
239
- console.error(`
240
- Unknown subcommand: ${subcommand}`);
241
- process.exit(1);
242
- }
243
- }
244
- }
245
- export {
246
- run
247
- };
@@ -1,230 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- SYSTEM_SERVICE_PATH
4
- } from "./chunk-3AIBT4TW.js";
5
- import {
6
- resolveVoluteBin
7
- } from "./chunk-JTDFJWI2.js";
8
- import {
9
- ensureVoluteGroup
10
- } from "./chunk-NWPT4ASZ.js";
11
- import "./chunk-B2CPS4QU.js";
12
- import "./chunk-K3NQKI34.js";
13
-
14
- // src/commands/setup.ts
15
- import { execFileSync } from "child_process";
16
- import { existsSync, mkdirSync, rmSync, unlinkSync, writeFileSync } from "fs";
17
- import { homedir } from "os";
18
- import { dirname } from "path";
19
- var SERVICE_NAME = "volute.service";
20
- var PROFILE_PATH = "/etc/profile.d/volute.sh";
21
- var WRAPPER_PATH = "/usr/local/bin/volute";
22
- var DATA_DIR = "/var/lib/volute";
23
- var MINDS_DIR = "/minds";
24
- var LEGACY_AGENTS_DIR = "/agents";
25
- var HOST_RE = /^[a-zA-Z0-9.:_-]+$/;
26
- function validateHost(host) {
27
- if (!HOST_RE.test(host)) {
28
- throw new Error(`Invalid host: ${host}`);
29
- }
30
- }
31
- function buildServicePath(voluteBin) {
32
- const binDir = dirname(voluteBin);
33
- const standardPaths = [
34
- "/usr/local/sbin",
35
- "/usr/local/bin",
36
- "/usr/sbin",
37
- "/usr/bin",
38
- "/sbin",
39
- "/bin"
40
- ];
41
- const parts = standardPaths.includes(binDir) ? standardPaths : [binDir, ...standardPaths];
42
- return parts.join(":");
43
- }
44
- function generateUnit(voluteBin, port, host) {
45
- const args = ["up", "--foreground"];
46
- if (port != null) args.push("--port", String(port));
47
- if (host) args.push("--host", host);
48
- const home = homedir();
49
- const binUnderHome = voluteBin.startsWith(`${home}/`);
50
- const lines = [
51
- "[Unit]",
52
- "Description=Volute Mind Manager",
53
- "After=network.target",
54
- "",
55
- "[Service]",
56
- "Type=exec",
57
- `ExecStart=${voluteBin} ${args.join(" ")}`,
58
- `Environment=PATH=${buildServicePath(voluteBin)}`,
59
- `Environment=VOLUTE_HOME=${DATA_DIR}`,
60
- `Environment=VOLUTE_MINDS_DIR=${MINDS_DIR}`,
61
- "Environment=VOLUTE_ISOLATION=user",
62
- "Restart=on-failure",
63
- "RestartSec=5",
64
- "ProtectSystem=true",
65
- `ReadWritePaths=${DATA_DIR} ${MINDS_DIR}`,
66
- "PrivateTmp=yes"
67
- ];
68
- if (!binUnderHome) {
69
- lines.push("ProtectHome=yes");
70
- } else {
71
- console.warn(`Warning: ProtectHome=yes omitted because volute binary is under ${home}.`);
72
- console.warn("Consider installing Node.js system-wide for stronger sandboxing.");
73
- }
74
- lines.push("RestrictSUIDSGID=yes", "", "[Install]", "WantedBy=multi-user.target", "");
75
- return lines.join("\n");
76
- }
77
- function install(port, host) {
78
- if (host) validateHost(host);
79
- if (process.getuid?.() !== 0) {
80
- console.error("Error: volute service install --system must be run as root (use sudo).");
81
- process.exit(1);
82
- }
83
- if (process.platform !== "linux") {
84
- console.error("Error: volute service install --system is only supported on Linux.");
85
- console.error("On macOS, use `volute service install` for user-level service management.");
86
- process.exit(1);
87
- }
88
- const voluteBin = resolveVoluteBin();
89
- mkdirSync(DATA_DIR, { recursive: true });
90
- console.log(`Created ${DATA_DIR}`);
91
- mkdirSync(MINDS_DIR, { recursive: true });
92
- console.log(`Created ${MINDS_DIR}`);
93
- ensureVoluteGroup({ force: true });
94
- console.log("Ensured volute group exists");
95
- try {
96
- execFileSync("git", ["config", "--system", "user.name"]);
97
- console.log("System git identity already configured, skipping");
98
- } catch {
99
- try {
100
- execFileSync("git", ["config", "--system", "user.name", "Volute"]);
101
- execFileSync("git", ["config", "--system", "user.email", "volute@localhost"]);
102
- console.log("Configured system git identity");
103
- } catch (err) {
104
- const msg = err instanceof Error ? err.message : String(err);
105
- console.warn(`Warning: failed to set system git config: ${msg}`);
106
- console.warn("Git commits by the daemon may fail. You can set this manually with:");
107
- console.warn(' git config --system user.name "Volute"');
108
- console.warn(' git config --system user.email "volute@localhost"');
109
- }
110
- }
111
- execFileSync("chmod", ["755", DATA_DIR]);
112
- execFileSync("chmod", ["755", MINDS_DIR]);
113
- console.log("Set permissions on directories");
114
- writeFileSync(
115
- PROFILE_PATH,
116
- `export VOLUTE_HOME=${DATA_DIR}
117
- export VOLUTE_MINDS_DIR=${MINDS_DIR}
118
- `
119
- );
120
- console.log(`Wrote ${PROFILE_PATH}`);
121
- const binDir = dirname(voluteBin);
122
- if (voluteBin !== WRAPPER_PATH && !voluteBin.startsWith("/usr/bin")) {
123
- const wrapper = `#!/bin/sh
124
- export PATH="${binDir}:$PATH"
125
- export VOLUTE_HOME="${DATA_DIR}"
126
- export VOLUTE_MINDS_DIR="${MINDS_DIR}"
127
- exec "${voluteBin}" "$@"
128
- `;
129
- writeFileSync(WRAPPER_PATH, wrapper, { mode: 493 });
130
- console.log(`Wrote ${WRAPPER_PATH} (wrapper for ${voluteBin})`);
131
- }
132
- writeFileSync(SYSTEM_SERVICE_PATH, generateUnit(voluteBin, port, host ?? "0.0.0.0"));
133
- console.log(`Wrote ${SYSTEM_SERVICE_PATH}`);
134
- try {
135
- execFileSync("systemctl", ["daemon-reload"]);
136
- } catch (err) {
137
- const e = err;
138
- console.error(`Failed to reload systemd after writing ${SYSTEM_SERVICE_PATH}.`);
139
- if (e.stderr) console.error(e.stderr.toString().trim());
140
- console.error(
141
- "Try running `systemctl daemon-reload` manually, then `systemctl enable --now volute`."
142
- );
143
- process.exit(1);
144
- }
145
- try {
146
- execFileSync("systemctl", ["enable", "--now", SERVICE_NAME]);
147
- console.log("Service installed, enabled, and started.");
148
- console.log(
149
- "Run `source /etc/profile.d/volute.sh` or start a new shell to use volute CLI commands."
150
- );
151
- console.log(`
152
- Volute daemon is running. Data directory: ${DATA_DIR}`);
153
- console.log("Use `systemctl status volute` to check status.");
154
- } catch (err) {
155
- const e = err;
156
- console.error("Service installed but failed to start.");
157
- if (e.stderr) console.error(e.stderr.toString().trim());
158
- console.error("Check `journalctl -xeu volute.service` for details.");
159
- process.exit(1);
160
- }
161
- }
162
- function uninstall(force) {
163
- if (process.getuid?.() !== 0) {
164
- console.error("Error: volute service uninstall --system must be run as root (use sudo).");
165
- process.exit(1);
166
- }
167
- if (!existsSync(SYSTEM_SERVICE_PATH)) {
168
- console.log("Service not installed.");
169
- return;
170
- }
171
- try {
172
- execFileSync("systemctl", ["disable", "--now", SERVICE_NAME], { stdio: "ignore" });
173
- } catch {
174
- console.warn("Warning: failed to disable service (may already be stopped)");
175
- }
176
- unlinkSync(SYSTEM_SERVICE_PATH);
177
- if (existsSync(PROFILE_PATH)) unlinkSync(PROFILE_PATH);
178
- if (existsSync(WRAPPER_PATH)) unlinkSync(WRAPPER_PATH);
179
- try {
180
- execFileSync("systemctl", ["daemon-reload"]);
181
- } catch {
182
- console.warn("Warning: failed to reload systemd daemon");
183
- }
184
- console.log("Service stopped and removed.");
185
- if (force) {
186
- try {
187
- const output = execFileSync("getent", ["group", "volute"], { encoding: "utf-8" });
188
- const members = output.split(":")[3]?.trim();
189
- if (members) {
190
- for (const user of members.split(",")) {
191
- const u = user.trim();
192
- try {
193
- execFileSync("userdel", [u], { stdio: "ignore" });
194
- } catch {
195
- console.warn(`Warning: failed to remove user ${u}`);
196
- }
197
- try {
198
- execFileSync("groupdel", [u], { stdio: "ignore" });
199
- } catch {
200
- }
201
- }
202
- }
203
- } catch {
204
- }
205
- if (existsSync(DATA_DIR)) {
206
- rmSync(DATA_DIR, { recursive: true, force: true });
207
- console.log(`Deleted ${DATA_DIR}`);
208
- }
209
- if (existsSync(MINDS_DIR)) {
210
- rmSync(MINDS_DIR, { recursive: true, force: true });
211
- console.log(`Deleted ${MINDS_DIR}`);
212
- }
213
- if (existsSync(LEGACY_AGENTS_DIR)) {
214
- rmSync(LEGACY_AGENTS_DIR, { recursive: true, force: true });
215
- console.log(`Deleted ${LEGACY_AGENTS_DIR} (legacy)`);
216
- }
217
- try {
218
- execFileSync("groupdel", ["volute"], { stdio: "ignore" });
219
- console.log("Removed volute group");
220
- } catch {
221
- }
222
- } else {
223
- console.log(`Data directory preserved: ${DATA_DIR}`);
224
- console.log("Use --force to also remove data and system users.");
225
- }
226
- }
227
- export {
228
- install,
229
- uninstall
230
- };
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- readGlobalConfig,
4
- run
5
- } from "./chunk-ZSH4G2P5.js";
6
- import "./chunk-3AIBT4TW.js";
7
- import "./chunk-D424ZQGI.js";
8
- import "./chunk-JTDFJWI2.js";
9
- import "./chunk-NWPT4ASZ.js";
10
- import "./chunk-B2CPS4QU.js";
11
- import "./chunk-K3NQKI34.js";
12
- export {
13
- readGlobalConfig,
14
- run
15
- };