volute 0.8.3 → 0.9.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 (27) hide show
  1. package/dist/{agent-YORVRB6I.js → agent-MB3OTRRK.js} +8 -8
  2. package/dist/api-client-YPKOZP2O.js +10 -0
  3. package/dist/{channel-RDGHBFSI.js → channel-G5D4VBXY.js} +67 -82
  4. package/dist/chunk-4RQBJWQX.js +17 -0
  5. package/dist/{chunk-23L3MKEV.js → chunk-STOEJOJO.js} +18 -4
  6. package/dist/cli.js +11 -11
  7. package/dist/{connector-ZP6MEFF4.js → connector-PK7D5GTN.js} +38 -21
  8. package/dist/{daemon-client-54J3EIZD.js → daemon-client-P44NU3KU.js} +1 -1
  9. package/dist/{daemon-restart-IMNCBWFV.js → daemon-restart-EKDXXHKH.js} +1 -1
  10. package/dist/daemon.js +252 -56
  11. package/dist/{delete-45TGQC4N.js → delete-WKQKE3FT.js} +7 -4
  12. package/dist/{env-KMNYGVZ2.js → env-HZMZSWWD.js} +85 -36
  13. package/dist/{history-PXJVYLVY.js → history-SH25BAA5.js} +13 -10
  14. package/dist/logs-V54B6QSG.js +77 -0
  15. package/dist/{package-2S7APQBC.js → package-WPX6LCYE.js} +1 -1
  16. package/dist/{restart-KVH3TK5N.js → restart-CCYM3MEC.js} +10 -4
  17. package/dist/{schedule-HCUCBNQI.js → schedule-XGBUF7NU.js} +26 -10
  18. package/dist/{send-BNC2S5BY.js → send-TFZ62XPZ.js} +37 -29
  19. package/dist/{start-QU73YTJW.js → start-6YRS6FF6.js} +7 -2
  20. package/dist/{status-Q6ZQJXNI.js → status-SIMKH3ZE.js} +8 -3
  21. package/dist/{stop-N7U5N6A7.js → stop-UQSNF4CG.js} +7 -2
  22. package/dist/{up-RZJMSVQS.js → up-J7AHQHIM.js} +1 -1
  23. package/dist/{upgrade-CZF6PN7Y.js → upgrade-BRNMSQBX.js} +13 -4
  24. package/dist/{variant-RKXPN5DH.js → variant-AQRAN6FR.js} +32 -15
  25. package/package.json +1 -1
  26. package/dist/logs-TZB3MTLZ.js +0 -37
  27. /package/dist/{chunk-6RDCTVQK.js → chunk-AWHQZDB4.js} +0 -0
@@ -9,36 +9,36 @@ async function run(args) {
9
9
  await import("./create-HGJHLABX.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-WKQKE3FT.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-V54B6QSG.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-BRNMSQBX.js").then((m) => m.run(args.slice(1)));
42
42
  break;
43
43
  case "import":
44
44
  await import("./import-CNEDF3TD.js").then((m) => m.run(args.slice(1)));
@@ -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
+ };
@@ -3,24 +3,16 @@ import {
3
3
  resolveAgentName
4
4
  } from "./chunk-AZEL2IEK.js";
5
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";
6
+ daemonFetch
7
+ } from "./chunk-STOEJOJO.js";
15
8
  import {
16
9
  parseArgs
17
10
  } from "./chunk-D424ZQGI.js";
11
+ import "./chunk-DP2DX4WV.js";
18
12
  import {
19
- daemonFetch
20
- } from "./chunk-23L3MKEV.js";
21
- 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
+ };
@@ -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,43 +9,43 @@ 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-WPX6LCYE.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-MB3OTRRK.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-TFZ62XPZ.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-SH25BAA5.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-AQRAN6FR.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-PK7D5GTN.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-G5D4VBXY.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-XGBUF7NU.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-HZMZSWWD.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-J7AHQHIM.js").then((m) => m.run(args));
43
43
  break;
44
44
  case "down":
45
45
  await import("./down-4DGRZRJU.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-EKDXXHKH.js").then((m) => m.run(args));
49
49
  break;
50
50
  case "setup":
51
51
  await import("./setup-7SPMWF2O.js").then((m) => m.run(args));
@@ -3,19 +3,16 @@ import {
3
3
  resolveAgentName
4
4
  } from "./chunk-AZEL2IEK.js";
5
5
  import {
6
- agentEnvPath,
7
- readEnv,
8
- writeEnv
9
- } from "./chunk-QF22MYDJ.js";
6
+ daemonFetch
7
+ } from "./chunk-STOEJOJO.js";
10
8
  import {
11
9
  parseArgs
12
10
  } from "./chunk-D424ZQGI.js";
11
+ import "./chunk-DP2DX4WV.js";
13
12
  import {
14
- daemonFetch
15
- } from "./chunk-23L3MKEV.js";
16
- import {
17
- agentDir
18
- } from "./chunk-DP2DX4WV.js";
13
+ getClient,
14
+ urlOf
15
+ } from "./chunk-4RQBJWQX.js";
19
16
  import "./chunk-K3NQKI34.js";
20
17
 
21
18
  // src/commands/connector.ts
@@ -84,8 +81,13 @@ async function connectConnector(args) {
84
81
  console.error("Usage: volute connector connect <type> [--agent <name>]");
85
82
  process.exit(1);
86
83
  }
87
- const url = `/api/agents/${encodeURIComponent(agentName)}/connectors/${encodeURIComponent(type)}`;
88
- let res = await daemonFetch(url, { method: "POST" });
84
+ const client = getClient();
85
+ const connectorUrl = urlOf(
86
+ client.api.agents[":name"].connectors[":type"].$url({
87
+ param: { name: agentName, type }
88
+ })
89
+ );
90
+ let res = await daemonFetch(connectorUrl, { method: "POST" });
89
91
  if (!res.ok) {
90
92
  const body = await res.json().catch(() => ({ error: "Unknown error" }));
91
93
  if (body.error === "missing_env" && "missing" in body) {
@@ -101,20 +103,32 @@ Set them with: volute env set <KEY> --agent ${agentName}`);
101
103
  }
102
104
  console.error(`${connectorName} connector requires some environment variables.
103
105
  `);
104
- const dir = agentDir(agentName);
105
- const envPath = agentEnvPath(dir);
106
- const env = readEnv(envPath);
107
106
  for (const v of missing) {
108
107
  const value = await promptValue(v.name, v.description);
109
108
  if (!value) {
110
109
  console.error(`No value provided for ${v.name}. Aborting.`);
111
110
  process.exit(1);
112
111
  }
113
- env[v.name] = value;
112
+ const envRes = await daemonFetch(
113
+ urlOf(
114
+ client.api.agents[":name"].env[":key"].$url({
115
+ param: { name: agentName, key: v.name }
116
+ })
117
+ ),
118
+ {
119
+ method: "PUT",
120
+ headers: { "Content-Type": "application/json" },
121
+ body: JSON.stringify({ value })
122
+ }
123
+ );
124
+ if (!envRes.ok) {
125
+ const errBody = await envRes.json().catch(() => ({}));
126
+ console.error(`Failed to set ${v.name}: ${errBody.error ?? `HTTP ${envRes.status}`}`);
127
+ process.exit(1);
128
+ }
114
129
  }
115
- writeEnv(envPath, env);
116
130
  console.log("Environment variables saved.\n");
117
- res = await daemonFetch(url, { method: "POST" });
131
+ res = await daemonFetch(connectorUrl, { method: "POST" });
118
132
  if (!res.ok) {
119
133
  const retryBody = await res.json().catch(() => ({ error: "Unknown error" }));
120
134
  console.error(
@@ -139,11 +153,14 @@ async function disconnectConnector(args) {
139
153
  console.error("Usage: volute connector disconnect <type> [--agent <name>]");
140
154
  process.exit(1);
141
155
  }
156
+ const client = getClient();
142
157
  const res = await daemonFetch(
143
- `/api/agents/${encodeURIComponent(agentName)}/connectors/${encodeURIComponent(type)}`,
144
- {
145
- method: "DELETE"
146
- }
158
+ urlOf(
159
+ client.api.agents[":name"].connectors[":type"].$url({
160
+ param: { name: agentName, type }
161
+ })
162
+ ),
163
+ { method: "DELETE" }
147
164
  );
148
165
  if (!res.ok) {
149
166
  const body = await res.json().catch(() => ({ error: "Unknown error" }));
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  daemonFetch
4
- } from "./chunk-23L3MKEV.js";
4
+ } from "./chunk-STOEJOJO.js";
5
5
  import "./chunk-DP2DX4WV.js";
6
6
  import "./chunk-K3NQKI34.js";
7
7
  export {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "./chunk-6RDCTVQK.js";
4
+ } from "./chunk-AWHQZDB4.js";
5
5
  import {
6
6
  stopDaemon
7
7
  } from "./chunk-YNNK4QN2.js";