volute 0.4.0 → 0.6.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 (82) hide show
  1. package/README.md +22 -22
  2. package/dist/agent-X7GJLBLW.js +79 -0
  3. package/dist/{agent-manager-AUCKMGPR.js → agent-manager-JDVXU3ON.js} +4 -4
  4. package/dist/channel-SMCNOIVQ.js +262 -0
  5. package/dist/chunk-AOKAQGO4.js +107 -0
  6. package/dist/{chunk-VRVVQIYY.js → chunk-AZEL2IEK.js} +1 -1
  7. package/dist/chunk-B3R6L2GW.js +24 -0
  8. package/dist/{chunk-MXUCNIBG.js → chunk-BX7KI4S3.js} +68 -3
  9. package/dist/{chunk-I6OHXCMV.js → chunk-G6ZNGLUX.js} +47 -9
  10. package/dist/{chunk-DNOXHLE5.js → chunk-H7AMDUIA.js} +1 -1
  11. package/dist/{chunk-YGFIWIOF.js → chunk-JR4UXCTO.js} +1 -1
  12. package/dist/{chunk-3C2XR4IY.js → chunk-UWHWAPGO.js} +120 -107
  13. package/dist/{chunk-SOZA2TLP.js → chunk-W76KWE23.js} +1 -1
  14. package/dist/{chunk-GSPKUPKU.js → chunk-XUA3JUFK.js} +2 -1
  15. package/dist/chunk-ZYGKG6VC.js +22 -0
  16. package/dist/chunk-ZZOOTYXK.js +583 -0
  17. package/dist/cli.js +83 -74
  18. package/dist/{connector-DKDJTLYZ.js → connector-Y7JPNROO.js} +11 -6
  19. package/dist/connectors/discord.js +34 -5
  20. package/dist/connectors/slack.js +36 -8
  21. package/dist/connectors/telegram.js +55 -6
  22. package/dist/create-G525LWEA.js +91 -0
  23. package/dist/{daemon-client-XR24PUJF.js → daemon-client-442IV43D.js} +2 -2
  24. package/dist/daemon.js +1273 -384
  25. package/dist/{delete-55MXCEY5.js → delete-2PH2CGDY.js} +7 -8
  26. package/dist/{down-3OB6UVAJ.js → down-FXWAN66A.js} +1 -1
  27. package/dist/{env-JB27UAC3.js → env-7GLUJCWS.js} +8 -5
  28. package/dist/{history-BKG74I43.js → history-H72ZUIBN.js} +3 -3
  29. package/dist/{import-4CI2ZUTJ.js → import-AVKQJDYC.js} +8 -8
  30. package/dist/{logs-NXFFGUKY.js → logs-EDGK26AK.js} +2 -2
  31. package/dist/message-SCOQDR3P.js +32 -0
  32. package/dist/{package-Z2SFO2SV.js → package-4DP4Y4UO.js} +1 -1
  33. package/dist/restart-O4ETYLJF.js +29 -0
  34. package/dist/{schedule-A35SH4HT.js → schedule-S6QVC5ON.js} +10 -5
  35. package/dist/send-G7PE4DOJ.js +72 -0
  36. package/dist/{setup-2FDVN7OF.js → setup-F4TCWVSP.js} +5 -5
  37. package/dist/{start-LDPMCMYT.js → start-VHQ7LNWM.js} +3 -3
  38. package/dist/{status-MVSQG54T.js → status-QAJWXKMZ.js} +3 -3
  39. package/dist/{stop-5PZTZCLL.js → stop-CAGCT5NI.js} +6 -7
  40. package/dist/{up-F7TMTLRE.js → up-CSX3ZUIU.js} +16 -4
  41. package/dist/update-XSIX3GGP.js +140 -0
  42. package/dist/update-check-5ZADDHCK.js +17 -0
  43. package/dist/{upgrade-6ZW2RD64.js → upgrade-YXKPWDRU.js} +16 -15
  44. package/dist/{variant-T64BKARF.js → variant-4Z6W3PP6.js} +15 -10
  45. package/dist/web-assets/assets/index-D5PzIndO.js +308 -0
  46. package/dist/web-assets/index.html +2 -2
  47. package/drizzle/0003_clean_ego.sql +12 -0
  48. package/drizzle/meta/0003_snapshot.json +417 -0
  49. package/drizzle/meta/_journal.json +7 -0
  50. package/package.json +1 -1
  51. package/templates/_base/.init/.config/hooks/startup-context.sh +19 -1
  52. package/templates/_base/.init/.config/scripts/session-reader.ts +59 -0
  53. package/templates/_base/_skills/sessions/SKILL.md +49 -0
  54. package/templates/_base/_skills/volute-agent/SKILL.md +114 -14
  55. package/templates/_base/home/.config/routes.json +10 -0
  56. package/templates/_base/home/VOLUTE.md +14 -35
  57. package/templates/_base/src/lib/format-prefix.ts +7 -1
  58. package/templates/_base/src/lib/router.ts +193 -19
  59. package/templates/_base/src/lib/routing.ts +55 -18
  60. package/templates/_base/src/lib/session-monitor.ts +400 -0
  61. package/templates/_base/src/lib/types.ts +5 -1
  62. package/templates/agent-sdk/.init/.config/routes.json +5 -0
  63. package/templates/agent-sdk/.init/CLAUDE.md +2 -2
  64. package/templates/agent-sdk/src/agent.ts +18 -1
  65. package/templates/agent-sdk/src/lib/hooks/session-context.ts +32 -0
  66. package/templates/agent-sdk/src/server.ts +8 -2
  67. package/templates/agent-sdk/volute-template.json +1 -1
  68. package/templates/pi/.init/.config/routes.json +5 -0
  69. package/templates/pi/.init/AGENTS.md +1 -1
  70. package/templates/pi/src/agent.ts +12 -4
  71. package/templates/pi/src/lib/session-context-extension.ts +33 -0
  72. package/templates/pi/src/server.ts +1 -1
  73. package/templates/pi/volute-template.json +1 -1
  74. package/dist/channel-DQ6UY7QB.js +0 -67
  75. package/dist/chunk-5OCWMTVS.js +0 -152
  76. package/dist/chunk-ZHCE4DPY.js +0 -110
  77. package/dist/create-ILVOG75A.js +0 -79
  78. package/dist/send-3U6OTKG7.js +0 -57
  79. package/dist/web-assets/assets/index-NS621maO.js +0 -296
  80. package/templates/agent-sdk/.init/.config/sessions.json +0 -4
  81. package/templates/pi/.init/.config/sessions.json +0 -1
  82. package/dist/{service-SA4TTMDU.js → service-HZNIDNJF.js} +3 -3
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, renameSync } from "node:fs";
1
+ import { existsSync, mkdirSync, renameSync, writeFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { createAgent } from "./agent.js";
4
4
  import { createFileHandlerResolver } from "./lib/file-handler.js";
@@ -41,13 +41,19 @@ const agent = createAgent({
41
41
  onIdentityReload: async () => {
42
42
  log("server", "identity file changed — restarting to reload");
43
43
  await agent.waitForCommits();
44
+ // Signal daemon to restart immediately (bypasses crash backoff)
45
+ try {
46
+ writeFileSync(resolve(".volute/restart.json"), JSON.stringify({ action: "reload" }));
47
+ } catch (err) {
48
+ log("server", "failed to write restart signal:", err);
49
+ }
44
50
  server.close();
45
51
  process.exit(0);
46
52
  },
47
53
  });
48
54
 
49
55
  const router = createRouter({
50
- configPath: resolve("home/.config/sessions.json"),
56
+ configPath: resolve("home/.config/routes.json"),
51
57
  agentHandler: agent.resolve,
52
58
  fileHandler: createFileHandlerResolver(resolve("home")),
53
59
  });
@@ -4,6 +4,6 @@
4
4
  "biome.json.tmpl": "biome.json",
5
5
  "home/.config/volute.json.tmpl": "home/.config/volute.json"
6
6
  },
7
- "substitute": ["package.json", ".init/SOUL.md"],
7
+ "substitute": ["package.json", ".init/SOUL.md", "home/.config/routes.json"],
8
8
  "skillsDir": "home/.claude/skills"
9
9
  }
@@ -0,0 +1,5 @@
1
+ {
2
+ "gateUnmatched": true,
3
+ "rules": [{ "channel": "volute:*", "isDM": true, "session": "${channel}" }],
4
+ "default": "main"
5
+ }
@@ -23,7 +23,7 @@ See the **memory** skill for detailed guidance.
23
23
 
24
24
  ## Sessions
25
25
 
26
- - You may have **multiple named sessions** — each maintains its own conversation history. See `VOLUTE.md` for how to configure session routing via `.config/sessions.json`.
26
+ - You may have **multiple named sessions** — each maintains its own conversation history. See `VOLUTE.md` for how to configure session routing via `.config/routes.json`.
27
27
  - Your conversation may be **resumed** from a previous session — orient yourself by reading recent daily logs if needed.
28
28
  - On a **fresh session**, read `MEMORY.md` and recent daily logs to remember where you left off.
29
29
  - On **compaction**, update today's daily log to preserve context before the conversation is trimmed.
@@ -11,6 +11,7 @@ import {
11
11
  } from "@mariozechner/pi-coding-agent";
12
12
  import { commitFileChange } from "./lib/auto-commit.js";
13
13
  import { log, logText, logThinking, logToolResult, logToolUse } from "./lib/logger.js";
14
+ import { createSessionContextExtension } from "./lib/session-context-extension.js";
14
15
  import type {
15
16
  HandlerMeta,
16
17
  HandlerResolver,
@@ -32,8 +33,10 @@ type PiSession = {
32
33
  currentMessageId?: string;
33
34
  };
34
35
 
35
- const DEFAULT_COMPACTION_MESSAGE =
36
- "Your conversation is approaching its context limit. Please update today's journal entry to preserve important context before the conversation is compacted.";
36
+ function defaultCompactionMessage(): string {
37
+ const today = new Date().toISOString().slice(0, 10);
38
+ return `Context is getting long — compaction is about to summarize this conversation. Before that happens, save anything important to files (MEMORY.md, memory/journal/${today}.md, etc.) since those survive compaction. Focus on: decisions made, open tasks, and anything you'd need to pick up where you left off.`;
39
+ }
37
40
 
38
41
  function resolveModel(modelStr: string) {
39
42
  const [provider, ...rest] = modelStr.split(":");
@@ -75,7 +78,7 @@ export function createAgent(options: {
75
78
  compactionMessage?: string;
76
79
  }): { resolve: HandlerResolver } {
77
80
  const sessions = new Map<string, PiSession>();
78
- const compactionMessage = options.compactionMessage ?? DEFAULT_COMPACTION_MESSAGE;
81
+ const compactionMessage = options.compactionMessage ?? defaultCompactionMessage();
79
82
 
80
83
  // Shared setup (created once)
81
84
  const modelStr = options.model || process.env.PI_MODEL || "anthropic:claude-sonnet-4-20250514";
@@ -135,11 +138,16 @@ export function createAgent(options: {
135
138
  retry: { enabled: true, maxRetries: 3 },
136
139
  });
137
140
 
141
+ const sessionContextExtension = createSessionContextExtension({
142
+ currentSession: session.name,
143
+ cwd: options.cwd,
144
+ });
145
+
138
146
  const resourceLoader = new DefaultResourceLoader({
139
147
  cwd: options.cwd,
140
148
  settingsManager,
141
149
  systemPrompt: options.systemPrompt,
142
- extensionFactories: [preCompactExtension],
150
+ extensionFactories: [preCompactExtension, sessionContextExtension],
143
151
  });
144
152
  await resourceLoader.reload();
145
153
 
@@ -0,0 +1,33 @@
1
+ import { resolve } from "node:path";
2
+ import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
3
+ import { getSessionUpdates, resolvePiJsonl } from "./session-monitor.js";
4
+
5
+ export function createSessionContextExtension(options: {
6
+ currentSession: string;
7
+ cwd: string;
8
+ }): ExtensionFactory {
9
+ return (pi) => {
10
+ pi.on("before_agent_start", () => {
11
+ try {
12
+ const sessionsDir = resolve(options.cwd, ".volute/pi-sessions");
13
+ const summary = getSessionUpdates({
14
+ currentSession: options.currentSession,
15
+ sessionsDir,
16
+ cursorFile: resolve(options.cwd, ".volute/session-cursors.json"),
17
+ jsonlResolver: (name) => resolvePiJsonl(sessionsDir, name),
18
+ format: "pi",
19
+ });
20
+ if (!summary) return {};
21
+ return {
22
+ message: {
23
+ customType: "session-update",
24
+ content: summary,
25
+ display: true,
26
+ },
27
+ };
28
+ } catch {
29
+ return {};
30
+ }
31
+ });
32
+ };
33
+ }
@@ -29,7 +29,7 @@ const agent = createAgent({
29
29
  });
30
30
 
31
31
  const router = createRouter({
32
- configPath: resolve("home/.config/sessions.json"),
32
+ configPath: resolve("home/.config/routes.json"),
33
33
  agentHandler: agent.resolve,
34
34
  fileHandler: createFileHandlerResolver(resolve("home")),
35
35
  });
@@ -4,6 +4,6 @@
4
4
  "biome.json.tmpl": "biome.json",
5
5
  "home/.config/volute.json.tmpl": "home/.config/volute.json"
6
6
  },
7
- "substitute": ["package.json", ".init/SOUL.md"],
7
+ "substitute": ["package.json", ".init/SOUL.md", "home/.config/routes.json"],
8
8
  "skillsDir": "home/.claude/skills"
9
9
  }
@@ -1,67 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- resolveAgentName
4
- } from "./chunk-VRVVQIYY.js";
5
- import {
6
- getChannelDriver
7
- } from "./chunk-5OCWMTVS.js";
8
- import {
9
- loadMergedEnv
10
- } from "./chunk-DNOXHLE5.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/channel.ts
20
- async function run(args) {
21
- const { positional, flags } = parseArgs(args, {
22
- agent: { type: "string" },
23
- limit: { type: "number" }
24
- });
25
- const subcommand = positional[0];
26
- const uri = positional[1];
27
- const message = positional[2];
28
- if (!subcommand || !uri || subcommand === "send" && !message) {
29
- console.error(`Usage:
30
- volute channel read <channel-uri> [--limit N] [--agent <name>]
31
- volute channel send <channel-uri> "<message>" [--agent <name>]`);
32
- process.exit(1);
33
- }
34
- const agentName = resolveAgentName(flags);
35
- const colonIdx = uri.indexOf(":");
36
- if (colonIdx === -1) {
37
- console.error(`Invalid channel URI: ${uri} (expected format: platform:id)`);
38
- process.exit(1);
39
- }
40
- const platform = uri.slice(0, colonIdx);
41
- const channelId = uri.slice(colonIdx + 1);
42
- const driver = getChannelDriver(platform);
43
- if (!driver) {
44
- console.error(`No channel driver for platform: ${platform}`);
45
- process.exit(1);
46
- }
47
- const { dir } = resolveAgent(agentName);
48
- const env = loadMergedEnv(dir);
49
- try {
50
- if (subcommand === "read") {
51
- const limit = flags.limit ?? 20;
52
- const output = await driver.read(env, channelId, limit);
53
- console.log(output);
54
- } else if (subcommand === "send") {
55
- await driver.send(env, channelId, message);
56
- } else {
57
- console.error(`Unknown subcommand: ${subcommand}`);
58
- process.exit(1);
59
- }
60
- } catch (err) {
61
- console.error(err instanceof Error ? err.message : String(err));
62
- process.exit(1);
63
- }
64
- }
65
- export {
66
- run
67
- };
@@ -1,152 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- __export
4
- } from "./chunk-K3NQKI34.js";
5
-
6
- // src/lib/channels/discord.ts
7
- var discord_exports = {};
8
- __export(discord_exports, {
9
- read: () => read,
10
- send: () => send
11
- });
12
- var API_BASE = "https://discord.com/api/v10";
13
- function requireToken(env) {
14
- const token = env.DISCORD_TOKEN;
15
- if (!token) throw new Error("DISCORD_TOKEN not set");
16
- return token;
17
- }
18
- async function read(env, channelId, limit) {
19
- const token = requireToken(env);
20
- const res = await fetch(`${API_BASE}/channels/${channelId}/messages?limit=${limit}`, {
21
- headers: { Authorization: `Bot ${token}` }
22
- });
23
- if (!res.ok) {
24
- throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
25
- }
26
- const messages = await res.json();
27
- return messages.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
28
- }
29
- async function send(env, channelId, message) {
30
- const token = requireToken(env);
31
- const res = await fetch(`${API_BASE}/channels/${channelId}/messages`, {
32
- method: "POST",
33
- headers: {
34
- Authorization: `Bot ${token}`,
35
- "Content-Type": "application/json"
36
- },
37
- body: JSON.stringify({ content: message })
38
- });
39
- if (!res.ok) {
40
- throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
41
- }
42
- }
43
-
44
- // src/lib/channels/slack.ts
45
- var slack_exports = {};
46
- __export(slack_exports, {
47
- read: () => read2,
48
- send: () => send2
49
- });
50
- var API_BASE2 = "https://slack.com/api";
51
- function requireToken2(env) {
52
- const token = env.SLACK_BOT_TOKEN;
53
- if (!token) throw new Error("SLACK_BOT_TOKEN not set");
54
- return token;
55
- }
56
- async function slackApi(token, method, body) {
57
- const res = await fetch(`${API_BASE2}/${method}`, {
58
- method: "POST",
59
- headers: {
60
- Authorization: `Bearer ${token}`,
61
- "Content-Type": "application/json"
62
- },
63
- body: JSON.stringify(body)
64
- });
65
- if (!res.ok) {
66
- throw new Error(`Slack API HTTP error: ${res.status} ${res.statusText}`);
67
- }
68
- const data = await res.json();
69
- if (!data.ok) {
70
- throw new Error(`Slack API error: ${data.error}`);
71
- }
72
- return data;
73
- }
74
- async function read2(env, channelId, limit) {
75
- const token = requireToken2(env);
76
- const data = await slackApi(token, "conversations.history", {
77
- channel: channelId,
78
- limit
79
- });
80
- return data.messages.reverse().map((m) => `${m.user ?? m.bot_id ?? "unknown"}: ${m.text}`).join("\n");
81
- }
82
- async function send2(env, channelId, message) {
83
- const token = requireToken2(env);
84
- await slackApi(token, "chat.postMessage", {
85
- channel: channelId,
86
- text: message
87
- });
88
- }
89
-
90
- // src/lib/channels/telegram.ts
91
- var telegram_exports = {};
92
- __export(telegram_exports, {
93
- read: () => read3,
94
- send: () => send3
95
- });
96
- var API_BASE3 = "https://api.telegram.org";
97
- function requireToken3(env) {
98
- const token = env.TELEGRAM_BOT_TOKEN;
99
- if (!token) throw new Error("TELEGRAM_BOT_TOKEN not set");
100
- return token;
101
- }
102
- async function read3(_env, _channelId, _limit) {
103
- throw new Error(
104
- "Telegram Bot API does not support reading chat history. Use volute channel send instead."
105
- );
106
- }
107
- async function send3(env, chatId, message) {
108
- const token = requireToken3(env);
109
- const res = await fetch(`${API_BASE3}/bot${token}/sendMessage`, {
110
- method: "POST",
111
- headers: { "Content-Type": "application/json" },
112
- body: JSON.stringify({ chat_id: chatId, text: message })
113
- });
114
- if (!res.ok) {
115
- const body = await res.text().catch(() => "");
116
- throw new Error(`Telegram API error: ${res.status} ${body}`);
117
- }
118
- }
119
-
120
- // src/lib/channels.ts
121
- var CHANNELS = {
122
- web: { name: "web", displayName: "Web UI", showToolCalls: true },
123
- discord: {
124
- name: "discord",
125
- displayName: "Discord",
126
- showToolCalls: false,
127
- driver: discord_exports
128
- },
129
- slack: {
130
- name: "slack",
131
- displayName: "Slack",
132
- showToolCalls: false,
133
- driver: slack_exports
134
- },
135
- telegram: {
136
- name: "telegram",
137
- displayName: "Telegram",
138
- showToolCalls: false,
139
- driver: telegram_exports
140
- },
141
- cli: { name: "cli", displayName: "CLI", showToolCalls: true },
142
- agent: { name: "agent", displayName: "Agent", showToolCalls: true },
143
- system: { name: "system", displayName: "System", showToolCalls: false }
144
- };
145
- function getChannelDriver(platform) {
146
- return CHANNELS[platform]?.driver ?? null;
147
- }
148
-
149
- export {
150
- CHANNELS,
151
- getChannelDriver
152
- };
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/lib/format-tool.ts
4
- function summarizeTool(name, input) {
5
- if (input && typeof input === "object") {
6
- const args = input;
7
- const val = args.path ?? args.command ?? args.query ?? args.url;
8
- if (typeof val === "string") {
9
- const brief = val.length > 60 ? `${val.slice(0, 57)}...` : val;
10
- return `[${name} ${brief}]`;
11
- }
12
- }
13
- return `[${name}]`;
14
- }
15
- function collectPart(event) {
16
- if (event.type === "text") return event.content ?? "";
17
- if (event.type === "tool_use") return summarizeTool(event.name ?? "", event.input);
18
- return null;
19
- }
20
-
21
- // src/lib/log-buffer.ts
22
- var LogBuffer = class {
23
- entries = [];
24
- maxSize = 1e3;
25
- subscribers = /* @__PURE__ */ new Set();
26
- append(entry) {
27
- this.entries.push(entry);
28
- if (this.entries.length > this.maxSize) {
29
- this.entries.shift();
30
- }
31
- for (const sub of this.subscribers) {
32
- sub(entry);
33
- }
34
- }
35
- getEntries() {
36
- return [...this.entries];
37
- }
38
- subscribe(fn) {
39
- this.subscribers.add(fn);
40
- return () => this.subscribers.delete(fn);
41
- }
42
- };
43
- var logBuffer = new LogBuffer();
44
-
45
- // src/lib/logger.ts
46
- function write(level, msg, data) {
47
- const entry = {
48
- level,
49
- msg,
50
- ts: (/* @__PURE__ */ new Date()).toISOString(),
51
- ...data ? { data } : {}
52
- };
53
- const line = JSON.stringify(entry);
54
- process.stderr.write(`${line}
55
- `);
56
- logBuffer.append(entry);
57
- }
58
- var log = {
59
- info: (msg, data) => write("info", msg, data),
60
- warn: (msg, data) => write("warn", msg, data),
61
- error: (msg, data) => write("error", msg, data)
62
- };
63
- var logger_default = log;
64
-
65
- // src/lib/ndjson.ts
66
- var MAX_BUFFER_SIZE = 1e6;
67
- async function* readNdjson(body) {
68
- const reader = body.getReader();
69
- const decoder = new TextDecoder();
70
- let buffer = "";
71
- try {
72
- while (true) {
73
- const { done, value } = await reader.read();
74
- if (done) break;
75
- buffer += decoder.decode(value, { stream: true });
76
- if (buffer.length > MAX_BUFFER_SIZE) {
77
- logger_default.warn("ndjson: buffer exceeded 1MB, resetting");
78
- buffer = "";
79
- continue;
80
- }
81
- const lines = buffer.split("\n");
82
- buffer = lines.pop() || "";
83
- for (const line of lines) {
84
- if (!line.trim()) continue;
85
- try {
86
- yield JSON.parse(line);
87
- } catch {
88
- logger_default.warn("ndjson: skipping invalid line", { line: line.slice(0, 100) });
89
- }
90
- }
91
- }
92
- if (buffer.trim()) {
93
- try {
94
- yield JSON.parse(buffer);
95
- } catch {
96
- logger_default.warn("ndjson: skipping invalid line", { line: buffer.slice(0, 100) });
97
- }
98
- }
99
- } finally {
100
- reader.releaseLock();
101
- }
102
- }
103
-
104
- export {
105
- summarizeTool,
106
- collectPart,
107
- logBuffer,
108
- logger_default,
109
- readNdjson
110
- };
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- applyInitFiles,
4
- composeTemplate,
5
- copyTemplateToDir,
6
- findTemplatesRoot
7
- } from "./chunk-GSPKUPKU.js";
8
- import {
9
- exec,
10
- execInherit
11
- } from "./chunk-5SKQ6J7T.js";
12
- import {
13
- parseArgs
14
- } from "./chunk-D424ZQGI.js";
15
- import {
16
- chownAgentDir,
17
- createAgentUser,
18
- ensureVoluteGroup
19
- } from "./chunk-SOZA2TLP.js";
20
- import {
21
- addAgent,
22
- agentDir,
23
- ensureVoluteHome,
24
- nextPort
25
- } from "./chunk-3C2XR4IY.js";
26
- import "./chunk-K3NQKI34.js";
27
-
28
- // src/commands/create.ts
29
- import { existsSync, rmSync } from "fs";
30
- async function run(args) {
31
- const { positional, flags } = parseArgs(args, {
32
- template: { type: "string" }
33
- });
34
- const name = positional[0];
35
- const template = flags.template ?? "agent-sdk";
36
- if (!name) {
37
- console.error("Usage: volute create <name> [--template <name>]");
38
- process.exit(1);
39
- }
40
- ensureVoluteHome();
41
- const dest = agentDir(name);
42
- if (existsSync(dest)) {
43
- console.error(`Agent already exists: ${name}`);
44
- process.exit(1);
45
- }
46
- const templatesRoot = findTemplatesRoot();
47
- const { composedDir, manifest } = composeTemplate(templatesRoot, template);
48
- try {
49
- copyTemplateToDir(composedDir, dest, name, manifest);
50
- applyInitFiles(dest);
51
- } finally {
52
- rmSync(composedDir, { recursive: true, force: true });
53
- }
54
- const port = nextPort();
55
- addAgent(name, port);
56
- console.log("Installing dependencies...");
57
- await execInherit("npm", ["install"], { 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);
72
- console.log(`
73
- Created agent: ${name} (port ${port})`);
74
- console.log(`
75
- volute start ${name}`);
76
- }
77
- export {
78
- run
79
- };
@@ -1,57 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- readNdjson,
4
- summarizeTool
5
- } from "./chunk-ZHCE4DPY.js";
6
- import {
7
- daemonFetch
8
- } from "./chunk-YGFIWIOF.js";
9
- import "./chunk-3C2XR4IY.js";
10
- import "./chunk-K3NQKI34.js";
11
-
12
- // src/commands/send.ts
13
- import { userInfo } from "os";
14
- async function run(args) {
15
- const name = args[0];
16
- const message = args[1];
17
- if (!name || !message) {
18
- console.error('Usage: volute send <name> "<message>"');
19
- process.exit(1);
20
- }
21
- const agentSelf = process.env.VOLUTE_AGENT;
22
- const sender = agentSelf || userInfo().username;
23
- const channel = agentSelf ? "agent" : "cli";
24
- const res = await daemonFetch(`/api/agents/${encodeURIComponent(name)}/message`, {
25
- method: "POST",
26
- headers: { "Content-Type": "application/json" },
27
- body: JSON.stringify({
28
- content: [{ type: "text", text: message }],
29
- channel,
30
- sender
31
- })
32
- });
33
- if (!res.ok) {
34
- const data = await res.json();
35
- console.error(data.error ?? `Failed to send message: ${res.status}`);
36
- process.exit(1);
37
- }
38
- if (!res.body) {
39
- console.error("No response body");
40
- process.exit(1);
41
- }
42
- for await (const event of readNdjson(res.body)) {
43
- if (event.type === "text") {
44
- process.stdout.write(event.content);
45
- } else if (event.type === "tool_use") {
46
- process.stderr.write(`${summarizeTool(event.name, event.input)}
47
- `);
48
- }
49
- if (event.type === "done") {
50
- break;
51
- }
52
- }
53
- process.stdout.write("\n");
54
- }
55
- export {
56
- run
57
- };