volute 0.2.1 → 0.3.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 (62) hide show
  1. package/README.md +46 -0
  2. package/dist/agent-manager-AUCKMGPR.js +15 -0
  3. package/dist/{channel-2WJRM7PE.js → channel-7FZ6D25H.js} +7 -7
  4. package/dist/{chunk-6UCG6MIX.js → chunk-3C2XR4IY.js} +2 -7
  5. package/dist/{chunk-XZN4WPNC.js → chunk-5SKQ6J7T.js} +9 -1
  6. package/dist/{chunk-KFNNHQK7.js → chunk-DNOXHLE5.js} +1 -1
  7. package/dist/{chunk-L3BQEZ4Z.js → chunk-I6OHXCMV.js} +75 -14
  8. package/dist/chunk-K3NQKI34.js +10 -0
  9. package/dist/chunk-NETNFBA5.js +28 -0
  10. package/dist/chunk-SOZA2TLP.js +81 -0
  11. package/dist/chunk-VRVVQIYY.js +15 -0
  12. package/dist/{chunk-4YXYAMFT.js → chunk-YGFIWIOF.js} +7 -6
  13. package/dist/cli.js +57 -51
  14. package/dist/connector-TVJULIRT.js +96 -0
  15. package/dist/connectors/discord.js +27 -3
  16. package/dist/{create-23AM7H5B.js → create-BRG2DBWI.js} +22 -5
  17. package/dist/daemon-client-XR24PUJF.js +9 -0
  18. package/dist/daemon.js +168 -138
  19. package/dist/{delete-GDMSOW3U.js → delete-GQ7JEK2S.js} +7 -2
  20. package/dist/{down-WTF73FE7.js → down-3OB6UVAJ.js} +10 -3
  21. package/dist/{env-YKUJOFHE.js → env-JB27UAC3.js} +3 -2
  22. package/dist/{history-7WVVKMUY.js → history-3VRUBGGV.js} +9 -8
  23. package/dist/{import-42DOLBDT.js → import-K4MP2GX7.js} +143 -36
  24. package/dist/{logs-SYRQOL6B.js → logs-NXFFGUKY.js} +8 -7
  25. package/dist/{schedule-J37XQM6E.js → schedule-4I5TYHFH.js} +41 -41
  26. package/dist/{send-PLOYEYER.js → send-UK3JBZIB.js} +3 -2
  27. package/dist/service-SA4TTMDU.js +195 -0
  28. package/dist/setup-SRS7AUAA.js +148 -0
  29. package/dist/{start-AG7QLULK.js → start-LDPMCMYT.js} +3 -2
  30. package/dist/{status-GCNU4M3K.js → status-MVSQG54T.js} +3 -2
  31. package/dist/{stop-IL5Q6NER.js → stop-5PZTZCLL.js} +3 -2
  32. package/dist/{up-ZC6G6K4K.js → up-UT3IMKCA.js} +5 -3
  33. package/dist/{upgrade-DD5TNJWU.js → upgrade-CDKECCGN.js} +35 -21
  34. package/dist/variant-CVYM3EQG.js +497 -0
  35. package/dist/web-assets/assets/index-BC5eSqbY.js +296 -0
  36. package/dist/web-assets/index.html +1 -1
  37. package/drizzle/0002_wealthy_the_call.sql +6 -0
  38. package/drizzle/meta/0002_snapshot.json +339 -0
  39. package/drizzle/meta/_journal.json +7 -0
  40. package/package.json +4 -1
  41. package/templates/_base/.init/SOUL.md +5 -1
  42. package/templates/_base/_skills/memory/SKILL.md +2 -2
  43. package/templates/_base/_skills/volute-agent/SKILL.md +30 -12
  44. package/templates/_base/home/VOLUTE.md +4 -2
  45. package/templates/_base/src/lib/auto-commit.ts +8 -3
  46. package/templates/_base/src/lib/types.ts +6 -2
  47. package/templates/_base/src/lib/volute-server.ts +5 -0
  48. package/templates/agent-sdk/.init/CLAUDE.md +15 -13
  49. package/templates/agent-sdk/src/agent.ts +12 -1
  50. package/templates/agent-sdk/src/lib/agent-sessions.ts +28 -4
  51. package/templates/pi/.init/AGENTS.md +11 -9
  52. package/templates/pi/src/agent.ts +16 -3
  53. package/templates/pi/src/lib/agent-sessions.ts +26 -4
  54. package/dist/agent-manager-SSJUZWOV.js +0 -13
  55. package/dist/chunk-MY74SUOL.js +0 -81
  56. package/dist/connect-X5V5IMRW.js +0 -48
  57. package/dist/daemon-client-VN24HM5T.js +0 -10
  58. package/dist/disconnect-5JWFZ6RV.js +0 -30
  59. package/dist/fork-GRSVMBKI.js +0 -119
  60. package/dist/merge-CSAVLSLY.js +0 -216
  61. package/dist/variants-QQIEKT6M.js +0 -60
  62. package/dist/web-assets/assets/index-DNNPoxMn.js +0 -158
package/dist/cli.js CHANGED
@@ -5,92 +5,98 @@ var command = process.argv[2];
5
5
  var args = process.argv.slice(3);
6
6
  switch (command) {
7
7
  case "create":
8
- await import("./create-23AM7H5B.js").then((m) => m.run(args));
8
+ await import("./create-BRG2DBWI.js").then((m) => m.run(args));
9
9
  break;
10
10
  case "start":
11
- await import("./start-AG7QLULK.js").then((m) => m.run(args));
11
+ await import("./start-LDPMCMYT.js").then((m) => m.run(args));
12
12
  break;
13
13
  case "stop":
14
- await import("./stop-IL5Q6NER.js").then((m) => m.run(args));
14
+ await import("./stop-5PZTZCLL.js").then((m) => m.run(args));
15
15
  break;
16
16
  case "logs":
17
- await import("./logs-SYRQOL6B.js").then((m) => m.run(args));
17
+ await import("./logs-NXFFGUKY.js").then((m) => m.run(args));
18
18
  break;
19
19
  case "status":
20
- await import("./status-GCNU4M3K.js").then((m) => m.run(args));
20
+ await import("./status-MVSQG54T.js").then((m) => m.run(args));
21
21
  break;
22
- case "fork":
23
- await import("./fork-GRSVMBKI.js").then((m) => m.run(args));
24
- break;
25
- case "variants":
26
- await import("./variants-QQIEKT6M.js").then((m) => m.run(args));
22
+ case "variant":
23
+ await import("./variant-CVYM3EQG.js").then((m) => m.run(args));
27
24
  break;
28
25
  case "send":
29
- await import("./send-PLOYEYER.js").then((m) => m.run(args));
30
- break;
31
- case "merge":
32
- await import("./merge-CSAVLSLY.js").then((m) => m.run(args));
26
+ await import("./send-UK3JBZIB.js").then((m) => m.run(args));
33
27
  break;
34
28
  case "import":
35
- await import("./import-42DOLBDT.js").then((m) => m.run(args));
29
+ await import("./import-K4MP2GX7.js").then((m) => m.run(args));
36
30
  break;
37
31
  case "delete":
38
- await import("./delete-GDMSOW3U.js").then((m) => m.run(args));
32
+ await import("./delete-GQ7JEK2S.js").then((m) => m.run(args));
39
33
  break;
40
34
  case "env":
41
- await import("./env-YKUJOFHE.js").then((m) => m.run(args));
42
- break;
43
- case "connect":
44
- await import("./connect-X5V5IMRW.js").then((m) => m.run(args));
35
+ await import("./env-JB27UAC3.js").then((m) => m.run(args));
45
36
  break;
46
- case "disconnect":
47
- await import("./disconnect-5JWFZ6RV.js").then((m) => m.run(args));
37
+ case "connector":
38
+ await import("./connector-TVJULIRT.js").then((m) => m.run(args));
48
39
  break;
49
40
  case "channel":
50
- await import("./channel-2WJRM7PE.js").then((m) => m.run(args));
41
+ await import("./channel-7FZ6D25H.js").then((m) => m.run(args));
51
42
  break;
52
43
  case "upgrade":
53
- await import("./upgrade-DD5TNJWU.js").then((m) => m.run(args));
44
+ await import("./upgrade-CDKECCGN.js").then((m) => m.run(args));
54
45
  break;
55
46
  case "up":
56
- await import("./up-ZC6G6K4K.js").then((m) => m.run(args));
47
+ await import("./up-UT3IMKCA.js").then((m) => m.run(args));
57
48
  break;
58
49
  case "down":
59
- await import("./down-WTF73FE7.js").then((m) => m.run(args));
50
+ await import("./down-3OB6UVAJ.js").then((m) => m.run(args));
60
51
  break;
61
52
  case "schedule":
62
- await import("./schedule-J37XQM6E.js").then((m) => m.run(args));
53
+ await import("./schedule-4I5TYHFH.js").then((m) => m.run(args));
63
54
  break;
64
55
  case "history":
65
- await import("./history-7WVVKMUY.js").then((m) => m.run(args));
56
+ await import("./history-3VRUBGGV.js").then((m) => m.run(args));
57
+ break;
58
+ case "service":
59
+ await import("./service-SA4TTMDU.js").then((m) => m.run(args));
60
+ break;
61
+ case "setup":
62
+ await import("./setup-SRS7AUAA.js").then((m) => m.run(args));
66
63
  break;
67
64
  default:
68
65
  console.log(`volute \u2014 create and manage AI agents
69
66
 
70
67
  Commands:
71
- volute create <name> Create a new agent
72
- volute start <name> Start an agent (daemonized)
73
- volute stop <name> Stop an agent
74
- volute status [<name>] Check agent status (or list all)
75
- volute logs <name> Tail agent logs
76
- volute send <name> "<msg>" Send a message to an agent
77
- volute fork <name> <variant> Create a variant (worktree + server)
78
- volute variants <name> List variants for an agent
79
- volute merge <name> <variant> Merge a variant back
80
- volute import <path> Import an OpenClaw workspace
81
- volute env <set|get|list|remove> Manage environment variables
82
- volute connect <type> <name> Enable a connector for an agent
83
- volute disconnect <type> <name> Disable a connector for an agent
84
- volute channel read <uri> Read recent messages from a channel
85
- volute channel send <uri> "<msg>" Send a message to a channel
86
- volute schedule list <agent> List schedules for an agent
87
- volute schedule add <agent> ... Add a cron schedule
88
- volute schedule remove <agent> ... Remove a schedule
89
- volute history [<agent>] View message history
90
- volute up [--port N] Start the daemon (default: 4200)
91
- volute down Stop the daemon
92
- volute upgrade <name> Upgrade agent to latest template
93
- volute delete <name> [--force] Delete an agent (--force removes files)`);
68
+ volute create <name> Create a new agent
69
+ volute start <name> Start an agent (daemonized)
70
+ volute stop <name> Stop an agent
71
+ volute status [<name>] Check agent status (or list all)
72
+ volute logs [--agent <name>] Tail agent logs
73
+ volute send <name> "<msg>" Send a message to an agent
74
+ volute variant create <name> Create a variant (worktree + server)
75
+ volute variant list List variants for an agent
76
+ volute variant merge <name> Merge a variant back
77
+ volute variant delete <name> Delete a variant
78
+ volute import <path> Import an OpenClaw workspace
79
+ volute env <set|get|list|remove> Manage environment variables
80
+ volute connector connect <type> Enable a connector for an agent
81
+ volute connector disconnect <type> Disable a connector for an agent
82
+ volute channel read <uri> Read recent messages from a channel
83
+ volute channel send <uri> "<msg>" Send a message to a channel
84
+ volute schedule list List schedules for an agent
85
+ volute schedule add ... Add a cron schedule
86
+ volute schedule remove ... Remove a schedule
87
+ volute history View message history
88
+ volute up [--port N] Start the daemon (default: 4200)
89
+ volute down Stop the daemon
90
+ volute upgrade <name> Upgrade agent to latest template
91
+ volute delete <name> [--force] Delete an agent (--force removes files)
92
+ volute service install [--port N] Install as system service (auto-start)
93
+ volute service uninstall Remove system service
94
+ volute service status Check service status
95
+ volute setup [--port N] [--host H] Install system service with user isolation
96
+ volute setup uninstall [--force] Remove system service + isolation
97
+
98
+ Agent commands (variant, connector, schedule, logs, history, channel) use
99
+ --agent <name> or VOLUTE_AGENT env var to identify the agent.`);
94
100
  if (command) {
95
101
  console.error(`
96
102
  Unknown command: ${command}`);
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadMergedEnv
4
+ } from "./chunk-DNOXHLE5.js";
5
+ import {
6
+ daemonFetch
7
+ } from "./chunk-YGFIWIOF.js";
8
+ import {
9
+ resolveAgentName
10
+ } from "./chunk-VRVVQIYY.js";
11
+ import {
12
+ parseArgs
13
+ } from "./chunk-D424ZQGI.js";
14
+ import {
15
+ resolveAgent
16
+ } from "./chunk-3C2XR4IY.js";
17
+ import "./chunk-K3NQKI34.js";
18
+
19
+ // src/commands/connector.ts
20
+ async function run(args) {
21
+ const subcommand = args[0];
22
+ switch (subcommand) {
23
+ case "connect":
24
+ await connectConnector(args.slice(1));
25
+ break;
26
+ case "disconnect":
27
+ await disconnectConnector(args.slice(1));
28
+ break;
29
+ default:
30
+ printUsage();
31
+ process.exit(subcommand ? 1 : 0);
32
+ }
33
+ }
34
+ function printUsage() {
35
+ console.error(`Usage:
36
+ volute connector connect <type> [--agent <name>]
37
+ volute connector disconnect <type> [--agent <name>]`);
38
+ }
39
+ async function connectConnector(args) {
40
+ const { positional, flags } = parseArgs(args, {
41
+ agent: { type: "string" }
42
+ });
43
+ const agentName = resolveAgentName(flags);
44
+ const type = positional[0];
45
+ if (!type) {
46
+ console.error("Usage: volute connector connect <type> [--agent <name>]");
47
+ process.exit(1);
48
+ }
49
+ const { dir } = resolveAgent(agentName);
50
+ if (type === "discord") {
51
+ const env = loadMergedEnv(dir);
52
+ if (!env.DISCORD_TOKEN) {
53
+ console.error("DISCORD_TOKEN not set. Run: volute env set DISCORD_TOKEN <token>");
54
+ process.exit(1);
55
+ }
56
+ } else {
57
+ console.error(`Unknown connector type: ${type}`);
58
+ process.exit(1);
59
+ }
60
+ const res = await daemonFetch(
61
+ `/api/agents/${encodeURIComponent(agentName)}/connectors/${encodeURIComponent(type)}`,
62
+ { method: "POST" }
63
+ );
64
+ if (!res.ok) {
65
+ const body = await res.json().catch(() => ({ error: "Unknown error" }));
66
+ console.error(`Failed to start ${type} connector: ${body.error}`);
67
+ process.exit(1);
68
+ }
69
+ console.log(`${type} connector for ${agentName} started.`);
70
+ }
71
+ async function disconnectConnector(args) {
72
+ const { positional, flags } = parseArgs(args, {
73
+ agent: { type: "string" }
74
+ });
75
+ const agentName = resolveAgentName(flags);
76
+ const type = positional[0];
77
+ if (!type) {
78
+ console.error("Usage: volute connector disconnect <type> [--agent <name>]");
79
+ process.exit(1);
80
+ }
81
+ const res = await daemonFetch(
82
+ `/api/agents/${encodeURIComponent(agentName)}/connectors/${encodeURIComponent(type)}`,
83
+ {
84
+ method: "DELETE"
85
+ }
86
+ );
87
+ if (!res.ok) {
88
+ const body = await res.json().catch(() => ({ error: "Unknown error" }));
89
+ console.error(`Failed to stop ${type} connector: ${body.error}`);
90
+ process.exit(1);
91
+ }
92
+ console.log(`${type} connector for ${agentName} stopped.`);
93
+ }
94
+ export {
95
+ run
96
+ };
@@ -24,7 +24,7 @@ if (!token) {
24
24
  var guildId = process.env.DISCORD_GUILD_ID;
25
25
  var daemonUrl = process.env.VOLUTE_DAEMON_URL;
26
26
  var daemonToken = process.env.VOLUTE_DAEMON_TOKEN;
27
- var baseUrl = daemonUrl ? `${daemonUrl}/api/agents/${encodeURIComponent(agentName)}` : `http://localhost:${agentPort}`;
27
+ var baseUrl = daemonUrl ? `${daemonUrl}/api/agents/${encodeURIComponent(agentName)}` : `http://127.0.0.1:${agentPort}`;
28
28
  var client = new Client({
29
29
  intents: [
30
30
  GatewayIntentBits.Guilds,
@@ -42,7 +42,7 @@ process.on("SIGINT", shutdown);
42
42
  process.on("SIGTERM", shutdown);
43
43
  client.once(Events.ClientReady, (c) => {
44
44
  console.log(`Connected to Discord as ${c.user.tag}`);
45
- console.log(`Bridging to agent: ${agentName} (port ${agentPort})`);
45
+ console.log(`Bridging to agent: ${agentName} via ${baseUrl}/message`);
46
46
  });
47
47
  client.on(Events.MessageCreate, async (message) => {
48
48
  if (message.author.bot) return;
@@ -72,7 +72,28 @@ client.on(Events.MessageCreate, async (message) => {
72
72
  if (content.length === 0) return;
73
73
  await handleAgentRequest(message, content);
74
74
  });
75
- client.login(token);
75
+ async function loginWithRetry() {
76
+ try {
77
+ await client.login(token);
78
+ } catch (err) {
79
+ const msg = err instanceof Error ? err.message : String(err);
80
+ const match = msg.match(/resets at (.+)/);
81
+ if (match) {
82
+ const resetAt = new Date(match[1]);
83
+ const waitMs = resetAt.getTime() - Date.now();
84
+ if (waitMs > 0) {
85
+ console.error(`Session limit hit, waiting until ${resetAt.toISOString()}...`);
86
+ await new Promise((r) => setTimeout(r, waitMs + 5e3));
87
+ return loginWithRetry();
88
+ }
89
+ }
90
+ throw err;
91
+ }
92
+ }
93
+ loginWithRetry().catch((err) => {
94
+ console.error("Failed to connect to Discord:", err);
95
+ process.exit(1);
96
+ });
76
97
  function splitMessage(text) {
77
98
  const chunks = [];
78
99
  while (text.length > DISCORD_MAX_LENGTH) {
@@ -186,6 +207,8 @@ async function handleAgentRequest(message, content) {
186
207
  })
187
208
  });
188
209
  if (!res.ok) {
210
+ const body = await res.text().catch(() => "");
211
+ console.error(`Agent returned ${res.status}: ${body}`);
189
212
  await message.reply(`Error: agent returned ${res.status}`);
190
213
  clearInterval(typingInterval);
191
214
  return;
@@ -211,6 +234,7 @@ async function handleAgentRequest(message, content) {
211
234
  }
212
235
  await flush();
213
236
  } catch (err) {
237
+ console.error(`Failed to reach agent at ${baseUrl}/message:`, err);
214
238
  const errMsg = err instanceof TypeError && err.cause?.code === "ECONNREFUSED" ? "Agent is not running" : `Error: ${err}`;
215
239
  await message.reply(errMsg).catch(() => {
216
240
  });
@@ -8,7 +8,12 @@ import {
8
8
  import {
9
9
  exec,
10
10
  execInherit
11
- } from "./chunk-XZN4WPNC.js";
11
+ } from "./chunk-5SKQ6J7T.js";
12
+ import {
13
+ chownAgentDir,
14
+ createAgentUser,
15
+ ensureVoluteGroup
16
+ } from "./chunk-SOZA2TLP.js";
12
17
  import {
13
18
  parseArgs
14
19
  } from "./chunk-D424ZQGI.js";
@@ -17,7 +22,8 @@ import {
17
22
  agentDir,
18
23
  ensureVoluteHome,
19
24
  nextPort
20
- } from "./chunk-6UCG6MIX.js";
25
+ } from "./chunk-3C2XR4IY.js";
26
+ import "./chunk-K3NQKI34.js";
21
27
 
22
28
  // src/commands/create.ts
23
29
  import { existsSync, rmSync } from "fs";
@@ -49,9 +55,20 @@ async function run(args) {
49
55
  addAgent(name, port);
50
56
  console.log("Installing dependencies...");
51
57
  await execInherit("npm", ["install"], { cwd: dest });
52
- await exec("git", ["init"], { cwd: dest });
53
- await exec("git", ["add", "-A"], { cwd: dest });
54
- await exec("git", ["commit", "-m", "initial commit"], { cwd: dest });
58
+ try {
59
+ await exec("git", ["init"], { cwd: dest });
60
+ await exec("git", ["add", "-A"], { cwd: dest });
61
+ await exec("git", ["commit", "-m", "initial commit"], { cwd: dest });
62
+ } catch {
63
+ console.warn(
64
+ "\nWarning: git init failed (git may not be installed or configured).",
65
+ "\nThe agent will work, but forking/variants won't be available.",
66
+ "\nTo fix: install git and run `git config --global user.name` / `git config --global user.email`"
67
+ );
68
+ }
69
+ ensureVoluteGroup();
70
+ createAgentUser(name);
71
+ chownAgentDir(dest, name);
55
72
  console.log(`
56
73
  Created agent: ${name} (port ${port})`);
57
74
  console.log(`
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ daemonFetch
4
+ } from "./chunk-YGFIWIOF.js";
5
+ import "./chunk-3C2XR4IY.js";
6
+ import "./chunk-K3NQKI34.js";
7
+ export {
8
+ daemonFetch
9
+ };