volute 0.4.0 → 0.5.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.
- package/README.md +22 -22
- package/dist/agent-Z2B6EFEQ.js +75 -0
- package/dist/{agent-manager-AUCKMGPR.js → agent-manager-PXBKA2GK.js} +4 -4
- package/dist/channel-MK5OK2SI.js +113 -0
- package/dist/chunk-5X7HGB6L.js +107 -0
- package/dist/{chunk-YGFIWIOF.js → chunk-7L4AN5D4.js} +1 -1
- package/dist/{chunk-VRVVQIYY.js → chunk-AZEL2IEK.js} +1 -1
- package/dist/chunk-B3R6L2GW.js +24 -0
- package/dist/{chunk-DNOXHLE5.js → chunk-HE67X4T6.js} +1 -1
- package/dist/{chunk-I6OHXCMV.js → chunk-MW2KFO3B.js} +47 -9
- package/dist/{chunk-5OCWMTVS.js → chunk-SMISE4SV.js} +77 -3
- package/dist/{chunk-SOZA2TLP.js → chunk-UAVD2AHX.js} +1 -1
- package/dist/{chunk-3C2XR4IY.js → chunk-UX25Z2ND.js} +113 -107
- package/dist/{chunk-GSPKUPKU.js → chunk-XUA3JUFK.js} +2 -1
- package/dist/chunk-ZYGKG6VC.js +22 -0
- package/dist/cli.js +86 -74
- package/dist/{connector-DKDJTLYZ.js → connector-LYEMXQEV.js} +11 -6
- package/dist/connectors/discord.js +3 -1
- package/dist/connectors/slack.js +14 -5
- package/dist/connectors/telegram.js +21 -2
- package/dist/conversation-ERXEQZTY.js +163 -0
- package/dist/create-RVCZN6HE.js +91 -0
- package/dist/{daemon-client-XR24PUJF.js → daemon-client-ZY6UUN2M.js} +2 -2
- package/dist/daemon.js +629 -177
- package/dist/{delete-55MXCEY5.js → delete-3QH7VYIN.js} +7 -8
- package/dist/{down-3OB6UVAJ.js → down-O7IFZLVJ.js} +1 -1
- package/dist/{env-JB27UAC3.js → env-4D4REPJF.js} +8 -5
- package/dist/{history-BKG74I43.js → history-OEONB53Z.js} +3 -3
- package/dist/{import-4CI2ZUTJ.js → import-MXJB2EII.js} +8 -8
- package/dist/{logs-NXFFGUKY.js → logs-DF342W4M.js} +2 -2
- package/dist/message-ADHWFHSI.js +32 -0
- package/dist/{package-Z2SFO2SV.js → package-VQOE7JNH.js} +1 -1
- package/dist/{schedule-A35SH4HT.js → schedule-NAG6F463.js} +10 -5
- package/dist/send-66QMKRUH.js +75 -0
- package/dist/{setup-2FDVN7OF.js → setup-RPRRGG2F.js} +5 -5
- package/dist/{start-LDPMCMYT.js → start-TUOXDSFL.js} +3 -3
- package/dist/{status-MVSQG54T.js → status-A36EHRO4.js} +3 -3
- package/dist/{stop-5PZTZCLL.js → stop-AOJZLQ5X.js} +6 -7
- package/dist/{up-F7TMTLRE.js → up-7ILD7GU7.js} +2 -2
- package/dist/update-LPSIAWQ2.js +140 -0
- package/dist/update-check-Y33QDCFL.js +17 -0
- package/dist/{upgrade-6ZW2RD64.js → upgrade-FX2TKJ2S.js} +16 -15
- package/dist/{variant-T64BKARF.js → variant-LAB67OC2.js} +15 -10
- package/dist/web-assets/assets/index-BbRmoxoA.js +308 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0003_clean_ego.sql +12 -0
- package/drizzle/meta/0003_snapshot.json +417 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/templates/_base/.init/.config/hooks/startup-context.sh +19 -1
- package/templates/_base/_skills/volute-agent/SKILL.md +110 -14
- package/templates/_base/home/.config/routes.json +10 -0
- package/templates/_base/home/VOLUTE.md +14 -35
- package/templates/_base/src/lib/format-prefix.ts +1 -1
- package/templates/_base/src/lib/router.ts +163 -16
- package/templates/_base/src/lib/routing.ts +55 -18
- package/templates/_base/src/lib/types.ts +3 -1
- package/templates/agent-sdk/.init/.config/routes.json +5 -0
- package/templates/agent-sdk/.init/CLAUDE.md +2 -2
- package/templates/agent-sdk/src/agent.ts +2 -1
- package/templates/agent-sdk/src/server.ts +8 -2
- package/templates/agent-sdk/volute-template.json +1 -1
- package/templates/pi/.init/.config/routes.json +5 -0
- package/templates/pi/.init/AGENTS.md +1 -1
- package/templates/pi/src/agent.ts +5 -3
- package/templates/pi/src/server.ts +1 -1
- package/templates/pi/volute-template.json +1 -1
- package/dist/channel-DQ6UY7QB.js +0 -67
- package/dist/chunk-ZHCE4DPY.js +0 -110
- package/dist/create-ILVOG75A.js +0 -79
- package/dist/send-3U6OTKG7.js +0 -57
- package/dist/web-assets/assets/index-NS621maO.js +0 -296
- package/templates/agent-sdk/.init/.config/sessions.json +0 -4
- package/templates/pi/.init/.config/sessions.json +0 -1
- package/dist/{service-SA4TTMDU.js → service-HZNIDNJF.js} +3 -3
|
@@ -1,27 +1,131 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/lib/variants.ts
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
5
|
+
import { resolve as resolve2 } from "path";
|
|
6
|
+
|
|
7
|
+
// src/lib/registry.ts
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
5
9
|
import { homedir } from "os";
|
|
6
|
-
import { resolve } from "path";
|
|
10
|
+
import { dirname, resolve } from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
7
12
|
function voluteHome() {
|
|
8
|
-
|
|
13
|
+
if (process.env.VOLUTE_HOME) return process.env.VOLUTE_HOME;
|
|
14
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
if (dir.endsWith("/src/lib")) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'VOLUTE_HOME must be set when running from source. For tests, run via "npm test" or add "--import ./test/setup.ts".'
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return resolve(homedir(), ".volute");
|
|
21
|
+
}
|
|
22
|
+
function ensureVoluteHome() {
|
|
23
|
+
mkdirSync(resolve(voluteHome(), "agents"), { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
function readRegistry() {
|
|
26
|
+
const registryPath = resolve(voluteHome(), "agents.json");
|
|
27
|
+
if (!existsSync(registryPath)) return [];
|
|
28
|
+
try {
|
|
29
|
+
const entries = JSON.parse(readFileSync(registryPath, "utf-8"));
|
|
30
|
+
return entries.map((e) => ({ ...e, running: e.running ?? false }));
|
|
31
|
+
} catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function writeRegistry(entries) {
|
|
36
|
+
ensureVoluteHome();
|
|
37
|
+
const registryPath = resolve(voluteHome(), "agents.json");
|
|
38
|
+
const tmpPath = `${registryPath}.tmp`;
|
|
39
|
+
writeFileSync(tmpPath, `${JSON.stringify(entries, null, 2)}
|
|
40
|
+
`);
|
|
41
|
+
renameSync(tmpPath, registryPath);
|
|
42
|
+
}
|
|
43
|
+
var AGENT_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
44
|
+
var AGENT_NAME_MAX = 64;
|
|
45
|
+
function validateAgentName(name) {
|
|
46
|
+
if (!name) return "Agent name is required";
|
|
47
|
+
if (name.length > AGENT_NAME_MAX)
|
|
48
|
+
return `Agent name must be at most ${AGENT_NAME_MAX} characters`;
|
|
49
|
+
if (!AGENT_NAME_RE.test(name)) {
|
|
50
|
+
return "Agent name must start with alphanumeric and contain only alphanumeric, dots, dashes, or underscores";
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function addAgent(name, port) {
|
|
55
|
+
const err = validateAgentName(name);
|
|
56
|
+
if (err) throw new Error(err);
|
|
57
|
+
const entries = readRegistry();
|
|
58
|
+
const filtered = entries.filter((e) => e.name !== name);
|
|
59
|
+
filtered.push({ name, port, created: (/* @__PURE__ */ new Date()).toISOString(), running: false });
|
|
60
|
+
writeRegistry(filtered);
|
|
9
61
|
}
|
|
62
|
+
function removeAgent(name) {
|
|
63
|
+
const entries = readRegistry();
|
|
64
|
+
writeRegistry(entries.filter((e) => e.name !== name));
|
|
65
|
+
}
|
|
66
|
+
function setAgentRunning(name, running) {
|
|
67
|
+
const entries = readRegistry();
|
|
68
|
+
const entry = entries.find((e) => e.name === name);
|
|
69
|
+
if (entry) {
|
|
70
|
+
entry.running = running;
|
|
71
|
+
writeRegistry(entries);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function findAgent(name) {
|
|
75
|
+
return readRegistry().find((e) => e.name === name);
|
|
76
|
+
}
|
|
77
|
+
function agentDir(name) {
|
|
78
|
+
return resolve(voluteHome(), "agents", name);
|
|
79
|
+
}
|
|
80
|
+
function nextPort() {
|
|
81
|
+
const entries = readRegistry();
|
|
82
|
+
const usedPorts = new Set(entries.map((e) => e.port));
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
for (const v of readVariants(entry.name)) {
|
|
85
|
+
if (v.port) usedPorts.add(v.port);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let port = 4100;
|
|
89
|
+
while (usedPorts.has(port)) port++;
|
|
90
|
+
if (port > 65535) throw new Error("No available ports \u2014 all ports 4100-65535 are allocated");
|
|
91
|
+
return port;
|
|
92
|
+
}
|
|
93
|
+
function resolveAgent(name) {
|
|
94
|
+
const [baseName, variantName] = name.split("@", 2);
|
|
95
|
+
const entry = findAgent(baseName);
|
|
96
|
+
if (!entry) {
|
|
97
|
+
throw new Error(`Unknown agent: ${baseName}`);
|
|
98
|
+
}
|
|
99
|
+
const dir = agentDir(baseName);
|
|
100
|
+
if (!existsSync(dir)) {
|
|
101
|
+
throw new Error(`Agent directory missing: ${dir}`);
|
|
102
|
+
}
|
|
103
|
+
if (variantName) {
|
|
104
|
+
const variant = findVariant(baseName, variantName);
|
|
105
|
+
if (!variant) {
|
|
106
|
+
throw new Error(`Unknown variant: ${variantName} (agent: ${baseName})`);
|
|
107
|
+
}
|
|
108
|
+
return { entry: { ...entry, port: variant.port }, dir: variant.path };
|
|
109
|
+
}
|
|
110
|
+
return { entry, dir };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/lib/variants.ts
|
|
10
114
|
function variantsPath() {
|
|
11
|
-
return
|
|
115
|
+
return resolve2(voluteHome(), "variants.json");
|
|
12
116
|
}
|
|
13
117
|
function readAllVariants() {
|
|
14
118
|
const path = variantsPath();
|
|
15
|
-
if (!
|
|
119
|
+
if (!existsSync2(path)) return {};
|
|
16
120
|
try {
|
|
17
|
-
return JSON.parse(
|
|
121
|
+
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
18
122
|
} catch {
|
|
19
123
|
return {};
|
|
20
124
|
}
|
|
21
125
|
}
|
|
22
126
|
function writeAllVariants(all) {
|
|
23
|
-
|
|
24
|
-
|
|
127
|
+
mkdirSync2(voluteHome(), { recursive: true });
|
|
128
|
+
writeFileSync2(variantsPath(), `${JSON.stringify(all, null, 2)}
|
|
25
129
|
`);
|
|
26
130
|
}
|
|
27
131
|
function readVariants(agentName) {
|
|
@@ -102,104 +206,6 @@ function validateBranchName(branch) {
|
|
|
102
206
|
return null;
|
|
103
207
|
}
|
|
104
208
|
|
|
105
|
-
// src/lib/registry.ts
|
|
106
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
107
|
-
import { homedir as homedir2 } from "os";
|
|
108
|
-
import { resolve as resolve2 } from "path";
|
|
109
|
-
function voluteHome2() {
|
|
110
|
-
return process.env.VOLUTE_HOME || resolve2(homedir2(), ".volute");
|
|
111
|
-
}
|
|
112
|
-
function ensureVoluteHome() {
|
|
113
|
-
mkdirSync2(resolve2(voluteHome2(), "agents"), { recursive: true });
|
|
114
|
-
}
|
|
115
|
-
function readRegistry() {
|
|
116
|
-
const registryPath = resolve2(voluteHome2(), "agents.json");
|
|
117
|
-
if (!existsSync2(registryPath)) return [];
|
|
118
|
-
try {
|
|
119
|
-
const entries = JSON.parse(readFileSync2(registryPath, "utf-8"));
|
|
120
|
-
return entries.map((e) => ({ ...e, running: e.running ?? false }));
|
|
121
|
-
} catch {
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
function writeRegistry(entries) {
|
|
126
|
-
ensureVoluteHome();
|
|
127
|
-
const registryPath = resolve2(voluteHome2(), "agents.json");
|
|
128
|
-
const tmpPath = `${registryPath}.tmp`;
|
|
129
|
-
writeFileSync2(tmpPath, `${JSON.stringify(entries, null, 2)}
|
|
130
|
-
`);
|
|
131
|
-
renameSync(tmpPath, registryPath);
|
|
132
|
-
}
|
|
133
|
-
var AGENT_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
134
|
-
var AGENT_NAME_MAX = 64;
|
|
135
|
-
function validateAgentName(name) {
|
|
136
|
-
if (!name) return "Agent name is required";
|
|
137
|
-
if (name.length > AGENT_NAME_MAX)
|
|
138
|
-
return `Agent name must be at most ${AGENT_NAME_MAX} characters`;
|
|
139
|
-
if (!AGENT_NAME_RE.test(name)) {
|
|
140
|
-
return "Agent name must start with alphanumeric and contain only alphanumeric, dots, dashes, or underscores";
|
|
141
|
-
}
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
function addAgent(name, port) {
|
|
145
|
-
const err = validateAgentName(name);
|
|
146
|
-
if (err) throw new Error(err);
|
|
147
|
-
const entries = readRegistry();
|
|
148
|
-
const filtered = entries.filter((e) => e.name !== name);
|
|
149
|
-
filtered.push({ name, port, created: (/* @__PURE__ */ new Date()).toISOString(), running: false });
|
|
150
|
-
writeRegistry(filtered);
|
|
151
|
-
}
|
|
152
|
-
function removeAgent(name) {
|
|
153
|
-
const entries = readRegistry();
|
|
154
|
-
writeRegistry(entries.filter((e) => e.name !== name));
|
|
155
|
-
}
|
|
156
|
-
function setAgentRunning(name, running) {
|
|
157
|
-
const entries = readRegistry();
|
|
158
|
-
const entry = entries.find((e) => e.name === name);
|
|
159
|
-
if (entry) {
|
|
160
|
-
entry.running = running;
|
|
161
|
-
writeRegistry(entries);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
function findAgent(name) {
|
|
165
|
-
return readRegistry().find((e) => e.name === name);
|
|
166
|
-
}
|
|
167
|
-
function agentDir(name) {
|
|
168
|
-
return resolve2(voluteHome2(), "agents", name);
|
|
169
|
-
}
|
|
170
|
-
function nextPort() {
|
|
171
|
-
const entries = readRegistry();
|
|
172
|
-
const usedPorts = new Set(entries.map((e) => e.port));
|
|
173
|
-
for (const entry of entries) {
|
|
174
|
-
for (const v of readVariants(entry.name)) {
|
|
175
|
-
if (v.port) usedPorts.add(v.port);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
let port = 4100;
|
|
179
|
-
while (usedPorts.has(port)) port++;
|
|
180
|
-
if (port > 65535) throw new Error("No available ports \u2014 all ports 4100-65535 are allocated");
|
|
181
|
-
return port;
|
|
182
|
-
}
|
|
183
|
-
function resolveAgent(name) {
|
|
184
|
-
const [baseName, variantName] = name.split("@", 2);
|
|
185
|
-
const entry = findAgent(baseName);
|
|
186
|
-
if (!entry) {
|
|
187
|
-
throw new Error(`Unknown agent: ${baseName}`);
|
|
188
|
-
}
|
|
189
|
-
const dir = agentDir(baseName);
|
|
190
|
-
if (!existsSync2(dir)) {
|
|
191
|
-
throw new Error(`Agent directory missing: ${dir}`);
|
|
192
|
-
}
|
|
193
|
-
if (variantName) {
|
|
194
|
-
const variant = findVariant(baseName, variantName);
|
|
195
|
-
if (!variant) {
|
|
196
|
-
throw new Error(`Unknown variant: ${variantName} (agent: ${baseName})`);
|
|
197
|
-
}
|
|
198
|
-
return { entry: { ...entry, port: variant.port }, dir: variant.path };
|
|
199
|
-
}
|
|
200
|
-
return { entry, dir };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
209
|
export {
|
|
204
210
|
readVariants,
|
|
205
211
|
writeVariants,
|
|
@@ -211,7 +217,7 @@ export {
|
|
|
211
217
|
removeAllVariants,
|
|
212
218
|
checkHealth,
|
|
213
219
|
validateBranchName,
|
|
214
|
-
|
|
220
|
+
voluteHome,
|
|
215
221
|
ensureVoluteHome,
|
|
216
222
|
readRegistry,
|
|
217
223
|
validateAgentName,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/read-stdin.ts
|
|
4
|
+
import { isatty } from "tty";
|
|
5
|
+
async function readStdin() {
|
|
6
|
+
if (isatty(0)) return void 0;
|
|
7
|
+
const chunks = [];
|
|
8
|
+
try {
|
|
9
|
+
for await (const chunk of process.stdin) {
|
|
10
|
+
chunks.push(chunk);
|
|
11
|
+
}
|
|
12
|
+
} catch (err) {
|
|
13
|
+
console.error(`Failed to read from stdin: ${err instanceof Error ? err.message : String(err)}`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const text = Buffer.concat(chunks).toString().replace(/\r?\n$/, "");
|
|
17
|
+
return text || void 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
readStdin
|
|
22
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,70 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
if (!process.env.VOLUTE_HOME) {
|
|
7
|
+
process.env.VOLUTE_HOME = resolve(homedir(), ".volute");
|
|
8
|
+
}
|
|
4
9
|
var command = process.argv[2];
|
|
5
10
|
var args = process.argv.slice(3);
|
|
6
11
|
if (command === "--version" || command === "-v") {
|
|
7
|
-
const { default: pkg } = await import("./package-
|
|
12
|
+
const { default: pkg } = await import("./package-VQOE7JNH.js");
|
|
8
13
|
console.log(pkg.version);
|
|
9
14
|
process.exit(0);
|
|
10
15
|
}
|
|
11
16
|
switch (command) {
|
|
12
|
-
case "
|
|
13
|
-
await import("./
|
|
14
|
-
break;
|
|
15
|
-
case "start":
|
|
16
|
-
await import("./start-LDPMCMYT.js").then((m) => m.run(args));
|
|
17
|
-
break;
|
|
18
|
-
case "stop":
|
|
19
|
-
await import("./stop-5PZTZCLL.js").then((m) => m.run(args));
|
|
20
|
-
break;
|
|
21
|
-
case "logs":
|
|
22
|
-
await import("./logs-NXFFGUKY.js").then((m) => m.run(args));
|
|
17
|
+
case "agent":
|
|
18
|
+
await import("./agent-Z2B6EFEQ.js").then((m) => m.run(args));
|
|
23
19
|
break;
|
|
24
|
-
case "
|
|
25
|
-
await import("./
|
|
20
|
+
case "message":
|
|
21
|
+
await import("./message-ADHWFHSI.js").then((m) => m.run(args));
|
|
26
22
|
break;
|
|
27
23
|
case "variant":
|
|
28
|
-
await import("./variant-
|
|
29
|
-
break;
|
|
30
|
-
case "send":
|
|
31
|
-
await import("./send-3U6OTKG7.js").then((m) => m.run(args));
|
|
32
|
-
break;
|
|
33
|
-
case "import":
|
|
34
|
-
await import("./import-4CI2ZUTJ.js").then((m) => m.run(args));
|
|
35
|
-
break;
|
|
36
|
-
case "delete":
|
|
37
|
-
await import("./delete-55MXCEY5.js").then((m) => m.run(args));
|
|
38
|
-
break;
|
|
39
|
-
case "env":
|
|
40
|
-
await import("./env-JB27UAC3.js").then((m) => m.run(args));
|
|
24
|
+
await import("./variant-LAB67OC2.js").then((m) => m.run(args));
|
|
41
25
|
break;
|
|
42
26
|
case "connector":
|
|
43
|
-
await import("./connector-
|
|
27
|
+
await import("./connector-LYEMXQEV.js").then((m) => m.run(args));
|
|
44
28
|
break;
|
|
45
29
|
case "channel":
|
|
46
|
-
await import("./channel-
|
|
30
|
+
await import("./channel-MK5OK2SI.js").then((m) => m.run(args));
|
|
31
|
+
break;
|
|
32
|
+
case "schedule":
|
|
33
|
+
await import("./schedule-NAG6F463.js").then((m) => m.run(args));
|
|
47
34
|
break;
|
|
48
|
-
case "
|
|
49
|
-
await import("./
|
|
35
|
+
case "conversation":
|
|
36
|
+
await import("./conversation-ERXEQZTY.js").then((m) => m.run(args));
|
|
37
|
+
break;
|
|
38
|
+
case "env":
|
|
39
|
+
await import("./env-4D4REPJF.js").then((m) => m.run(args));
|
|
50
40
|
break;
|
|
51
41
|
case "up":
|
|
52
|
-
await import("./up-
|
|
42
|
+
await import("./up-7ILD7GU7.js").then((m) => m.run(args));
|
|
53
43
|
break;
|
|
54
44
|
case "down":
|
|
55
|
-
await import("./down-
|
|
56
|
-
break;
|
|
57
|
-
case "schedule":
|
|
58
|
-
await import("./schedule-A35SH4HT.js").then((m) => m.run(args));
|
|
45
|
+
await import("./down-O7IFZLVJ.js").then((m) => m.run(args));
|
|
59
46
|
break;
|
|
60
|
-
case "
|
|
61
|
-
await import("./
|
|
47
|
+
case "setup":
|
|
48
|
+
await import("./setup-RPRRGG2F.js").then((m) => m.run(args));
|
|
62
49
|
break;
|
|
63
50
|
case "service":
|
|
64
|
-
await import("./service-
|
|
51
|
+
await import("./service-HZNIDNJF.js").then((m) => m.run(args));
|
|
65
52
|
break;
|
|
66
|
-
case "
|
|
67
|
-
await import("./
|
|
53
|
+
case "update":
|
|
54
|
+
await import("./update-LPSIAWQ2.js").then((m) => m.run(args));
|
|
68
55
|
break;
|
|
69
56
|
case "--help":
|
|
70
57
|
case "-h":
|
|
@@ -72,45 +59,70 @@ switch (command) {
|
|
|
72
59
|
console.log(`volute \u2014 create and manage AI agents
|
|
73
60
|
|
|
74
61
|
Commands:
|
|
75
|
-
volute create <name>
|
|
76
|
-
volute start <name>
|
|
77
|
-
volute stop <name>
|
|
78
|
-
volute
|
|
79
|
-
volute
|
|
80
|
-
volute
|
|
81
|
-
volute
|
|
82
|
-
volute
|
|
83
|
-
volute
|
|
84
|
-
|
|
85
|
-
volute
|
|
86
|
-
volute
|
|
87
|
-
|
|
88
|
-
volute
|
|
89
|
-
volute
|
|
90
|
-
volute
|
|
91
|
-
volute
|
|
92
|
-
|
|
93
|
-
volute
|
|
94
|
-
volute
|
|
95
|
-
|
|
96
|
-
volute
|
|
97
|
-
volute
|
|
98
|
-
|
|
99
|
-
volute
|
|
100
|
-
volute
|
|
101
|
-
volute
|
|
102
|
-
|
|
103
|
-
volute
|
|
62
|
+
volute agent create <name> Create a new agent
|
|
63
|
+
volute agent start <name> Start an agent (daemonized)
|
|
64
|
+
volute agent stop <name> Stop an agent
|
|
65
|
+
volute agent delete <name> [--force] Delete an agent (--force removes files)
|
|
66
|
+
volute agent list List all agents
|
|
67
|
+
volute agent status <name> Check agent status
|
|
68
|
+
volute agent logs <name> [--follow] Tail agent logs
|
|
69
|
+
volute agent upgrade <name> Upgrade agent to latest template
|
|
70
|
+
volute agent import <path> Import an OpenClaw workspace
|
|
71
|
+
|
|
72
|
+
volute message send <name> "<msg>" Send a message to an agent
|
|
73
|
+
volute message history [--agent <name>] View message history
|
|
74
|
+
|
|
75
|
+
volute variant create <name> Create a variant (worktree + server)
|
|
76
|
+
volute variant list List variants for an agent
|
|
77
|
+
volute variant merge <name> Merge a variant back
|
|
78
|
+
volute variant delete <name> Delete a variant
|
|
79
|
+
|
|
80
|
+
volute connector connect <type> Enable a connector for an agent
|
|
81
|
+
volute connector disconnect <type> Disable a connector for an agent
|
|
82
|
+
|
|
83
|
+
volute channel read <uri> Read recent messages from a channel
|
|
84
|
+
volute channel send <uri> "<msg>" Send a message to a channel
|
|
85
|
+
|
|
86
|
+
volute schedule list List schedules for an agent
|
|
87
|
+
volute schedule add ... Add a cron schedule
|
|
88
|
+
volute schedule remove ... Remove a schedule
|
|
89
|
+
|
|
90
|
+
volute conversation create ... Create a group conversation
|
|
91
|
+
volute conversation list List conversations
|
|
92
|
+
volute conversation send <id> "<msg>" Send a message to a conversation
|
|
93
|
+
|
|
94
|
+
volute env <set|get|list|remove> Manage environment variables
|
|
95
|
+
|
|
96
|
+
volute up [--port N] Start the daemon (default: 4200)
|
|
97
|
+
volute down Stop the daemon
|
|
98
|
+
|
|
99
|
+
volute service install [--port N] Install as system service (auto-start)
|
|
100
|
+
volute service uninstall Remove system service
|
|
101
|
+
volute service status Check service status
|
|
102
|
+
volute setup [--port N] [--host H] Install system service with user isolation
|
|
103
|
+
volute setup uninstall [--force] Remove system service + isolation
|
|
104
|
+
|
|
105
|
+
volute update Update to latest version
|
|
104
106
|
|
|
105
107
|
Options:
|
|
106
|
-
--version, -v
|
|
107
|
-
--help, -h
|
|
108
|
+
--version, -v Show version number
|
|
109
|
+
--help, -h Show this help message
|
|
108
110
|
|
|
109
|
-
Agent commands (variant, connector, schedule,
|
|
110
|
-
--agent <name> or VOLUTE_AGENT env var to identify the agent.`);
|
|
111
|
+
Agent-scoped commands (variant, connector, schedule, channel, conversation, message history)
|
|
112
|
+
use --agent <name> or VOLUTE_AGENT env var to identify the agent.`);
|
|
111
113
|
break;
|
|
112
114
|
default:
|
|
113
115
|
console.error(`Unknown command: ${command}
|
|
114
116
|
Run 'volute --help' for usage.`);
|
|
115
117
|
process.exit(1);
|
|
116
118
|
}
|
|
119
|
+
if (command !== "update") {
|
|
120
|
+
import("./update-check-Y33QDCFL.js").then((m) => m.checkForUpdate()).then((result) => {
|
|
121
|
+
if (result.updateAvailable) {
|
|
122
|
+
console.error(`
|
|
123
|
+
Update available: ${result.current} \u2192 ${result.latest}`);
|
|
124
|
+
console.error(" Run `volute update` to update\n");
|
|
125
|
+
}
|
|
126
|
+
}).catch(() => {
|
|
127
|
+
});
|
|
128
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveAgentName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-AZEL2IEK.js";
|
|
5
5
|
import {
|
|
6
6
|
agentEnvPath,
|
|
7
7
|
readEnv,
|
|
8
8
|
writeEnv
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-HE67X4T6.js";
|
|
10
10
|
import {
|
|
11
11
|
parseArgs
|
|
12
12
|
} from "./chunk-D424ZQGI.js";
|
|
13
13
|
import {
|
|
14
14
|
daemonFetch
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-7L4AN5D4.js";
|
|
16
16
|
import {
|
|
17
17
|
agentDir
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-UX25Z2ND.js";
|
|
19
19
|
import "./chunk-K3NQKI34.js";
|
|
20
20
|
|
|
21
21
|
// src/commands/connector.ts
|
|
@@ -28,13 +28,18 @@ async function run(args) {
|
|
|
28
28
|
case "disconnect":
|
|
29
29
|
await disconnectConnector(args.slice(1));
|
|
30
30
|
break;
|
|
31
|
+
case "--help":
|
|
32
|
+
case "-h":
|
|
33
|
+
case void 0:
|
|
34
|
+
printUsage();
|
|
35
|
+
break;
|
|
31
36
|
default:
|
|
32
37
|
printUsage();
|
|
33
|
-
process.exit(
|
|
38
|
+
process.exit(1);
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
function printUsage() {
|
|
37
|
-
console.
|
|
42
|
+
console.log(`Usage:
|
|
38
43
|
volute connector connect <type> [--agent <name>]
|
|
39
44
|
volute connector disconnect <type> [--agent <name>]`);
|
|
40
45
|
}
|
|
@@ -90,6 +90,7 @@ client.on(Events.MessageCreate, async (message) => {
|
|
|
90
90
|
const senderName = message.author.displayName || message.author.username;
|
|
91
91
|
const channelKey = `discord:${message.channelId}`;
|
|
92
92
|
const channelName = !isDM && "name" in message.channel ? message.channel.name : void 0;
|
|
93
|
+
const participantCount = isDM ? 2 : message.guild?.memberCount;
|
|
93
94
|
const payload = {
|
|
94
95
|
content,
|
|
95
96
|
channel: channelKey,
|
|
@@ -97,7 +98,8 @@ client.on(Events.MessageCreate, async (message) => {
|
|
|
97
98
|
platform: "Discord",
|
|
98
99
|
...isDM ? { isDM: true } : {},
|
|
99
100
|
...channelName ? { channelName } : {},
|
|
100
|
-
...message.guild?.name ? {
|
|
101
|
+
...message.guild?.name ? { serverName: message.guild.name } : {},
|
|
102
|
+
...participantCount ? { participantCount } : {}
|
|
101
103
|
};
|
|
102
104
|
if (isFollowedChannel && !isMentioned) {
|
|
103
105
|
await fireAndForget(env, payload);
|
package/dist/connectors/slack.js
CHANGED
|
@@ -27,11 +27,12 @@ var app = new App({
|
|
|
27
27
|
appToken
|
|
28
28
|
});
|
|
29
29
|
var botUserId;
|
|
30
|
+
var serverName;
|
|
30
31
|
app.message(async ({ message, say }) => {
|
|
31
32
|
if (message.subtype) return;
|
|
32
33
|
if (!("user" in message) || !("text" in message)) return;
|
|
33
34
|
if ("bot_id" in message && message.bot_id) return;
|
|
34
|
-
const isDM = message.channel_type === "im";
|
|
35
|
+
const isDM = message.channel_type === "im" || message.channel_type === "mpim";
|
|
35
36
|
const isMentioned = !isDM && botUserId && message.text?.includes(`<@${botUserId}>`);
|
|
36
37
|
const isFollowedChannel = !isDM && followedChannelIds.has(message.channel);
|
|
37
38
|
if (!isDM && !isMentioned && !isFollowedChannel) return;
|
|
@@ -65,14 +66,16 @@ app.message(async ({ message, say }) => {
|
|
|
65
66
|
}
|
|
66
67
|
if (content.length === 0) return;
|
|
67
68
|
let channelName;
|
|
68
|
-
|
|
69
|
+
let numMembers;
|
|
70
|
+
if (message.channel_type !== "im") {
|
|
69
71
|
try {
|
|
70
72
|
const info = await app.client.conversations.info({
|
|
71
73
|
channel: message.channel
|
|
72
74
|
});
|
|
73
75
|
channelName = info.channel?.name;
|
|
76
|
+
numMembers = info.channel?.num_members;
|
|
74
77
|
} catch (err) {
|
|
75
|
-
console.warn(`Failed to get channel
|
|
78
|
+
console.warn(`Failed to get channel info: ${err}`);
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
let senderName = message.user;
|
|
@@ -85,13 +88,16 @@ app.message(async ({ message, say }) => {
|
|
|
85
88
|
console.warn(`Failed to get user info: ${err}`);
|
|
86
89
|
}
|
|
87
90
|
const channelKey = `slack:${message.channel}`;
|
|
91
|
+
const participantCount = message.channel_type === "im" ? 2 : numMembers;
|
|
88
92
|
const payload = {
|
|
89
93
|
content,
|
|
90
94
|
channel: channelKey,
|
|
91
95
|
sender: senderName,
|
|
92
96
|
platform: "Slack",
|
|
93
97
|
...isDM ? { isDM: true } : {},
|
|
94
|
-
...channelName ? { channelName } : {}
|
|
98
|
+
...channelName ? { channelName } : {},
|
|
99
|
+
...serverName ? { serverName } : {},
|
|
100
|
+
...participantCount ? { participantCount } : {}
|
|
95
101
|
};
|
|
96
102
|
if (isFollowedChannel && !isMentioned) {
|
|
97
103
|
await fireAndForget(env, payload);
|
|
@@ -134,7 +140,10 @@ async function start() {
|
|
|
134
140
|
throw new Error("auth.test succeeded but returned no user_id");
|
|
135
141
|
}
|
|
136
142
|
botUserId = auth.user_id;
|
|
137
|
-
|
|
143
|
+
serverName = auth.team;
|
|
144
|
+
console.log(
|
|
145
|
+
`Connected to Slack as bot user ${botUserId}${serverName ? ` in ${serverName}` : ""}`
|
|
146
|
+
);
|
|
138
147
|
console.log(`Bridging to agent: ${env.agentName} via ${env.baseUrl}/message`);
|
|
139
148
|
if (followedChannelNames.length > 0) {
|
|
140
149
|
try {
|
|
@@ -40,13 +40,22 @@ bot.on(message("text"), async (ctx) => {
|
|
|
40
40
|
if (content.length === 0) return;
|
|
41
41
|
const senderName = ctx.message.from.first_name + (ctx.message.from.last_name ? ` ${ctx.message.from.last_name}` : "");
|
|
42
42
|
const chatTitle = "title" in ctx.chat ? ctx.chat.title : void 0;
|
|
43
|
+
let participantCount = isDM ? 2 : void 0;
|
|
44
|
+
if (!isDM) {
|
|
45
|
+
try {
|
|
46
|
+
participantCount = await ctx.telegram.getChatMembersCount(ctx.chat.id);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.warn(`Failed to get member count for chat ${ctx.chat.id}: ${err}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
43
51
|
const payload = {
|
|
44
52
|
content,
|
|
45
53
|
channel: `telegram:${ctx.chat.id}`,
|
|
46
54
|
sender: senderName,
|
|
47
55
|
platform: "Telegram",
|
|
48
56
|
...isDM ? { isDM: true } : {},
|
|
49
|
-
...chatTitle ? { channelName: chatTitle } : {}
|
|
57
|
+
...chatTitle ? { channelName: chatTitle } : {},
|
|
58
|
+
...participantCount ? { participantCount } : {}
|
|
50
59
|
};
|
|
51
60
|
if (isFollowedChat && !isMentioned) {
|
|
52
61
|
await fireAndForget(env, payload);
|
|
@@ -88,12 +97,22 @@ bot.on(message("photo"), async (ctx) => {
|
|
|
88
97
|
if (content.length === 0) return;
|
|
89
98
|
const senderName = ctx.message.from.first_name + (ctx.message.from.last_name ? ` ${ctx.message.from.last_name}` : "");
|
|
90
99
|
const chatTitle = "title" in ctx.chat ? ctx.chat.title : void 0;
|
|
100
|
+
let participantCount = isDM ? 2 : void 0;
|
|
101
|
+
if (!isDM) {
|
|
102
|
+
try {
|
|
103
|
+
participantCount = await ctx.telegram.getChatMembersCount(ctx.chat.id);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.warn(`Failed to get member count for chat ${ctx.chat.id}: ${err}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
91
108
|
const payload = {
|
|
92
109
|
content,
|
|
93
110
|
channel: `telegram:${ctx.chat.id}`,
|
|
94
111
|
sender: senderName,
|
|
95
112
|
platform: "Telegram",
|
|
96
|
-
...
|
|
113
|
+
...isDM ? { isDM: true } : {},
|
|
114
|
+
...chatTitle ? { channelName: chatTitle } : {},
|
|
115
|
+
...participantCount ? { participantCount } : {}
|
|
97
116
|
};
|
|
98
117
|
if (isFollowedChat) {
|
|
99
118
|
await fireAndForget(env, payload);
|