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
|
@@ -4,19 +4,20 @@ import {
|
|
|
4
4
|
} from "./chunk-AZEL2IEK.js";
|
|
5
5
|
import {
|
|
6
6
|
deleteAgentUser as deleteAgentUser2
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ECPQXRLB.js";
|
|
8
8
|
import {
|
|
9
9
|
parseArgs
|
|
10
10
|
} from "./chunk-D424ZQGI.js";
|
|
11
11
|
import {
|
|
12
12
|
deleteAgentUser
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-IQXBMFZG.js";
|
|
14
14
|
import {
|
|
15
15
|
agentDir,
|
|
16
16
|
findAgent,
|
|
17
17
|
removeAgent,
|
|
18
|
-
removeAllVariants
|
|
19
|
-
|
|
18
|
+
removeAllVariants,
|
|
19
|
+
stateDir
|
|
20
|
+
} from "./chunk-DP2DX4WV.js";
|
|
20
21
|
import "./chunk-K3NQKI34.js";
|
|
21
22
|
|
|
22
23
|
// src/commands/delete.ts
|
|
@@ -32,7 +33,7 @@ async function run(args) {
|
|
|
32
33
|
process.exit(1);
|
|
33
34
|
}
|
|
34
35
|
try {
|
|
35
|
-
const { daemonFetch } = await import("./daemon-client-
|
|
36
|
+
const { daemonFetch } = await import("./daemon-client-54J3EIZD.js");
|
|
36
37
|
const res = await daemonFetch(`/api/agents/${encodeURIComponent(name)}/stop`, {
|
|
37
38
|
method: "POST"
|
|
38
39
|
});
|
|
@@ -46,6 +47,10 @@ async function run(args) {
|
|
|
46
47
|
removeAgent(name);
|
|
47
48
|
await deleteAgentUser2(name);
|
|
48
49
|
console.log(`Removed ${name} from registry.`);
|
|
50
|
+
const state = stateDir(name);
|
|
51
|
+
if (existsSync(state)) {
|
|
52
|
+
rmSync(state, { recursive: true, force: true });
|
|
53
|
+
}
|
|
49
54
|
if (existsSync(dir)) {
|
|
50
55
|
if (!flags.force) {
|
|
51
56
|
console.log(`Directory: ${dir}`);
|
|
@@ -5,20 +5,20 @@ import {
|
|
|
5
5
|
readEnv,
|
|
6
6
|
sharedEnvPath,
|
|
7
7
|
writeEnv
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QF22MYDJ.js";
|
|
9
9
|
import {
|
|
10
10
|
parseArgs
|
|
11
11
|
} from "./chunk-D424ZQGI.js";
|
|
12
12
|
import {
|
|
13
|
-
|
|
14
|
-
} from "./chunk-
|
|
13
|
+
findAgent
|
|
14
|
+
} from "./chunk-DP2DX4WV.js";
|
|
15
15
|
import "./chunk-K3NQKI34.js";
|
|
16
16
|
|
|
17
17
|
// src/commands/env.ts
|
|
18
18
|
function getEnvPath(agentName) {
|
|
19
19
|
if (agentName) {
|
|
20
|
-
|
|
21
|
-
return agentEnvPath(
|
|
20
|
+
if (!findAgent(agentName)) throw new Error(`Unknown agent: ${agentName}`);
|
|
21
|
+
return agentEnvPath(agentName);
|
|
22
22
|
}
|
|
23
23
|
return sharedEnvPath();
|
|
24
24
|
}
|
|
@@ -85,8 +85,7 @@ async function run(args) {
|
|
|
85
85
|
process.exit(1);
|
|
86
86
|
}
|
|
87
87
|
if (flags.agent) {
|
|
88
|
-
const
|
|
89
|
-
const merged = loadMergedEnv(dir);
|
|
88
|
+
const merged = loadMergedEnv(flags.agent);
|
|
90
89
|
if (key in merged) {
|
|
91
90
|
console.log(merged[key]);
|
|
92
91
|
} else {
|
|
@@ -106,9 +105,8 @@ async function run(args) {
|
|
|
106
105
|
}
|
|
107
106
|
case "list": {
|
|
108
107
|
if (flags.agent) {
|
|
109
|
-
const { dir } = resolveAgent(flags.agent);
|
|
110
108
|
const shared = readEnv(sharedEnvPath());
|
|
111
|
-
const agent = readEnv(agentEnvPath(
|
|
109
|
+
const agent = readEnv(agentEnvPath(flags.agent));
|
|
112
110
|
const allKeys = /* @__PURE__ */ new Set([...Object.keys(shared), ...Object.keys(agent)]);
|
|
113
111
|
if (allKeys.size === 0) {
|
|
114
112
|
console.log("No environment variables set.");
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
} from "./chunk-D424ZQGI.js";
|
|
8
8
|
import {
|
|
9
9
|
daemonFetch
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-23L3MKEV.js";
|
|
11
|
+
import "./chunk-DP2DX4WV.js";
|
|
12
12
|
import "./chunk-K3NQKI34.js";
|
|
13
13
|
|
|
14
14
|
// src/commands/history.ts
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
agentEnvPath,
|
|
8
8
|
readEnv,
|
|
9
9
|
writeEnv
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-QF22MYDJ.js";
|
|
11
11
|
import {
|
|
12
12
|
composeTemplate,
|
|
13
13
|
copyTemplateToDir,
|
|
@@ -19,13 +19,13 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
exec,
|
|
21
21
|
execInherit
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-5C5JWR2L.js";
|
|
23
23
|
import {
|
|
24
24
|
addAgent,
|
|
25
25
|
agentDir,
|
|
26
26
|
ensureVoluteHome,
|
|
27
27
|
nextPort
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-DP2DX4WV.js";
|
|
29
29
|
import "./chunk-K3NQKI34.js";
|
|
30
30
|
|
|
31
31
|
// src/commands/import.ts
|
|
@@ -390,7 +390,7 @@ ${user.trimEnd()}
|
|
|
390
390
|
console.warn(`Session import not supported for template: ${template}`);
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
|
-
importOpenClawConnectors(dest);
|
|
393
|
+
importOpenClawConnectors(name, dest);
|
|
394
394
|
console.log(`
|
|
395
395
|
Imported agent: ${name} (port ${port})`);
|
|
396
396
|
console.log(`
|
|
@@ -475,7 +475,7 @@ function importPiSession(sessionFile, agentDirPath) {
|
|
|
475
475
|
`);
|
|
476
476
|
console.log(`Imported session (${lines.length} entries)`);
|
|
477
477
|
}
|
|
478
|
-
function importOpenClawConnectors(agentDirPath) {
|
|
478
|
+
function importOpenClawConnectors(agentName, agentDirPath) {
|
|
479
479
|
const configPath = resolve3(homedir2(), ".openclaw/openclaw.json");
|
|
480
480
|
if (!existsSync(configPath)) return;
|
|
481
481
|
let config;
|
|
@@ -487,7 +487,7 @@ function importOpenClawConnectors(agentDirPath) {
|
|
|
487
487
|
}
|
|
488
488
|
const discord = config.channels?.discord;
|
|
489
489
|
if (!discord?.enabled || !discord.token) return;
|
|
490
|
-
const envPath = agentEnvPath(
|
|
490
|
+
const envPath = agentEnvPath(agentName);
|
|
491
491
|
const env = readEnv(envPath);
|
|
492
492
|
env.DISCORD_TOKEN = discord.token;
|
|
493
493
|
writeEnv(envPath, env);
|
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
parseArgs
|
|
7
7
|
} from "./chunk-D424ZQGI.js";
|
|
8
8
|
import {
|
|
9
|
-
resolveAgent
|
|
10
|
-
|
|
9
|
+
resolveAgent,
|
|
10
|
+
stateDir
|
|
11
|
+
} from "./chunk-DP2DX4WV.js";
|
|
11
12
|
import "./chunk-K3NQKI34.js";
|
|
12
13
|
|
|
13
14
|
// src/commands/logs.ts
|
|
@@ -21,8 +22,8 @@ async function run(args) {
|
|
|
21
22
|
n: { type: "number" }
|
|
22
23
|
});
|
|
23
24
|
const name = resolveAgentName(flags);
|
|
24
|
-
|
|
25
|
-
const logFile = resolve(
|
|
25
|
+
resolveAgent(name);
|
|
26
|
+
const logFile = resolve(stateDir(name), "logs", "agent.log");
|
|
26
27
|
if (!existsSync(logFile)) {
|
|
27
28
|
console.error(`No log file found. Has ${name} been started?`);
|
|
28
29
|
process.exit(1);
|
|
@@ -4,7 +4,7 @@ import "./chunk-K3NQKI34.js";
|
|
|
4
4
|
// package.json
|
|
5
5
|
var package_default = {
|
|
6
6
|
name: "volute",
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.8.1",
|
|
8
8
|
description: "CLI for creating and managing self-modifying AI agents powered by the Claude Agent SDK",
|
|
9
9
|
type: "module",
|
|
10
10
|
license: "MIT",
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-AZEL2IEK.js";
|
|
5
5
|
import {
|
|
6
6
|
daemonFetch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-23L3MKEV.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveAgent
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DP2DX4WV.js";
|
|
11
11
|
import "./chunk-K3NQKI34.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/restart.ts
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
} from "./chunk-D424ZQGI.js";
|
|
8
8
|
import {
|
|
9
9
|
daemonFetch
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-23L3MKEV.js";
|
|
11
|
+
import "./chunk-DP2DX4WV.js";
|
|
12
12
|
import "./chunk-K3NQKI34.js";
|
|
13
13
|
|
|
14
14
|
// src/commands/schedule.ts
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveAgentName
|
|
4
|
+
} from "./chunk-AZEL2IEK.js";
|
|
5
|
+
import {
|
|
6
|
+
getChannelDriver
|
|
7
|
+
} from "./chunk-LIPPXNIE.js";
|
|
8
|
+
import {
|
|
9
|
+
loadMergedEnv
|
|
10
|
+
} from "./chunk-QF22MYDJ.js";
|
|
11
|
+
import "./chunk-N6MLQ26B.js";
|
|
12
|
+
import {
|
|
13
|
+
parseArgs
|
|
14
|
+
} from "./chunk-D424ZQGI.js";
|
|
15
|
+
import {
|
|
16
|
+
daemonFetch
|
|
17
|
+
} from "./chunk-23L3MKEV.js";
|
|
18
|
+
import {
|
|
19
|
+
resolveAgent
|
|
20
|
+
} from "./chunk-DP2DX4WV.js";
|
|
21
|
+
import "./chunk-K3NQKI34.js";
|
|
22
|
+
|
|
23
|
+
// src/commands/send.ts
|
|
24
|
+
import { userInfo } from "os";
|
|
25
|
+
|
|
26
|
+
// src/lib/parse-target.ts
|
|
27
|
+
function parseTarget(target) {
|
|
28
|
+
const colonIdx = target.indexOf(":");
|
|
29
|
+
if (colonIdx !== -1) {
|
|
30
|
+
const platform = target.slice(0, colonIdx);
|
|
31
|
+
const identifier = target.slice(colonIdx + 1);
|
|
32
|
+
return {
|
|
33
|
+
platform,
|
|
34
|
+
identifier,
|
|
35
|
+
uri: target,
|
|
36
|
+
isDM: identifier.startsWith("@")
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (target.startsWith("@")) {
|
|
40
|
+
return {
|
|
41
|
+
platform: "volute",
|
|
42
|
+
identifier: target,
|
|
43
|
+
uri: `volute:${target}`,
|
|
44
|
+
isDM: true
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
platform: "volute",
|
|
49
|
+
identifier: target,
|
|
50
|
+
uri: `volute:${target}`,
|
|
51
|
+
isDM: false
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/lib/read-stdin.ts
|
|
56
|
+
import { isatty } from "tty";
|
|
57
|
+
async function readStdin() {
|
|
58
|
+
if (isatty(0)) return void 0;
|
|
59
|
+
const chunks = [];
|
|
60
|
+
try {
|
|
61
|
+
for await (const chunk of process.stdin) {
|
|
62
|
+
chunks.push(chunk);
|
|
63
|
+
}
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error(`Failed to read from stdin: ${err instanceof Error ? err.message : String(err)}`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
const text = Buffer.concat(chunks).toString().replace(/\r?\n$/, "");
|
|
69
|
+
return text || void 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/commands/send.ts
|
|
73
|
+
async function run(args) {
|
|
74
|
+
const { positional, flags } = parseArgs(args, {
|
|
75
|
+
agent: { type: "string" }
|
|
76
|
+
});
|
|
77
|
+
const target = positional[0];
|
|
78
|
+
const message = positional[1] ?? await readStdin();
|
|
79
|
+
if (!target || !message) {
|
|
80
|
+
console.error('Usage: volute send <target> "<message>" [--agent <name>]');
|
|
81
|
+
console.error(' echo "message" | volute send <target> [--agent <name>]');
|
|
82
|
+
console.error("");
|
|
83
|
+
console.error("Examples:");
|
|
84
|
+
console.error(' volute send @other-agent "hello"');
|
|
85
|
+
console.error(' volute send animal-chat "hello everyone"');
|
|
86
|
+
console.error(' volute send discord:server/channel "hello"');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const parsed = parseTarget(target);
|
|
90
|
+
const driver = getChannelDriver(parsed.platform);
|
|
91
|
+
if (!driver) {
|
|
92
|
+
console.error(`No driver for platform: ${parsed.platform}`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
let channelUri = parsed.uri;
|
|
96
|
+
if (parsed.isDM && parsed.platform === "volute") {
|
|
97
|
+
const targetName = parsed.identifier.slice(1);
|
|
98
|
+
const agentSelf = process.env.VOLUTE_AGENT;
|
|
99
|
+
const sender = agentSelf || userInfo().username;
|
|
100
|
+
if (!driver.createConversation) {
|
|
101
|
+
console.error("Volute driver does not support creating conversations");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const env = {
|
|
105
|
+
VOLUTE_AGENT: targetName,
|
|
106
|
+
VOLUTE_SENDER: sender
|
|
107
|
+
};
|
|
108
|
+
try {
|
|
109
|
+
channelUri = await driver.createConversation(env, [sender]);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
await driver.send(env, channelUri, message);
|
|
116
|
+
console.log("Message sent.");
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
if (agentSelf) {
|
|
122
|
+
try {
|
|
123
|
+
await daemonFetch(`/api/agents/${encodeURIComponent(agentSelf)}/history`, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: { "Content-Type": "application/json" },
|
|
126
|
+
body: JSON.stringify({ channel: channelUri, content: message })
|
|
127
|
+
});
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error(`Failed to persist to history: ${err instanceof Error ? err.message : err}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
const agentName = resolveAgentName(flags);
|
|
134
|
+
const { dir } = resolveAgent(agentName);
|
|
135
|
+
const env = {
|
|
136
|
+
...loadMergedEnv(agentName),
|
|
137
|
+
VOLUTE_AGENT: agentName,
|
|
138
|
+
VOLUTE_AGENT_DIR: dir
|
|
139
|
+
};
|
|
140
|
+
try {
|
|
141
|
+
await driver.send(env, channelUri, message);
|
|
142
|
+
console.log("Message sent.");
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
if (process.env.VOLUTE_AGENT) {
|
|
148
|
+
try {
|
|
149
|
+
await daemonFetch(`/api/agents/${encodeURIComponent(agentName)}/history`, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: { "Content-Type": "application/json" },
|
|
152
|
+
body: JSON.stringify({ channel: channelUri, content: message })
|
|
153
|
+
});
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.error(`Failed to persist to history: ${err instanceof Error ? err.message : err}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export {
|
|
161
|
+
run
|
|
162
|
+
};
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-D424ZQGI.js";
|
|
5
5
|
import {
|
|
6
6
|
resolveVoluteBin
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5C5JWR2L.js";
|
|
8
8
|
import "./chunk-K3NQKI34.js";
|
|
9
9
|
|
|
10
10
|
// src/commands/service.ts
|
|
@@ -82,6 +82,13 @@ async function install(port, host) {
|
|
|
82
82
|
await execFileAsync("launchctl", ["load", path]);
|
|
83
83
|
console.log("Service installed and loaded. Volute daemon will start on login.");
|
|
84
84
|
} else if (platform === "linux") {
|
|
85
|
+
if (process.getuid?.() === 0) {
|
|
86
|
+
console.error(
|
|
87
|
+
"Error: `volute service install` uses systemd user services, which don't work as root."
|
|
88
|
+
);
|
|
89
|
+
console.error("Use `volute setup` instead to install a system-level service.");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
85
92
|
const path = unitPath();
|
|
86
93
|
mkdirSync(resolve(homedir(), ".config", "systemd", "user"), { recursive: true });
|
|
87
94
|
writeFileSync(path, generateUnit(voluteBin, port, host));
|
|
@@ -4,44 +4,73 @@ import {
|
|
|
4
4
|
} from "./chunk-D424ZQGI.js";
|
|
5
5
|
import {
|
|
6
6
|
ensureVoluteGroup
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IQXBMFZG.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveVoluteBin
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-5C5JWR2L.js";
|
|
11
|
+
import "./chunk-DP2DX4WV.js";
|
|
12
12
|
import "./chunk-K3NQKI34.js";
|
|
13
13
|
|
|
14
14
|
// src/commands/setup.ts
|
|
15
15
|
import { execFileSync } from "child_process";
|
|
16
16
|
import { existsSync, mkdirSync, rmSync, unlinkSync, writeFileSync } from "fs";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import { dirname } from "path";
|
|
17
19
|
var SERVICE_NAME = "volute.service";
|
|
18
20
|
var SERVICE_PATH = `/etc/systemd/system/${SERVICE_NAME}`;
|
|
19
21
|
var DATA_DIR = "/var/lib/volute";
|
|
22
|
+
var AGENTS_DIR = "/agents";
|
|
20
23
|
var HOST_RE = /^[a-zA-Z0-9.:_-]+$/;
|
|
21
24
|
function validateHost(host) {
|
|
22
25
|
if (!HOST_RE.test(host)) {
|
|
23
26
|
throw new Error(`Invalid host: ${host}`);
|
|
24
27
|
}
|
|
25
28
|
}
|
|
29
|
+
function buildServicePath(voluteBin) {
|
|
30
|
+
const binDir = dirname(voluteBin);
|
|
31
|
+
const standardPaths = [
|
|
32
|
+
"/usr/local/sbin",
|
|
33
|
+
"/usr/local/bin",
|
|
34
|
+
"/usr/sbin",
|
|
35
|
+
"/usr/bin",
|
|
36
|
+
"/sbin",
|
|
37
|
+
"/bin"
|
|
38
|
+
];
|
|
39
|
+
const parts = standardPaths.includes(binDir) ? standardPaths : [binDir, ...standardPaths];
|
|
40
|
+
return parts.join(":");
|
|
41
|
+
}
|
|
26
42
|
function generateUnit(voluteBin, port, host) {
|
|
27
43
|
const args = ["up", "--foreground"];
|
|
28
44
|
if (port != null) args.push("--port", String(port));
|
|
29
45
|
if (host) args.push("--host", host);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
const home = homedir();
|
|
47
|
+
const binUnderHome = voluteBin.startsWith(`${home}/`);
|
|
48
|
+
const lines = [
|
|
49
|
+
"[Unit]",
|
|
50
|
+
"Description=Volute Agent Manager",
|
|
51
|
+
"After=network.target",
|
|
52
|
+
"",
|
|
53
|
+
"[Service]",
|
|
54
|
+
"Type=exec",
|
|
55
|
+
`ExecStart=${voluteBin} ${args.join(" ")}`,
|
|
56
|
+
`Environment=PATH=${buildServicePath(voluteBin)}`,
|
|
57
|
+
`Environment=VOLUTE_HOME=${DATA_DIR}`,
|
|
58
|
+
`Environment=VOLUTE_AGENTS_DIR=${AGENTS_DIR}`,
|
|
59
|
+
"Environment=VOLUTE_ISOLATION=user",
|
|
60
|
+
"Restart=on-failure",
|
|
61
|
+
"RestartSec=5",
|
|
62
|
+
"ProtectSystem=strict",
|
|
63
|
+
`ReadWritePaths=${DATA_DIR} ${AGENTS_DIR}`,
|
|
64
|
+
"PrivateTmp=yes"
|
|
65
|
+
];
|
|
66
|
+
if (!binUnderHome) {
|
|
67
|
+
lines.push("ProtectHome=yes");
|
|
68
|
+
} else {
|
|
69
|
+
console.warn(`Warning: ProtectHome=yes omitted because volute binary is under ${home}.`);
|
|
70
|
+
console.warn("Consider installing Node.js system-wide for stronger sandboxing.");
|
|
71
|
+
}
|
|
72
|
+
lines.push("RestrictSUIDSGID=yes", "", "[Install]", "WantedBy=multi-user.target", "");
|
|
73
|
+
return lines.join("\n");
|
|
45
74
|
}
|
|
46
75
|
function install(port, host) {
|
|
47
76
|
if (host) validateHost(host);
|
|
@@ -57,18 +86,39 @@ function install(port, host) {
|
|
|
57
86
|
const voluteBin = resolveVoluteBin();
|
|
58
87
|
mkdirSync(DATA_DIR, { recursive: true });
|
|
59
88
|
console.log(`Created ${DATA_DIR}`);
|
|
89
|
+
mkdirSync(AGENTS_DIR, { recursive: true });
|
|
90
|
+
console.log(`Created ${AGENTS_DIR}`);
|
|
60
91
|
ensureVoluteGroup({ force: true });
|
|
61
92
|
console.log("Ensured volute group exists");
|
|
62
93
|
execFileSync("chmod", ["755", DATA_DIR]);
|
|
63
|
-
|
|
94
|
+
execFileSync("chmod", ["755", AGENTS_DIR]);
|
|
95
|
+
console.log("Set permissions on directories");
|
|
64
96
|
writeFileSync(SERVICE_PATH, generateUnit(voluteBin, port, host ?? "0.0.0.0"));
|
|
65
97
|
console.log(`Wrote ${SERVICE_PATH}`);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
98
|
+
try {
|
|
99
|
+
execFileSync("systemctl", ["daemon-reload"]);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const e = err;
|
|
102
|
+
console.error(`Failed to reload systemd after writing ${SERVICE_PATH}.`);
|
|
103
|
+
if (e.stderr) console.error(e.stderr.toString().trim());
|
|
104
|
+
console.error(
|
|
105
|
+
"Try running `systemctl daemon-reload` manually, then `systemctl enable --now volute`."
|
|
106
|
+
);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
execFileSync("systemctl", ["enable", "--now", SERVICE_NAME]);
|
|
111
|
+
console.log("Service installed, enabled, and started.");
|
|
112
|
+
console.log(`
|
|
70
113
|
Volute daemon is running. Data directory: ${DATA_DIR}`);
|
|
71
|
-
|
|
114
|
+
console.log("Use `systemctl status volute` to check status.");
|
|
115
|
+
} catch (err) {
|
|
116
|
+
const e = err;
|
|
117
|
+
console.error("Service installed but failed to start.");
|
|
118
|
+
if (e.stderr) console.error(e.stderr.toString().trim());
|
|
119
|
+
console.error("Check `journalctl -xeu volute.service` for details.");
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
72
122
|
}
|
|
73
123
|
function uninstall(force) {
|
|
74
124
|
if (process.getuid?.() !== 0) {
|
|
@@ -97,10 +147,15 @@ function uninstall(force) {
|
|
|
97
147
|
const members = output.split(":")[3]?.trim();
|
|
98
148
|
if (members) {
|
|
99
149
|
for (const user of members.split(",")) {
|
|
150
|
+
const u = user.trim();
|
|
100
151
|
try {
|
|
101
|
-
execFileSync("userdel", [
|
|
152
|
+
execFileSync("userdel", [u], { stdio: "ignore" });
|
|
153
|
+
} catch {
|
|
154
|
+
console.warn(`Warning: failed to remove user ${u}`);
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
execFileSync("groupdel", [u], { stdio: "ignore" });
|
|
102
158
|
} catch {
|
|
103
|
-
console.warn(`Warning: failed to remove user ${user.trim()}`);
|
|
104
159
|
}
|
|
105
160
|
}
|
|
106
161
|
}
|
|
@@ -110,6 +165,10 @@ function uninstall(force) {
|
|
|
110
165
|
rmSync(DATA_DIR, { recursive: true, force: true });
|
|
111
166
|
console.log(`Deleted ${DATA_DIR}`);
|
|
112
167
|
}
|
|
168
|
+
if (existsSync(AGENTS_DIR)) {
|
|
169
|
+
rmSync(AGENTS_DIR, { recursive: true, force: true });
|
|
170
|
+
console.log(`Deleted ${AGENTS_DIR}`);
|
|
171
|
+
}
|
|
113
172
|
try {
|
|
114
173
|
execFileSync("groupdel", ["volute"], { stdio: "ignore" });
|
|
115
174
|
console.log("Removed volute group");
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
daemonFetch
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-23L3MKEV.js";
|
|
5
5
|
import {
|
|
6
6
|
resolveAgent
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-DP2DX4WV.js";
|
|
8
8
|
import "./chunk-K3NQKI34.js";
|
|
9
9
|
|
|
10
10
|
// src/commands/start.ts
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-AZEL2IEK.js";
|
|
5
5
|
import {
|
|
6
6
|
daemonFetch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-23L3MKEV.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveAgent
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DP2DX4WV.js";
|
|
11
11
|
import "./chunk-K3NQKI34.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/stop.ts
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
checkForUpdate
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RT6Y7AR3.js";
|
|
5
5
|
import {
|
|
6
6
|
execInherit,
|
|
7
7
|
resolveVoluteBin
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5C5JWR2L.js";
|
|
9
9
|
import {
|
|
10
10
|
voluteHome
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-DP2DX4WV.js";
|
|
12
12
|
import "./chunk-K3NQKI34.js";
|
|
13
13
|
|
|
14
14
|
// src/commands/update.ts
|