volute 0.3.0 → 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 (31) hide show
  1. package/dist/{agent-manager-2LU6KULR.js → agent-manager-AUCKMGPR.js} +4 -4
  2. package/dist/{channel-H7N4SGR2.js → channel-7FZ6D25H.js} +2 -2
  3. package/dist/{chunk-RALYNMHR.js → chunk-3C2XR4IY.js} +1 -1
  4. package/dist/{chunk-YEIHRP2J.js → chunk-DNOXHLE5.js} +1 -1
  5. package/dist/{chunk-IPIPLGME.js → chunk-I6OHXCMV.js} +4 -4
  6. package/dist/{chunk-DEUAVGSA.js → chunk-SOZA2TLP.js} +1 -1
  7. package/dist/{chunk-VVD3XO3E.js → chunk-YGFIWIOF.js} +1 -1
  8. package/dist/cli.js +19 -18
  9. package/dist/{connector-6LWB5PRU.js → connector-TVJULIRT.js} +3 -3
  10. package/dist/connectors/discord.js +5 -2
  11. package/dist/{create-RSWWMGKT.js → create-BRG2DBWI.js} +2 -2
  12. package/dist/{daemon-client-27KMQQKX.js → daemon-client-XR24PUJF.js} +2 -2
  13. package/dist/daemon.js +11 -11
  14. package/dist/{delete-4ERL2QHH.js → delete-GQ7JEK2S.js} +3 -3
  15. package/dist/{down-HRC4MQCT.js → down-3OB6UVAJ.js} +1 -1
  16. package/dist/{env-DBWDTIP6.js → env-JB27UAC3.js} +2 -2
  17. package/dist/{history-W7BD2H74.js → history-3VRUBGGV.js} +2 -2
  18. package/dist/{import-6HTSSDFW.js → import-K4MP2GX7.js} +2 -2
  19. package/dist/{logs-NHWGHNBF.js → logs-NXFFGUKY.js} +1 -1
  20. package/dist/{schedule-DKZ2E2CL.js → schedule-4I5TYHFH.js} +2 -2
  21. package/dist/{send-5LEJXPYV.js → send-UK3JBZIB.js} +2 -2
  22. package/dist/{setup-ZMNTOJAV.js → setup-SRS7AUAA.js} +2 -2
  23. package/dist/{start-2BSXX6BS.js → start-LDPMCMYT.js} +2 -2
  24. package/dist/{status-N23CV27T.js → status-MVSQG54T.js} +2 -2
  25. package/dist/{stop-DSKBIJ2D.js → stop-5PZTZCLL.js} +2 -2
  26. package/dist/{up-4UGID4DM.js → up-UT3IMKCA.js} +1 -1
  27. package/dist/{upgrade-BGFVRCVP.js → upgrade-CDKECCGN.js} +32 -19
  28. package/dist/{variant-JPLJTS2P.js → variant-CVYM3EQG.js} +126 -14
  29. package/package.json +1 -1
  30. package/templates/_base/_skills/volute-agent/SKILL.md +2 -1
  31. package/dist/chunk-MY74SUOL.js +0 -81
@@ -3,10 +3,10 @@ import {
3
3
  AgentManager,
4
4
  getAgentManager,
5
5
  initAgentManager
6
- } from "./chunk-IPIPLGME.js";
7
- import "./chunk-YEIHRP2J.js";
8
- import "./chunk-DEUAVGSA.js";
9
- import "./chunk-RALYNMHR.js";
6
+ } from "./chunk-I6OHXCMV.js";
7
+ import "./chunk-DNOXHLE5.js";
8
+ import "./chunk-SOZA2TLP.js";
9
+ import "./chunk-3C2XR4IY.js";
10
10
  import "./chunk-K3NQKI34.js";
11
11
  export {
12
12
  AgentManager,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadMergedEnv
4
- } from "./chunk-YEIHRP2J.js";
4
+ } from "./chunk-DNOXHLE5.js";
5
5
  import {
6
6
  resolveAgentName
7
7
  } from "./chunk-VRVVQIYY.js";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-D424ZQGI.js";
11
11
  import {
12
12
  resolveAgent
13
- } from "./chunk-RALYNMHR.js";
13
+ } from "./chunk-3C2XR4IY.js";
14
14
  import "./chunk-K3NQKI34.js";
15
15
 
16
16
  // src/lib/channels/discord.ts
@@ -81,7 +81,7 @@ function removeAllVariants(agentName) {
81
81
  }
82
82
  async function checkHealth(port) {
83
83
  try {
84
- const res = await fetch(`http://localhost:${port}/health`, {
84
+ const res = await fetch(`http://127.0.0.1:${port}/health`, {
85
85
  signal: AbortSignal.timeout(2e3)
86
86
  });
87
87
  if (!res.ok) return { ok: false };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-RALYNMHR.js";
4
+ } from "./chunk-3C2XR4IY.js";
5
5
 
6
6
  // src/lib/env.ts
7
7
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadMergedEnv
4
- } from "./chunk-YEIHRP2J.js";
4
+ } from "./chunk-DNOXHLE5.js";
5
5
  import {
6
6
  applyIsolation
7
- } from "./chunk-DEUAVGSA.js";
7
+ } from "./chunk-SOZA2TLP.js";
8
8
  import {
9
9
  agentDir,
10
10
  findAgent,
@@ -13,7 +13,7 @@ import {
13
13
  setVariantRunning,
14
14
  validateBranchName,
15
15
  voluteHome
16
- } from "./chunk-RALYNMHR.js";
16
+ } from "./chunk-3C2XR4IY.js";
17
17
 
18
18
  // src/lib/agent-manager.ts
19
19
  import { execFile, spawn } from "child_process";
@@ -89,7 +89,7 @@ var AgentManager = class {
89
89
  const { dir, isVariant, baseName, variantName } = target;
90
90
  const port = target.port;
91
91
  try {
92
- const res = await fetch(`http://localhost:${port}/health`);
92
+ const res = await fetch(`http://127.0.0.1:${port}/health`);
93
93
  if (res.ok) {
94
94
  console.error(`[daemon] killing orphan process on port ${port}`);
95
95
  await killProcessOnPort(port);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  validateAgentName
4
- } from "./chunk-RALYNMHR.js";
4
+ } from "./chunk-3C2XR4IY.js";
5
5
 
6
6
  // src/lib/isolation.ts
7
7
  import { execFile, execFileSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-RALYNMHR.js";
4
+ } from "./chunk-3C2XR4IY.js";
5
5
 
6
6
  // src/lib/daemon-client.ts
7
7
  import { existsSync, readFileSync } from "fs";
package/dist/cli.js CHANGED
@@ -5,61 +5,61 @@ 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-RSWWMGKT.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-2BSXX6BS.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-DSKBIJ2D.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-NHWGHNBF.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-N23CV27T.js").then((m) => m.run(args));
20
+ await import("./status-MVSQG54T.js").then((m) => m.run(args));
21
21
  break;
22
22
  case "variant":
23
- await import("./variant-JPLJTS2P.js").then((m) => m.run(args));
23
+ await import("./variant-CVYM3EQG.js").then((m) => m.run(args));
24
24
  break;
25
25
  case "send":
26
- await import("./send-5LEJXPYV.js").then((m) => m.run(args));
26
+ await import("./send-UK3JBZIB.js").then((m) => m.run(args));
27
27
  break;
28
28
  case "import":
29
- await import("./import-6HTSSDFW.js").then((m) => m.run(args));
29
+ await import("./import-K4MP2GX7.js").then((m) => m.run(args));
30
30
  break;
31
31
  case "delete":
32
- await import("./delete-4ERL2QHH.js").then((m) => m.run(args));
32
+ await import("./delete-GQ7JEK2S.js").then((m) => m.run(args));
33
33
  break;
34
34
  case "env":
35
- await import("./env-DBWDTIP6.js").then((m) => m.run(args));
35
+ await import("./env-JB27UAC3.js").then((m) => m.run(args));
36
36
  break;
37
37
  case "connector":
38
- await import("./connector-6LWB5PRU.js").then((m) => m.run(args));
38
+ await import("./connector-TVJULIRT.js").then((m) => m.run(args));
39
39
  break;
40
40
  case "channel":
41
- await import("./channel-H7N4SGR2.js").then((m) => m.run(args));
41
+ await import("./channel-7FZ6D25H.js").then((m) => m.run(args));
42
42
  break;
43
43
  case "upgrade":
44
- await import("./upgrade-BGFVRCVP.js").then((m) => m.run(args));
44
+ await import("./upgrade-CDKECCGN.js").then((m) => m.run(args));
45
45
  break;
46
46
  case "up":
47
- await import("./up-4UGID4DM.js").then((m) => m.run(args));
47
+ await import("./up-UT3IMKCA.js").then((m) => m.run(args));
48
48
  break;
49
49
  case "down":
50
- await import("./down-HRC4MQCT.js").then((m) => m.run(args));
50
+ await import("./down-3OB6UVAJ.js").then((m) => m.run(args));
51
51
  break;
52
52
  case "schedule":
53
- await import("./schedule-DKZ2E2CL.js").then((m) => m.run(args));
53
+ await import("./schedule-4I5TYHFH.js").then((m) => m.run(args));
54
54
  break;
55
55
  case "history":
56
- await import("./history-W7BD2H74.js").then((m) => m.run(args));
56
+ await import("./history-3VRUBGGV.js").then((m) => m.run(args));
57
57
  break;
58
58
  case "service":
59
59
  await import("./service-SA4TTMDU.js").then((m) => m.run(args));
60
60
  break;
61
61
  case "setup":
62
- await import("./setup-ZMNTOJAV.js").then((m) => m.run(args));
62
+ await import("./setup-SRS7AUAA.js").then((m) => m.run(args));
63
63
  break;
64
64
  default:
65
65
  console.log(`volute \u2014 create and manage AI agents
@@ -74,6 +74,7 @@ Commands:
74
74
  volute variant create <name> Create a variant (worktree + server)
75
75
  volute variant list List variants for an agent
76
76
  volute variant merge <name> Merge a variant back
77
+ volute variant delete <name> Delete a variant
77
78
  volute import <path> Import an OpenClaw workspace
78
79
  volute env <set|get|list|remove> Manage environment variables
79
80
  volute connector connect <type> Enable a connector for an agent
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadMergedEnv
4
- } from "./chunk-YEIHRP2J.js";
4
+ } from "./chunk-DNOXHLE5.js";
5
5
  import {
6
6
  daemonFetch
7
- } from "./chunk-VVD3XO3E.js";
7
+ } from "./chunk-YGFIWIOF.js";
8
8
  import {
9
9
  resolveAgentName
10
10
  } from "./chunk-VRVVQIYY.js";
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-D424ZQGI.js";
14
14
  import {
15
15
  resolveAgent
16
- } from "./chunk-RALYNMHR.js";
16
+ } from "./chunk-3C2XR4IY.js";
17
17
  import "./chunk-K3NQKI34.js";
18
18
 
19
19
  // src/commands/connector.ts
@@ -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;
@@ -207,6 +207,8 @@ async function handleAgentRequest(message, content) {
207
207
  })
208
208
  });
209
209
  if (!res.ok) {
210
+ const body = await res.text().catch(() => "");
211
+ console.error(`Agent returned ${res.status}: ${body}`);
210
212
  await message.reply(`Error: agent returned ${res.status}`);
211
213
  clearInterval(typingInterval);
212
214
  return;
@@ -232,6 +234,7 @@ async function handleAgentRequest(message, content) {
232
234
  }
233
235
  await flush();
234
236
  } catch (err) {
237
+ console.error(`Failed to reach agent at ${baseUrl}/message:`, err);
235
238
  const errMsg = err instanceof TypeError && err.cause?.code === "ECONNREFUSED" ? "Agent is not running" : `Error: ${err}`;
236
239
  await message.reply(errMsg).catch(() => {
237
240
  });
@@ -13,7 +13,7 @@ import {
13
13
  chownAgentDir,
14
14
  createAgentUser,
15
15
  ensureVoluteGroup
16
- } from "./chunk-DEUAVGSA.js";
16
+ } from "./chunk-SOZA2TLP.js";
17
17
  import {
18
18
  parseArgs
19
19
  } from "./chunk-D424ZQGI.js";
@@ -22,7 +22,7 @@ import {
22
22
  agentDir,
23
23
  ensureVoluteHome,
24
24
  nextPort
25
- } from "./chunk-RALYNMHR.js";
25
+ } from "./chunk-3C2XR4IY.js";
26
26
  import "./chunk-K3NQKI34.js";
27
27
 
28
28
  // src/commands/create.ts
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  daemonFetch
4
- } from "./chunk-VVD3XO3E.js";
5
- import "./chunk-RALYNMHR.js";
4
+ } from "./chunk-YGFIWIOF.js";
5
+ import "./chunk-3C2XR4IY.js";
6
6
  import "./chunk-K3NQKI34.js";
7
7
  export {
8
8
  daemonFetch
package/dist/daemon.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  initAgentManager,
6
6
  loadJsonMap,
7
7
  saveJsonMap
8
- } from "./chunk-IPIPLGME.js";
8
+ } from "./chunk-I6OHXCMV.js";
9
9
  import {
10
10
  logBuffer,
11
11
  logger_default,
@@ -17,10 +17,10 @@ import {
17
17
  } from "./chunk-NETNFBA5.js";
18
18
  import {
19
19
  loadMergedEnv
20
- } from "./chunk-YEIHRP2J.js";
20
+ } from "./chunk-DNOXHLE5.js";
21
21
  import {
22
22
  applyIsolation
23
- } from "./chunk-DEUAVGSA.js";
23
+ } from "./chunk-SOZA2TLP.js";
24
24
  import {
25
25
  agentDir,
26
26
  checkHealth,
@@ -34,7 +34,7 @@ import {
34
34
  setAgentRunning,
35
35
  setVariantRunning,
36
36
  voluteHome
37
- } from "./chunk-RALYNMHR.js";
37
+ } from "./chunk-3C2XR4IY.js";
38
38
  import {
39
39
  __export
40
40
  } from "./chunk-K3NQKI34.js";
@@ -138,7 +138,7 @@ var ConnectorManager = class {
138
138
  VOLUTE_AGENT_PORT: String(agentPort),
139
139
  VOLUTE_AGENT_NAME: agentName,
140
140
  ...daemonPort ? {
141
- VOLUTE_DAEMON_URL: `http://localhost:${daemonPort}`,
141
+ VOLUTE_DAEMON_URL: `http://127.0.0.1:${daemonPort}`,
142
142
  VOLUTE_DAEMON_TOKEN: process.env.VOLUTE_DAEMON_TOKEN
143
143
  } : {},
144
144
  ...connectorEnv
@@ -377,7 +377,7 @@ var Scheduler = class {
377
377
  try {
378
378
  let res;
379
379
  if (this.daemonPort && this.daemonToken) {
380
- const daemonUrl = `http://localhost:${this.daemonPort}`;
380
+ const daemonUrl = `http://127.0.0.1:${this.daemonPort}`;
381
381
  res = await fetch(`${daemonUrl}/api/agents/${encodeURIComponent(agentName)}/message`, {
382
382
  method: "POST",
383
383
  headers: {
@@ -388,7 +388,7 @@ var Scheduler = class {
388
388
  body
389
389
  });
390
390
  } else {
391
- res = await fetch(`http://localhost:${entry.port}/message`, {
391
+ res = await fetch(`http://127.0.0.1:${entry.port}/message`, {
392
392
  method: "POST",
393
393
  headers: { "Content-Type": "application/json" },
394
394
  body
@@ -866,7 +866,7 @@ var app = new Hono().get("/", async (c) => {
866
866
  console.error(`[daemon] failed to persist inbound message for ${baseName}:`, err);
867
867
  }
868
868
  }
869
- const res = await fetch(`http://localhost:${port}/message`, {
869
+ const res = await fetch(`http://127.0.0.1:${port}/message`, {
870
870
  method: "POST",
871
871
  headers: { "Content-Type": "application/json" },
872
872
  body
@@ -1116,7 +1116,7 @@ var app3 = new Hono3().post("/:name/chat", zValidator2("json", chatSchema), asyn
1116
1116
  if (!variant) return c.json({ error: `Unknown variant: ${variantName}` }, 404);
1117
1117
  port = variant.port;
1118
1118
  }
1119
- const { getAgentManager: getAgentManager2 } = await import("./agent-manager-2LU6KULR.js");
1119
+ const { getAgentManager: getAgentManager2 } = await import("./agent-manager-AUCKMGPR.js");
1120
1120
  if (!getAgentManager2().isRunning(name)) {
1121
1121
  return c.json({ error: "Agent is not running" }, 409);
1122
1122
  }
@@ -1155,7 +1155,7 @@ var app3 = new Hono3().post("/:name/chat", zValidator2("json", chatSchema), asyn
1155
1155
  sender: user.username,
1156
1156
  content: body.message ?? "[image]"
1157
1157
  });
1158
- const res = await fetch(`http://localhost:${port}/message`, {
1158
+ const res = await fetch(`http://127.0.0.1:${port}/message`, {
1159
1159
  method: "POST",
1160
1160
  headers: { "Content-Type": "application/json" },
1161
1161
  body: JSON.stringify({
@@ -1451,7 +1451,7 @@ var app8 = new Hono8().get("/:name/schedules", (c) => {
1451
1451
  const body = await c.req.text();
1452
1452
  const message = `[webhook: ${event}] ${body}`;
1453
1453
  try {
1454
- const res = await fetch(`http://localhost:${entry.port}/message`, {
1454
+ const res = await fetch(`http://127.0.0.1:${entry.port}/message`, {
1455
1455
  method: "POST",
1456
1456
  headers: { "Content-Type": "application/json" },
1457
1457
  body: JSON.stringify({
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  deleteAgentUser
4
- } from "./chunk-DEUAVGSA.js";
4
+ } from "./chunk-SOZA2TLP.js";
5
5
  import {
6
6
  parseArgs
7
7
  } from "./chunk-D424ZQGI.js";
@@ -10,7 +10,7 @@ import {
10
10
  findAgent,
11
11
  removeAgent,
12
12
  removeAllVariants
13
- } from "./chunk-RALYNMHR.js";
13
+ } from "./chunk-3C2XR4IY.js";
14
14
  import "./chunk-K3NQKI34.js";
15
15
 
16
16
  // src/commands/delete.ts
@@ -30,7 +30,7 @@ async function run(args) {
30
30
  process.exit(1);
31
31
  }
32
32
  try {
33
- const { daemonFetch } = await import("./daemon-client-27KMQQKX.js");
33
+ const { daemonFetch } = await import("./daemon-client-XR24PUJF.js");
34
34
  const res = await daemonFetch(`/api/agents/${encodeURIComponent(name)}/stop`, {
35
35
  method: "POST"
36
36
  });
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-RALYNMHR.js";
4
+ } from "./chunk-3C2XR4IY.js";
5
5
  import "./chunk-K3NQKI34.js";
6
6
 
7
7
  // src/commands/down.ts
@@ -5,13 +5,13 @@ import {
5
5
  readEnv,
6
6
  sharedEnvPath,
7
7
  writeEnv
8
- } from "./chunk-YEIHRP2J.js";
8
+ } from "./chunk-DNOXHLE5.js";
9
9
  import {
10
10
  parseArgs
11
11
  } from "./chunk-D424ZQGI.js";
12
12
  import {
13
13
  resolveAgent
14
- } from "./chunk-RALYNMHR.js";
14
+ } from "./chunk-3C2XR4IY.js";
15
15
  import "./chunk-K3NQKI34.js";
16
16
 
17
17
  // src/commands/env.ts
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  daemonFetch
4
- } from "./chunk-VVD3XO3E.js";
4
+ } from "./chunk-YGFIWIOF.js";
5
5
  import {
6
6
  resolveAgentName
7
7
  } from "./chunk-VRVVQIYY.js";
8
8
  import {
9
9
  parseArgs
10
10
  } from "./chunk-D424ZQGI.js";
11
- import "./chunk-RALYNMHR.js";
11
+ import "./chunk-3C2XR4IY.js";
12
12
  import "./chunk-K3NQKI34.js";
13
13
 
14
14
  // src/commands/history.ts
@@ -7,7 +7,7 @@ import {
7
7
  agentEnvPath,
8
8
  readEnv,
9
9
  writeEnv
10
- } from "./chunk-YEIHRP2J.js";
10
+ } from "./chunk-DNOXHLE5.js";
11
11
  import {
12
12
  composeTemplate,
13
13
  copyTemplateToDir,
@@ -25,7 +25,7 @@ import {
25
25
  agentDir,
26
26
  ensureVoluteHome,
27
27
  nextPort
28
- } from "./chunk-RALYNMHR.js";
28
+ } from "./chunk-3C2XR4IY.js";
29
29
  import "./chunk-K3NQKI34.js";
30
30
 
31
31
  // src/commands/import.ts
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-D424ZQGI.js";
8
8
  import {
9
9
  resolveAgent
10
- } from "./chunk-RALYNMHR.js";
10
+ } from "./chunk-3C2XR4IY.js";
11
11
  import "./chunk-K3NQKI34.js";
12
12
 
13
13
  // src/commands/logs.ts
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  daemonFetch
4
- } from "./chunk-VVD3XO3E.js";
4
+ } from "./chunk-YGFIWIOF.js";
5
5
  import {
6
6
  resolveAgentName
7
7
  } from "./chunk-VRVVQIYY.js";
8
8
  import {
9
9
  parseArgs
10
10
  } from "./chunk-D424ZQGI.js";
11
- import "./chunk-RALYNMHR.js";
11
+ import "./chunk-3C2XR4IY.js";
12
12
  import "./chunk-K3NQKI34.js";
13
13
 
14
14
  // src/commands/schedule.ts
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-N4YNKR3Q.js";
5
5
  import {
6
6
  daemonFetch
7
- } from "./chunk-VVD3XO3E.js";
8
- import "./chunk-RALYNMHR.js";
7
+ } from "./chunk-YGFIWIOF.js";
8
+ import "./chunk-3C2XR4IY.js";
9
9
  import "./chunk-K3NQKI34.js";
10
10
 
11
11
  // src/commands/send.ts
@@ -4,11 +4,11 @@ import {
4
4
  } from "./chunk-5SKQ6J7T.js";
5
5
  import {
6
6
  ensureVoluteGroup
7
- } from "./chunk-DEUAVGSA.js";
7
+ } from "./chunk-SOZA2TLP.js";
8
8
  import {
9
9
  parseArgs
10
10
  } from "./chunk-D424ZQGI.js";
11
- import "./chunk-RALYNMHR.js";
11
+ import "./chunk-3C2XR4IY.js";
12
12
  import "./chunk-K3NQKI34.js";
13
13
 
14
14
  // src/commands/setup.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  daemonFetch
4
- } from "./chunk-VVD3XO3E.js";
4
+ } from "./chunk-YGFIWIOF.js";
5
5
  import {
6
6
  resolveAgent
7
- } from "./chunk-RALYNMHR.js";
7
+ } from "./chunk-3C2XR4IY.js";
8
8
  import "./chunk-K3NQKI34.js";
9
9
 
10
10
  // src/commands/start.ts
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  daemonFetch
4
- } from "./chunk-VVD3XO3E.js";
5
- import "./chunk-RALYNMHR.js";
4
+ } from "./chunk-YGFIWIOF.js";
5
+ import "./chunk-3C2XR4IY.js";
6
6
  import "./chunk-K3NQKI34.js";
7
7
 
8
8
  // src/commands/status.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  daemonFetch
4
- } from "./chunk-VVD3XO3E.js";
4
+ } from "./chunk-YGFIWIOF.js";
5
5
  import {
6
6
  resolveAgent
7
- } from "./chunk-RALYNMHR.js";
7
+ } from "./chunk-3C2XR4IY.js";
8
8
  import "./chunk-K3NQKI34.js";
9
9
 
10
10
  // src/commands/stop.ts
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-D424ZQGI.js";
5
5
  import {
6
6
  voluteHome
7
- } from "./chunk-RALYNMHR.js";
7
+ } from "./chunk-3C2XR4IY.js";
8
8
  import "./chunk-K3NQKI34.js";
9
9
 
10
10
  // src/commands/up.ts
@@ -1,7 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- spawnServer
4
- } from "./chunk-MY74SUOL.js";
5
2
  import {
6
3
  composeTemplate,
7
4
  copyTemplateToDir,
@@ -11,13 +8,17 @@ import {
11
8
  exec,
12
9
  execInherit
13
10
  } from "./chunk-5SKQ6J7T.js";
11
+ import {
12
+ daemonFetch
13
+ } from "./chunk-YGFIWIOF.js";
14
14
  import {
15
15
  parseArgs
16
16
  } from "./chunk-D424ZQGI.js";
17
17
  import {
18
18
  addVariant,
19
+ nextPort,
19
20
  resolveAgent
20
- } from "./chunk-RALYNMHR.js";
21
+ } from "./chunk-3C2XR4IY.js";
21
22
  import "./chunk-K3NQKI34.js";
22
23
 
23
24
  // src/commands/upgrade.ts
@@ -41,12 +42,12 @@ async function run(args) {
41
42
  await continueUpgrade(agentName, projectRoot);
42
43
  return;
43
44
  }
44
- const worktreeDir = resolve(projectRoot, ".worktrees", VARIANT_NAME);
45
+ const worktreeDir = resolve(projectRoot, ".variants", VARIANT_NAME);
45
46
  if (existsSync(worktreeDir)) {
46
47
  console.error(
47
- `Upgrade worktree already exists: ${worktreeDir}
48
+ `Upgrade variant already exists: ${worktreeDir}
48
49
  If a previous upgrade is in progress, use --continue to finish it.
49
- Otherwise, remove it with: git -C ${projectRoot} worktree remove .worktrees/${VARIANT_NAME}`
50
+ Otherwise, remove it with: volute variant delete ${VARIANT_NAME} --agent ${agentName}`
50
51
  );
51
52
  process.exit(1);
52
53
  }
@@ -58,7 +59,7 @@ Otherwise, remove it with: git -C ${projectRoot} worktree remove .worktrees/${VA
58
59
  console.log("Updating template branch...");
59
60
  await updateTemplateBranch(projectRoot, template, agentName);
60
61
  console.log("Creating upgrade variant...");
61
- const parentDir = resolve(projectRoot, ".worktrees");
62
+ const parentDir = resolve(projectRoot, ".variants");
62
63
  if (!existsSync(parentDir)) {
63
64
  mkdirSync(parentDir, { recursive: true });
64
65
  }
@@ -78,7 +79,7 @@ Then run:`);
78
79
  await installAndVerify(agentName, worktreeDir);
79
80
  }
80
81
  async function updateTemplateBranch(projectRoot, template, agentName) {
81
- const tempWorktree = resolve(projectRoot, ".worktrees", "_template_update");
82
+ const tempWorktree = resolve(projectRoot, ".variants", "_template_update");
82
83
  let branchExists = false;
83
84
  try {
84
85
  await exec("git", ["rev-parse", "--verify", TEMPLATE_BRANCH], {
@@ -161,7 +162,7 @@ async function mergeTemplateBranch(worktreeDir) {
161
162
  }
162
163
  }
163
164
  async function continueUpgrade(agentName, projectRoot) {
164
- const worktreeDir = resolve(projectRoot, ".worktrees", VARIANT_NAME);
165
+ const worktreeDir = resolve(projectRoot, ".variants", VARIANT_NAME);
165
166
  if (!existsSync(worktreeDir)) {
166
167
  console.error("No upgrade in progress. Run `volute upgrade` first.");
167
168
  process.exit(1);
@@ -184,22 +185,34 @@ async function continueUpgrade(agentName, projectRoot) {
184
185
  async function installAndVerify(agentName, worktreeDir) {
185
186
  console.log("Installing dependencies...");
186
187
  await execInherit("npm", ["install"], { cwd: worktreeDir });
187
- console.log("Starting upgrade variant...");
188
- const result = await spawnServer(worktreeDir, 0, { detached: true });
189
- if (!result) {
190
- console.error("Server failed to start within timeout");
191
- process.exit(1);
192
- }
193
- const { actualPort } = result;
188
+ const variantPort = nextPort();
194
189
  addVariant(agentName, {
195
190
  name: VARIANT_NAME,
196
191
  branch: VARIANT_NAME,
197
192
  path: worktreeDir,
198
- port: actualPort,
193
+ port: variantPort,
199
194
  created: (/* @__PURE__ */ new Date()).toISOString()
200
195
  });
196
+ console.log("Starting upgrade variant...");
197
+ try {
198
+ const res = await daemonFetch(
199
+ `/api/agents/${encodeURIComponent(`${agentName}@${VARIANT_NAME}`)}/start`,
200
+ { method: "POST" }
201
+ );
202
+ if (!res.ok) {
203
+ const data = await res.json();
204
+ console.error(data.error ?? "Failed to start variant");
205
+ process.exit(1);
206
+ }
207
+ } catch {
208
+ console.error("Failed to start variant. Is the daemon running? (volute up)");
209
+ console.error(
210
+ `The variant was created but not started. Use: volute start ${agentName}@${VARIANT_NAME}`
211
+ );
212
+ process.exit(1);
213
+ }
201
214
  console.log(`
202
- Upgrade variant running on port ${actualPort}`);
215
+ Upgrade variant running on port ${variantPort}`);
203
216
  console.log(`
204
217
  Next steps:`);
205
218
  console.log(` volute send ${agentName}@${VARIANT_NAME} "hello" # chat with upgraded variant`);
@@ -1,14 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- spawnServer
4
- } from "./chunk-MY74SUOL.js";
5
2
  import {
6
3
  exec,
7
4
  execInherit
8
5
  } from "./chunk-5SKQ6J7T.js";
9
6
  import {
10
7
  daemonFetch
11
- } from "./chunk-VVD3XO3E.js";
8
+ } from "./chunk-YGFIWIOF.js";
12
9
  import {
13
10
  resolveAgentName
14
11
  } from "./chunk-VRVVQIYY.js";
@@ -25,12 +22,88 @@ import {
25
22
  resolveAgent,
26
23
  validateBranchName,
27
24
  writeVariants
28
- } from "./chunk-RALYNMHR.js";
25
+ } from "./chunk-3C2XR4IY.js";
29
26
  import "./chunk-K3NQKI34.js";
30
27
 
31
28
  // src/commands/variant.ts
32
- import { existsSync, mkdirSync, writeFileSync } from "fs";
29
+ import { existsSync, mkdirSync as mkdirSync2, writeFileSync } from "fs";
30
+ import { resolve as resolve2 } from "path";
31
+
32
+ // src/lib/spawn-server.ts
33
+ import { spawn } from "child_process";
34
+ import { closeSync, mkdirSync, openSync, readFileSync } from "fs";
33
35
  import { resolve } from "path";
36
+ function tsxBin(cwd) {
37
+ return resolve(cwd, "node_modules", ".bin", "tsx");
38
+ }
39
+ function spawnServer(cwd, port, options) {
40
+ if (options?.detached) {
41
+ return spawnDetached(cwd, port);
42
+ }
43
+ return spawnAttached(cwd, port);
44
+ }
45
+ function spawnAttached(cwd, port) {
46
+ const child = spawn(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
47
+ cwd,
48
+ stdio: ["ignore", "pipe", "pipe"]
49
+ });
50
+ return new Promise((resolve3) => {
51
+ const timeout = setTimeout(() => resolve3(null), 3e4);
52
+ function checkOutput(data) {
53
+ const match = data.toString().match(/listening on :(\d+)/);
54
+ if (match) {
55
+ clearTimeout(timeout);
56
+ resolve3({ child, actualPort: parseInt(match[1], 10) });
57
+ }
58
+ }
59
+ child.stdout?.on("data", checkOutput);
60
+ child.stderr?.on("data", checkOutput);
61
+ child.on("error", () => {
62
+ clearTimeout(timeout);
63
+ resolve3(null);
64
+ });
65
+ child.on("exit", () => {
66
+ clearTimeout(timeout);
67
+ resolve3(null);
68
+ });
69
+ });
70
+ }
71
+ function spawnDetached(cwd, port) {
72
+ const logsDir = resolve(cwd, ".volute", "logs");
73
+ mkdirSync(logsDir, { recursive: true });
74
+ const logPath = resolve(logsDir, "agent.log");
75
+ const logFd = openSync(logPath, "a");
76
+ const child = spawn(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
77
+ cwd,
78
+ stdio: ["ignore", logFd, logFd],
79
+ detached: true
80
+ });
81
+ child.unref();
82
+ closeSync(logFd);
83
+ return new Promise((res) => {
84
+ let done = false;
85
+ function finish(result) {
86
+ if (done) return;
87
+ done = true;
88
+ clearInterval(interval);
89
+ clearTimeout(timeout);
90
+ res(result);
91
+ }
92
+ const interval = setInterval(() => {
93
+ try {
94
+ const content = readFileSync(logPath, "utf-8");
95
+ const match = content.match(/listening on :(\d+)/);
96
+ if (match) {
97
+ finish({ child, actualPort: parseInt(match[1], 10) });
98
+ }
99
+ } catch {
100
+ }
101
+ }, 100);
102
+ const timeout = setTimeout(() => finish(null), 3e4);
103
+ child.on("error", () => finish(null));
104
+ child.on("exit", () => finish(null));
105
+ });
106
+ }
34
107
 
35
108
  // src/lib/verify.ts
36
109
  async function verify(port) {
@@ -41,7 +114,7 @@ async function verify(port) {
41
114
  }
42
115
  console.log(" Health check: OK");
43
116
  try {
44
- const res = await fetch(`http://localhost:${port}/message`, {
117
+ const res = await fetch(`http://127.0.0.1:${port}/message`, {
45
118
  method: "POST",
46
119
  headers: { "Content-Type": "application/json" },
47
120
  body: JSON.stringify({
@@ -106,6 +179,9 @@ async function run(args) {
106
179
  case "merge":
107
180
  await mergeVariant(args.slice(1));
108
181
  break;
182
+ case "delete":
183
+ await deleteVariant(args.slice(1));
184
+ break;
109
185
  default:
110
186
  printUsage();
111
187
  process.exit(subcommand ? 1 : 0);
@@ -115,7 +191,8 @@ function printUsage() {
115
191
  console.error(`Usage:
116
192
  volute variant create <variant> [--agent <name>] [--soul "..."] [--port N] [--no-start] [--json]
117
193
  volute variant list [--agent <name>] [--json]
118
- volute variant merge <variant> [--agent <name>] [--summary "..." --memory "..."] [--skip-verify]`);
194
+ volute variant merge <variant> [--agent <name>] [--summary "..." --memory "..."] [--skip-verify]
195
+ volute variant delete <variant> [--agent <name>]`);
119
196
  }
120
197
  async function createVariant(args) {
121
198
  const { positional, flags } = parseArgs(args, {
@@ -141,12 +218,12 @@ async function createVariant(args) {
141
218
  process.exit(1);
142
219
  }
143
220
  const { dir: projectRoot } = resolveAgent(agentName);
144
- const variantDir = resolve(projectRoot, ".variants", variantName);
221
+ const variantDir = resolve2(projectRoot, ".variants", variantName);
145
222
  if (existsSync(variantDir)) {
146
223
  console.error(`Variant directory already exists: ${variantDir}`);
147
224
  process.exit(1);
148
225
  }
149
- mkdirSync(resolve(projectRoot, ".variants"), { recursive: true });
226
+ mkdirSync2(resolve2(projectRoot, ".variants"), { recursive: true });
150
227
  try {
151
228
  await exec("git", ["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
152
229
  } catch (e) {
@@ -167,7 +244,7 @@ async function createVariant(args) {
167
244
  process.exit(1);
168
245
  }
169
246
  if (soul) {
170
- writeFileSync(resolve(variantDir, "home/SOUL.md"), soul);
247
+ writeFileSync(resolve2(variantDir, "home/SOUL.md"), soul);
171
248
  }
172
249
  const variantPort = port ?? nextPort();
173
250
  const variant = {
@@ -353,10 +430,10 @@ async function mergeVariant(args) {
353
430
  console.error("npm install failed:", e);
354
431
  }
355
432
  console.log(`Variant ${variantName} merged and cleaned up.`);
356
- const voluteDir = resolve(projectRoot, ".volute");
357
- if (!existsSync(voluteDir)) mkdirSync(voluteDir, { recursive: true });
433
+ const voluteDir = resolve2(projectRoot, ".volute");
434
+ if (!existsSync(voluteDir)) mkdirSync2(voluteDir, { recursive: true });
358
435
  writeFileSync(
359
- resolve(voluteDir, "merged.json"),
436
+ resolve2(voluteDir, "merged.json"),
360
437
  JSON.stringify({
361
438
  name: variantName,
362
439
  ...flags.summary && { summary: flags.summary },
@@ -380,6 +457,41 @@ async function mergeVariant(args) {
380
457
  console.log(`Daemon not running. Start the agent manually: volute start ${agentName}`);
381
458
  }
382
459
  }
460
+ async function deleteVariant(args) {
461
+ const { positional, flags } = parseArgs(args, {
462
+ agent: { type: "string" }
463
+ });
464
+ const agentName = resolveAgentName(flags);
465
+ const variantName = positional[0];
466
+ if (!variantName) {
467
+ console.error("Usage: volute variant delete <variant> [--agent <name>]");
468
+ process.exit(1);
469
+ }
470
+ const { dir: projectRoot } = resolveAgent(agentName);
471
+ const variant = findVariant(agentName, variantName);
472
+ if (!variant) {
473
+ console.error(`Unknown variant: ${variantName}`);
474
+ process.exit(1);
475
+ }
476
+ try {
477
+ await daemonFetch(`/api/agents/${encodeURIComponent(`${agentName}@${variantName}`)}/stop`, {
478
+ method: "POST"
479
+ });
480
+ } catch {
481
+ }
482
+ if (existsSync(variant.path)) {
483
+ try {
484
+ await exec("git", ["worktree", "remove", "--force", variant.path], { cwd: projectRoot });
485
+ } catch {
486
+ }
487
+ }
488
+ try {
489
+ await exec("git", ["branch", "-D", variant.branch], { cwd: projectRoot });
490
+ } catch {
491
+ }
492
+ removeVariant(agentName, variantName);
493
+ console.log(`Variant ${variantName} deleted.`);
494
+ }
383
495
  export {
384
496
  run
385
497
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volute",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "CLI for creating and managing self-modifying AI agents powered by the Claude Agent SDK",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,6 +18,7 @@ You manage yourself through the `volute` CLI. Commands that operate on "your" ag
18
18
  | `volute variant create <name> [--soul "..."] [--port N]` | Create a variant to experiment with changes |
19
19
  | `volute variant list` | List your variants |
20
20
  | `volute variant merge <name> [--summary "..." --memory "..."]` | Merge a variant back |
21
+ | `volute variant delete <name>` | Delete a variant without merging |
21
22
  | `volute upgrade [--template <name>] [--continue]` | Upgrade your server code |
22
23
  | `volute connector connect <type>` | Enable a connector (e.g. discord) |
23
24
  | `volute connector disconnect <type>` | Disable a connector |
@@ -57,7 +58,7 @@ Edit `.config/hooks/startup-context.sh` to customize what you see when a new ses
57
58
  Variants let you experiment safely — fork yourself, try changes, and merge back what works. Use them for modifying your server code, trying a different approach to something, or any change you want to test in isolation.
58
59
 
59
60
  1. `volute variant create experiment` — creates an isolated copy with its own server
60
- 2. Make changes in the variant's worktree (at `../.worktrees/experiment/`)
61
+ 2. Make changes in the variant's worktree (at `../.variants/experiment/`)
61
62
  3. Test: `volute send $VOLUTE_AGENT@experiment "hello"`
62
63
  4. `volute variant merge experiment --summary "..." --memory "..."` — merges back after verification
63
64
 
@@ -1,81 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/lib/spawn-server.ts
4
- import { spawn } from "child_process";
5
- import { closeSync, mkdirSync, openSync, readFileSync } from "fs";
6
- import { resolve } from "path";
7
- function tsxBin(cwd) {
8
- return resolve(cwd, "node_modules", ".bin", "tsx");
9
- }
10
- function spawnServer(cwd, port, options) {
11
- if (options?.detached) {
12
- return spawnDetached(cwd, port);
13
- }
14
- return spawnAttached(cwd, port);
15
- }
16
- function spawnAttached(cwd, port) {
17
- const child = spawn(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
18
- cwd,
19
- stdio: ["ignore", "pipe", "pipe"]
20
- });
21
- return new Promise((resolve2) => {
22
- const timeout = setTimeout(() => resolve2(null), 3e4);
23
- function checkOutput(data) {
24
- const match = data.toString().match(/listening on :(\d+)/);
25
- if (match) {
26
- clearTimeout(timeout);
27
- resolve2({ child, actualPort: parseInt(match[1], 10) });
28
- }
29
- }
30
- child.stdout?.on("data", checkOutput);
31
- child.stderr?.on("data", checkOutput);
32
- child.on("error", () => {
33
- clearTimeout(timeout);
34
- resolve2(null);
35
- });
36
- child.on("exit", () => {
37
- clearTimeout(timeout);
38
- resolve2(null);
39
- });
40
- });
41
- }
42
- function spawnDetached(cwd, port) {
43
- const logsDir = resolve(cwd, ".volute", "logs");
44
- mkdirSync(logsDir, { recursive: true });
45
- const logPath = resolve(logsDir, "agent.log");
46
- const logFd = openSync(logPath, "a");
47
- const child = spawn(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
48
- cwd,
49
- stdio: ["ignore", logFd, logFd],
50
- detached: true
51
- });
52
- child.unref();
53
- closeSync(logFd);
54
- return new Promise((res) => {
55
- let done = false;
56
- function finish(result) {
57
- if (done) return;
58
- done = true;
59
- clearInterval(interval);
60
- clearTimeout(timeout);
61
- res(result);
62
- }
63
- const interval = setInterval(() => {
64
- try {
65
- const content = readFileSync(logPath, "utf-8");
66
- const match = content.match(/listening on :(\d+)/);
67
- if (match) {
68
- finish({ child, actualPort: parseInt(match[1], 10) });
69
- }
70
- } catch {
71
- }
72
- }, 100);
73
- const timeout = setTimeout(() => finish(null), 3e4);
74
- child.on("error", () => finish(null));
75
- child.on("exit", () => finish(null));
76
- });
77
- }
78
-
79
- export {
80
- spawnServer
81
- };