volute 0.1.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/LICENSE +21 -0
- package/README.md +227 -0
- package/dist/channel-Q642YUZE.js +90 -0
- package/dist/chunk-5YW4B7CG.js +181 -0
- package/dist/chunk-A5ZJEMHT.js +40 -0
- package/dist/chunk-D424ZQGI.js +31 -0
- package/dist/chunk-GSPKUPKU.js +120 -0
- package/dist/chunk-H5XQARAP.js +48 -0
- package/dist/chunk-KSMIWOCN.js +84 -0
- package/dist/chunk-N4QN44LC.js +74 -0
- package/dist/chunk-XZN4WPNC.js +34 -0
- package/dist/cli.js +95 -0
- package/dist/connect-LW6G23AV.js +48 -0
- package/dist/connectors/discord.js +213 -0
- package/dist/create-3K6O2SDC.js +62 -0
- package/dist/daemon-client-ZTHW7ROS.js +10 -0
- package/dist/daemon.js +1731 -0
- package/dist/delete-JNGY7ZFH.js +54 -0
- package/dist/disconnect-ACVTKTRE.js +30 -0
- package/dist/down-FYCUYC5H.js +71 -0
- package/dist/env-7SLRN3MG.js +159 -0
- package/dist/fork-BB3DZ426.js +112 -0
- package/dist/import-W2AMTEV5.js +410 -0
- package/dist/logs-BUHRIQ2L.js +35 -0
- package/dist/merge-446QTE7Q.js +219 -0
- package/dist/schedule-KKSOVUDF.js +113 -0
- package/dist/send-WQSVSRDD.js +50 -0
- package/dist/start-LKMWS6ZE.js +29 -0
- package/dist/status-CIEKUI3V.js +50 -0
- package/dist/stop-YTOAGYE4.js +29 -0
- package/dist/up-AJJ4GCXY.js +111 -0
- package/dist/upgrade-JACA6YMO.js +211 -0
- package/dist/variants-HPY4DEWU.js +60 -0
- package/dist/web-assets/assets/index-DNNPoxMn.js +158 -0
- package/dist/web-assets/index.html +15 -0
- package/package.json +76 -0
- package/templates/_base/.init/MEMORY.md +2 -0
- package/templates/_base/.init/SOUL.md +2 -0
- package/templates/_base/.init/memory/.gitkeep +0 -0
- package/templates/_base/_skills/memory/SKILL.md +30 -0
- package/templates/_base/_skills/volute-agent/SKILL.md +53 -0
- package/templates/_base/biome.json.tmpl +21 -0
- package/templates/_base/home/VOLUTE.md +19 -0
- package/templates/_base/src/lib/auto-commit.ts +46 -0
- package/templates/_base/src/lib/logger.ts +47 -0
- package/templates/_base/src/lib/types.ts +24 -0
- package/templates/_base/src/lib/volute-server.ts +98 -0
- package/templates/_base/tsconfig.json +13 -0
- package/templates/_base/volute.json.tmpl +3 -0
- package/templates/agent-sdk/.init/CLAUDE.md +36 -0
- package/templates/agent-sdk/package.json.tmpl +20 -0
- package/templates/agent-sdk/src/lib/agent.ts +199 -0
- package/templates/agent-sdk/src/lib/hooks/auto-commit.ts +14 -0
- package/templates/agent-sdk/src/lib/hooks/identity-reload.ts +26 -0
- package/templates/agent-sdk/src/lib/hooks/pre-compact.ts +20 -0
- package/templates/agent-sdk/src/lib/message-channel.ts +37 -0
- package/templates/agent-sdk/src/server.ts +158 -0
- package/templates/agent-sdk/volute-template.json +9 -0
- package/templates/pi/.init/AGENTS.md +26 -0
- package/templates/pi/package.json.tmpl +20 -0
- package/templates/pi/src/lib/agent.ts +205 -0
- package/templates/pi/src/server.ts +121 -0
- package/templates/pi/volute-template.json +9 -0
- package/templates/pi/volute.json.tmpl +3 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/template.ts
|
|
4
|
+
import {
|
|
5
|
+
cpSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
rmSync,
|
|
12
|
+
statSync,
|
|
13
|
+
writeFileSync
|
|
14
|
+
} from "fs";
|
|
15
|
+
import { tmpdir } from "os";
|
|
16
|
+
import { dirname, join, relative, resolve } from "path";
|
|
17
|
+
function findTemplatesRoot() {
|
|
18
|
+
let dir = dirname(new URL(import.meta.url).pathname);
|
|
19
|
+
for (let i = 0; i < 5; i++) {
|
|
20
|
+
const candidate = resolve(dir, "templates");
|
|
21
|
+
if (existsSync(resolve(candidate, "_base"))) return candidate;
|
|
22
|
+
dir = dirname(dir);
|
|
23
|
+
}
|
|
24
|
+
console.error(
|
|
25
|
+
"Templates directory not found. Searched up from:",
|
|
26
|
+
dirname(new URL(import.meta.url).pathname)
|
|
27
|
+
);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
function composeTemplate(templatesRoot, templateName) {
|
|
31
|
+
const baseDir = resolve(templatesRoot, "_base");
|
|
32
|
+
const templateDir = resolve(templatesRoot, templateName);
|
|
33
|
+
if (!existsSync(baseDir)) {
|
|
34
|
+
console.error("Base template not found:", baseDir);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
if (!existsSync(templateDir)) {
|
|
38
|
+
console.error(`Template not found: ${templateName}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const composedDir = resolve(tmpdir(), `volute-template-${Date.now()}`);
|
|
42
|
+
mkdirSync(composedDir, { recursive: true });
|
|
43
|
+
cpSync(baseDir, composedDir, { recursive: true });
|
|
44
|
+
for (const file of listFiles(templateDir)) {
|
|
45
|
+
const src = resolve(templateDir, file);
|
|
46
|
+
const dest = resolve(composedDir, file);
|
|
47
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
48
|
+
cpSync(src, dest);
|
|
49
|
+
}
|
|
50
|
+
const manifestPath = resolve(composedDir, "volute-template.json");
|
|
51
|
+
if (!existsSync(manifestPath)) {
|
|
52
|
+
rmSync(composedDir, { recursive: true, force: true });
|
|
53
|
+
console.error(`Template manifest not found: ${templateName}/volute-template.json`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
57
|
+
const skillsSrc = resolve(composedDir, "_skills");
|
|
58
|
+
if (existsSync(skillsSrc)) {
|
|
59
|
+
const skillsDest = resolve(composedDir, manifest.skillsDir);
|
|
60
|
+
mkdirSync(skillsDest, { recursive: true });
|
|
61
|
+
cpSync(skillsSrc, skillsDest, { recursive: true });
|
|
62
|
+
rmSync(skillsSrc, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
rmSync(manifestPath);
|
|
65
|
+
return { composedDir, manifest };
|
|
66
|
+
}
|
|
67
|
+
function copyTemplateToDir(composedDir, destDir, agentName, manifest) {
|
|
68
|
+
cpSync(composedDir, destDir, { recursive: true });
|
|
69
|
+
for (const [from, to] of Object.entries(manifest.rename)) {
|
|
70
|
+
const fromPath = resolve(destDir, from);
|
|
71
|
+
if (existsSync(fromPath)) {
|
|
72
|
+
renameSync(fromPath, resolve(destDir, to));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const file of manifest.substitute) {
|
|
76
|
+
const path = resolve(destDir, file);
|
|
77
|
+
if (existsSync(path)) {
|
|
78
|
+
const content = readFileSync(path, "utf-8");
|
|
79
|
+
writeFileSync(path, content.replaceAll("{{name}}", agentName));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function applyInitFiles(destDir) {
|
|
84
|
+
const initDir = resolve(destDir, ".init");
|
|
85
|
+
if (!existsSync(initDir)) return;
|
|
86
|
+
const homeDir = resolve(destDir, "home");
|
|
87
|
+
for (const file of listFiles(initDir)) {
|
|
88
|
+
const src = resolve(initDir, file);
|
|
89
|
+
const dest = resolve(homeDir, file);
|
|
90
|
+
const parent = dirname(dest);
|
|
91
|
+
if (!existsSync(parent)) {
|
|
92
|
+
mkdirSync(parent, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
cpSync(src, dest);
|
|
95
|
+
}
|
|
96
|
+
rmSync(initDir, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
function listFiles(dir) {
|
|
99
|
+
const results = [];
|
|
100
|
+
function walk(current) {
|
|
101
|
+
for (const entry of readdirSync(current)) {
|
|
102
|
+
const full = join(current, entry);
|
|
103
|
+
if (statSync(full).isDirectory()) {
|
|
104
|
+
if (entry === ".git") continue;
|
|
105
|
+
walk(full);
|
|
106
|
+
} else {
|
|
107
|
+
results.push(relative(dir, full));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
walk(dir);
|
|
112
|
+
return results;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
findTemplatesRoot,
|
|
117
|
+
composeTemplate,
|
|
118
|
+
copyTemplateToDir,
|
|
119
|
+
applyInitFiles
|
|
120
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
VOLUTE_HOME
|
|
4
|
+
} from "./chunk-5YW4B7CG.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/daemon-client.ts
|
|
7
|
+
import { existsSync, readFileSync } from "fs";
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
function readDaemonConfig() {
|
|
10
|
+
const configPath = resolve(VOLUTE_HOME, "daemon.json");
|
|
11
|
+
if (!existsSync(configPath)) {
|
|
12
|
+
console.error("Volute is not running. Start with: volute up");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
17
|
+
} catch {
|
|
18
|
+
console.error("Volute is not running. Start with: volute up");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function getDaemonUrl() {
|
|
23
|
+
const config = readDaemonConfig();
|
|
24
|
+
return `http://localhost:${config.port}`;
|
|
25
|
+
}
|
|
26
|
+
async function daemonFetch(path, options) {
|
|
27
|
+
const config = readDaemonConfig();
|
|
28
|
+
const url = `http://localhost:${config.port}`;
|
|
29
|
+
const headers = new Headers(options?.headers);
|
|
30
|
+
if (config.token) {
|
|
31
|
+
headers.set("Authorization", `Bearer ${config.token}`);
|
|
32
|
+
}
|
|
33
|
+
headers.set("Origin", url);
|
|
34
|
+
try {
|
|
35
|
+
return await fetch(`${url}${path}`, { ...options, headers });
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (err instanceof TypeError && err.cause?.code === "ECONNREFUSED") {
|
|
38
|
+
console.error("Volute is not running. Start with: volute up");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
getDaemonUrl,
|
|
47
|
+
daemonFetch
|
|
48
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/log-buffer.ts
|
|
4
|
+
var LogBuffer = class {
|
|
5
|
+
entries = [];
|
|
6
|
+
maxSize = 1e3;
|
|
7
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
8
|
+
append(entry) {
|
|
9
|
+
this.entries.push(entry);
|
|
10
|
+
if (this.entries.length > this.maxSize) {
|
|
11
|
+
this.entries.shift();
|
|
12
|
+
}
|
|
13
|
+
for (const sub of this.subscribers) {
|
|
14
|
+
sub(entry);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
getEntries() {
|
|
18
|
+
return [...this.entries];
|
|
19
|
+
}
|
|
20
|
+
subscribe(fn) {
|
|
21
|
+
this.subscribers.add(fn);
|
|
22
|
+
return () => this.subscribers.delete(fn);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var logBuffer = new LogBuffer();
|
|
26
|
+
|
|
27
|
+
// src/lib/logger.ts
|
|
28
|
+
function write(level, msg, data) {
|
|
29
|
+
const entry = {
|
|
30
|
+
level,
|
|
31
|
+
msg,
|
|
32
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33
|
+
...data ? { data } : {}
|
|
34
|
+
};
|
|
35
|
+
const line = JSON.stringify(entry);
|
|
36
|
+
process.stderr.write(`${line}
|
|
37
|
+
`);
|
|
38
|
+
logBuffer.append(entry);
|
|
39
|
+
}
|
|
40
|
+
var log = {
|
|
41
|
+
info: (msg, data) => write("info", msg, data),
|
|
42
|
+
warn: (msg, data) => write("warn", msg, data),
|
|
43
|
+
error: (msg, data) => write("error", msg, data)
|
|
44
|
+
};
|
|
45
|
+
var logger_default = log;
|
|
46
|
+
|
|
47
|
+
// src/lib/ndjson.ts
|
|
48
|
+
async function* readNdjson(body) {
|
|
49
|
+
const reader = body.getReader();
|
|
50
|
+
const decoder = new TextDecoder();
|
|
51
|
+
let buffer = "";
|
|
52
|
+
try {
|
|
53
|
+
while (true) {
|
|
54
|
+
const { done, value } = await reader.read();
|
|
55
|
+
if (done) break;
|
|
56
|
+
buffer += decoder.decode(value, { stream: true });
|
|
57
|
+
const lines = buffer.split("\n");
|
|
58
|
+
buffer = lines.pop() || "";
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
if (!line.trim()) continue;
|
|
61
|
+
try {
|
|
62
|
+
yield JSON.parse(line);
|
|
63
|
+
} catch {
|
|
64
|
+
logger_default.warn("ndjson: skipping invalid line", { line: line.slice(0, 100) });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (buffer.trim()) {
|
|
69
|
+
try {
|
|
70
|
+
yield JSON.parse(buffer);
|
|
71
|
+
} catch {
|
|
72
|
+
logger_default.warn("ndjson: skipping invalid line", { line: buffer.slice(0, 100) });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
reader.releaseLock();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export {
|
|
81
|
+
logBuffer,
|
|
82
|
+
logger_default,
|
|
83
|
+
readNdjson
|
|
84
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/spawn-server.ts
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
function tsxBin(cwd) {
|
|
7
|
+
return resolve(cwd, "node_modules", ".bin", "tsx");
|
|
8
|
+
}
|
|
9
|
+
function spawnServer(cwd, port, options) {
|
|
10
|
+
if (options?.detached) {
|
|
11
|
+
return spawnDetached(cwd, port);
|
|
12
|
+
}
|
|
13
|
+
return spawnAttached(cwd, port);
|
|
14
|
+
}
|
|
15
|
+
function spawnAttached(cwd, port) {
|
|
16
|
+
const child = spawn(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
|
|
17
|
+
cwd,
|
|
18
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
19
|
+
});
|
|
20
|
+
return new Promise((resolve2) => {
|
|
21
|
+
const timeout = setTimeout(() => resolve2(null), 3e4);
|
|
22
|
+
function checkOutput(data) {
|
|
23
|
+
const match = data.toString().match(/listening on :(\d+)/);
|
|
24
|
+
if (match) {
|
|
25
|
+
clearTimeout(timeout);
|
|
26
|
+
resolve2({ child, actualPort: parseInt(match[1], 10) });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
child.stdout?.on("data", checkOutput);
|
|
30
|
+
child.stderr?.on("data", checkOutput);
|
|
31
|
+
child.on("error", () => {
|
|
32
|
+
clearTimeout(timeout);
|
|
33
|
+
resolve2(null);
|
|
34
|
+
});
|
|
35
|
+
child.on("exit", () => {
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
resolve2(null);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function spawnDetached(cwd, port) {
|
|
42
|
+
const child = spawn(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
|
|
43
|
+
cwd,
|
|
44
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
45
|
+
detached: true
|
|
46
|
+
});
|
|
47
|
+
return new Promise((resolve2) => {
|
|
48
|
+
const timeout = setTimeout(() => resolve2(null), 3e4);
|
|
49
|
+
function checkOutput(data) {
|
|
50
|
+
const match = data.toString().match(/listening on :(\d+)/);
|
|
51
|
+
if (match) {
|
|
52
|
+
clearTimeout(timeout);
|
|
53
|
+
child.stdout?.destroy();
|
|
54
|
+
child.stderr?.destroy();
|
|
55
|
+
child.unref();
|
|
56
|
+
resolve2({ child, actualPort: parseInt(match[1], 10) });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
child.stdout?.on("data", checkOutput);
|
|
60
|
+
child.stderr?.on("data", checkOutput);
|
|
61
|
+
child.on("error", () => {
|
|
62
|
+
clearTimeout(timeout);
|
|
63
|
+
resolve2(null);
|
|
64
|
+
});
|
|
65
|
+
child.on("exit", () => {
|
|
66
|
+
clearTimeout(timeout);
|
|
67
|
+
resolve2(null);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
spawnServer
|
|
74
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/exec.ts
|
|
4
|
+
import { execFile as execFileCb, spawn } from "child_process";
|
|
5
|
+
function exec(cmd, args, options) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
execFileCb(cmd, args, { cwd: options?.cwd }, (err, stdout, stderr) => {
|
|
8
|
+
if (err) {
|
|
9
|
+
err.stderr = stderr;
|
|
10
|
+
reject(err);
|
|
11
|
+
} else {
|
|
12
|
+
resolve(stdout);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function execInherit(cmd, args, options) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const child = spawn(cmd, args, {
|
|
20
|
+
cwd: options?.cwd,
|
|
21
|
+
stdio: "inherit"
|
|
22
|
+
});
|
|
23
|
+
child.on("error", reject);
|
|
24
|
+
child.on("close", (code) => {
|
|
25
|
+
if (code === 0) resolve();
|
|
26
|
+
else reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
exec,
|
|
33
|
+
execInherit
|
|
34
|
+
};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
var command = process.argv[2];
|
|
5
|
+
var args = process.argv.slice(3);
|
|
6
|
+
switch (command) {
|
|
7
|
+
case "create":
|
|
8
|
+
await import("./create-3K6O2SDC.js").then((m) => m.run(args));
|
|
9
|
+
break;
|
|
10
|
+
case "start":
|
|
11
|
+
await import("./start-LKMWS6ZE.js").then((m) => m.run(args));
|
|
12
|
+
break;
|
|
13
|
+
case "stop":
|
|
14
|
+
await import("./stop-YTOAGYE4.js").then((m) => m.run(args));
|
|
15
|
+
break;
|
|
16
|
+
case "logs":
|
|
17
|
+
await import("./logs-BUHRIQ2L.js").then((m) => m.run(args));
|
|
18
|
+
break;
|
|
19
|
+
case "status":
|
|
20
|
+
await import("./status-CIEKUI3V.js").then((m) => m.run(args));
|
|
21
|
+
break;
|
|
22
|
+
case "fork":
|
|
23
|
+
await import("./fork-BB3DZ426.js").then((m) => m.run(args));
|
|
24
|
+
break;
|
|
25
|
+
case "variants":
|
|
26
|
+
await import("./variants-HPY4DEWU.js").then((m) => m.run(args));
|
|
27
|
+
break;
|
|
28
|
+
case "send":
|
|
29
|
+
await import("./send-WQSVSRDD.js").then((m) => m.run(args));
|
|
30
|
+
break;
|
|
31
|
+
case "merge":
|
|
32
|
+
await import("./merge-446QTE7Q.js").then((m) => m.run(args));
|
|
33
|
+
break;
|
|
34
|
+
case "import":
|
|
35
|
+
await import("./import-W2AMTEV5.js").then((m) => m.run(args));
|
|
36
|
+
break;
|
|
37
|
+
case "delete":
|
|
38
|
+
await import("./delete-JNGY7ZFH.js").then((m) => m.run(args));
|
|
39
|
+
break;
|
|
40
|
+
case "env":
|
|
41
|
+
await import("./env-7SLRN3MG.js").then((m) => m.run(args));
|
|
42
|
+
break;
|
|
43
|
+
case "connect":
|
|
44
|
+
await import("./connect-LW6G23AV.js").then((m) => m.run(args));
|
|
45
|
+
break;
|
|
46
|
+
case "disconnect":
|
|
47
|
+
await import("./disconnect-ACVTKTRE.js").then((m) => m.run(args));
|
|
48
|
+
break;
|
|
49
|
+
case "channel":
|
|
50
|
+
await import("./channel-Q642YUZE.js").then((m) => m.run(args));
|
|
51
|
+
break;
|
|
52
|
+
case "upgrade":
|
|
53
|
+
await import("./upgrade-JACA6YMO.js").then((m) => m.run(args));
|
|
54
|
+
break;
|
|
55
|
+
case "up":
|
|
56
|
+
await import("./up-AJJ4GCXY.js").then((m) => m.run(args));
|
|
57
|
+
break;
|
|
58
|
+
case "down":
|
|
59
|
+
await import("./down-FYCUYC5H.js").then((m) => m.run(args));
|
|
60
|
+
break;
|
|
61
|
+
case "schedule":
|
|
62
|
+
await import("./schedule-KKSOVUDF.js").then((m) => m.run(args));
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
console.log(`volute \u2014 create and manage AI agents
|
|
66
|
+
|
|
67
|
+
Commands:
|
|
68
|
+
volute create <name> Create a new agent
|
|
69
|
+
volute start <name> Start an agent (daemonized)
|
|
70
|
+
volute stop <name> Stop an agent
|
|
71
|
+
volute status [<name>] Check agent status (or list all)
|
|
72
|
+
volute logs <name> Tail agent logs
|
|
73
|
+
volute send <name> "<msg>" Send a message to an agent
|
|
74
|
+
volute fork <name> <variant> Create a variant (worktree + server)
|
|
75
|
+
volute variants <name> List variants for an agent
|
|
76
|
+
volute merge <name> <variant> Merge a variant back
|
|
77
|
+
volute import <path> Import an OpenClaw workspace
|
|
78
|
+
volute env <set|get|list|remove> Manage environment variables
|
|
79
|
+
volute connect <type> <name> Enable a connector for an agent
|
|
80
|
+
volute disconnect <type> <name> Disable a connector for an agent
|
|
81
|
+
volute channel read <uri> Read recent messages from a channel
|
|
82
|
+
volute channel send <uri> "<msg>" Send a message to a channel
|
|
83
|
+
volute schedule list <agent> List schedules for an agent
|
|
84
|
+
volute schedule add <agent> ... Add a cron schedule
|
|
85
|
+
volute schedule remove <agent> ... Remove a schedule
|
|
86
|
+
volute up [--port N] Start the daemon (default: 4200)
|
|
87
|
+
volute down Stop the daemon
|
|
88
|
+
volute upgrade <name> Upgrade agent to latest template
|
|
89
|
+
volute delete <name> [--force] Delete an agent (--force removes files)`);
|
|
90
|
+
if (command) {
|
|
91
|
+
console.error(`
|
|
92
|
+
Unknown command: ${command}`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadMergedEnv
|
|
4
|
+
} from "./chunk-A5ZJEMHT.js";
|
|
5
|
+
import {
|
|
6
|
+
daemonFetch
|
|
7
|
+
} from "./chunk-H5XQARAP.js";
|
|
8
|
+
import {
|
|
9
|
+
parseArgs
|
|
10
|
+
} from "./chunk-D424ZQGI.js";
|
|
11
|
+
import {
|
|
12
|
+
resolveAgent
|
|
13
|
+
} from "./chunk-5YW4B7CG.js";
|
|
14
|
+
|
|
15
|
+
// src/commands/connect.ts
|
|
16
|
+
async function run(args) {
|
|
17
|
+
const { positional } = parseArgs(args, {});
|
|
18
|
+
const type = positional[0];
|
|
19
|
+
const name = positional[1];
|
|
20
|
+
if (!type || !name) {
|
|
21
|
+
console.error("Usage: volute connect <type> <agent>");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const { dir } = resolveAgent(name);
|
|
25
|
+
if (type === "discord") {
|
|
26
|
+
const env = loadMergedEnv(dir);
|
|
27
|
+
if (!env.DISCORD_TOKEN) {
|
|
28
|
+
console.error("DISCORD_TOKEN not set. Run: volute env set DISCORD_TOKEN <token>");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
console.error(`Unknown connector type: ${type}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
const res = await daemonFetch(
|
|
36
|
+
`/api/agents/${encodeURIComponent(name)}/connectors/${encodeURIComponent(type)}`,
|
|
37
|
+
{ method: "POST" }
|
|
38
|
+
);
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
41
|
+
console.error(`Failed to start ${type} connector: ${body.error}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
console.log(`${type} connector for ${name} started.`);
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
run
|
|
48
|
+
};
|