volute 0.9.0 → 0.10.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.
Files changed (37) hide show
  1. package/dist/{agent-MB3OTRRK.js → agent-ECRX44DB.js} +5 -5
  2. package/dist/{agent-manager-CMMH5KQQ.js → agent-manager-4OCID725.js} +2 -2
  3. package/dist/{chunk-IQXBMFZG.js → chunk-46S7YHUB.js} +18 -6
  4. package/dist/{chunk-YNNK4QN2.js → chunk-FYQGANL6.js} +40 -2
  5. package/dist/{chunk-W6TMWYU3.js → chunk-KR6WRAJ4.js} +3 -3
  6. package/dist/chunk-R3VB7NF5.js +205 -0
  7. package/dist/cli.js +14 -14
  8. package/dist/create-VBZZNJOG.js +38 -0
  9. package/dist/daemon-restart-7X72OXOW.js +61 -0
  10. package/dist/daemon.js +1492 -170
  11. package/dist/delete-BOTVU4YO.js +35 -0
  12. package/dist/{down-4DGRZRJU.js → down-4LIQG3CE.js} +3 -1
  13. package/dist/import-2BZUWT23.js +21 -0
  14. package/dist/{package-WPX6LCYE.js → package-TNE337RE.js} +1 -1
  15. package/dist/{setup-7SPMWF2O.js → setup-6QFIHXSH.js} +5 -5
  16. package/dist/{up-J7AHQHIM.js → up-FCYL2IPZ.js} +1 -1
  17. package/dist/upgrade-RSE4CZNE.js +55 -0
  18. package/dist/variant-7IZF6OWO.js +215 -0
  19. package/package.json +1 -1
  20. package/dist/chunk-ECPQXRLB.js +0 -264
  21. package/dist/chunk-NETNFBA5.js +0 -28
  22. package/dist/chunk-XUA3JUFK.js +0 -121
  23. package/dist/create-HGJHLABX.js +0 -96
  24. package/dist/daemon-restart-EKDXXHKH.js +0 -28
  25. package/dist/delete-WKQKE3FT.js +0 -70
  26. package/dist/import-CNEDF3TD.js +0 -532
  27. package/dist/upgrade-BRNMSQBX.js +0 -233
  28. package/dist/variant-AQRAN6FR.js +0 -493
  29. package/dist/{channel-G5D4VBXY.js → channel-2WHBRDTD.js} +3 -3
  30. package/dist/{chunk-AWHQZDB4.js → chunk-M5AEQLB3.js} +0 -0
  31. package/dist/{connector-PK7D5GTN.js → connector-L2HBLZBW.js} +3 -3
  32. package/dist/{env-HZMZSWWD.js → env-CGORIKVF.js} +3 -3
  33. package/dist/{history-SH25BAA5.js → history-NI5QP27M.js} +3 -3
  34. package/dist/{logs-V54B6QSG.js → logs-APWVWGNX.js} +3 -3
  35. package/dist/{schedule-XGBUF7NU.js → schedule-E4MFGYSA.js} +3 -3
  36. package/dist/{send-TFZ62XPZ.js → send-X6OQGSD6.js} +4 -4
  37. package/dist/{service-56CY4S6Z.js → service-OW35VZ5G.js} +3 -3
@@ -6,7 +6,7 @@ async function run(args) {
6
6
  const subcommand = args[0];
7
7
  switch (subcommand) {
8
8
  case "create":
9
- await import("./create-HGJHLABX.js").then((m) => m.run(args.slice(1)));
9
+ await import("./create-VBZZNJOG.js").then((m) => m.run(args.slice(1)));
10
10
  break;
11
11
  case "start":
12
12
  await import("./start-6YRS6FF6.js").then((m) => m.run(args.slice(1)));
@@ -18,7 +18,7 @@ async function run(args) {
18
18
  await import("./restart-CCYM3MEC.js").then((m) => m.run(args.slice(1)));
19
19
  break;
20
20
  case "delete":
21
- await import("./delete-WKQKE3FT.js").then((m) => m.run(args.slice(1)));
21
+ await import("./delete-BOTVU4YO.js").then((m) => m.run(args.slice(1)));
22
22
  break;
23
23
  case "list":
24
24
  await import("./status-SIMKH3ZE.js").then((m) => m.run(args.slice(1)));
@@ -34,14 +34,14 @@ async function run(args) {
34
34
  case "logs": {
35
35
  const rest = args.slice(1);
36
36
  const logsArgs = transformAgentFlag(rest);
37
- await import("./logs-V54B6QSG.js").then((m) => m.run(logsArgs));
37
+ await import("./logs-APWVWGNX.js").then((m) => m.run(logsArgs));
38
38
  break;
39
39
  }
40
40
  case "upgrade":
41
- await import("./upgrade-BRNMSQBX.js").then((m) => m.run(args.slice(1)));
41
+ await import("./upgrade-RSE4CZNE.js").then((m) => m.run(args.slice(1)));
42
42
  break;
43
43
  case "import":
44
- await import("./import-CNEDF3TD.js").then((m) => m.run(args.slice(1)));
44
+ await import("./import-2BZUWT23.js").then((m) => m.run(args.slice(1)));
45
45
  break;
46
46
  case "--help":
47
47
  case "-h":
@@ -3,9 +3,9 @@ import {
3
3
  AgentManager,
4
4
  getAgentManager,
5
5
  initAgentManager
6
- } from "./chunk-W6TMWYU3.js";
6
+ } from "./chunk-KR6WRAJ4.js";
7
+ import "./chunk-46S7YHUB.js";
7
8
  import "./chunk-QF22MYDJ.js";
8
- import "./chunk-IQXBMFZG.js";
9
9
  import "./chunk-DP2DX4WV.js";
10
10
  import "./chunk-K3NQKI34.js";
11
11
  export {
@@ -22,9 +22,10 @@ function ensureVoluteGroup(opts) {
22
22
  execFileSync("getent", ["group", "volute"], { stdio: "ignore" });
23
23
  } catch {
24
24
  try {
25
- execFileSync("groupadd", ["volute"], { stdio: "ignore" });
25
+ execFileSync("groupadd", ["volute"], { stdio: ["ignore", "ignore", "pipe"] });
26
26
  } catch (err) {
27
- throw new Error(`Failed to create volute group: ${err}`);
27
+ const stderr = err?.stderr?.toString().trim();
28
+ throw new Error(`Failed to create volute group${stderr ? `: ${stderr}` : ""}`);
28
29
  }
29
30
  }
30
31
  }
@@ -38,10 +39,11 @@ function createAgentUser(name) {
38
39
  }
39
40
  try {
40
41
  execFileSync("useradd", ["-r", "-M", "-G", "volute", "-s", "/usr/sbin/nologin", user], {
41
- stdio: "ignore"
42
+ stdio: ["ignore", "ignore", "pipe"]
42
43
  });
43
44
  } catch (err) {
44
- throw new Error(`Failed to create user ${user}: ${err}`);
45
+ const stderr = err?.stderr?.toString().trim();
46
+ throw new Error(`Failed to create user ${user}${stderr ? `: ${stderr}` : ""}`);
45
47
  }
46
48
  }
47
49
  function deleteAgentUser(name) {
@@ -68,8 +70,18 @@ async function applyIsolation(spawnOpts, agentName) {
68
70
  function chownAgentDir(dir, name) {
69
71
  if (!isIsolationEnabled()) return;
70
72
  const user = agentUserName(name);
71
- execFileSync("chown", ["-R", `${user}:${user}`, dir]);
72
- execFileSync("chmod", ["700", dir]);
73
+ try {
74
+ execFileSync("chown", ["-R", `${user}:${user}`, dir], { stdio: ["ignore", "ignore", "pipe"] });
75
+ } catch (err) {
76
+ const stderr = err?.stderr?.toString().trim();
77
+ throw new Error(`Failed to chown ${dir} to ${user}${stderr ? `: ${stderr}` : ""}`);
78
+ }
79
+ try {
80
+ execFileSync("chmod", ["700", dir], { stdio: ["ignore", "ignore", "pipe"] });
81
+ } catch (err) {
82
+ const stderr = err?.stderr?.toString().trim();
83
+ throw new Error(`Failed to chmod ${dir}${stderr ? `: ${stderr}` : ""}`);
84
+ }
73
85
  }
74
86
 
75
87
  export {
@@ -1,7 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ daemonFetch
4
+ } from "./chunk-STOEJOJO.js";
2
5
  import {
3
6
  voluteHome
4
7
  } from "./chunk-DP2DX4WV.js";
8
+ import {
9
+ getClient,
10
+ urlOf
11
+ } from "./chunk-4RQBJWQX.js";
5
12
 
6
13
  // src/commands/down.ts
7
14
  import { execFileSync } from "child_process";
@@ -112,8 +119,39 @@ async function run(_args) {
112
119
  const result = await stopDaemon();
113
120
  if (result.stopped) return;
114
121
  if (result.reason === "systemd") {
115
- console.error("Volute is managed by a systemd service.");
116
- console.error("Use: sudo systemctl stop volute");
122
+ const client = getClient();
123
+ await daemonFetch(urlOf(client.api.system.stop.$url()), { method: "POST" });
124
+ const home = voluteHome();
125
+ const configPath = resolve(home, "daemon.json");
126
+ let hostname = "localhost";
127
+ let port = 4200;
128
+ if (existsSync(configPath)) {
129
+ try {
130
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
131
+ hostname = config.hostname || "localhost";
132
+ port = config.port ?? 4200;
133
+ } catch {
134
+ }
135
+ }
136
+ if (hostname === "0.0.0.0") hostname = "127.0.0.1";
137
+ if (hostname === "::") hostname = "[::1]";
138
+ const url = new URL("http://localhost");
139
+ url.hostname = hostname;
140
+ url.port = String(port);
141
+ const healthUrl = `${url.origin}/api/health`;
142
+ const maxWait = 1e4;
143
+ const start = Date.now();
144
+ while (Date.now() - start < maxWait) {
145
+ await new Promise((r) => setTimeout(r, 500));
146
+ try {
147
+ await fetch(healthUrl);
148
+ } catch {
149
+ console.log("Daemon stopped.");
150
+ return;
151
+ }
152
+ }
153
+ console.error("Daemon may not have stopped. Check with: volute service status");
154
+ process.exit(1);
117
155
  } else if (result.reason === "orphan") {
118
156
  console.error(`Daemon appears to be running on port ${result.port} but PID file is missing.`);
119
157
  console.error(`Kill the process manually: lsof -ti :${result.port} | xargs kill`);
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ applyIsolation
4
+ } from "./chunk-46S7YHUB.js";
2
5
  import {
3
6
  loadMergedEnv
4
7
  } from "./chunk-QF22MYDJ.js";
5
- import {
6
- applyIsolation
7
- } from "./chunk-IQXBMFZG.js";
8
8
  import {
9
9
  agentDir,
10
10
  findAgent,
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ agentEnvPath,
4
+ readEnv,
5
+ writeEnv
6
+ } from "./chunk-QF22MYDJ.js";
7
+ import {
8
+ parseArgs
9
+ } from "./chunk-D424ZQGI.js";
10
+
11
+ // src/commands/import.ts
12
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
13
+ import { homedir } from "os";
14
+ import { basename, resolve as resolve2 } from "path";
15
+
16
+ // src/lib/volute-config.ts
17
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
18
+ import { dirname, resolve } from "path";
19
+ function readJson(path) {
20
+ if (!existsSync(path)) return null;
21
+ try {
22
+ return JSON.parse(readFileSync(path, "utf-8"));
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ function readVoluteConfig(agentDir) {
28
+ const path = resolve(agentDir, "home/.config/volute.json");
29
+ return readJson(path);
30
+ }
31
+ function writeVoluteConfig(agentDir, config) {
32
+ const path = resolve(agentDir, "home/.config/volute.json");
33
+ mkdirSync(dirname(path), { recursive: true });
34
+ writeFileSync(path, `${JSON.stringify(config, null, 2)}
35
+ `);
36
+ }
37
+
38
+ // src/commands/import.ts
39
+ async function run(args) {
40
+ const { positional, flags } = parseArgs(args, {
41
+ name: { type: "string" },
42
+ session: { type: "string" },
43
+ template: { type: "string" }
44
+ });
45
+ const wsDir = resolveWorkspace(positional[0]);
46
+ const { daemonFetch } = await import("./daemon-client-P44NU3KU.js");
47
+ const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
48
+ const client = getClient();
49
+ const res = await daemonFetch(urlOf(client.api.agents.import.$url()), {
50
+ method: "POST",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify({
53
+ workspacePath: wsDir,
54
+ name: flags.name,
55
+ template: flags.template,
56
+ sessionPath: flags.session
57
+ })
58
+ });
59
+ const data = await res.json();
60
+ if (!res.ok) {
61
+ console.error(data.error ?? "Failed to import agent");
62
+ process.exit(1);
63
+ }
64
+ console.log(`
65
+ ${data.message ?? `Imported agent: ${data.name} (port ${data.port})`}`);
66
+ console.log(`
67
+ volute agent start ${data.name}`);
68
+ }
69
+ function resolveWorkspace(explicitPath) {
70
+ if (explicitPath) {
71
+ const wsDir = resolve2(explicitPath);
72
+ if (!existsSync2(resolve2(wsDir, "SOUL.md")) || !existsSync2(resolve2(wsDir, "IDENTITY.md"))) {
73
+ console.error("Not a valid OpenClaw workspace: missing SOUL.md or IDENTITY.md");
74
+ process.exit(1);
75
+ }
76
+ return wsDir;
77
+ }
78
+ const cwd = process.cwd();
79
+ if (existsSync2(resolve2(cwd, "SOUL.md")) && existsSync2(resolve2(cwd, "IDENTITY.md"))) {
80
+ console.log(`Using workspace: ${cwd}`);
81
+ return cwd;
82
+ }
83
+ const openclawWs = resolve2(homedir(), ".openclaw/workspace");
84
+ if (existsSync2(resolve2(openclawWs, "SOUL.md")) && existsSync2(resolve2(openclawWs, "IDENTITY.md"))) {
85
+ console.log(`Using workspace: ${openclawWs}`);
86
+ return openclawWs;
87
+ }
88
+ console.error(
89
+ "Usage: volute agent import [<workspace-path>] [--name <name>] [--session <path>] [--template <name>]\n\nNo OpenClaw workspace found. Provide a path, run from a workspace, or ensure ~/.openclaw/workspace exists."
90
+ );
91
+ process.exit(1);
92
+ }
93
+ function findOpenClawSession(workspaceDir) {
94
+ const agentsDir = resolve2(homedir(), ".openclaw/agents");
95
+ if (!existsSync2(agentsDir)) return void 0;
96
+ const matches = [];
97
+ try {
98
+ for (const agent of readdirSync(agentsDir)) {
99
+ const sessionsDir = resolve2(agentsDir, agent, "sessions");
100
+ if (!existsSync2(sessionsDir)) continue;
101
+ for (const file of readdirSync(sessionsDir)) {
102
+ if (!file.endsWith(".jsonl")) continue;
103
+ const fullPath = resolve2(sessionsDir, file);
104
+ if (sessionMatchesWorkspace(fullPath, workspaceDir)) {
105
+ matches.push({ path: fullPath, mtime: statSync(fullPath).mtimeMs });
106
+ }
107
+ }
108
+ }
109
+ } catch (err) {
110
+ console.warn("Warning: error scanning OpenClaw sessions:", err);
111
+ return void 0;
112
+ }
113
+ if (matches.length === 0) return void 0;
114
+ matches.sort((a, b) => b.mtime - a.mtime);
115
+ console.log(`Found session: ${matches[0].path}`);
116
+ return matches[0].path;
117
+ }
118
+ function sessionMatchesWorkspace(sessionPath, workspaceDir) {
119
+ try {
120
+ const fd = readFileSync2(sessionPath, "utf-8");
121
+ const firstLine = fd.slice(0, fd.indexOf("\n"));
122
+ const header = JSON.parse(firstLine);
123
+ return header.type === "session" && resolve2(header.cwd) === resolve2(workspaceDir);
124
+ } catch {
125
+ return false;
126
+ }
127
+ }
128
+ function importPiSession(sessionFile, agentDirPath) {
129
+ const homeDir = resolve2(agentDirPath, "home");
130
+ const piSessionDir = resolve2(agentDirPath, ".volute/pi-sessions/main");
131
+ mkdirSync2(piSessionDir, { recursive: true });
132
+ const content = readFileSync2(sessionFile, "utf-8");
133
+ const lines = content.trim().split("\n");
134
+ try {
135
+ const header = JSON.parse(lines[0]);
136
+ if (header.type === "session") {
137
+ header.cwd = homeDir;
138
+ lines[0] = JSON.stringify(header);
139
+ }
140
+ } catch {
141
+ }
142
+ const filename = basename(sessionFile);
143
+ const destPath = resolve2(piSessionDir, filename);
144
+ writeFileSync2(destPath, `${lines.join("\n")}
145
+ `);
146
+ console.log(`Imported session (${lines.length} entries)`);
147
+ }
148
+ function importOpenClawConnectors(agentName, agentDirPath) {
149
+ const configPath = resolve2(homedir(), ".openclaw/openclaw.json");
150
+ if (!existsSync2(configPath)) return;
151
+ let config;
152
+ try {
153
+ config = JSON.parse(readFileSync2(configPath, "utf-8"));
154
+ } catch (err) {
155
+ console.warn("Warning: failed to parse openclaw.json:", err);
156
+ return;
157
+ }
158
+ const discord = config.channels?.discord;
159
+ if (!discord?.enabled || !discord.token) return;
160
+ const envPath = agentEnvPath(agentName);
161
+ const env = readEnv(envPath);
162
+ env.DISCORD_TOKEN = discord.token;
163
+ writeEnv(envPath, env);
164
+ const channelNames = /* @__PURE__ */ new Set();
165
+ if (discord.guilds) {
166
+ for (const guild of Object.values(discord.guilds)) {
167
+ if (!guild.channels) continue;
168
+ for (const [name, ch] of Object.entries(guild.channels)) {
169
+ if (ch.allow) channelNames.add(name);
170
+ }
171
+ }
172
+ }
173
+ const voluteConfig = readVoluteConfig(agentDirPath) ?? {};
174
+ const connectors = new Set(voluteConfig.connectors ?? []);
175
+ connectors.add("discord");
176
+ voluteConfig.connectors = [...connectors];
177
+ if (channelNames.size > 0) {
178
+ voluteConfig.discord = { channels: [...channelNames] };
179
+ }
180
+ writeVoluteConfig(agentDirPath, voluteConfig);
181
+ console.log("Imported Discord connector config");
182
+ if (channelNames.size > 0) {
183
+ console.log(`Imported followed channels: ${[...channelNames].join(", ")}`);
184
+ }
185
+ }
186
+ function parseNameFromIdentity(identity) {
187
+ const match = identity.match(/\*\*Name:\*\*\s*(.+)/);
188
+ if (match) {
189
+ const raw = match[1].trim();
190
+ if (!raw || raw.startsWith("*") || raw.startsWith("(")) return void 0;
191
+ return raw.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9.-]/g, "");
192
+ }
193
+ return void 0;
194
+ }
195
+
196
+ export {
197
+ readVoluteConfig,
198
+ writeVoluteConfig,
199
+ run,
200
+ findOpenClawSession,
201
+ sessionMatchesWorkspace,
202
+ importPiSession,
203
+ importOpenClawConnectors,
204
+ parseNameFromIdentity
205
+ };
package/dist/cli.js CHANGED
@@ -9,49 +9,49 @@ 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-WPX6LCYE.js");
12
+ const { default: pkg } = await import("./package-TNE337RE.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-MB3OTRRK.js").then((m) => m.run(args));
18
+ await import("./agent-ECRX44DB.js").then((m) => m.run(args));
19
19
  break;
20
20
  case "send":
21
- await import("./send-TFZ62XPZ.js").then((m) => m.run(args));
21
+ await import("./send-X6OQGSD6.js").then((m) => m.run(args));
22
22
  break;
23
23
  case "history":
24
- await import("./history-SH25BAA5.js").then((m) => m.run(args));
24
+ await import("./history-NI5QP27M.js").then((m) => m.run(args));
25
25
  break;
26
26
  case "variant":
27
- await import("./variant-AQRAN6FR.js").then((m) => m.run(args));
27
+ await import("./variant-7IZF6OWO.js").then((m) => m.run(args));
28
28
  break;
29
29
  case "connector":
30
- await import("./connector-PK7D5GTN.js").then((m) => m.run(args));
30
+ await import("./connector-L2HBLZBW.js").then((m) => m.run(args));
31
31
  break;
32
32
  case "channel":
33
- await import("./channel-G5D4VBXY.js").then((m) => m.run(args));
33
+ await import("./channel-2WHBRDTD.js").then((m) => m.run(args));
34
34
  break;
35
35
  case "schedule":
36
- await import("./schedule-XGBUF7NU.js").then((m) => m.run(args));
36
+ await import("./schedule-E4MFGYSA.js").then((m) => m.run(args));
37
37
  break;
38
38
  case "env":
39
- await import("./env-HZMZSWWD.js").then((m) => m.run(args));
39
+ await import("./env-CGORIKVF.js").then((m) => m.run(args));
40
40
  break;
41
41
  case "up":
42
- await import("./up-J7AHQHIM.js").then((m) => m.run(args));
42
+ await import("./up-FCYL2IPZ.js").then((m) => m.run(args));
43
43
  break;
44
44
  case "down":
45
- await import("./down-4DGRZRJU.js").then((m) => m.run(args));
45
+ await import("./down-4LIQG3CE.js").then((m) => m.run(args));
46
46
  break;
47
47
  case "restart":
48
- await import("./daemon-restart-EKDXXHKH.js").then((m) => m.run(args));
48
+ await import("./daemon-restart-7X72OXOW.js").then((m) => m.run(args));
49
49
  break;
50
50
  case "setup":
51
- await import("./setup-7SPMWF2O.js").then((m) => m.run(args));
51
+ await import("./setup-6QFIHXSH.js").then((m) => m.run(args));
52
52
  break;
53
53
  case "service":
54
- await import("./service-56CY4S6Z.js").then((m) => m.run(args));
54
+ await import("./service-OW35VZ5G.js").then((m) => m.run(args));
55
55
  break;
56
56
  case "update":
57
57
  await import("./update-3TGXUTO2.js").then((m) => m.run(args));
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseArgs
4
+ } from "./chunk-D424ZQGI.js";
5
+ import "./chunk-K3NQKI34.js";
6
+
7
+ // src/commands/create.ts
8
+ async function run(args) {
9
+ const { positional, flags } = parseArgs(args, {
10
+ template: { type: "string" }
11
+ });
12
+ const name = positional[0];
13
+ const template = flags.template ?? "agent-sdk";
14
+ if (!name) {
15
+ console.error("Usage: volute agent create <name> [--template <name>]");
16
+ process.exit(1);
17
+ }
18
+ const { daemonFetch } = await import("./daemon-client-P44NU3KU.js");
19
+ const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
20
+ const client = getClient();
21
+ const res = await daemonFetch(urlOf(client.api.agents.$url()), {
22
+ method: "POST",
23
+ headers: { "Content-Type": "application/json" },
24
+ body: JSON.stringify({ name, template })
25
+ });
26
+ const data = await res.json();
27
+ if (!res.ok) {
28
+ console.error(data.error ?? "Failed to create agent");
29
+ process.exit(1);
30
+ }
31
+ console.log(`
32
+ ${data.message ?? `Created agent: ${data.name} (port ${data.port})`}`);
33
+ console.log(`
34
+ volute agent start ${data.name ?? name}`);
35
+ }
36
+ export {
37
+ run
38
+ };
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ run
4
+ } from "./chunk-M5AEQLB3.js";
5
+ import {
6
+ stopDaemon
7
+ } from "./chunk-FYQGANL6.js";
8
+ import "./chunk-D424ZQGI.js";
9
+ import {
10
+ daemonFetch
11
+ } from "./chunk-STOEJOJO.js";
12
+ import {
13
+ voluteHome
14
+ } from "./chunk-DP2DX4WV.js";
15
+ import {
16
+ getClient,
17
+ urlOf
18
+ } from "./chunk-4RQBJWQX.js";
19
+ import "./chunk-K3NQKI34.js";
20
+
21
+ // src/commands/daemon-restart.ts
22
+ import { readFileSync } from "fs";
23
+ import { resolve } from "path";
24
+ async function run2(args) {
25
+ const result = await stopDaemon();
26
+ if (!result.stopped && result.reason === "systemd") {
27
+ const client = getClient();
28
+ await daemonFetch(urlOf(client.api.system.restart.$url()), { method: "POST" });
29
+ const config = JSON.parse(readFileSync(resolve(voluteHome(), "daemon.json"), "utf-8"));
30
+ let hostname = config.hostname || "localhost";
31
+ if (hostname === "0.0.0.0") hostname = "127.0.0.1";
32
+ if (hostname === "::") hostname = "[::1]";
33
+ const url = new URL("http://localhost");
34
+ url.hostname = hostname;
35
+ url.port = String(config.port ?? 4200);
36
+ const healthUrl = `${url.origin}/api/health`;
37
+ const maxWait = 15e3;
38
+ const start = Date.now();
39
+ while (Date.now() - start < maxWait) {
40
+ try {
41
+ const res = await fetch(healthUrl);
42
+ if (res.ok) {
43
+ console.log("Daemon restarted.");
44
+ return;
45
+ }
46
+ } catch {
47
+ }
48
+ await new Promise((r) => setTimeout(r, 500));
49
+ }
50
+ console.error("Daemon did not restart within 15s. Check logs.");
51
+ process.exit(1);
52
+ }
53
+ if (!result.stopped && result.reason === "kill-failed") {
54
+ console.error("Cannot restart: failed to stop the running daemon.");
55
+ process.exit(1);
56
+ }
57
+ await run(args);
58
+ }
59
+ export {
60
+ run2 as run
61
+ };