volute 0.7.0 → 0.8.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.
- package/README.md +16 -14
- package/dist/{agent-7JF7MT73.js → agent-YORVRB6I.js} +10 -10
- package/dist/{agent-manager-IMZ7ZMBF.js → agent-manager-CMMH5KQQ.js} +4 -4
- package/dist/{channel-SMCNOIVQ.js → channel-RDGHBFSI.js} +16 -56
- package/dist/{chunk-JR4UXCTO.js → chunk-23L3MKEV.js} +1 -1
- package/dist/{chunk-5SKQ6J7T.js → chunk-5C5JWR2L.js} +15 -7
- package/dist/{chunk-UWHWAPGO.js → chunk-DP2DX4WV.js} +9 -1
- package/dist/{chunk-7ACDT3P2.js → chunk-ECPQXRLB.js} +1 -2
- package/dist/{chunk-LLJNZPCU.js → chunk-HZ5LTOEJ.js} +1 -1
- package/dist/{chunk-W76KWE23.js → chunk-IQXBMFZG.js} +6 -4
- package/dist/{chunk-ZZOOTYXK.js → chunk-LIPPXNIE.js} +60 -74
- package/dist/{chunk-BX7KI4S3.js → chunk-N6MLQ26B.js} +23 -96
- package/dist/{chunk-H7AMDUIA.js → chunk-QF22MYDJ.js} +6 -5
- package/dist/{chunk-NKXULRSW.js → chunk-RT6Y7AR3.js} +1 -1
- package/dist/{chunk-62X577Y7.js → chunk-W6TMWYU3.js} +126 -73
- package/dist/{chunk-EG45HBSJ.js → chunk-XSJ27WEM.js} +1 -1
- package/dist/cli.js +22 -20
- package/dist/{connector-Y7JPNROO.js → connector-ZP6MEFF4.js} +3 -3
- package/dist/connectors/discord.js +18 -59
- package/dist/connectors/slack.js +21 -38
- package/dist/connectors/telegram.js +31 -49
- package/dist/{create-G525LWEA.js → create-HGJHLABX.js} +22 -17
- package/dist/{daemon-client-442IV43D.js → daemon-client-54J3EIZD.js} +2 -2
- package/dist/{daemon-restart-4HVEKYFY.js → daemon-restart-CPBLMMRI.js} +3 -3
- package/dist/daemon.js +342 -402
- package/dist/{delete-UOU4AFQN.js → delete-45TGQC4N.js} +10 -5
- package/dist/{down-AZVH5TCD.js → down-O4EWZTVA.js} +2 -2
- package/dist/{env-7GLUJCWS.js → env-KMNYGVZ2.js} +7 -9
- package/dist/{history-H72ZUIBN.js → history-PXJVYLVY.js} +2 -2
- package/dist/{import-AVKQJDYC.js → import-CNEDF3TD.js} +6 -6
- package/dist/{logs-EDGK26AK.js → logs-TZB3MTLZ.js} +5 -4
- package/dist/{package-T2WAVJOU.js → package-RJSONENE.js} +1 -1
- package/dist/{restart-O4ETYLJF.js → restart-KVH3TK5N.js} +2 -2
- package/dist/{schedule-S6QVC5ON.js → schedule-HCUCBNQI.js} +2 -2
- package/dist/send-BNC2S5BY.js +162 -0
- package/dist/{service-HZNIDNJF.js → service-XCADRKIS.js} +8 -1
- package/dist/{setup-F4TCWVSP.js → setup-32KH5KLN.js} +85 -26
- package/dist/{start-VHQ7LNWM.js → start-QU73YTJW.js} +2 -2
- package/dist/{status-QAJWXKMZ.js → status-Q6ZQJXNI.js} +2 -2
- package/dist/{stop-CAGCT5NI.js → stop-N7U5N6A7.js} +2 -2
- package/dist/{up-RWZF6MLT.js → up-V6EAA7OZ.js} +2 -2
- package/dist/{update-F7QWV2LB.js → update-EUCZ7XGG.js} +3 -3
- package/dist/{update-check-B4J6IEQ4.js → update-check-SM4244SU.js} +2 -2
- package/dist/{upgrade-YXKPWDRU.js → upgrade-CZF6PN7Y.js} +4 -4
- package/dist/{variant-4Z6W3PP6.js → variant-RKXPN5DH.js} +20 -46
- package/dist/web-assets/assets/index-D-3zx6vs.js +307 -0
- package/dist/web-assets/index.html +1 -1
- package/drizzle/0004_magical_silverclaw.sql +1 -0
- package/drizzle/meta/0004_snapshot.json +410 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/templates/_base/_skills/volute-agent/SKILL.md +32 -16
- package/templates/_base/home/.config/routes.json +4 -8
- package/templates/_base/home/VOLUTE.md +16 -14
- package/templates/_base/src/lib/auto-reply.ts +38 -0
- package/templates/_base/src/lib/daemon-client.ts +53 -0
- package/templates/_base/src/lib/router.ts +66 -14
- package/templates/_base/src/lib/routing.ts +48 -9
- package/templates/_base/src/lib/startup.ts +1 -25
- package/templates/_base/src/lib/types.ts +2 -1
- package/templates/_base/src/lib/volute-server.ts +29 -14
- package/templates/agent-sdk/src/agent.ts +53 -111
- package/templates/agent-sdk/src/lib/content.ts +41 -0
- package/templates/agent-sdk/src/lib/session-store.ts +43 -0
- package/templates/agent-sdk/src/lib/stream-consumer.ts +66 -0
- package/templates/agent-sdk/src/server.ts +5 -13
- package/templates/pi/.init/AGENTS.md +5 -5
- package/templates/pi/src/agent.ts +32 -84
- package/templates/pi/src/lib/content.ts +15 -0
- package/templates/pi/src/lib/event-handler.ts +74 -0
- package/templates/pi/src/lib/resolve-model.ts +21 -0
- package/templates/pi/src/server.ts +3 -7
- package/dist/chunk-B3R6L2GW.js +0 -24
- package/dist/chunk-ZYGKG6VC.js +0 -22
- package/dist/message-SCOQDR3P.js +0 -32
- package/dist/send-G7PE4DOJ.js +0 -72
- package/dist/web-assets/assets/index-B1CqjUYD.js +0 -308
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadMergedEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QF22MYDJ.js";
|
|
5
5
|
import {
|
|
6
6
|
applyIsolation
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IQXBMFZG.js";
|
|
8
8
|
import {
|
|
9
9
|
agentDir,
|
|
10
10
|
findAgent,
|
|
11
11
|
findVariant,
|
|
12
12
|
setAgentRunning,
|
|
13
13
|
setVariantRunning,
|
|
14
|
-
|
|
14
|
+
stateDir,
|
|
15
15
|
voluteHome
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DP2DX4WV.js";
|
|
17
17
|
|
|
18
18
|
// src/lib/agent-manager.ts
|
|
19
19
|
import { execFile, spawn } from "child_process";
|
|
20
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2,
|
|
20
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
21
21
|
import { resolve } from "path";
|
|
22
22
|
import { promisify } from "util";
|
|
23
23
|
|
|
@@ -59,14 +59,21 @@ function clearJsonMap(path, map) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// src/lib/rotating-log.ts
|
|
62
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
createWriteStream,
|
|
64
|
+
existsSync as existsSync2,
|
|
65
|
+
renameSync,
|
|
66
|
+
rmSync,
|
|
67
|
+
statSync
|
|
68
|
+
} from "fs";
|
|
63
69
|
import { Writable } from "stream";
|
|
64
70
|
var MAX_SIZE = 10 * 1024 * 1024;
|
|
65
71
|
var RotatingLog = class extends Writable {
|
|
66
|
-
constructor(path, maxSize = MAX_SIZE) {
|
|
72
|
+
constructor(path, maxSize = MAX_SIZE, maxFiles = 5) {
|
|
67
73
|
super();
|
|
68
74
|
this.path = path;
|
|
69
75
|
this.maxSize = maxSize;
|
|
76
|
+
this.maxFiles = maxFiles;
|
|
70
77
|
this.on("error", () => {
|
|
71
78
|
});
|
|
72
79
|
try {
|
|
@@ -82,6 +89,13 @@ var RotatingLog = class extends Writable {
|
|
|
82
89
|
this.size += chunk.length;
|
|
83
90
|
if (this.size > this.maxSize) {
|
|
84
91
|
try {
|
|
92
|
+
const oldest = `${this.path}.${this.maxFiles}`;
|
|
93
|
+
if (existsSync2(oldest)) rmSync(oldest);
|
|
94
|
+
for (let i = this.maxFiles - 1; i >= 1; i--) {
|
|
95
|
+
const from = `${this.path}.${i}`;
|
|
96
|
+
const to = `${this.path}.${i + 1}`;
|
|
97
|
+
if (existsSync2(from)) renameSync(from, to);
|
|
98
|
+
}
|
|
85
99
|
renameSync(this.path, `${this.path}.1`);
|
|
86
100
|
const oldStream = this.stream;
|
|
87
101
|
this.stream = createWriteStream(this.path);
|
|
@@ -99,6 +113,9 @@ var RotatingLog = class extends Writable {
|
|
|
99
113
|
|
|
100
114
|
// src/lib/agent-manager.ts
|
|
101
115
|
var execFileAsync = promisify(execFile);
|
|
116
|
+
function agentPidPath(name) {
|
|
117
|
+
return resolve(stateDir(name), "agent.pid");
|
|
118
|
+
}
|
|
102
119
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
103
120
|
var BASE_RESTART_DELAY = 3e3;
|
|
104
121
|
var MAX_RESTART_DELAY = 6e4;
|
|
@@ -107,6 +124,7 @@ var AgentManager = class {
|
|
|
107
124
|
stopping = /* @__PURE__ */ new Set();
|
|
108
125
|
shuttingDown = false;
|
|
109
126
|
restartAttempts = /* @__PURE__ */ new Map();
|
|
127
|
+
pendingContext = /* @__PURE__ */ new Map();
|
|
110
128
|
resolveTarget(name) {
|
|
111
129
|
const [baseName, variantName] = name.split("@", 2);
|
|
112
130
|
const entry = findAgent(baseName);
|
|
@@ -127,6 +145,34 @@ var AgentManager = class {
|
|
|
127
145
|
const target = this.resolveTarget(name);
|
|
128
146
|
const { dir, isVariant, baseName, variantName } = target;
|
|
129
147
|
const port = target.port;
|
|
148
|
+
const pidFile = agentPidPath(name);
|
|
149
|
+
try {
|
|
150
|
+
if (existsSync3(pidFile)) {
|
|
151
|
+
const stalePid = parseInt(readFileSync2(pidFile, "utf-8").trim(), 10);
|
|
152
|
+
if (stalePid > 0) {
|
|
153
|
+
try {
|
|
154
|
+
process.kill(stalePid, 0);
|
|
155
|
+
const { stdout } = await execFileAsync("ps", ["-p", String(stalePid), "-o", "args="]);
|
|
156
|
+
if (stdout.includes("server.ts")) {
|
|
157
|
+
console.error(`[daemon] killing stale agent process ${stalePid} for ${name}`);
|
|
158
|
+
process.kill(-stalePid, "SIGTERM");
|
|
159
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
160
|
+
} else {
|
|
161
|
+
console.error(
|
|
162
|
+
`[daemon] stale PID ${stalePid} for ${name} is not an agent process, skipping`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
} catch (err) {
|
|
166
|
+
if (err.code !== "ESRCH") {
|
|
167
|
+
console.error(`[daemon] failed to check/kill stale process for ${name}:`, err);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
rmSync2(pidFile, { force: true });
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error(`[daemon] failed to read PID file for ${name}:`, err);
|
|
175
|
+
}
|
|
130
176
|
try {
|
|
131
177
|
const res = await fetch(`http://127.0.0.1:${port}/health`);
|
|
132
178
|
if (res.ok) {
|
|
@@ -136,13 +182,18 @@ var AgentManager = class {
|
|
|
136
182
|
}
|
|
137
183
|
} catch {
|
|
138
184
|
}
|
|
139
|
-
const
|
|
140
|
-
const logsDir = resolve(voluteDir, "logs");
|
|
185
|
+
const logsDir = resolve(stateDir(name), "logs");
|
|
141
186
|
mkdirSync(logsDir, { recursive: true });
|
|
142
187
|
const logStream = new RotatingLog(resolve(logsDir, "agent.log"));
|
|
143
|
-
const agentEnv = loadMergedEnv(
|
|
144
|
-
const
|
|
145
|
-
|
|
188
|
+
const agentEnv = loadMergedEnv(name);
|
|
189
|
+
const env = {
|
|
190
|
+
...process.env,
|
|
191
|
+
...agentEnv,
|
|
192
|
+
VOLUTE_AGENT: name,
|
|
193
|
+
VOLUTE_STATE_DIR: stateDir(name),
|
|
194
|
+
VOLUTE_AGENT_DIR: dir,
|
|
195
|
+
VOLUTE_AGENT_PORT: String(port)
|
|
196
|
+
};
|
|
146
197
|
const tsxBin = resolve(dir, "node_modules", ".bin", "tsx");
|
|
147
198
|
const spawnOpts = {
|
|
148
199
|
cwd: dir,
|
|
@@ -185,82 +236,83 @@ var AgentManager = class {
|
|
|
185
236
|
}
|
|
186
237
|
throw err;
|
|
187
238
|
}
|
|
239
|
+
if (child.pid) {
|
|
240
|
+
try {
|
|
241
|
+
writeFileSync2(pidFile, String(child.pid));
|
|
242
|
+
} catch (err) {
|
|
243
|
+
console.error(`[daemon] failed to write PID file for ${name}:`, err);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
188
246
|
if (this.restartAttempts.delete(name)) this.saveCrashAttempts();
|
|
189
|
-
this.setupCrashRecovery(name, child
|
|
247
|
+
this.setupCrashRecovery(name, child);
|
|
190
248
|
if (isVariant) {
|
|
191
249
|
setVariantRunning(baseName, variantName, true);
|
|
192
250
|
} else {
|
|
193
251
|
setAgentRunning(name, true);
|
|
194
252
|
}
|
|
195
253
|
console.error(`[daemon] started agent ${name} on port ${port}`);
|
|
254
|
+
await this.deliverPendingContext(name);
|
|
255
|
+
}
|
|
256
|
+
setPendingContext(name, context) {
|
|
257
|
+
this.pendingContext.set(name, context);
|
|
258
|
+
}
|
|
259
|
+
async deliverPendingContext(name) {
|
|
260
|
+
const context = this.pendingContext.get(name);
|
|
261
|
+
if (!context) return;
|
|
262
|
+
const tracked = this.agents.get(name);
|
|
263
|
+
if (!tracked) return;
|
|
264
|
+
this.pendingContext.delete(name);
|
|
265
|
+
const parts = [];
|
|
266
|
+
if (context.type === "merge" || context.type === "merged") {
|
|
267
|
+
parts.push(`[system] Variant "${context.name}" has been merged and you have been restarted.`);
|
|
268
|
+
} else {
|
|
269
|
+
parts.push("[system] You have been restarted.");
|
|
270
|
+
}
|
|
271
|
+
if (context.summary) parts.push(`Changes: ${context.summary}`);
|
|
272
|
+
if (context.justification) parts.push(`Why: ${context.justification}`);
|
|
273
|
+
if (context.memory) parts.push(`Context: ${context.memory}`);
|
|
274
|
+
try {
|
|
275
|
+
await fetch(`http://127.0.0.1:${tracked.port}/message`, {
|
|
276
|
+
method: "POST",
|
|
277
|
+
headers: { "Content-Type": "application/json" },
|
|
278
|
+
body: JSON.stringify({
|
|
279
|
+
content: [{ type: "text", text: parts.join("\n") }],
|
|
280
|
+
channel: "system"
|
|
281
|
+
})
|
|
282
|
+
});
|
|
283
|
+
} catch (err) {
|
|
284
|
+
console.error(`[daemon] failed to deliver pending context to ${name}:`, err);
|
|
285
|
+
}
|
|
196
286
|
}
|
|
197
|
-
setupCrashRecovery(name, child
|
|
287
|
+
setupCrashRecovery(name, child) {
|
|
198
288
|
child.on("exit", async (code) => {
|
|
199
289
|
this.agents.delete(name);
|
|
200
290
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
201
291
|
console.error(`[daemon] agent ${name} exited with code ${code}`);
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
204
|
-
console.error(`[daemon]
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const attempts = this.restartAttempts.get(name) ?? 0;
|
|
211
|
-
if (attempts >= MAX_RESTART_ATTEMPTS) {
|
|
212
|
-
console.error(`[daemon] ${name} crashed ${attempts} times \u2014 giving up on restart`);
|
|
213
|
-
const [base, variant] = name.split("@", 2);
|
|
214
|
-
if (variant) {
|
|
215
|
-
setVariantRunning(base, variant, false);
|
|
216
|
-
} else {
|
|
217
|
-
setAgentRunning(name, false);
|
|
218
|
-
}
|
|
219
|
-
return;
|
|
292
|
+
const attempts = this.restartAttempts.get(name) ?? 0;
|
|
293
|
+
if (attempts >= MAX_RESTART_ATTEMPTS) {
|
|
294
|
+
console.error(`[daemon] ${name} crashed ${attempts} times \u2014 giving up on restart`);
|
|
295
|
+
const [base, variant] = name.split("@", 2);
|
|
296
|
+
if (variant) {
|
|
297
|
+
setVariantRunning(base, variant, false);
|
|
298
|
+
} else {
|
|
299
|
+
setAgentRunning(name, false);
|
|
220
300
|
}
|
|
221
|
-
|
|
222
|
-
this.restartAttempts.set(name, attempts + 1);
|
|
223
|
-
this.saveCrashAttempts();
|
|
224
|
-
console.error(
|
|
225
|
-
`[daemon] crash recovery for ${name} \u2014 attempt ${attempts + 1}/${MAX_RESTART_ATTEMPTS}, restarting in ${delay}ms`
|
|
226
|
-
);
|
|
227
|
-
setTimeout(() => {
|
|
228
|
-
if (this.shuttingDown) return;
|
|
229
|
-
this.startAgent(name).catch((err) => {
|
|
230
|
-
console.error(`[daemon] failed to restart ${name}:`, err);
|
|
231
|
-
});
|
|
232
|
-
}, delay);
|
|
301
|
+
return;
|
|
233
302
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (err) {
|
|
245
|
-
console.error(`[daemon] invalid variant name in restart.json for ${name}: ${err}`);
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
console.error(`[daemon] merging variant for ${name}: ${signal.name}`);
|
|
249
|
-
const mergeArgs = ["merge", name, signal.name];
|
|
250
|
-
if (signal.summary) mergeArgs.push("--summary", signal.summary);
|
|
251
|
-
if (signal.justification) mergeArgs.push("--justification", signal.justification);
|
|
252
|
-
if (signal.memory) mergeArgs.push("--memory", signal.memory);
|
|
253
|
-
const { VOLUTE_DAEMON_TOKEN: _t, ...mergeEnv } = process.env;
|
|
254
|
-
await execFileAsync("volute", mergeArgs, {
|
|
255
|
-
cwd: dir,
|
|
256
|
-
env: { ...mergeEnv, VOLUTE_SUPERVISOR: "1" }
|
|
303
|
+
const delay = Math.min(BASE_RESTART_DELAY * 2 ** attempts, MAX_RESTART_DELAY);
|
|
304
|
+
this.restartAttempts.set(name, attempts + 1);
|
|
305
|
+
this.saveCrashAttempts();
|
|
306
|
+
console.error(
|
|
307
|
+
`[daemon] crash recovery for ${name} \u2014 attempt ${attempts + 1}/${MAX_RESTART_ATTEMPTS}, restarting in ${delay}ms`
|
|
308
|
+
);
|
|
309
|
+
setTimeout(() => {
|
|
310
|
+
if (this.shuttingDown) return;
|
|
311
|
+
this.startAgent(name).catch((err) => {
|
|
312
|
+
console.error(`[daemon] failed to restart ${name}:`, err);
|
|
257
313
|
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
} catch (e) {
|
|
261
|
-
console.error(`[daemon] failed to handle restart for ${name}:`, e);
|
|
262
|
-
return false;
|
|
263
|
-
}
|
|
314
|
+
}, delay);
|
|
315
|
+
});
|
|
264
316
|
}
|
|
265
317
|
async stopAgent(name) {
|
|
266
318
|
const tracked = this.agents.get(name);
|
|
@@ -285,6 +337,7 @@ var AgentManager = class {
|
|
|
285
337
|
});
|
|
286
338
|
this.stopping.delete(name);
|
|
287
339
|
if (this.restartAttempts.delete(name)) this.saveCrashAttempts();
|
|
340
|
+
rmSync2(agentPidPath(name), { force: true });
|
|
288
341
|
if (!this.shuttingDown) {
|
|
289
342
|
const [baseName, variantName] = name.split("@", 2);
|
|
290
343
|
if (variantName) {
|
package/dist/cli.js
CHANGED
|
@@ -9,49 +9,52 @@ 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-
|
|
12
|
+
const { default: pkg } = await import("./package-RJSONENE.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-
|
|
18
|
+
await import("./agent-YORVRB6I.js").then((m) => m.run(args));
|
|
19
19
|
break;
|
|
20
|
-
case "
|
|
21
|
-
await import("./
|
|
20
|
+
case "send":
|
|
21
|
+
await import("./send-BNC2S5BY.js").then((m) => m.run(args));
|
|
22
|
+
break;
|
|
23
|
+
case "history":
|
|
24
|
+
await import("./history-PXJVYLVY.js").then((m) => m.run(args));
|
|
22
25
|
break;
|
|
23
26
|
case "variant":
|
|
24
|
-
await import("./variant-
|
|
27
|
+
await import("./variant-RKXPN5DH.js").then((m) => m.run(args));
|
|
25
28
|
break;
|
|
26
29
|
case "connector":
|
|
27
|
-
await import("./connector-
|
|
30
|
+
await import("./connector-ZP6MEFF4.js").then((m) => m.run(args));
|
|
28
31
|
break;
|
|
29
32
|
case "channel":
|
|
30
|
-
await import("./channel-
|
|
33
|
+
await import("./channel-RDGHBFSI.js").then((m) => m.run(args));
|
|
31
34
|
break;
|
|
32
35
|
case "schedule":
|
|
33
|
-
await import("./schedule-
|
|
36
|
+
await import("./schedule-HCUCBNQI.js").then((m) => m.run(args));
|
|
34
37
|
break;
|
|
35
38
|
case "env":
|
|
36
|
-
await import("./env-
|
|
39
|
+
await import("./env-KMNYGVZ2.js").then((m) => m.run(args));
|
|
37
40
|
break;
|
|
38
41
|
case "up":
|
|
39
|
-
await import("./up-
|
|
42
|
+
await import("./up-V6EAA7OZ.js").then((m) => m.run(args));
|
|
40
43
|
break;
|
|
41
44
|
case "down":
|
|
42
|
-
await import("./down-
|
|
45
|
+
await import("./down-O4EWZTVA.js").then((m) => m.run(args));
|
|
43
46
|
break;
|
|
44
47
|
case "restart":
|
|
45
|
-
await import("./daemon-restart-
|
|
48
|
+
await import("./daemon-restart-CPBLMMRI.js").then((m) => m.run(args));
|
|
46
49
|
break;
|
|
47
50
|
case "setup":
|
|
48
|
-
await import("./setup-
|
|
51
|
+
await import("./setup-32KH5KLN.js").then((m) => m.run(args));
|
|
49
52
|
break;
|
|
50
53
|
case "service":
|
|
51
|
-
await import("./service-
|
|
54
|
+
await import("./service-XCADRKIS.js").then((m) => m.run(args));
|
|
52
55
|
break;
|
|
53
56
|
case "update":
|
|
54
|
-
await import("./update-
|
|
57
|
+
await import("./update-EUCZ7XGG.js").then((m) => m.run(args));
|
|
55
58
|
break;
|
|
56
59
|
case "--help":
|
|
57
60
|
case "-h":
|
|
@@ -70,8 +73,8 @@ Commands:
|
|
|
70
73
|
volute agent upgrade <name> Upgrade agent to latest template
|
|
71
74
|
volute agent import <path> Import an OpenClaw workspace
|
|
72
75
|
|
|
73
|
-
volute
|
|
74
|
-
volute
|
|
76
|
+
volute send <target> "<msg>" Send a message (agent DM, channel, etc.)
|
|
77
|
+
volute history [--agent <name>] View message history
|
|
75
78
|
|
|
76
79
|
volute variant create <name> Create a variant (worktree + server)
|
|
77
80
|
volute variant list List variants for an agent
|
|
@@ -82,7 +85,6 @@ Commands:
|
|
|
82
85
|
volute connector disconnect <type> Disable a connector for an agent
|
|
83
86
|
|
|
84
87
|
volute channel read <uri> Read recent messages from a channel
|
|
85
|
-
volute channel send <uri> "<msg>" Send a message to a channel
|
|
86
88
|
volute channel list [<platform>] List conversations on a platform
|
|
87
89
|
volute channel users <platform> List users on a platform
|
|
88
90
|
volute channel create <platform> ... Create a conversation on a platform
|
|
@@ -109,7 +111,7 @@ Options:
|
|
|
109
111
|
--version, -v Show version number
|
|
110
112
|
--help, -h Show this help message
|
|
111
113
|
|
|
112
|
-
Agent-scoped commands (variant, connector, schedule, channel
|
|
114
|
+
Agent-scoped commands (send, history, variant, connector, schedule, channel)
|
|
113
115
|
use --agent <name> or VOLUTE_AGENT env var to identify the agent.`);
|
|
114
116
|
break;
|
|
115
117
|
default:
|
|
@@ -118,7 +120,7 @@ Run 'volute --help' for usage.`);
|
|
|
118
120
|
process.exit(1);
|
|
119
121
|
}
|
|
120
122
|
if (command !== "update") {
|
|
121
|
-
import("./update-check-
|
|
123
|
+
import("./update-check-SM4244SU.js").then((m) => m.checkForUpdate()).then((result) => {
|
|
122
124
|
if (result.updateAvailable) {
|
|
123
125
|
console.error(`
|
|
124
126
|
Update available: ${result.current} \u2192 ${result.latest}`);
|
|
@@ -6,16 +6,16 @@ import {
|
|
|
6
6
|
agentEnvPath,
|
|
7
7
|
readEnv,
|
|
8
8
|
writeEnv
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QF22MYDJ.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-23L3MKEV.js";
|
|
16
16
|
import {
|
|
17
17
|
agentDir
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-DP2DX4WV.js";
|
|
19
19
|
import "./chunk-K3NQKI34.js";
|
|
20
20
|
|
|
21
21
|
// src/commands/connector.ts
|
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildChannelSlug,
|
|
4
|
-
fireAndForget,
|
|
5
|
-
handleAgentMessage,
|
|
6
4
|
loadEnv,
|
|
7
5
|
loadFollowedChannels,
|
|
8
6
|
reportTyping,
|
|
7
|
+
sendToAgent,
|
|
9
8
|
slugify,
|
|
10
|
-
splitMessage,
|
|
11
9
|
writeChannelEntry
|
|
12
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-N6MLQ26B.js";
|
|
11
|
+
import "../chunk-DP2DX4WV.js";
|
|
13
12
|
import "../chunk-K3NQKI34.js";
|
|
14
13
|
|
|
15
14
|
// src/connectors/discord.ts
|
|
16
|
-
import {
|
|
17
|
-
AttachmentBuilder,
|
|
18
|
-
ChannelType,
|
|
19
|
-
Client,
|
|
20
|
-
Events,
|
|
21
|
-
GatewayIntentBits,
|
|
22
|
-
Partials
|
|
23
|
-
} from "discord.js";
|
|
24
|
-
var DISCORD_MAX_LENGTH = 2e3;
|
|
15
|
+
import { ChannelType, Client, Events, GatewayIntentBits, Partials } from "discord.js";
|
|
25
16
|
var TYPING_INTERVAL_MS = 8e3;
|
|
26
17
|
var env = loadEnv();
|
|
27
18
|
var token = process.env.DISCORD_TOKEN;
|
|
@@ -102,14 +93,16 @@ client.on(Events.MessageCreate, async (message) => {
|
|
|
102
93
|
channelName: channelName ?? message.channelId,
|
|
103
94
|
serverName: message.guild?.name
|
|
104
95
|
});
|
|
105
|
-
|
|
106
|
-
writeChannelEntry(env.
|
|
96
|
+
try {
|
|
97
|
+
writeChannelEntry(env.agentName, channelKey, {
|
|
107
98
|
platformId: message.channelId,
|
|
108
99
|
platform: "discord",
|
|
109
100
|
name: channelName ? `#${channelName}` : void 0,
|
|
110
101
|
server: message.guild?.name,
|
|
111
102
|
type: isDM ? "dm" : "channel"
|
|
112
103
|
});
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.error(`[discord] failed to write channel entry for ${channelKey}:`, err);
|
|
113
106
|
}
|
|
114
107
|
const participantCount = isDM ? 2 : message.guild?.memberCount;
|
|
115
108
|
const payload = {
|
|
@@ -124,7 +117,11 @@ client.on(Events.MessageCreate, async (message) => {
|
|
|
124
117
|
};
|
|
125
118
|
reportTyping(env, channelKey, senderName, false);
|
|
126
119
|
if (isFollowedChannel && !isMentioned) {
|
|
127
|
-
await
|
|
120
|
+
const result = await sendToAgent(env, payload);
|
|
121
|
+
if (!result.ok)
|
|
122
|
+
message.reply(result.error ?? "Failed to process message").catch((err) => {
|
|
123
|
+
console.warn(`[discord] failed to send error reply: ${err}`);
|
|
124
|
+
});
|
|
128
125
|
return;
|
|
129
126
|
}
|
|
130
127
|
await handleDiscordMessage(message, payload);
|
|
@@ -146,50 +143,12 @@ async function handleDiscordMessage(message, payload) {
|
|
|
146
143
|
channel.sendTyping().catch((err) => {
|
|
147
144
|
console.warn(`[discord] sendTyping failed: ${err}`);
|
|
148
145
|
});
|
|
149
|
-
let replied = false;
|
|
150
146
|
try {
|
|
151
|
-
await
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const ext = img.media_type.split("/")[1] || "png";
|
|
157
|
-
return new AttachmentBuilder(Buffer.from(img.data, "base64"), {
|
|
158
|
-
name: `image-${i}.${ext}`
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
if (chunks.length === 0 && imageFiles.length > 0) {
|
|
162
|
-
const sendFn = replied ? channel.send.bind(channel) : message.reply.bind(message);
|
|
163
|
-
await sendFn({ content: "\u200B", files: imageFiles }).catch((err) => {
|
|
164
|
-
console.error(`Failed to send message: ${err}`);
|
|
165
|
-
});
|
|
166
|
-
replied = true;
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
170
|
-
const isLast = i === chunks.length - 1;
|
|
171
|
-
const opts = {
|
|
172
|
-
content: chunks[i]
|
|
173
|
-
};
|
|
174
|
-
if (isLast && imageFiles.length > 0) opts.files = imageFiles;
|
|
175
|
-
try {
|
|
176
|
-
if (!replied) {
|
|
177
|
-
await message.reply(opts);
|
|
178
|
-
replied = true;
|
|
179
|
-
} else {
|
|
180
|
-
await channel.send(opts);
|
|
181
|
-
}
|
|
182
|
-
} catch (err) {
|
|
183
|
-
console.error(`Failed to send message: ${err}`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
onError: async (msg) => {
|
|
188
|
-
await message.reply(msg).catch((err) => {
|
|
189
|
-
console.error(`[discord] failed to send error reply: ${err}`);
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
});
|
|
147
|
+
const result = await sendToAgent(env, payload);
|
|
148
|
+
if (!result.ok)
|
|
149
|
+
message.reply(result.error ?? "Failed to process message").catch((err) => {
|
|
150
|
+
console.warn(`[discord] failed to send error reply: ${err}`);
|
|
151
|
+
});
|
|
193
152
|
} finally {
|
|
194
153
|
clearInterval(typingInterval);
|
|
195
154
|
}
|
package/dist/connectors/slack.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildChannelSlug,
|
|
4
|
-
fireAndForget,
|
|
5
|
-
handleAgentMessage,
|
|
6
4
|
loadEnv,
|
|
7
5
|
loadFollowedChannels,
|
|
8
6
|
onShutdown,
|
|
9
|
-
|
|
7
|
+
sendToAgent,
|
|
10
8
|
writeChannelEntry
|
|
11
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-N6MLQ26B.js";
|
|
10
|
+
import "../chunk-DP2DX4WV.js";
|
|
12
11
|
import "../chunk-K3NQKI34.js";
|
|
13
12
|
|
|
14
13
|
// src/connectors/slack.ts
|
|
15
14
|
import { App } from "@slack/bolt";
|
|
16
|
-
var SLACK_MAX_LENGTH = 4e3;
|
|
17
15
|
var env = loadEnv();
|
|
18
16
|
var botToken = process.env.SLACK_BOT_TOKEN;
|
|
19
17
|
var appToken = process.env.SLACK_APP_TOKEN;
|
|
@@ -30,7 +28,7 @@ var app = new App({
|
|
|
30
28
|
});
|
|
31
29
|
var botUserId;
|
|
32
30
|
var serverName;
|
|
33
|
-
app.message(async ({ message
|
|
31
|
+
app.message(async ({ message }) => {
|
|
34
32
|
if (message.subtype) return;
|
|
35
33
|
if (!("user" in message) || !("text" in message)) return;
|
|
36
34
|
if ("bot_id" in message && message.bot_id) return;
|
|
@@ -98,14 +96,16 @@ app.message(async ({ message, say }) => {
|
|
|
98
96
|
channelName: channelName ?? message.channel,
|
|
99
97
|
serverName
|
|
100
98
|
});
|
|
101
|
-
|
|
102
|
-
writeChannelEntry(env.
|
|
99
|
+
try {
|
|
100
|
+
writeChannelEntry(env.agentName, channelKey, {
|
|
103
101
|
platformId: message.channel,
|
|
104
102
|
platform: "slack",
|
|
105
103
|
name: channelName ? `#${channelName}` : void 0,
|
|
106
104
|
server: serverName,
|
|
107
105
|
type: isDM ? "dm" : "channel"
|
|
108
106
|
});
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error(`[slack] failed to write channel entry for ${channelKey}:`, err);
|
|
109
109
|
}
|
|
110
110
|
const participantCount = message.channel_type === "im" ? 2 : numMembers;
|
|
111
111
|
const payload = {
|
|
@@ -119,38 +119,21 @@ app.message(async ({ message, say }) => {
|
|
|
119
119
|
...participantCount ? { participantCount } : {}
|
|
120
120
|
};
|
|
121
121
|
if (isFollowedChannel && !isMentioned) {
|
|
122
|
-
await
|
|
122
|
+
const result2 = await sendToAgent(env, payload);
|
|
123
|
+
if (!result2.ok)
|
|
124
|
+
app.client.chat.postMessage({
|
|
125
|
+
channel: message.channel,
|
|
126
|
+
text: result2.error ?? "Failed to process message"
|
|
127
|
+
}).catch((err) => {
|
|
128
|
+
console.warn(`[slack] failed to send error reply: ${err}`);
|
|
129
|
+
});
|
|
123
130
|
return;
|
|
124
131
|
}
|
|
125
|
-
await
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
await app.client.filesUploadV2({
|
|
131
|
-
channel_id: message.channel,
|
|
132
|
-
file: Buffer.from(img.data, "base64"),
|
|
133
|
-
filename: `image.${ext}`
|
|
134
|
-
});
|
|
135
|
-
} catch (err) {
|
|
136
|
-
console.error(`Failed to upload image: ${err}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (!text2) return;
|
|
140
|
-
const chunks = splitMessage(text2, SLACK_MAX_LENGTH);
|
|
141
|
-
for (const chunk of chunks) {
|
|
142
|
-
try {
|
|
143
|
-
await say(chunk);
|
|
144
|
-
} catch (err) {
|
|
145
|
-
console.error(`Failed to send message: ${err}`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
onError: async (msg) => {
|
|
150
|
-
await say(msg).catch(() => {
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
});
|
|
132
|
+
const result = await sendToAgent(env, payload);
|
|
133
|
+
if (!result.ok)
|
|
134
|
+
app.client.chat.postMessage({ channel: message.channel, text: result.error ?? "Failed to process message" }).catch((err) => {
|
|
135
|
+
console.warn(`[slack] failed to send error reply: ${err}`);
|
|
136
|
+
});
|
|
154
137
|
});
|
|
155
138
|
async function start() {
|
|
156
139
|
await app.start();
|