volute 0.12.0 → 0.13.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 (61) hide show
  1. package/dist/{agent-2AQPI3QV.js → agent-HYX2ZTFM.js} +10 -10
  2. package/dist/{agent-manager-RMWXST3T.js → agent-manager-HHBAAL2D.js} +4 -4
  3. package/dist/{channel-2WHBRDTD.js → channel-72YET5JC.js} +2 -2
  4. package/dist/{chunk-QF22MYDJ.js → chunk-BJDLYTPS.js} +1 -1
  5. package/dist/{chunk-46S7YHUB.js → chunk-EN3NHRQC.js} +12 -19
  6. package/dist/{chunk-DP2DX4WV.js → chunk-ESTOWEG2.js} +12 -3
  7. package/dist/{chunk-POLMWJFC.js → chunk-GPZCPGV3.js} +1 -1
  8. package/dist/{chunk-ZKNBD5P3.js → chunk-IELXXS7E.js} +2 -2
  9. package/dist/{chunk-STOEJOJO.js → chunk-J3IHIXDB.js} +1 -1
  10. package/dist/{chunk-R3VB7NF5.js → chunk-KYHC7LHS.js} +2 -2
  11. package/dist/{chunk-YY2QX2J6.js → chunk-LG4ROCHN.js} +1 -1
  12. package/dist/{chunk-PHJCXAWJ.js → chunk-NQKKTRET.js} +19 -37
  13. package/dist/{chunk-IZEQ47HW.js → chunk-PEQQ7MRI.js} +2 -2
  14. package/dist/{chunk-WKPMR5B2.js → chunk-PTK3GBCG.js} +2 -2
  15. package/dist/{chunk-RGWADNLT.js → chunk-RH3XLDY2.js} +2 -2
  16. package/dist/{chunk-WTJI3JVR.js → chunk-VRAOTXDF.js} +9 -6
  17. package/dist/chunk-XUA3JUFK.js +121 -0
  18. package/dist/cli.js +26 -17
  19. package/dist/{connector-L2HBLZBW.js → connector-Z5KYVTZ5.js} +2 -2
  20. package/dist/connectors/discord.js +2 -2
  21. package/dist/connectors/slack.js +2 -2
  22. package/dist/connectors/telegram.js +2 -2
  23. package/dist/{create-VBZZNJOG.js → create-AAI52BC2.js} +1 -1
  24. package/dist/{daemon-client-P44NU3KU.js → daemon-client-WXN43USO.js} +2 -2
  25. package/dist/{daemon-restart-IVJ7X4PF.js → daemon-restart-4OXIGWV6.js} +6 -5
  26. package/dist/daemon.js +502 -540
  27. package/dist/{delete-BOTVU4YO.js → delete-BJ3LNU2I.js} +1 -1
  28. package/dist/down-IMZE7V42.js +14 -0
  29. package/dist/{env-CGORIKVF.js → env-EOO2C7L7.js} +2 -2
  30. package/dist/{history-NI5QP27M.js → history-J7TURCZS.js} +2 -2
  31. package/dist/{import-2BZUWT23.js → import-TKF67X4R.js} +3 -3
  32. package/dist/{logs-APWVWGNX.js → logs-KHBOS6IZ.js} +2 -2
  33. package/dist/{package-5LQNWBH7.js → package-TPXKJXLG.js} +1 -1
  34. package/dist/{restart-CCYM3MEC.js → restart-CMP63H6A.js} +2 -2
  35. package/dist/{schedule-E4MFGYSA.js → schedule-YMAJZ52M.js} +2 -2
  36. package/dist/seed-M6QPHFTV.js +68 -0
  37. package/dist/{send-V5ESR5O2.js → send-EZYEIVMA.js} +18 -6
  38. package/dist/{service-UL3OCODG.js → service-DDFZA7Q3.js} +4 -3
  39. package/dist/{setup-7N4KYOYN.js → setup-DCZWBBD7.js} +7 -7
  40. package/dist/sprout-EAWETTWZ.js +89 -0
  41. package/dist/{start-6YRS6FF6.js → start-54JXJ7VV.js} +2 -2
  42. package/dist/{status-DFWM342I.js → status-PMMS4XUH.js} +7 -5
  43. package/dist/{stop-UQSNF4CG.js → stop-WXU42NWP.js} +2 -2
  44. package/dist/{up-A6XT6AFX.js → up-N2OWDCT6.js} +5 -4
  45. package/dist/{update-PV3XM6DX.js → update-FXLGIIWH.js} +5 -4
  46. package/dist/{update-check-YPGH5X4E.js → update-check-NBHTSTHK.js} +2 -2
  47. package/dist/{upgrade-RSE4CZNE.js → upgrade-TEI7N6CQ.js} +1 -1
  48. package/dist/{variant-7IZF6OWO.js → variant-ZCZS3JAP.js} +4 -4
  49. package/dist/web-assets/assets/index-TqXd1QOX.js +307 -0
  50. package/dist/web-assets/index.html +1 -1
  51. package/package.json +1 -1
  52. package/templates/_base/_skills/orientation/SKILL.md +58 -0
  53. package/templates/_base/home/.config/config.json.tmpl +3 -0
  54. package/templates/_base/src/lib/startup.ts +8 -4
  55. package/templates/agent-sdk/volute-template.json +1 -1
  56. package/templates/pi/home/.config/config.json.tmpl +3 -0
  57. package/templates/pi/volute-template.json +1 -1
  58. package/dist/down-O2EQJ5DO.js +0 -13
  59. package/dist/web-assets/assets/index-D-3zx6vs.js +0 -307
  60. package/templates/_base/home/.config/volute.json.tmpl +0 -3
  61. package/templates/pi/home/.config/volute.json.tmpl +0 -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-VBZZNJOG.js").then((m) => m.run(args.slice(1)));
9
+ await import("./create-AAI52BC2.js").then((m) => m.run(args.slice(1)));
10
10
  break;
11
11
  case "start":
12
- await import("./start-6YRS6FF6.js").then((m) => m.run(args.slice(1)));
12
+ await import("./start-54JXJ7VV.js").then((m) => m.run(args.slice(1)));
13
13
  break;
14
14
  case "stop":
15
- await import("./stop-UQSNF4CG.js").then((m) => m.run(args.slice(1)));
15
+ await import("./stop-WXU42NWP.js").then((m) => m.run(args.slice(1)));
16
16
  break;
17
17
  case "restart":
18
- await import("./restart-CCYM3MEC.js").then((m) => m.run(args.slice(1)));
18
+ await import("./restart-CMP63H6A.js").then((m) => m.run(args.slice(1)));
19
19
  break;
20
20
  case "delete":
21
- await import("./delete-BOTVU4YO.js").then((m) => m.run(args.slice(1)));
21
+ await import("./delete-BJ3LNU2I.js").then((m) => m.run(args.slice(1)));
22
22
  break;
23
23
  case "list":
24
- await import("./status-DFWM342I.js").then((m) => m.run(args.slice(1)));
24
+ await import("./status-PMMS4XUH.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-DFWM342I.js").then((m) => m.run(rest));
31
+ await import("./status-PMMS4XUH.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-APWVWGNX.js").then((m) => m.run(logsArgs));
37
+ await import("./logs-KHBOS6IZ.js").then((m) => m.run(logsArgs));
38
38
  break;
39
39
  }
40
40
  case "upgrade":
41
- await import("./upgrade-RSE4CZNE.js").then((m) => m.run(args.slice(1)));
41
+ await import("./upgrade-TEI7N6CQ.js").then((m) => m.run(args.slice(1)));
42
42
  break;
43
43
  case "import":
44
- await import("./import-2BZUWT23.js").then((m) => m.run(args.slice(1)));
44
+ await import("./import-TKF67X4R.js").then((m) => m.run(args.slice(1)));
45
45
  break;
46
46
  case "--help":
47
47
  case "-h":
@@ -3,10 +3,10 @@ import {
3
3
  AgentManager,
4
4
  getAgentManager,
5
5
  initAgentManager
6
- } from "./chunk-PHJCXAWJ.js";
7
- import "./chunk-46S7YHUB.js";
8
- import "./chunk-QF22MYDJ.js";
9
- import "./chunk-DP2DX4WV.js";
6
+ } from "./chunk-NQKKTRET.js";
7
+ import "./chunk-BJDLYTPS.js";
8
+ import "./chunk-EN3NHRQC.js";
9
+ import "./chunk-ESTOWEG2.js";
10
10
  import "./chunk-K3NQKI34.js";
11
11
  export {
12
12
  AgentManager,
@@ -7,8 +7,8 @@ import {
7
7
  } from "./chunk-D424ZQGI.js";
8
8
  import {
9
9
  daemonFetch
10
- } from "./chunk-STOEJOJO.js";
11
- import "./chunk-DP2DX4WV.js";
10
+ } from "./chunk-J3IHIXDB.js";
11
+ import "./chunk-ESTOWEG2.js";
12
12
  import {
13
13
  getClient,
14
14
  urlOf
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  stateDir,
4
4
  voluteHome
5
- } from "./chunk-DP2DX4WV.js";
5
+ } from "./chunk-ESTOWEG2.js";
6
6
 
7
7
  // src/lib/env.ts
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -1,12 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  validateAgentName
4
- } from "./chunk-DP2DX4WV.js";
4
+ } from "./chunk-ESTOWEG2.js";
5
5
 
6
6
  // src/lib/isolation.ts
7
- import { execFile, execFileSync } from "child_process";
8
- import { promisify } from "util";
9
- var execFileAsync = promisify(execFile);
7
+ import { execFileSync } from "child_process";
10
8
  function isIsolationEnabled() {
11
9
  return process.env.VOLUTE_ISOLATION === "user";
12
10
  }
@@ -29,7 +27,7 @@ function ensureVoluteGroup(opts) {
29
27
  }
30
28
  }
31
29
  }
32
- function createAgentUser(name) {
30
+ function createAgentUser(name, homeDir) {
33
31
  if (!isIsolationEnabled()) return;
34
32
  const user = agentUserName(name);
35
33
  try {
@@ -38,7 +36,10 @@ function createAgentUser(name) {
38
36
  } catch {
39
37
  }
40
38
  try {
41
- execFileSync("useradd", ["-r", "-M", "-G", "volute", "-s", "/usr/sbin/nologin", user], {
39
+ const args = ["-r", "-M", "-G", "volute", "-s", "/usr/sbin/nologin"];
40
+ if (homeDir) args.push("-d", homeDir);
41
+ args.push(user);
42
+ execFileSync("useradd", args, {
42
43
  stdio: ["ignore", "ignore", "pipe"]
43
44
  });
44
45
  } catch (err) {
@@ -54,18 +55,11 @@ function deleteAgentUser(name) {
54
55
  } catch {
55
56
  }
56
57
  }
57
- async function getAgentUserIds(name) {
58
- const user = agentUserName(name);
59
- const { stdout: uidStr } = await execFileAsync("id", ["-u", user]);
60
- const { stdout: gidStr } = await execFileAsync("id", ["-g", user]);
61
- return { uid: parseInt(uidStr.trim(), 10), gid: parseInt(gidStr.trim(), 10) };
62
- }
63
- async function applyIsolation(spawnOpts, agentName) {
64
- if (!isIsolationEnabled()) return;
58
+ function wrapForIsolation(cmd, args, agentName) {
59
+ if (!isIsolationEnabled()) return [cmd, args];
65
60
  const baseName = agentName.split("@", 2)[0];
66
- const { uid, gid } = await getAgentUserIds(baseName);
67
- spawnOpts.uid = uid;
68
- spawnOpts.gid = gid;
61
+ const user = agentUserName(baseName);
62
+ return ["runuser", ["-u", user, "--", cmd, ...args]];
69
63
  }
70
64
  function chownAgentDir(dir, name) {
71
65
  if (!isIsolationEnabled()) return;
@@ -89,7 +83,6 @@ export {
89
83
  ensureVoluteGroup,
90
84
  createAgentUser,
91
85
  deleteAgentUser,
92
- getAgentUserIds,
93
- applyIsolation,
86
+ wrapForIsolation,
94
87
  chownAgentDir
95
88
  };
@@ -28,7 +28,7 @@ function readRegistry() {
28
28
  if (!existsSync(registryPath)) return [];
29
29
  try {
30
30
  const entries = JSON.parse(readFileSync(registryPath, "utf-8"));
31
- return entries.map((e) => ({ ...e, running: e.running ?? false }));
31
+ return entries.map((e) => ({ ...e, running: e.running ?? false, stage: e.stage ?? "mind" }));
32
32
  } catch {
33
33
  return [];
34
34
  }
@@ -52,12 +52,12 @@ function validateAgentName(name) {
52
52
  }
53
53
  return null;
54
54
  }
55
- function addAgent(name, port) {
55
+ function addAgent(name, port, stage) {
56
56
  const err = validateAgentName(name);
57
57
  if (err) throw new Error(err);
58
58
  const entries = readRegistry();
59
59
  const filtered = entries.filter((e) => e.name !== name);
60
- filtered.push({ name, port, created: (/* @__PURE__ */ new Date()).toISOString(), running: false });
60
+ filtered.push({ name, port, created: (/* @__PURE__ */ new Date()).toISOString(), running: false, stage });
61
61
  writeRegistry(filtered);
62
62
  }
63
63
  function removeAgent(name) {
@@ -72,6 +72,14 @@ function setAgentRunning(name, running) {
72
72
  writeRegistry(entries);
73
73
  }
74
74
  }
75
+ function setAgentStage(name, stage) {
76
+ const entries = readRegistry();
77
+ const entry = entries.find((e) => e.name === name);
78
+ if (entry) {
79
+ entry.stage = stage;
80
+ writeRegistry(entries);
81
+ }
82
+ }
75
83
  function findAgent(name) {
76
84
  return readRegistry().find((e) => e.name === name);
77
85
  }
@@ -237,6 +245,7 @@ export {
237
245
  addAgent,
238
246
  removeAgent,
239
247
  setAgentRunning,
248
+ setAgentStage,
240
249
  findAgent,
241
250
  agentDir,
242
251
  stateDir,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  stateDir
4
- } from "./chunk-DP2DX4WV.js";
4
+ } from "./chunk-ESTOWEG2.js";
5
5
 
6
6
  // src/lib/slugify.ts
7
7
  function slugify(text) {
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  execInherit
4
- } from "./chunk-WTJI3JVR.js";
4
+ } from "./chunk-VRAOTXDF.js";
5
5
  import {
6
6
  voluteHome
7
- } from "./chunk-DP2DX4WV.js";
7
+ } from "./chunk-ESTOWEG2.js";
8
8
 
9
9
  // src/lib/service-mode.ts
10
10
  import { execFileSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-DP2DX4WV.js";
4
+ } from "./chunk-ESTOWEG2.js";
5
5
 
6
6
  // src/lib/daemon-client.ts
7
7
  import { existsSync, readFileSync } from "fs";
@@ -3,7 +3,7 @@ import {
3
3
  agentEnvPath,
4
4
  readEnv,
5
5
  writeEnv
6
- } from "./chunk-QF22MYDJ.js";
6
+ } from "./chunk-BJDLYTPS.js";
7
7
  import {
8
8
  parseArgs
9
9
  } from "./chunk-D424ZQGI.js";
@@ -43,7 +43,7 @@ async function run(args) {
43
43
  template: { type: "string" }
44
44
  });
45
45
  const wsDir = resolveWorkspace(positional[0]);
46
- const { daemonFetch } = await import("./daemon-client-P44NU3KU.js");
46
+ const { daemonFetch } = await import("./daemon-client-WXN43USO.js");
47
47
  const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
48
48
  const client = getClient();
49
49
  const res = await daemonFetch(urlOf(client.api.agents.import.$url()), {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-DP2DX4WV.js";
4
+ } from "./chunk-ESTOWEG2.js";
5
5
 
6
6
  // src/lib/update-check.ts
7
7
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- applyIsolation,
4
- chownAgentDir,
5
- isIsolationEnabled
6
- } from "./chunk-46S7YHUB.js";
7
2
  import {
8
3
  loadMergedEnv
9
- } from "./chunk-QF22MYDJ.js";
4
+ } from "./chunk-BJDLYTPS.js";
5
+ import {
6
+ chownAgentDir,
7
+ isIsolationEnabled,
8
+ wrapForIsolation
9
+ } from "./chunk-EN3NHRQC.js";
10
10
  import {
11
11
  agentDir,
12
12
  findAgent,
@@ -15,11 +15,11 @@ import {
15
15
  setVariantRunning,
16
16
  stateDir,
17
17
  voluteHome
18
- } from "./chunk-DP2DX4WV.js";
18
+ } from "./chunk-ESTOWEG2.js";
19
19
 
20
20
  // src/lib/agent-manager.ts
21
21
  import { execFile, spawn } from "child_process";
22
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
22
+ import { chmodSync, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
23
23
  import { resolve } from "path";
24
24
  import { promisify } from "util";
25
25
 
@@ -208,44 +208,22 @@ var AgentManager = class {
208
208
  };
209
209
  if (isIsolationEnabled()) {
210
210
  env.HOME = resolve(dir, "home");
211
- }
212
- if (isIsolationEnabled() && process.env.CLAUDE_CONFIG_DIR) {
213
- const agentClaudeDir = resolve(dir, "home", ".claude");
214
- try {
215
- mkdirSync(agentClaudeDir, { recursive: true });
216
- } catch (err) {
217
- throw new Error(
218
- `Cannot start agent ${name}: failed to create config directory at ${agentClaudeDir}: ${err instanceof Error ? err.message : err}`
219
- );
211
+ if (process.env.CLAUDE_CONFIG_DIR) {
212
+ const projectsDir = resolve(process.env.CLAUDE_CONFIG_DIR, "projects");
213
+ mkdirSync(projectsDir, { recursive: true });
214
+ chmodSync(projectsDir, 1528);
220
215
  }
221
- const sharedCreds = resolve(process.env.CLAUDE_CONFIG_DIR, ".credentials.json");
222
- const agentCreds = resolve(agentClaudeDir, ".credentials.json");
223
- if (existsSync3(sharedCreds)) {
224
- try {
225
- writeFileSync2(agentCreds, readFileSync2(sharedCreds));
226
- } catch (err) {
227
- throw new Error(
228
- `Cannot start agent ${name}: failed to copy credentials to ${agentClaudeDir}: ${err instanceof Error ? err.message : err}`
229
- );
230
- }
231
- } else {
232
- console.warn(
233
- `[daemon] shared credentials not found at ${sharedCreds} for agent ${name}. Copy ~/.claude/.credentials.json to ${process.env.CLAUDE_CONFIG_DIR}/.credentials.json or set ANTHROPIC_API_KEY in the agent's environment.`
234
- );
235
- }
236
- const baseName2 = name.split("@", 2)[0];
237
- chownAgentDir(agentClaudeDir, baseName2);
238
- env.CLAUDE_CONFIG_DIR = agentClaudeDir;
239
216
  }
240
217
  const tsxBin = resolve(dir, "node_modules", ".bin", "tsx");
218
+ const tsxArgs = ["src/server.ts", "--port", String(port)];
219
+ const [spawnCmd, spawnArgs] = wrapForIsolation(tsxBin, tsxArgs, name);
241
220
  const spawnOpts = {
242
221
  cwd: dir,
243
222
  stdio: ["ignore", "pipe", "pipe"],
244
223
  detached: true,
245
224
  env
246
225
  };
247
- await applyIsolation(spawnOpts, name);
248
- const child = spawn(tsxBin, ["src/server.ts", "--port", String(port)], spawnOpts);
226
+ const child = spawn(spawnCmd, spawnArgs, spawnOpts);
249
227
  this.agents.set(name, { child, port });
250
228
  child.stdout?.pipe(logStream);
251
229
  child.stderr?.pipe(logStream);
@@ -308,6 +286,10 @@ var AgentManager = class {
308
286
  const parts = [];
309
287
  if (context.type === "merge" || context.type === "merged") {
310
288
  parts.push(`[system] Variant "${context.name}" has been merged and you have been restarted.`);
289
+ } else if (context.type === "sprouted") {
290
+ parts.push(
291
+ "[system] You've sprouted. You now have full agent capabilities \u2014 connectors, schedules, variants, and the complete volute CLI. Check your new skills for details."
292
+ );
311
293
  } else {
312
294
  parts.push("[system] You have been restarted.");
313
295
  }
@@ -4,13 +4,13 @@ import {
4
4
  modeLabel,
5
5
  pollHealth,
6
6
  startService
7
- } from "./chunk-ZKNBD5P3.js";
7
+ } from "./chunk-IELXXS7E.js";
8
8
  import {
9
9
  parseArgs
10
10
  } from "./chunk-D424ZQGI.js";
11
11
  import {
12
12
  voluteHome
13
- } from "./chunk-DP2DX4WV.js";
13
+ } from "./chunk-ESTOWEG2.js";
14
14
 
15
15
  // src/commands/up.ts
16
16
  import { spawn } from "child_process";
@@ -5,10 +5,10 @@ import {
5
5
  slugify,
6
6
  splitMessage,
7
7
  writeChannelEntry
8
- } from "./chunk-POLMWJFC.js";
8
+ } from "./chunk-GPZCPGV3.js";
9
9
  import {
10
10
  voluteHome
11
- } from "./chunk-DP2DX4WV.js";
11
+ } from "./chunk-ESTOWEG2.js";
12
12
  import {
13
13
  __export
14
14
  } from "./chunk-K3NQKI34.js";
@@ -5,10 +5,10 @@ import {
5
5
  pollHealthDown,
6
6
  readDaemonConfig,
7
7
  stopService
8
- } from "./chunk-ZKNBD5P3.js";
8
+ } from "./chunk-IELXXS7E.js";
9
9
  import {
10
10
  voluteHome
11
- } from "./chunk-DP2DX4WV.js";
11
+ } from "./chunk-ESTOWEG2.js";
12
12
 
13
13
  // src/commands/down.ts
14
14
  import { existsSync, readFileSync, unlinkSync } from "fs";
@@ -1,13 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ wrapForIsolation
4
+ } from "./chunk-EN3NHRQC.js";
2
5
 
3
6
  // src/lib/exec.ts
4
7
  import { execFile as execFileCb, execFileSync, spawn } from "child_process";
5
8
  function exec(cmd, args, options) {
9
+ const [wrappedCmd, wrappedArgs] = options?.agentName ? wrapForIsolation(cmd, args, options.agentName) : [cmd, args];
6
10
  return new Promise((resolve, reject) => {
7
11
  execFileCb(
8
- cmd,
9
- args,
10
- { cwd: options?.cwd, uid: options?.uid, gid: options?.gid, env: options?.env },
12
+ wrappedCmd,
13
+ wrappedArgs,
14
+ { cwd: options?.cwd, env: options?.env },
11
15
  (err, stdout, stderr) => {
12
16
  if (err) {
13
17
  err.stderr = stderr;
@@ -31,11 +35,10 @@ function resolveVoluteBin() {
31
35
  }
32
36
  }
33
37
  function execInherit(cmd, args, options) {
38
+ const [wrappedCmd, wrappedArgs] = options?.agentName ? wrapForIsolation(cmd, args, options.agentName) : [cmd, args];
34
39
  return new Promise((resolve, reject) => {
35
- const child = spawn(cmd, args, {
40
+ const child = spawn(wrappedCmd, wrappedArgs, {
36
41
  cwd: options?.cwd,
37
- uid: options?.uid,
38
- gid: options?.gid,
39
42
  env: options?.env,
40
43
  stdio: "inherit"
41
44
  });
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/template.ts
4
+ import {
5
+ cpSync,
6
+ existsSync,
7
+ mkdirSync,
8
+ readdirSync,
9
+ readFileSync,
10
+ renameSync,
11
+ rmSync,
12
+ statSync,
13
+ writeFileSync
14
+ } from "fs";
15
+ import { tmpdir } from "os";
16
+ import { dirname, join, relative, resolve } from "path";
17
+ function findTemplatesRoot() {
18
+ let dir = dirname(new URL(import.meta.url).pathname);
19
+ for (let i = 0; i < 5; i++) {
20
+ const candidate = resolve(dir, "templates");
21
+ if (existsSync(resolve(candidate, "_base"))) return candidate;
22
+ dir = dirname(dir);
23
+ }
24
+ console.error(
25
+ "Templates directory not found. Searched up from:",
26
+ dirname(new URL(import.meta.url).pathname)
27
+ );
28
+ process.exit(1);
29
+ }
30
+ function composeTemplate(templatesRoot, templateName) {
31
+ const baseDir = resolve(templatesRoot, "_base");
32
+ const templateDir = resolve(templatesRoot, templateName);
33
+ if (!existsSync(baseDir)) {
34
+ console.error("Base template not found:", baseDir);
35
+ process.exit(1);
36
+ }
37
+ if (!existsSync(templateDir)) {
38
+ console.error(`Template not found: ${templateName}`);
39
+ process.exit(1);
40
+ }
41
+ const composedDir = resolve(tmpdir(), `volute-template-${Date.now()}`);
42
+ mkdirSync(composedDir, { recursive: true });
43
+ cpSync(baseDir, composedDir, { recursive: true });
44
+ for (const file of listFiles(templateDir)) {
45
+ const src = resolve(templateDir, file);
46
+ const dest = resolve(composedDir, file);
47
+ mkdirSync(dirname(dest), { recursive: true });
48
+ cpSync(src, dest);
49
+ }
50
+ const manifestPath = resolve(composedDir, "volute-template.json");
51
+ if (!existsSync(manifestPath)) {
52
+ rmSync(composedDir, { recursive: true, force: true });
53
+ console.error(`Template manifest not found: ${templateName}/volute-template.json`);
54
+ process.exit(1);
55
+ }
56
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
57
+ const skillsSrc = resolve(composedDir, "_skills");
58
+ if (existsSync(skillsSrc)) {
59
+ const skillsDest = resolve(composedDir, manifest.skillsDir);
60
+ mkdirSync(skillsDest, { recursive: true });
61
+ cpSync(skillsSrc, skillsDest, { recursive: true });
62
+ rmSync(skillsSrc, { recursive: true, force: true });
63
+ }
64
+ rmSync(manifestPath);
65
+ return { composedDir, manifest };
66
+ }
67
+ function copyTemplateToDir(composedDir, destDir, agentName, manifest) {
68
+ cpSync(composedDir, destDir, { recursive: true });
69
+ for (const [from, to] of Object.entries(manifest.rename)) {
70
+ const fromPath = resolve(destDir, from);
71
+ if (existsSync(fromPath)) {
72
+ renameSync(fromPath, resolve(destDir, to));
73
+ }
74
+ }
75
+ for (const file of manifest.substitute) {
76
+ const path = resolve(destDir, file);
77
+ if (existsSync(path)) {
78
+ const content = readFileSync(path, "utf-8");
79
+ writeFileSync(path, content.replaceAll("{{name}}", agentName));
80
+ }
81
+ }
82
+ }
83
+ function applyInitFiles(destDir) {
84
+ const initDir = resolve(destDir, ".init");
85
+ if (!existsSync(initDir)) return;
86
+ const homeDir = resolve(destDir, "home");
87
+ for (const file of listFiles(initDir)) {
88
+ const src = resolve(initDir, file);
89
+ const dest = resolve(homeDir, file);
90
+ const parent = dirname(dest);
91
+ if (!existsSync(parent)) {
92
+ mkdirSync(parent, { recursive: true });
93
+ }
94
+ cpSync(src, dest);
95
+ }
96
+ rmSync(initDir, { recursive: true, force: true });
97
+ }
98
+ function listFiles(dir) {
99
+ const results = [];
100
+ function walk(current) {
101
+ for (const entry of readdirSync(current)) {
102
+ const full = join(current, entry);
103
+ if (statSync(full).isDirectory()) {
104
+ if (entry === ".git") continue;
105
+ walk(full);
106
+ } else {
107
+ results.push(relative(dir, full));
108
+ }
109
+ }
110
+ }
111
+ walk(dir);
112
+ return results;
113
+ }
114
+
115
+ export {
116
+ findTemplatesRoot,
117
+ composeTemplate,
118
+ copyTemplateToDir,
119
+ applyInitFiles,
120
+ listFiles
121
+ };
package/dist/cli.js CHANGED
@@ -9,55 +9,61 @@ 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-5LQNWBH7.js");
12
+ const { default: pkg } = await import("./package-TPXKJXLG.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-2AQPI3QV.js").then((m) => m.run(args));
18
+ await import("./agent-HYX2ZTFM.js").then((m) => m.run(args));
19
19
  break;
20
20
  case "send":
21
- await import("./send-V5ESR5O2.js").then((m) => m.run(args));
21
+ await import("./send-EZYEIVMA.js").then((m) => m.run(args));
22
22
  break;
23
23
  case "history":
24
- await import("./history-NI5QP27M.js").then((m) => m.run(args));
24
+ await import("./history-J7TURCZS.js").then((m) => m.run(args));
25
25
  break;
26
26
  case "variant":
27
- await import("./variant-7IZF6OWO.js").then((m) => m.run(args));
27
+ await import("./variant-ZCZS3JAP.js").then((m) => m.run(args));
28
28
  break;
29
29
  case "connector":
30
- await import("./connector-L2HBLZBW.js").then((m) => m.run(args));
30
+ await import("./connector-Z5KYVTZ5.js").then((m) => m.run(args));
31
31
  break;
32
32
  case "channel":
33
- await import("./channel-2WHBRDTD.js").then((m) => m.run(args));
33
+ await import("./channel-72YET5JC.js").then((m) => m.run(args));
34
34
  break;
35
35
  case "schedule":
36
- await import("./schedule-E4MFGYSA.js").then((m) => m.run(args));
36
+ await import("./schedule-YMAJZ52M.js").then((m) => m.run(args));
37
37
  break;
38
38
  case "env":
39
- await import("./env-CGORIKVF.js").then((m) => m.run(args));
39
+ await import("./env-EOO2C7L7.js").then((m) => m.run(args));
40
40
  break;
41
41
  case "up":
42
- await import("./up-A6XT6AFX.js").then((m) => m.run(args));
42
+ await import("./up-N2OWDCT6.js").then((m) => m.run(args));
43
43
  break;
44
44
  case "down":
45
- await import("./down-O2EQJ5DO.js").then((m) => m.run(args));
45
+ await import("./down-IMZE7V42.js").then((m) => m.run(args));
46
46
  break;
47
47
  case "restart":
48
- await import("./daemon-restart-IVJ7X4PF.js").then((m) => m.run(args));
48
+ await import("./daemon-restart-4OXIGWV6.js").then((m) => m.run(args));
49
49
  break;
50
50
  case "setup":
51
- await import("./setup-7N4KYOYN.js").then((m) => m.run(args));
51
+ await import("./setup-DCZWBBD7.js").then((m) => m.run(args));
52
52
  break;
53
53
  case "service":
54
- await import("./service-UL3OCODG.js").then((m) => m.run(args));
54
+ await import("./service-DDFZA7Q3.js").then((m) => m.run(args));
55
55
  break;
56
56
  case "update":
57
- await import("./update-PV3XM6DX.js").then((m) => m.run(args));
57
+ await import("./update-FXLGIIWH.js").then((m) => m.run(args));
58
58
  break;
59
59
  case "status":
60
- await import("./status-DFWM342I.js").then((m) => m.run(args));
60
+ await import("./status-PMMS4XUH.js").then((m) => m.run(args));
61
+ break;
62
+ case "seed":
63
+ await import("./seed-M6QPHFTV.js").then((m) => m.run(args));
64
+ break;
65
+ case "sprout":
66
+ await import("./sprout-EAWETTWZ.js").then((m) => m.run(args));
61
67
  break;
62
68
  case "--help":
63
69
  case "-h":
@@ -108,6 +114,9 @@ Commands:
108
114
  volute setup [--port N] [--host H] Install system service with user isolation
109
115
  volute setup uninstall [--force] Remove system service + isolation
110
116
 
117
+ volute seed <name> Plant a seed agent (orientation mode)
118
+ volute sprout Complete orientation, become a full agent
119
+
111
120
  volute update Update to latest version
112
121
  volute status Show daemon status and agents
113
122
 
@@ -124,7 +133,7 @@ Run 'volute --help' for usage.`);
124
133
  process.exit(1);
125
134
  }
126
135
  if (command !== "update") {
127
- import("./update-check-YPGH5X4E.js").then((m) => m.checkForUpdate()).then((result) => {
136
+ import("./update-check-NBHTSTHK.js").then((m) => m.checkForUpdate()).then((result) => {
128
137
  if (result.updateAvailable) {
129
138
  console.error(`
130
139
  Update available: ${result.current} \u2192 ${result.latest}`);