volute 0.8.3 → 0.10.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/dist/{agent-YORVRB6I.js → agent-ECRX44DB.js} +10 -10
- package/dist/{agent-manager-CMMH5KQQ.js → agent-manager-MRHHKAB6.js} +2 -2
- package/dist/api-client-YPKOZP2O.js +10 -0
- package/dist/{channel-RDGHBFSI.js → channel-2WHBRDTD.js} +66 -81
- package/dist/chunk-4RQBJWQX.js +17 -0
- package/dist/{chunk-YNNK4QN2.js → chunk-FYQGANL6.js} +40 -2
- package/dist/chunk-R3VB7NF5.js +205 -0
- package/dist/{chunk-23L3MKEV.js → chunk-STOEJOJO.js} +18 -4
- package/dist/cli.js +14 -14
- package/dist/{connector-ZP6MEFF4.js → connector-L2HBLZBW.js} +37 -20
- package/dist/create-VBZZNJOG.js +38 -0
- package/dist/{daemon-client-54J3EIZD.js → daemon-client-P44NU3KU.js} +1 -1
- package/dist/daemon-restart-QCLR6ZZV.js +61 -0
- package/dist/daemon.js +1732 -214
- package/dist/delete-BOTVU4YO.js +35 -0
- package/dist/{down-4DGRZRJU.js → down-4LIQG3CE.js} +3 -1
- package/dist/{env-KMNYGVZ2.js → env-CGORIKVF.js} +86 -37
- package/dist/{history-PXJVYLVY.js → history-NI5QP27M.js} +11 -8
- package/dist/import-2BZUWT23.js +21 -0
- package/dist/logs-APWVWGNX.js +77 -0
- package/dist/{package-2S7APQBC.js → package-ERGXEDAF.js} +1 -1
- package/dist/{restart-KVH3TK5N.js → restart-CCYM3MEC.js} +10 -4
- package/dist/{schedule-HCUCBNQI.js → schedule-E4MFGYSA.js} +24 -8
- package/dist/{send-BNC2S5BY.js → send-X6OQGSD6.js} +36 -28
- package/dist/{start-QU73YTJW.js → start-6YRS6FF6.js} +7 -2
- package/dist/{status-Q6ZQJXNI.js → status-SIMKH3ZE.js} +8 -3
- package/dist/{stop-N7U5N6A7.js → stop-UQSNF4CG.js} +7 -2
- package/dist/{up-RZJMSVQS.js → up-MNNPCMFF.js} +1 -1
- package/dist/upgrade-RSE4CZNE.js +55 -0
- package/dist/variant-7IZF6OWO.js +215 -0
- package/package.json +1 -1
- package/dist/chunk-ECPQXRLB.js +0 -264
- package/dist/chunk-NETNFBA5.js +0 -28
- package/dist/chunk-XUA3JUFK.js +0 -121
- package/dist/create-HGJHLABX.js +0 -96
- package/dist/daemon-restart-IMNCBWFV.js +0 -28
- package/dist/delete-45TGQC4N.js +0 -67
- package/dist/import-CNEDF3TD.js +0 -532
- package/dist/logs-TZB3MTLZ.js +0 -37
- package/dist/upgrade-CZF6PN7Y.js +0 -224
- package/dist/variant-RKXPN5DH.js +0 -476
- package/dist/{chunk-6RDCTVQK.js → chunk-4NAGJV3I.js} +0 -0
- package/dist/{chunk-W6TMWYU3.js → chunk-WV4W7BAT.js} +3 -3
- package/dist/{service-56CY4S6Z.js → service-OW35VZ5G.js} +3 -3
- package/dist/{setup-7SPMWF2O.js → setup-ABMZK6LS.js} +3 -3
|
@@ -6,42 +6,42 @@ async function run(args) {
|
|
|
6
6
|
const subcommand = args[0];
|
|
7
7
|
switch (subcommand) {
|
|
8
8
|
case "create":
|
|
9
|
-
await import("./create-
|
|
9
|
+
await import("./create-VBZZNJOG.js").then((m) => m.run(args.slice(1)));
|
|
10
10
|
break;
|
|
11
11
|
case "start":
|
|
12
|
-
await import("./start-
|
|
12
|
+
await import("./start-6YRS6FF6.js").then((m) => m.run(args.slice(1)));
|
|
13
13
|
break;
|
|
14
14
|
case "stop":
|
|
15
|
-
await import("./stop-
|
|
15
|
+
await import("./stop-UQSNF4CG.js").then((m) => m.run(args.slice(1)));
|
|
16
16
|
break;
|
|
17
17
|
case "restart":
|
|
18
|
-
await import("./restart-
|
|
18
|
+
await import("./restart-CCYM3MEC.js").then((m) => m.run(args.slice(1)));
|
|
19
19
|
break;
|
|
20
20
|
case "delete":
|
|
21
|
-
await import("./delete-
|
|
21
|
+
await import("./delete-BOTVU4YO.js").then((m) => m.run(args.slice(1)));
|
|
22
22
|
break;
|
|
23
23
|
case "list":
|
|
24
|
-
await import("./status-
|
|
24
|
+
await import("./status-SIMKH3ZE.js").then((m) => m.run(args.slice(1)));
|
|
25
25
|
break;
|
|
26
26
|
case "status": {
|
|
27
27
|
const rest = args.slice(1);
|
|
28
28
|
if (!rest[0] && process.env.VOLUTE_AGENT) {
|
|
29
29
|
rest.unshift(process.env.VOLUTE_AGENT);
|
|
30
30
|
}
|
|
31
|
-
await import("./status-
|
|
31
|
+
await import("./status-SIMKH3ZE.js").then((m) => m.run(rest));
|
|
32
32
|
break;
|
|
33
33
|
}
|
|
34
34
|
case "logs": {
|
|
35
35
|
const rest = args.slice(1);
|
|
36
36
|
const logsArgs = transformAgentFlag(rest);
|
|
37
|
-
await import("./logs-
|
|
37
|
+
await import("./logs-APWVWGNX.js").then((m) => m.run(logsArgs));
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
40
40
|
case "upgrade":
|
|
41
|
-
await import("./upgrade-
|
|
41
|
+
await import("./upgrade-RSE4CZNE.js").then((m) => m.run(args.slice(1)));
|
|
42
42
|
break;
|
|
43
43
|
case "import":
|
|
44
|
-
await import("./import-
|
|
44
|
+
await import("./import-2BZUWT23.js").then((m) => m.run(args.slice(1)));
|
|
45
45
|
break;
|
|
46
46
|
case "--help":
|
|
47
47
|
case "-h":
|
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
AgentManager,
|
|
4
4
|
getAgentManager,
|
|
5
5
|
initAgentManager
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-QF22MYDJ.js";
|
|
6
|
+
} from "./chunk-WV4W7BAT.js";
|
|
8
7
|
import "./chunk-IQXBMFZG.js";
|
|
8
|
+
import "./chunk-QF22MYDJ.js";
|
|
9
9
|
import "./chunk-DP2DX4WV.js";
|
|
10
10
|
import "./chunk-K3NQKI34.js";
|
|
11
11
|
export {
|
|
@@ -2,25 +2,17 @@
|
|
|
2
2
|
import {
|
|
3
3
|
resolveAgentName
|
|
4
4
|
} from "./chunk-AZEL2IEK.js";
|
|
5
|
-
import {
|
|
6
|
-
CHANNELS,
|
|
7
|
-
getChannelDriver
|
|
8
|
-
} from "./chunk-LIPPXNIE.js";
|
|
9
|
-
import {
|
|
10
|
-
loadMergedEnv
|
|
11
|
-
} from "./chunk-QF22MYDJ.js";
|
|
12
|
-
import {
|
|
13
|
-
writeChannelEntry
|
|
14
|
-
} from "./chunk-N6MLQ26B.js";
|
|
15
5
|
import {
|
|
16
6
|
parseArgs
|
|
17
7
|
} from "./chunk-D424ZQGI.js";
|
|
18
8
|
import {
|
|
19
9
|
daemonFetch
|
|
20
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-STOEJOJO.js";
|
|
11
|
+
import "./chunk-DP2DX4WV.js";
|
|
21
12
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
getClient,
|
|
14
|
+
urlOf
|
|
15
|
+
} from "./chunk-4RQBJWQX.js";
|
|
24
16
|
import "./chunk-K3NQKI34.js";
|
|
25
17
|
|
|
26
18
|
// src/commands/channel.ts
|
|
@@ -72,17 +64,20 @@ async function readChannel(args) {
|
|
|
72
64
|
}
|
|
73
65
|
const agentName = resolveAgentName(flags);
|
|
74
66
|
const { platform } = parseUri(uri);
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
67
|
+
const limit = flags.limit ?? 20;
|
|
68
|
+
const client = getClient();
|
|
69
|
+
const url = client.api.agents[":name"].channels.read.$url({ param: { name: agentName } });
|
|
70
|
+
url.searchParams.set("platform", platform);
|
|
71
|
+
url.searchParams.set("uri", uri);
|
|
72
|
+
url.searchParams.set("limit", String(limit));
|
|
73
|
+
const res = await daemonFetch(urlOf(url));
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
const body = await res.json().catch(() => ({}));
|
|
76
|
+
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
84
77
|
process.exit(1);
|
|
85
78
|
}
|
|
79
|
+
const output = await res.text();
|
|
80
|
+
console.log(output);
|
|
86
81
|
}
|
|
87
82
|
async function listChannels(args) {
|
|
88
83
|
const { positional, flags } = parseArgs(args, {
|
|
@@ -90,29 +85,27 @@ async function listChannels(args) {
|
|
|
90
85
|
});
|
|
91
86
|
const platform = positional[0];
|
|
92
87
|
const agentName = resolveAgentName(flags);
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
console.log(parts.join(" "));
|
|
88
|
+
const client = getClient();
|
|
89
|
+
const url = client.api.agents[":name"].channels.list.$url({ param: { name: agentName } });
|
|
90
|
+
if (platform) url.searchParams.set("platform", platform);
|
|
91
|
+
const res = await daemonFetch(urlOf(url));
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
const body = await res.json().catch(() => ({}));
|
|
94
|
+
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
const results = await res.json();
|
|
98
|
+
for (const [p, convs] of Object.entries(results)) {
|
|
99
|
+
for (const conv of convs) {
|
|
100
|
+
if (conv.error) {
|
|
101
|
+
console.error(`${p}: ${conv.error}`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const parts = [conv.id.padEnd(24), conv.name.padEnd(28), conv.type];
|
|
105
|
+
if (conv.participantCount != null) {
|
|
106
|
+
parts.push(String(conv.participantCount));
|
|
113
107
|
}
|
|
114
|
-
|
|
115
|
-
console.error(`${p}: ${err instanceof Error ? err.message : String(err)}`);
|
|
108
|
+
console.log(parts.join(" "));
|
|
116
109
|
}
|
|
117
110
|
}
|
|
118
111
|
}
|
|
@@ -125,23 +118,20 @@ async function listUsers(args) {
|
|
|
125
118
|
console.error("Usage: volute channel users <platform> [--agent <name>]");
|
|
126
119
|
process.exit(1);
|
|
127
120
|
}
|
|
128
|
-
const driver = requireDriver(platform);
|
|
129
|
-
if (!driver.listUsers) {
|
|
130
|
-
console.error(`Platform ${platform} does not support listing users`);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
121
|
const agentName = resolveAgentName(flags);
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
} catch (err) {
|
|
142
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
122
|
+
const client = getClient();
|
|
123
|
+
const url = client.api.agents[":name"].channels.users.$url({ param: { name: agentName } });
|
|
124
|
+
url.searchParams.set("platform", platform);
|
|
125
|
+
const res = await daemonFetch(urlOf(url));
|
|
126
|
+
if (!res.ok) {
|
|
127
|
+
const body = await res.json().catch(() => ({}));
|
|
128
|
+
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
143
129
|
process.exit(1);
|
|
144
130
|
}
|
|
131
|
+
const users = await res.json();
|
|
132
|
+
for (const user of users) {
|
|
133
|
+
console.log(`${user.username.padEnd(20)} ${user.id.padEnd(20)} ${user.type ?? ""}`);
|
|
134
|
+
}
|
|
145
135
|
}
|
|
146
136
|
async function createChannel(args) {
|
|
147
137
|
const { positional, flags } = parseArgs(args, {
|
|
@@ -156,22 +146,24 @@ async function createChannel(args) {
|
|
|
156
146
|
);
|
|
157
147
|
process.exit(1);
|
|
158
148
|
}
|
|
159
|
-
const driver = requireDriver(platform);
|
|
160
|
-
if (!driver.createConversation) {
|
|
161
|
-
console.error(`Platform ${platform} does not support creating conversations`);
|
|
162
|
-
process.exit(1);
|
|
163
|
-
}
|
|
164
149
|
const agentName = resolveAgentName(flags);
|
|
165
|
-
const dir = agentDir(agentName);
|
|
166
|
-
const env = { ...loadMergedEnv(agentName), VOLUTE_AGENT: agentName, VOLUTE_AGENT_DIR: dir };
|
|
167
150
|
const participants = flags.participants.split(",").map((s) => s.trim());
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
151
|
+
const client = getClient();
|
|
152
|
+
const res = await daemonFetch(
|
|
153
|
+
urlOf(client.api.agents[":name"].channels.create.$url({ param: { name: agentName } })),
|
|
154
|
+
{
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: { "Content-Type": "application/json" },
|
|
157
|
+
body: JSON.stringify({ platform, participants, name: flags.name })
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
if (!res.ok) {
|
|
161
|
+
const body = await res.json().catch(() => ({}));
|
|
162
|
+
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
173
163
|
process.exit(1);
|
|
174
164
|
}
|
|
165
|
+
const data = await res.json();
|
|
166
|
+
console.log(data.slug);
|
|
175
167
|
}
|
|
176
168
|
async function typingChannel(args) {
|
|
177
169
|
const { positional, flags } = parseArgs(args, {
|
|
@@ -184,9 +176,10 @@ async function typingChannel(args) {
|
|
|
184
176
|
}
|
|
185
177
|
const agentName = resolveAgentName(flags);
|
|
186
178
|
try {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
);
|
|
179
|
+
const client = getClient();
|
|
180
|
+
const url = client.api.agents[":name"].typing.$url({ param: { name: agentName } });
|
|
181
|
+
url.searchParams.set("channel", uri);
|
|
182
|
+
const res = await daemonFetch(urlOf(url));
|
|
190
183
|
if (!res.ok) {
|
|
191
184
|
const body = await res.json().catch(() => ({}));
|
|
192
185
|
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
@@ -209,14 +202,6 @@ function parseUri(uri) {
|
|
|
209
202
|
}
|
|
210
203
|
return { platform: uri.slice(0, colonIdx), channelId: uri.slice(colonIdx + 1) };
|
|
211
204
|
}
|
|
212
|
-
function requireDriver(platform) {
|
|
213
|
-
const driver = getChannelDriver(platform);
|
|
214
|
-
if (!driver) {
|
|
215
|
-
console.error(`No channel driver for platform: ${platform}`);
|
|
216
|
-
process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
return driver;
|
|
219
|
-
}
|
|
220
205
|
export {
|
|
221
206
|
run
|
|
222
207
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/api-client.ts
|
|
4
|
+
import { hc } from "hono/client";
|
|
5
|
+
var _client;
|
|
6
|
+
function getClient() {
|
|
7
|
+
_client ??= hc("http://localhost");
|
|
8
|
+
return _client;
|
|
9
|
+
}
|
|
10
|
+
function urlOf(url) {
|
|
11
|
+
return url.pathname + url.search;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
getClient,
|
|
16
|
+
urlOf
|
|
17
|
+
};
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
daemonFetch
|
|
4
|
+
} from "./chunk-STOEJOJO.js";
|
|
2
5
|
import {
|
|
3
6
|
voluteHome
|
|
4
7
|
} from "./chunk-DP2DX4WV.js";
|
|
8
|
+
import {
|
|
9
|
+
getClient,
|
|
10
|
+
urlOf
|
|
11
|
+
} from "./chunk-4RQBJWQX.js";
|
|
5
12
|
|
|
6
13
|
// src/commands/down.ts
|
|
7
14
|
import { execFileSync } from "child_process";
|
|
@@ -112,8 +119,39 @@ async function run(_args) {
|
|
|
112
119
|
const result = await stopDaemon();
|
|
113
120
|
if (result.stopped) return;
|
|
114
121
|
if (result.reason === "systemd") {
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
const client = getClient();
|
|
123
|
+
await daemonFetch(urlOf(client.api.system.stop.$url()), { method: "POST" });
|
|
124
|
+
const home = voluteHome();
|
|
125
|
+
const configPath = resolve(home, "daemon.json");
|
|
126
|
+
let hostname = "localhost";
|
|
127
|
+
let port = 4200;
|
|
128
|
+
if (existsSync(configPath)) {
|
|
129
|
+
try {
|
|
130
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
131
|
+
hostname = config.hostname || "localhost";
|
|
132
|
+
port = config.port ?? 4200;
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (hostname === "0.0.0.0") hostname = "127.0.0.1";
|
|
137
|
+
if (hostname === "::") hostname = "[::1]";
|
|
138
|
+
const url = new URL("http://localhost");
|
|
139
|
+
url.hostname = hostname;
|
|
140
|
+
url.port = String(port);
|
|
141
|
+
const healthUrl = `${url.origin}/api/health`;
|
|
142
|
+
const maxWait = 1e4;
|
|
143
|
+
const start = Date.now();
|
|
144
|
+
while (Date.now() - start < maxWait) {
|
|
145
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
146
|
+
try {
|
|
147
|
+
await fetch(healthUrl);
|
|
148
|
+
} catch {
|
|
149
|
+
console.log("Daemon stopped.");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
console.error("Daemon may not have stopped. Check with: volute service status");
|
|
154
|
+
process.exit(1);
|
|
117
155
|
} else if (result.reason === "orphan") {
|
|
118
156
|
console.error(`Daemon appears to be running on port ${result.port} but PID file is missing.`);
|
|
119
157
|
console.error(`Kill the process manually: lsof -ti :${result.port} | xargs kill`);
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
agentEnvPath,
|
|
4
|
+
readEnv,
|
|
5
|
+
writeEnv
|
|
6
|
+
} from "./chunk-QF22MYDJ.js";
|
|
7
|
+
import {
|
|
8
|
+
parseArgs
|
|
9
|
+
} from "./chunk-D424ZQGI.js";
|
|
10
|
+
|
|
11
|
+
// src/commands/import.ts
|
|
12
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
13
|
+
import { homedir } from "os";
|
|
14
|
+
import { basename, resolve as resolve2 } from "path";
|
|
15
|
+
|
|
16
|
+
// src/lib/volute-config.ts
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
18
|
+
import { dirname, resolve } from "path";
|
|
19
|
+
function readJson(path) {
|
|
20
|
+
if (!existsSync(path)) return null;
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function readVoluteConfig(agentDir) {
|
|
28
|
+
const path = resolve(agentDir, "home/.config/volute.json");
|
|
29
|
+
return readJson(path);
|
|
30
|
+
}
|
|
31
|
+
function writeVoluteConfig(agentDir, config) {
|
|
32
|
+
const path = resolve(agentDir, "home/.config/volute.json");
|
|
33
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
34
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/commands/import.ts
|
|
39
|
+
async function run(args) {
|
|
40
|
+
const { positional, flags } = parseArgs(args, {
|
|
41
|
+
name: { type: "string" },
|
|
42
|
+
session: { type: "string" },
|
|
43
|
+
template: { type: "string" }
|
|
44
|
+
});
|
|
45
|
+
const wsDir = resolveWorkspace(positional[0]);
|
|
46
|
+
const { daemonFetch } = await import("./daemon-client-P44NU3KU.js");
|
|
47
|
+
const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
|
|
48
|
+
const client = getClient();
|
|
49
|
+
const res = await daemonFetch(urlOf(client.api.agents.import.$url()), {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: { "Content-Type": "application/json" },
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
workspacePath: wsDir,
|
|
54
|
+
name: flags.name,
|
|
55
|
+
template: flags.template,
|
|
56
|
+
sessionPath: flags.session
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
const data = await res.json();
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
console.error(data.error ?? "Failed to import agent");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
console.log(`
|
|
65
|
+
${data.message ?? `Imported agent: ${data.name} (port ${data.port})`}`);
|
|
66
|
+
console.log(`
|
|
67
|
+
volute agent start ${data.name}`);
|
|
68
|
+
}
|
|
69
|
+
function resolveWorkspace(explicitPath) {
|
|
70
|
+
if (explicitPath) {
|
|
71
|
+
const wsDir = resolve2(explicitPath);
|
|
72
|
+
if (!existsSync2(resolve2(wsDir, "SOUL.md")) || !existsSync2(resolve2(wsDir, "IDENTITY.md"))) {
|
|
73
|
+
console.error("Not a valid OpenClaw workspace: missing SOUL.md or IDENTITY.md");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
return wsDir;
|
|
77
|
+
}
|
|
78
|
+
const cwd = process.cwd();
|
|
79
|
+
if (existsSync2(resolve2(cwd, "SOUL.md")) && existsSync2(resolve2(cwd, "IDENTITY.md"))) {
|
|
80
|
+
console.log(`Using workspace: ${cwd}`);
|
|
81
|
+
return cwd;
|
|
82
|
+
}
|
|
83
|
+
const openclawWs = resolve2(homedir(), ".openclaw/workspace");
|
|
84
|
+
if (existsSync2(resolve2(openclawWs, "SOUL.md")) && existsSync2(resolve2(openclawWs, "IDENTITY.md"))) {
|
|
85
|
+
console.log(`Using workspace: ${openclawWs}`);
|
|
86
|
+
return openclawWs;
|
|
87
|
+
}
|
|
88
|
+
console.error(
|
|
89
|
+
"Usage: volute agent import [<workspace-path>] [--name <name>] [--session <path>] [--template <name>]\n\nNo OpenClaw workspace found. Provide a path, run from a workspace, or ensure ~/.openclaw/workspace exists."
|
|
90
|
+
);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
function findOpenClawSession(workspaceDir) {
|
|
94
|
+
const agentsDir = resolve2(homedir(), ".openclaw/agents");
|
|
95
|
+
if (!existsSync2(agentsDir)) return void 0;
|
|
96
|
+
const matches = [];
|
|
97
|
+
try {
|
|
98
|
+
for (const agent of readdirSync(agentsDir)) {
|
|
99
|
+
const sessionsDir = resolve2(agentsDir, agent, "sessions");
|
|
100
|
+
if (!existsSync2(sessionsDir)) continue;
|
|
101
|
+
for (const file of readdirSync(sessionsDir)) {
|
|
102
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
103
|
+
const fullPath = resolve2(sessionsDir, file);
|
|
104
|
+
if (sessionMatchesWorkspace(fullPath, workspaceDir)) {
|
|
105
|
+
matches.push({ path: fullPath, mtime: statSync(fullPath).mtimeMs });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.warn("Warning: error scanning OpenClaw sessions:", err);
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
if (matches.length === 0) return void 0;
|
|
114
|
+
matches.sort((a, b) => b.mtime - a.mtime);
|
|
115
|
+
console.log(`Found session: ${matches[0].path}`);
|
|
116
|
+
return matches[0].path;
|
|
117
|
+
}
|
|
118
|
+
function sessionMatchesWorkspace(sessionPath, workspaceDir) {
|
|
119
|
+
try {
|
|
120
|
+
const fd = readFileSync2(sessionPath, "utf-8");
|
|
121
|
+
const firstLine = fd.slice(0, fd.indexOf("\n"));
|
|
122
|
+
const header = JSON.parse(firstLine);
|
|
123
|
+
return header.type === "session" && resolve2(header.cwd) === resolve2(workspaceDir);
|
|
124
|
+
} catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function importPiSession(sessionFile, agentDirPath) {
|
|
129
|
+
const homeDir = resolve2(agentDirPath, "home");
|
|
130
|
+
const piSessionDir = resolve2(agentDirPath, ".volute/pi-sessions/main");
|
|
131
|
+
mkdirSync2(piSessionDir, { recursive: true });
|
|
132
|
+
const content = readFileSync2(sessionFile, "utf-8");
|
|
133
|
+
const lines = content.trim().split("\n");
|
|
134
|
+
try {
|
|
135
|
+
const header = JSON.parse(lines[0]);
|
|
136
|
+
if (header.type === "session") {
|
|
137
|
+
header.cwd = homeDir;
|
|
138
|
+
lines[0] = JSON.stringify(header);
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
const filename = basename(sessionFile);
|
|
143
|
+
const destPath = resolve2(piSessionDir, filename);
|
|
144
|
+
writeFileSync2(destPath, `${lines.join("\n")}
|
|
145
|
+
`);
|
|
146
|
+
console.log(`Imported session (${lines.length} entries)`);
|
|
147
|
+
}
|
|
148
|
+
function importOpenClawConnectors(agentName, agentDirPath) {
|
|
149
|
+
const configPath = resolve2(homedir(), ".openclaw/openclaw.json");
|
|
150
|
+
if (!existsSync2(configPath)) return;
|
|
151
|
+
let config;
|
|
152
|
+
try {
|
|
153
|
+
config = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.warn("Warning: failed to parse openclaw.json:", err);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const discord = config.channels?.discord;
|
|
159
|
+
if (!discord?.enabled || !discord.token) return;
|
|
160
|
+
const envPath = agentEnvPath(agentName);
|
|
161
|
+
const env = readEnv(envPath);
|
|
162
|
+
env.DISCORD_TOKEN = discord.token;
|
|
163
|
+
writeEnv(envPath, env);
|
|
164
|
+
const channelNames = /* @__PURE__ */ new Set();
|
|
165
|
+
if (discord.guilds) {
|
|
166
|
+
for (const guild of Object.values(discord.guilds)) {
|
|
167
|
+
if (!guild.channels) continue;
|
|
168
|
+
for (const [name, ch] of Object.entries(guild.channels)) {
|
|
169
|
+
if (ch.allow) channelNames.add(name);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const voluteConfig = readVoluteConfig(agentDirPath) ?? {};
|
|
174
|
+
const connectors = new Set(voluteConfig.connectors ?? []);
|
|
175
|
+
connectors.add("discord");
|
|
176
|
+
voluteConfig.connectors = [...connectors];
|
|
177
|
+
if (channelNames.size > 0) {
|
|
178
|
+
voluteConfig.discord = { channels: [...channelNames] };
|
|
179
|
+
}
|
|
180
|
+
writeVoluteConfig(agentDirPath, voluteConfig);
|
|
181
|
+
console.log("Imported Discord connector config");
|
|
182
|
+
if (channelNames.size > 0) {
|
|
183
|
+
console.log(`Imported followed channels: ${[...channelNames].join(", ")}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function parseNameFromIdentity(identity) {
|
|
187
|
+
const match = identity.match(/\*\*Name:\*\*\s*(.+)/);
|
|
188
|
+
if (match) {
|
|
189
|
+
const raw = match[1].trim();
|
|
190
|
+
if (!raw || raw.startsWith("*") || raw.startsWith("(")) return void 0;
|
|
191
|
+
return raw.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9.-]/g, "");
|
|
192
|
+
}
|
|
193
|
+
return void 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export {
|
|
197
|
+
readVoluteConfig,
|
|
198
|
+
writeVoluteConfig,
|
|
199
|
+
run,
|
|
200
|
+
findOpenClawSession,
|
|
201
|
+
sessionMatchesWorkspace,
|
|
202
|
+
importPiSession,
|
|
203
|
+
importOpenClawConnectors,
|
|
204
|
+
parseNameFromIdentity
|
|
205
|
+
};
|
|
@@ -9,19 +9,33 @@ import { resolve } from "path";
|
|
|
9
9
|
function readDaemonConfig() {
|
|
10
10
|
const configPath = resolve(voluteHome(), "daemon.json");
|
|
11
11
|
if (!existsSync(configPath)) {
|
|
12
|
-
|
|
12
|
+
if (existsSync("/etc/systemd/system/volute.service") && !process.env.VOLUTE_HOME) {
|
|
13
|
+
console.error("Volute is running as a system service but VOLUTE_HOME is not set.");
|
|
14
|
+
console.error("Re-run setup to update the CLI wrapper: sudo volute setup");
|
|
15
|
+
console.error("Then start a new shell or run: source /etc/profile.d/volute.sh");
|
|
16
|
+
} else {
|
|
17
|
+
console.error("Volute is not running. Start with: volute up");
|
|
18
|
+
}
|
|
13
19
|
process.exit(1);
|
|
14
20
|
}
|
|
15
21
|
try {
|
|
16
22
|
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
17
|
-
} catch {
|
|
18
|
-
|
|
23
|
+
} catch (err) {
|
|
24
|
+
const code = err.code;
|
|
25
|
+
if (code === "EACCES") {
|
|
26
|
+
console.error(`Permission denied reading ${configPath}. Try: sudo volute ...`);
|
|
27
|
+
} else {
|
|
28
|
+
console.error("Volute is not running. Start with: volute up");
|
|
29
|
+
}
|
|
19
30
|
process.exit(1);
|
|
20
31
|
}
|
|
21
32
|
}
|
|
22
33
|
function buildUrl(config) {
|
|
23
34
|
const url = new URL("http://localhost");
|
|
24
|
-
|
|
35
|
+
let hostname = config.hostname || "localhost";
|
|
36
|
+
if (hostname === "0.0.0.0") hostname = "127.0.0.1";
|
|
37
|
+
if (hostname === "::") hostname = "[::1]";
|
|
38
|
+
url.hostname = hostname;
|
|
25
39
|
url.port = String(config.port);
|
|
26
40
|
return url.origin;
|
|
27
41
|
}
|
package/dist/cli.js
CHANGED
|
@@ -9,49 +9,49 @@ 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-ERGXEDAF.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-ECRX44DB.js").then((m) => m.run(args));
|
|
19
19
|
break;
|
|
20
20
|
case "send":
|
|
21
|
-
await import("./send-
|
|
21
|
+
await import("./send-X6OQGSD6.js").then((m) => m.run(args));
|
|
22
22
|
break;
|
|
23
23
|
case "history":
|
|
24
|
-
await import("./history-
|
|
24
|
+
await import("./history-NI5QP27M.js").then((m) => m.run(args));
|
|
25
25
|
break;
|
|
26
26
|
case "variant":
|
|
27
|
-
await import("./variant-
|
|
27
|
+
await import("./variant-7IZF6OWO.js").then((m) => m.run(args));
|
|
28
28
|
break;
|
|
29
29
|
case "connector":
|
|
30
|
-
await import("./connector-
|
|
30
|
+
await import("./connector-L2HBLZBW.js").then((m) => m.run(args));
|
|
31
31
|
break;
|
|
32
32
|
case "channel":
|
|
33
|
-
await import("./channel-
|
|
33
|
+
await import("./channel-2WHBRDTD.js").then((m) => m.run(args));
|
|
34
34
|
break;
|
|
35
35
|
case "schedule":
|
|
36
|
-
await import("./schedule-
|
|
36
|
+
await import("./schedule-E4MFGYSA.js").then((m) => m.run(args));
|
|
37
37
|
break;
|
|
38
38
|
case "env":
|
|
39
|
-
await import("./env-
|
|
39
|
+
await import("./env-CGORIKVF.js").then((m) => m.run(args));
|
|
40
40
|
break;
|
|
41
41
|
case "up":
|
|
42
|
-
await import("./up-
|
|
42
|
+
await import("./up-MNNPCMFF.js").then((m) => m.run(args));
|
|
43
43
|
break;
|
|
44
44
|
case "down":
|
|
45
|
-
await import("./down-
|
|
45
|
+
await import("./down-4LIQG3CE.js").then((m) => m.run(args));
|
|
46
46
|
break;
|
|
47
47
|
case "restart":
|
|
48
|
-
await import("./daemon-restart-
|
|
48
|
+
await import("./daemon-restart-QCLR6ZZV.js").then((m) => m.run(args));
|
|
49
49
|
break;
|
|
50
50
|
case "setup":
|
|
51
|
-
await import("./setup-
|
|
51
|
+
await import("./setup-ABMZK6LS.js").then((m) => m.run(args));
|
|
52
52
|
break;
|
|
53
53
|
case "service":
|
|
54
|
-
await import("./service-
|
|
54
|
+
await import("./service-OW35VZ5G.js").then((m) => m.run(args));
|
|
55
55
|
break;
|
|
56
56
|
case "update":
|
|
57
57
|
await import("./update-3TGXUTO2.js").then((m) => m.run(args));
|