volute 0.8.3 → 0.10.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 (45) hide show
  1. package/dist/{agent-YORVRB6I.js → agent-ECRX44DB.js} +10 -10
  2. package/dist/{agent-manager-CMMH5KQQ.js → agent-manager-MRHHKAB6.js} +2 -2
  3. package/dist/api-client-YPKOZP2O.js +10 -0
  4. package/dist/{channel-RDGHBFSI.js → channel-2WHBRDTD.js} +66 -81
  5. package/dist/chunk-4RQBJWQX.js +17 -0
  6. package/dist/{chunk-YNNK4QN2.js → chunk-FYQGANL6.js} +40 -2
  7. package/dist/chunk-R3VB7NF5.js +205 -0
  8. package/dist/{chunk-23L3MKEV.js → chunk-STOEJOJO.js} +18 -4
  9. package/dist/cli.js +14 -14
  10. package/dist/{connector-ZP6MEFF4.js → connector-L2HBLZBW.js} +37 -20
  11. package/dist/create-VBZZNJOG.js +38 -0
  12. package/dist/{daemon-client-54J3EIZD.js → daemon-client-P44NU3KU.js} +1 -1
  13. package/dist/daemon-restart-QCLR6ZZV.js +61 -0
  14. package/dist/daemon.js +1732 -214
  15. package/dist/delete-BOTVU4YO.js +35 -0
  16. package/dist/{down-4DGRZRJU.js → down-4LIQG3CE.js} +3 -1
  17. package/dist/{env-KMNYGVZ2.js → env-CGORIKVF.js} +86 -37
  18. package/dist/{history-PXJVYLVY.js → history-NI5QP27M.js} +11 -8
  19. package/dist/import-2BZUWT23.js +21 -0
  20. package/dist/logs-APWVWGNX.js +77 -0
  21. package/dist/{package-2S7APQBC.js → package-ERGXEDAF.js} +1 -1
  22. package/dist/{restart-KVH3TK5N.js → restart-CCYM3MEC.js} +10 -4
  23. package/dist/{schedule-HCUCBNQI.js → schedule-E4MFGYSA.js} +24 -8
  24. package/dist/{send-BNC2S5BY.js → send-X6OQGSD6.js} +36 -28
  25. package/dist/{start-QU73YTJW.js → start-6YRS6FF6.js} +7 -2
  26. package/dist/{status-Q6ZQJXNI.js → status-SIMKH3ZE.js} +8 -3
  27. package/dist/{stop-N7U5N6A7.js → stop-UQSNF4CG.js} +7 -2
  28. package/dist/{up-RZJMSVQS.js → up-MNNPCMFF.js} +1 -1
  29. package/dist/upgrade-RSE4CZNE.js +55 -0
  30. package/dist/variant-7IZF6OWO.js +215 -0
  31. package/package.json +1 -1
  32. package/dist/chunk-ECPQXRLB.js +0 -264
  33. package/dist/chunk-NETNFBA5.js +0 -28
  34. package/dist/chunk-XUA3JUFK.js +0 -121
  35. package/dist/create-HGJHLABX.js +0 -96
  36. package/dist/daemon-restart-IMNCBWFV.js +0 -28
  37. package/dist/delete-45TGQC4N.js +0 -67
  38. package/dist/import-CNEDF3TD.js +0 -532
  39. package/dist/logs-TZB3MTLZ.js +0 -37
  40. package/dist/upgrade-CZF6PN7Y.js +0 -224
  41. package/dist/variant-RKXPN5DH.js +0 -476
  42. package/dist/{chunk-6RDCTVQK.js → chunk-4NAGJV3I.js} +0 -0
  43. package/dist/{chunk-W6TMWYU3.js → chunk-WV4W7BAT.js} +3 -3
  44. package/dist/{service-56CY4S6Z.js → service-OW35VZ5G.js} +3 -3
  45. package/dist/{setup-7SPMWF2O.js → setup-ABMZK6LS.js} +3 -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-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
- await import("./start-QU73YTJW.js").then((m) => m.run(args.slice(1)));
12
+ await import("./start-6YRS6FF6.js").then((m) => m.run(args.slice(1)));
13
13
  break;
14
14
  case "stop":
15
- await import("./stop-N7U5N6A7.js").then((m) => m.run(args.slice(1)));
15
+ await import("./stop-UQSNF4CG.js").then((m) => m.run(args.slice(1)));
16
16
  break;
17
17
  case "restart":
18
- await import("./restart-KVH3TK5N.js").then((m) => m.run(args.slice(1)));
18
+ await import("./restart-CCYM3MEC.js").then((m) => m.run(args.slice(1)));
19
19
  break;
20
20
  case "delete":
21
- await import("./delete-45TGQC4N.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
- await import("./status-Q6ZQJXNI.js").then((m) => m.run(args.slice(1)));
24
+ await import("./status-SIMKH3ZE.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-Q6ZQJXNI.js").then((m) => m.run(rest));
31
+ await import("./status-SIMKH3ZE.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-TZB3MTLZ.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-CZF6PN7Y.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";
7
- import "./chunk-QF22MYDJ.js";
6
+ } from "./chunk-WV4W7BAT.js";
8
7
  import "./chunk-IQXBMFZG.js";
8
+ import "./chunk-QF22MYDJ.js";
9
9
  import "./chunk-DP2DX4WV.js";
10
10
  import "./chunk-K3NQKI34.js";
11
11
  export {
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getClient,
4
+ urlOf
5
+ } from "./chunk-4RQBJWQX.js";
6
+ import "./chunk-K3NQKI34.js";
7
+ export {
8
+ getClient,
9
+ urlOf
10
+ };
@@ -2,25 +2,17 @@
2
2
  import {
3
3
  resolveAgentName
4
4
  } from "./chunk-AZEL2IEK.js";
5
- import {
6
- CHANNELS,
7
- getChannelDriver
8
- } from "./chunk-LIPPXNIE.js";
9
- import {
10
- loadMergedEnv
11
- } from "./chunk-QF22MYDJ.js";
12
- import {
13
- writeChannelEntry
14
- } from "./chunk-N6MLQ26B.js";
15
5
  import {
16
6
  parseArgs
17
7
  } from "./chunk-D424ZQGI.js";
18
8
  import {
19
9
  daemonFetch
20
- } from "./chunk-23L3MKEV.js";
10
+ } from "./chunk-STOEJOJO.js";
11
+ import "./chunk-DP2DX4WV.js";
21
12
  import {
22
- agentDir
23
- } from "./chunk-DP2DX4WV.js";
13
+ getClient,
14
+ urlOf
15
+ } from "./chunk-4RQBJWQX.js";
24
16
  import "./chunk-K3NQKI34.js";
25
17
 
26
18
  // src/commands/channel.ts
@@ -72,17 +64,20 @@ async function readChannel(args) {
72
64
  }
73
65
  const agentName = resolveAgentName(flags);
74
66
  const { platform } = parseUri(uri);
75
- const driver = requireDriver(platform);
76
- const dir = agentDir(agentName);
77
- const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
78
- try {
79
- const limit = flags.limit ?? 20;
80
- const output = await driver.read(env, uri, limit);
81
- console.log(output);
82
- } catch (err) {
83
- console.error(err instanceof Error ? err.message : String(err));
67
+ const limit = flags.limit ?? 20;
68
+ const client = getClient();
69
+ const url = client.api.agents[":name"].channels.read.$url({ param: { name: agentName } });
70
+ url.searchParams.set("platform", platform);
71
+ url.searchParams.set("uri", uri);
72
+ url.searchParams.set("limit", String(limit));
73
+ const res = await daemonFetch(urlOf(url));
74
+ if (!res.ok) {
75
+ const body = await res.json().catch(() => ({}));
76
+ console.error(body.error ?? `Server responded with ${res.status}`);
84
77
  process.exit(1);
85
78
  }
79
+ const output = await res.text();
80
+ console.log(output);
86
81
  }
87
82
  async function listChannels(args) {
88
83
  const { positional, flags } = parseArgs(args, {
@@ -90,29 +85,27 @@ async function listChannels(args) {
90
85
  });
91
86
  const platform = positional[0];
92
87
  const agentName = resolveAgentName(flags);
93
- const dir = agentDir(agentName);
94
- const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
95
- const platforms = platform ? [platform] : Object.keys(CHANNELS);
96
- for (const p of platforms) {
97
- const driver = getChannelDriver(p);
98
- if (!driver?.listConversations) continue;
99
- try {
100
- const convs = await driver.listConversations(env);
101
- for (const conv of convs) {
102
- writeChannelEntry(agentName, conv.id, {
103
- platformId: conv.platformId,
104
- platform: p,
105
- name: conv.name,
106
- type: conv.type
107
- });
108
- const parts = [conv.id.padEnd(24), conv.name.padEnd(28), conv.type];
109
- if (conv.participantCount != null) {
110
- parts.push(String(conv.participantCount));
111
- }
112
- console.log(parts.join(" "));
88
+ const client = getClient();
89
+ const url = client.api.agents[":name"].channels.list.$url({ param: { name: agentName } });
90
+ if (platform) url.searchParams.set("platform", platform);
91
+ const res = await daemonFetch(urlOf(url));
92
+ if (!res.ok) {
93
+ const body = await res.json().catch(() => ({}));
94
+ console.error(body.error ?? `Server responded with ${res.status}`);
95
+ process.exit(1);
96
+ }
97
+ const results = await res.json();
98
+ for (const [p, convs] of Object.entries(results)) {
99
+ for (const conv of convs) {
100
+ if (conv.error) {
101
+ console.error(`${p}: ${conv.error}`);
102
+ continue;
103
+ }
104
+ const parts = [conv.id.padEnd(24), conv.name.padEnd(28), conv.type];
105
+ if (conv.participantCount != null) {
106
+ parts.push(String(conv.participantCount));
113
107
  }
114
- } catch (err) {
115
- console.error(`${p}: ${err instanceof Error ? err.message : String(err)}`);
108
+ console.log(parts.join(" "));
116
109
  }
117
110
  }
118
111
  }
@@ -125,23 +118,20 @@ async function listUsers(args) {
125
118
  console.error("Usage: volute channel users <platform> [--agent <name>]");
126
119
  process.exit(1);
127
120
  }
128
- const driver = requireDriver(platform);
129
- if (!driver.listUsers) {
130
- console.error(`Platform ${platform} does not support listing users`);
131
- process.exit(1);
132
- }
133
121
  const agentName = resolveAgentName(flags);
134
- const dir = agentDir(agentName);
135
- const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
136
- try {
137
- const users = await driver.listUsers(env);
138
- for (const user of users) {
139
- console.log(`${user.username.padEnd(20)} ${user.id.padEnd(20)} ${user.type ?? ""}`);
140
- }
141
- } catch (err) {
142
- console.error(err instanceof Error ? err.message : String(err));
122
+ const client = getClient();
123
+ const url = client.api.agents[":name"].channels.users.$url({ param: { name: agentName } });
124
+ url.searchParams.set("platform", platform);
125
+ const res = await daemonFetch(urlOf(url));
126
+ if (!res.ok) {
127
+ const body = await res.json().catch(() => ({}));
128
+ console.error(body.error ?? `Server responded with ${res.status}`);
143
129
  process.exit(1);
144
130
  }
131
+ const users = await res.json();
132
+ for (const user of users) {
133
+ console.log(`${user.username.padEnd(20)} ${user.id.padEnd(20)} ${user.type ?? ""}`);
134
+ }
145
135
  }
146
136
  async function createChannel(args) {
147
137
  const { positional, flags } = parseArgs(args, {
@@ -156,22 +146,24 @@ async function createChannel(args) {
156
146
  );
157
147
  process.exit(1);
158
148
  }
159
- const driver = requireDriver(platform);
160
- if (!driver.createConversation) {
161
- console.error(`Platform ${platform} does not support creating conversations`);
162
- process.exit(1);
163
- }
164
149
  const agentName = resolveAgentName(flags);
165
- const dir = agentDir(agentName);
166
- const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
167
150
  const participants = flags.participants.split(",").map((s) => s.trim());
168
- try {
169
- const slug = await driver.createConversation(env, participants, flags.name);
170
- console.log(slug);
171
- } catch (err) {
172
- console.error(err instanceof Error ? err.message : String(err));
151
+ const client = getClient();
152
+ const res = await daemonFetch(
153
+ urlOf(client.api.agents[":name"].channels.create.$url({ param: { name: agentName } })),
154
+ {
155
+ method: "POST",
156
+ headers: { "Content-Type": "application/json" },
157
+ body: JSON.stringify({ platform, participants, name: flags.name })
158
+ }
159
+ );
160
+ if (!res.ok) {
161
+ const body = await res.json().catch(() => ({}));
162
+ console.error(body.error ?? `Server responded with ${res.status}`);
173
163
  process.exit(1);
174
164
  }
165
+ const data = await res.json();
166
+ console.log(data.slug);
175
167
  }
176
168
  async function typingChannel(args) {
177
169
  const { positional, flags } = parseArgs(args, {
@@ -184,9 +176,10 @@ async function typingChannel(args) {
184
176
  }
185
177
  const agentName = resolveAgentName(flags);
186
178
  try {
187
- const res = await daemonFetch(
188
- `/api/agents/${encodeURIComponent(agentName)}/typing?channel=${encodeURIComponent(uri)}`
189
- );
179
+ const client = getClient();
180
+ const url = client.api.agents[":name"].typing.$url({ param: { name: agentName } });
181
+ url.searchParams.set("channel", uri);
182
+ const res = await daemonFetch(urlOf(url));
190
183
  if (!res.ok) {
191
184
  const body = await res.json().catch(() => ({}));
192
185
  console.error(body.error ?? `Server responded with ${res.status}`);
@@ -209,14 +202,6 @@ function parseUri(uri) {
209
202
  }
210
203
  return { platform: uri.slice(0, colonIdx), channelId: uri.slice(colonIdx + 1) };
211
204
  }
212
- function requireDriver(platform) {
213
- const driver = getChannelDriver(platform);
214
- if (!driver) {
215
- console.error(`No channel driver for platform: ${platform}`);
216
- process.exit(1);
217
- }
218
- return driver;
219
- }
220
205
  export {
221
206
  run
222
207
  };
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/api-client.ts
4
+ import { hc } from "hono/client";
5
+ var _client;
6
+ function getClient() {
7
+ _client ??= hc("http://localhost");
8
+ return _client;
9
+ }
10
+ function urlOf(url) {
11
+ return url.pathname + url.search;
12
+ }
13
+
14
+ export {
15
+ getClient,
16
+ urlOf
17
+ };
@@ -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`);
@@ -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
+ };
@@ -9,19 +9,33 @@ import { resolve } from "path";
9
9
  function readDaemonConfig() {
10
10
  const configPath = resolve(voluteHome(), "daemon.json");
11
11
  if (!existsSync(configPath)) {
12
- console.error("Volute is not running. Start with: volute up");
12
+ if (existsSync("/etc/systemd/system/volute.service") && !process.env.VOLUTE_HOME) {
13
+ console.error("Volute is running as a system service but VOLUTE_HOME is not set.");
14
+ console.error("Re-run setup to update the CLI wrapper: sudo volute setup");
15
+ console.error("Then start a new shell or run: source /etc/profile.d/volute.sh");
16
+ } else {
17
+ console.error("Volute is not running. Start with: volute up");
18
+ }
13
19
  process.exit(1);
14
20
  }
15
21
  try {
16
22
  return JSON.parse(readFileSync(configPath, "utf-8"));
17
- } catch {
18
- console.error("Volute is not running. Start with: volute up");
23
+ } catch (err) {
24
+ const code = err.code;
25
+ if (code === "EACCES") {
26
+ console.error(`Permission denied reading ${configPath}. Try: sudo volute ...`);
27
+ } else {
28
+ console.error("Volute is not running. Start with: volute up");
29
+ }
19
30
  process.exit(1);
20
31
  }
21
32
  }
22
33
  function buildUrl(config) {
23
34
  const url = new URL("http://localhost");
24
- url.hostname = config.hostname || "localhost";
35
+ let hostname = config.hostname || "localhost";
36
+ if (hostname === "0.0.0.0") hostname = "127.0.0.1";
37
+ if (hostname === "::") hostname = "[::1]";
38
+ url.hostname = hostname;
25
39
  url.port = String(config.port);
26
40
  return url.origin;
27
41
  }
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-2S7APQBC.js");
12
+ const { default: pkg } = await import("./package-ERGXEDAF.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-YORVRB6I.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-BNC2S5BY.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-PXJVYLVY.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-RKXPN5DH.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-ZP6MEFF4.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-RDGHBFSI.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-HCUCBNQI.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-KMNYGVZ2.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-RZJMSVQS.js").then((m) => m.run(args));
42
+ await import("./up-MNNPCMFF.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-IMNCBWFV.js").then((m) => m.run(args));
48
+ await import("./daemon-restart-QCLR6ZZV.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-ABMZK6LS.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));