volute 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/chunk-37X7ECMF.js +50 -0
- package/dist/{chunk-PDLAZJGC.js → chunk-3FD4ZZUL.js} +1 -0
- package/dist/chunk-RVKR2R7F.js +37 -0
- package/dist/chunk-RWKVSSLY.js +26 -0
- package/dist/cli.js +26 -7
- package/dist/{connector-JFAHYFQX.js → connector-3ELFMI2R.js} +5 -32
- package/dist/{daemon-restart-IZGEF4NA.js → daemon-restart-MS5FI44G.js} +1 -1
- package/dist/daemon.js +190 -16
- package/dist/{env-6LXDUZDA.js → env-6IDWGBUH.js} +4 -31
- package/dist/login-ORQDXLBM.js +56 -0
- package/dist/logout-XC5AUO5I.js +18 -0
- package/dist/{package-TDCEK6C2.js → package-3QGV3KX6.js} +1 -1
- package/dist/pages-6IV4VQTU.js +36 -0
- package/dist/publish-Q4RPSJLL.js +80 -0
- package/dist/register-LDE6LRXY.js +65 -0
- package/dist/{send-SV4K2TDE.js → send-KBBZNYG6.js} +1 -1
- package/dist/status-OKNA6AR3.js +47 -0
- package/dist/{up-C4MV6EXV.js → up-GZLWZAQE.js} +1 -1
- package/dist/web-assets/assets/{index-CeFLp8DZ.js → index-B1XIIGCh.js} +8 -8
- package/dist/web-assets/index.html +1 -1
- package/package.json +1 -1
- package/templates/_base/_skills/volute-mind/SKILL.md +28 -1
- /package/dist/{chunk-BEFIBW5B.js → chunk-LLBBVTEY.js} +0 -0
package/README.md
CHANGED
|
@@ -161,6 +161,36 @@ volute schedule list --agent atlas
|
|
|
161
161
|
volute schedule remove --agent atlas --id <schedule-id>
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
## Pages
|
|
165
|
+
|
|
166
|
+
Publish a mind's `home/pages/` directory to the web via [volute.systems](https://volute.systems).
|
|
167
|
+
|
|
168
|
+
### Setup
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
# Register a system name (one-time)
|
|
172
|
+
volute pages register --name my-system
|
|
173
|
+
|
|
174
|
+
# Or log in with an existing key
|
|
175
|
+
volute pages login --key vp_...
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Publishing
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
volute pages publish --mind atlas
|
|
182
|
+
# Published 3 file(s) to https://my-system.volute.systems/~atlas/
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The command uploads everything in the mind's `home/pages/` directory. Minds can publish their own pages since `VOLUTE_MIND` is set automatically.
|
|
186
|
+
|
|
187
|
+
### Status & Logout
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
volute pages status --mind atlas # show published URL, file count, last publish time
|
|
191
|
+
volute pages logout # remove stored credentials
|
|
192
|
+
```
|
|
193
|
+
|
|
164
194
|
## Environment variables
|
|
165
195
|
|
|
166
196
|
Manage secrets and config. Supports shared (all agents) and per-agent scoping.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
voluteHome
|
|
4
|
+
} from "./chunk-M77QBTEH.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/systems-config.ts
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
var DEFAULT_API_URL = "https://volute.systems";
|
|
10
|
+
function configPath() {
|
|
11
|
+
return resolve(voluteHome(), "systems.json");
|
|
12
|
+
}
|
|
13
|
+
function readSystemsConfig() {
|
|
14
|
+
const path = configPath();
|
|
15
|
+
if (!existsSync(path)) return null;
|
|
16
|
+
const raw = readFileSync(path, "utf-8");
|
|
17
|
+
let data;
|
|
18
|
+
try {
|
|
19
|
+
data = JSON.parse(raw);
|
|
20
|
+
} catch {
|
|
21
|
+
console.error(`Warning: ${path} contains invalid JSON. Run "volute logout" and re-login.`);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (!data.apiKey || !data.system) return null;
|
|
25
|
+
return {
|
|
26
|
+
apiKey: data.apiKey,
|
|
27
|
+
system: data.system,
|
|
28
|
+
apiUrl: data.apiUrl || DEFAULT_API_URL
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function writeSystemsConfig(config) {
|
|
32
|
+
mkdirSync(voluteHome(), { recursive: true });
|
|
33
|
+
writeFileSync(configPath(), `${JSON.stringify(config, null, 2)}
|
|
34
|
+
`, { mode: 384 });
|
|
35
|
+
}
|
|
36
|
+
function deleteSystemsConfig() {
|
|
37
|
+
try {
|
|
38
|
+
unlinkSync(configPath());
|
|
39
|
+
return true;
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (err.code === "ENOENT") return false;
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
readSystemsConfig,
|
|
48
|
+
writeSystemsConfig,
|
|
49
|
+
deleteSystemsConfig
|
|
50
|
+
};
|
|
@@ -548,6 +548,7 @@ var CHANNELS = {
|
|
|
548
548
|
showToolCalls: false,
|
|
549
549
|
driver: telegram_exports
|
|
550
550
|
},
|
|
551
|
+
mail: { name: "mail", displayName: "Email", showToolCalls: false },
|
|
551
552
|
system: { name: "system", displayName: "System", showToolCalls: false }
|
|
552
553
|
};
|
|
553
554
|
function getChannelDriver(platform) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/prompt.ts
|
|
4
|
+
function promptLine(prompt) {
|
|
5
|
+
process.stderr.write(prompt);
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
let value = "";
|
|
8
|
+
const onData = (buf) => {
|
|
9
|
+
for (const byte of buf) {
|
|
10
|
+
if (byte === 3) {
|
|
11
|
+
process.stderr.write("\n");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
if (byte === 13 || byte === 10) {
|
|
15
|
+
process.stderr.write("\n");
|
|
16
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
17
|
+
process.stdin.removeListener("data", onData);
|
|
18
|
+
process.stdin.pause();
|
|
19
|
+
resolve(value);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (byte === 127 || byte === 8) {
|
|
23
|
+
value = value.slice(0, -1);
|
|
24
|
+
} else {
|
|
25
|
+
value += String.fromCharCode(byte);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
30
|
+
process.stdin.resume();
|
|
31
|
+
process.stdin.on("data", onData);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
promptLine
|
|
37
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/systems-fetch.ts
|
|
4
|
+
async function systemsFetch(url, init) {
|
|
5
|
+
try {
|
|
6
|
+
return await fetch(url, init);
|
|
7
|
+
} catch (err) {
|
|
8
|
+
const cause = err.cause;
|
|
9
|
+
const code = cause?.code;
|
|
10
|
+
const host = new URL(url).host;
|
|
11
|
+
if (code === "ENOTFOUND") {
|
|
12
|
+
console.error(`Could not resolve ${host}. Check your internet connection.`);
|
|
13
|
+
} else if (code === "ECONNREFUSED") {
|
|
14
|
+
console.error(`Connection refused by ${host}. The service may be down.`);
|
|
15
|
+
} else {
|
|
16
|
+
console.error(
|
|
17
|
+
`Network error connecting to ${host}: ${cause?.message ?? err.message}`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
systemsFetch
|
|
26
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ 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-3QGV3KX6.js");
|
|
13
13
|
console.log(pkg.version);
|
|
14
14
|
process.exit(0);
|
|
15
15
|
}
|
|
@@ -18,7 +18,7 @@ switch (command) {
|
|
|
18
18
|
await import("./mind-OJN6RBZW.js").then((m) => m.run(args));
|
|
19
19
|
break;
|
|
20
20
|
case "send":
|
|
21
|
-
await import("./send-
|
|
21
|
+
await import("./send-KBBZNYG6.js").then((m) => m.run(args));
|
|
22
22
|
break;
|
|
23
23
|
case "history":
|
|
24
24
|
await import("./history-LKCJJMUV.js").then((m) => m.run(args));
|
|
@@ -27,7 +27,7 @@ switch (command) {
|
|
|
27
27
|
await import("./variant-X5QFG6KK.js").then((m) => m.run(args));
|
|
28
28
|
break;
|
|
29
29
|
case "connector":
|
|
30
|
-
await import("./connector-
|
|
30
|
+
await import("./connector-3ELFMI2R.js").then((m) => m.run(args));
|
|
31
31
|
break;
|
|
32
32
|
case "channel":
|
|
33
33
|
await import("./channel-SLURLIRV.js").then((m) => m.run(args));
|
|
@@ -36,16 +36,16 @@ switch (command) {
|
|
|
36
36
|
await import("./schedule-AGYLDMNS.js").then((m) => m.run(args));
|
|
37
37
|
break;
|
|
38
38
|
case "env":
|
|
39
|
-
await import("./env-
|
|
39
|
+
await import("./env-6IDWGBUH.js").then((m) => m.run(args));
|
|
40
40
|
break;
|
|
41
41
|
case "up":
|
|
42
|
-
await import("./up-
|
|
42
|
+
await import("./up-GZLWZAQE.js").then((m) => m.run(args));
|
|
43
43
|
break;
|
|
44
44
|
case "down":
|
|
45
45
|
await import("./down-A56B5JLK.js").then((m) => m.run(args));
|
|
46
46
|
break;
|
|
47
47
|
case "restart":
|
|
48
|
-
await import("./daemon-restart-
|
|
48
|
+
await import("./daemon-restart-MS5FI44G.js").then((m) => m.run(args));
|
|
49
49
|
break;
|
|
50
50
|
case "setup":
|
|
51
51
|
await import("./setup-DJKIZKGW.js").then((m) => m.run(args));
|
|
@@ -65,6 +65,18 @@ switch (command) {
|
|
|
65
65
|
case "sprout":
|
|
66
66
|
await import("./sprout-TJ3BHVOG.js").then((m) => m.run(args));
|
|
67
67
|
break;
|
|
68
|
+
case "pages":
|
|
69
|
+
await import("./pages-6IV4VQTU.js").then((m) => m.run(args));
|
|
70
|
+
break;
|
|
71
|
+
case "register":
|
|
72
|
+
await import("./register-LDE6LRXY.js").then((m) => m.run(args));
|
|
73
|
+
break;
|
|
74
|
+
case "login":
|
|
75
|
+
await import("./login-ORQDXLBM.js").then((m) => m.run(args));
|
|
76
|
+
break;
|
|
77
|
+
case "logout":
|
|
78
|
+
await import("./logout-XC5AUO5I.js").then((m) => m.run());
|
|
79
|
+
break;
|
|
68
80
|
case "--help":
|
|
69
81
|
case "-h":
|
|
70
82
|
case void 0:
|
|
@@ -114,6 +126,13 @@ Commands:
|
|
|
114
126
|
volute setup [--port N] [--host H] Install system service with user isolation
|
|
115
127
|
volute setup uninstall [--force] Remove system service + isolation
|
|
116
128
|
|
|
129
|
+
volute register [--name <name>] Register a system on volute.systems
|
|
130
|
+
volute login [--key <key>] Log in with an existing API key
|
|
131
|
+
volute logout Remove stored credentials
|
|
132
|
+
|
|
133
|
+
volute pages publish [--mind <name>] Publish mind's pages/ directory
|
|
134
|
+
volute pages status [--mind <name>] Show publish status
|
|
135
|
+
|
|
117
136
|
volute seed <name> Plant a seed mind (orientation mode)
|
|
118
137
|
volute sprout Complete orientation, become a full mind
|
|
119
138
|
|
|
@@ -124,7 +143,7 @@ Options:
|
|
|
124
143
|
--version, -v Show version number
|
|
125
144
|
--help, -h Show this help message
|
|
126
145
|
|
|
127
|
-
Mind-scoped commands (send, history, variant, connector, schedule, channel)
|
|
146
|
+
Mind-scoped commands (send, history, variant, connector, schedule, channel, pages)
|
|
128
147
|
use --mind <name> or VOLUTE_MIND env var to identify the mind.`);
|
|
129
148
|
break;
|
|
130
149
|
default:
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
promptLine
|
|
4
|
+
} from "./chunk-RVKR2R7F.js";
|
|
2
5
|
import {
|
|
3
6
|
resolveMindName
|
|
4
7
|
} from "./chunk-NAOW2CLO.js";
|
|
@@ -40,37 +43,6 @@ function printUsage() {
|
|
|
40
43
|
volute connector connect <type> [--mind <name>]
|
|
41
44
|
volute connector disconnect <type> [--mind <name>]`);
|
|
42
45
|
}
|
|
43
|
-
async function promptValue(key, description) {
|
|
44
|
-
process.stderr.write(`${description}
|
|
45
|
-
Enter value for ${key}: `);
|
|
46
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
47
|
-
return new Promise((resolve) => {
|
|
48
|
-
let value = "";
|
|
49
|
-
const onData = (buf) => {
|
|
50
|
-
for (const byte of buf) {
|
|
51
|
-
if (byte === 3) {
|
|
52
|
-
process.stderr.write("\n");
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
if (byte === 13 || byte === 10) {
|
|
56
|
-
process.stderr.write("\n");
|
|
57
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
58
|
-
process.stdin.removeListener("data", onData);
|
|
59
|
-
process.stdin.pause();
|
|
60
|
-
resolve(value);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (byte === 127 || byte === 8) {
|
|
64
|
-
value = value.slice(0, -1);
|
|
65
|
-
} else {
|
|
66
|
-
value += String.fromCharCode(byte);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
process.stdin.resume();
|
|
71
|
-
process.stdin.on("data", onData);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
46
|
async function connectConnector(args) {
|
|
75
47
|
const { positional, flags } = parseArgs(args, {
|
|
76
48
|
mind: { type: "string" }
|
|
@@ -104,7 +76,8 @@ Set them with: volute env set <KEY> --mind ${mindName}`);
|
|
|
104
76
|
console.error(`${connectorName} connector requires some environment variables.
|
|
105
77
|
`);
|
|
106
78
|
for (const v of missing) {
|
|
107
|
-
const value = await
|
|
79
|
+
const value = await promptLine(`${v.description}
|
|
80
|
+
Enter value for ${v.name}: `);
|
|
108
81
|
if (!value) {
|
|
109
82
|
console.error(`No value provided for ${v.name}. Aborting.`);
|
|
110
83
|
process.exit(1);
|
package/dist/daemon.js
CHANGED
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
findTemplatesRoot,
|
|
7
7
|
listFiles
|
|
8
8
|
} from "./chunk-PO5Q2AYN.js";
|
|
9
|
+
import {
|
|
10
|
+
readSystemsConfig
|
|
11
|
+
} from "./chunk-37X7ECMF.js";
|
|
9
12
|
import {
|
|
10
13
|
RotatingLog,
|
|
11
14
|
clearJsonMap,
|
|
@@ -32,7 +35,7 @@ import {
|
|
|
32
35
|
import {
|
|
33
36
|
CHANNELS,
|
|
34
37
|
getChannelDriver
|
|
35
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-3FD4ZZUL.js";
|
|
36
39
|
import {
|
|
37
40
|
exec,
|
|
38
41
|
gitExec,
|
|
@@ -431,6 +434,159 @@ function getConnectorManager() {
|
|
|
431
434
|
return instance;
|
|
432
435
|
}
|
|
433
436
|
|
|
437
|
+
// src/lib/mail-poller.ts
|
|
438
|
+
function formatEmailContent(email) {
|
|
439
|
+
if (email.body) {
|
|
440
|
+
return email.subject ? `Subject: ${email.subject}
|
|
441
|
+
|
|
442
|
+
${email.body}` : email.body;
|
|
443
|
+
}
|
|
444
|
+
if (email.html) {
|
|
445
|
+
return email.subject ? `Subject: ${email.subject}
|
|
446
|
+
|
|
447
|
+
[HTML email \u2014 plain text not available]` : "[HTML email \u2014 plain text not available]";
|
|
448
|
+
}
|
|
449
|
+
return email.subject ? `Subject: ${email.subject}` : "[Empty email]";
|
|
450
|
+
}
|
|
451
|
+
var MailPoller = class {
|
|
452
|
+
interval = null;
|
|
453
|
+
daemonPort = null;
|
|
454
|
+
daemonToken = null;
|
|
455
|
+
lastPoll = null;
|
|
456
|
+
running = false;
|
|
457
|
+
start(daemonPort, daemonToken) {
|
|
458
|
+
if (this.running) {
|
|
459
|
+
console.error("[mail] already running \u2014 ignoring duplicate start");
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const config = readSystemsConfig();
|
|
463
|
+
if (!config) {
|
|
464
|
+
console.error("[mail] no systems config \u2014 mail polling disabled");
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
this.daemonPort = daemonPort ?? null;
|
|
468
|
+
this.daemonToken = daemonToken ?? null;
|
|
469
|
+
this.lastPoll = (/* @__PURE__ */ new Date()).toISOString();
|
|
470
|
+
this.running = true;
|
|
471
|
+
this.interval = setInterval(() => this.poll(), 3e4);
|
|
472
|
+
console.error("[mail] polling started");
|
|
473
|
+
}
|
|
474
|
+
stop() {
|
|
475
|
+
if (this.interval) clearInterval(this.interval);
|
|
476
|
+
this.interval = null;
|
|
477
|
+
this.running = false;
|
|
478
|
+
}
|
|
479
|
+
isRunning() {
|
|
480
|
+
return this.running;
|
|
481
|
+
}
|
|
482
|
+
async poll() {
|
|
483
|
+
const config = readSystemsConfig();
|
|
484
|
+
if (!config) {
|
|
485
|
+
console.error("[mail] systems config removed \u2014 stopping mail polling");
|
|
486
|
+
this.stop();
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const since = this.lastPoll ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
490
|
+
const url = `${config.apiUrl}/api/mail/system/poll?since=${encodeURIComponent(since)}`;
|
|
491
|
+
try {
|
|
492
|
+
const res = await fetch(url, {
|
|
493
|
+
headers: { Authorization: `Bearer ${config.apiKey}` }
|
|
494
|
+
});
|
|
495
|
+
if (!res.ok) {
|
|
496
|
+
console.error(`[mail] poll failed: HTTP ${res.status}`);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const data = await res.json();
|
|
500
|
+
if (!Array.isArray(data.emails)) {
|
|
501
|
+
console.error("[mail] poll response missing emails array");
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
for (const email of data.emails) {
|
|
505
|
+
await this.deliver(email.mind, email);
|
|
506
|
+
}
|
|
507
|
+
if (data.emails.length > 0) {
|
|
508
|
+
this.lastPoll = data.emails[data.emails.length - 1].receivedAt;
|
|
509
|
+
} else {
|
|
510
|
+
this.lastPoll = (/* @__PURE__ */ new Date()).toISOString();
|
|
511
|
+
}
|
|
512
|
+
} catch (err) {
|
|
513
|
+
console.error("[mail] poll error:", err);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async deliver(mind, email) {
|
|
517
|
+
const entry = findMind(mind);
|
|
518
|
+
if (!entry || !entry.running) {
|
|
519
|
+
console.error(`[mail] skipping delivery to ${mind}: ${!entry ? "not found" : "not running"}`);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const channel = `mail:${email.from.address}`;
|
|
523
|
+
const sender = email.from.name || email.from.address;
|
|
524
|
+
const text2 = formatEmailContent(email);
|
|
525
|
+
const body = JSON.stringify({
|
|
526
|
+
content: [{ type: "text", text: text2 }],
|
|
527
|
+
channel,
|
|
528
|
+
sender,
|
|
529
|
+
platform: "Email",
|
|
530
|
+
isDM: true
|
|
531
|
+
});
|
|
532
|
+
if (!this.daemonPort || !this.daemonToken) {
|
|
533
|
+
console.error(`[mail] cannot deliver to ${mind}: daemon port/token not set`);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const daemonUrl = `http://${daemonLoopback()}:${this.daemonPort}`;
|
|
537
|
+
const controller = new AbortController();
|
|
538
|
+
const timeout = setTimeout(() => controller.abort(), 12e4);
|
|
539
|
+
try {
|
|
540
|
+
const res = await fetch(`${daemonUrl}/api/minds/${encodeURIComponent(mind)}/message`, {
|
|
541
|
+
method: "POST",
|
|
542
|
+
headers: {
|
|
543
|
+
"Content-Type": "application/json",
|
|
544
|
+
Authorization: `Bearer ${this.daemonToken}`,
|
|
545
|
+
Origin: daemonUrl
|
|
546
|
+
},
|
|
547
|
+
body,
|
|
548
|
+
signal: controller.signal
|
|
549
|
+
});
|
|
550
|
+
if (!res.ok) {
|
|
551
|
+
console.error(`[mail] deliver to ${mind} got HTTP ${res.status}`);
|
|
552
|
+
} else {
|
|
553
|
+
console.error(`[mail] delivered email from ${email.from.address} to ${mind}`);
|
|
554
|
+
}
|
|
555
|
+
await res.text().catch(() => {
|
|
556
|
+
});
|
|
557
|
+
} catch (err) {
|
|
558
|
+
console.error(`[mail] failed to deliver to ${mind}:`, err);
|
|
559
|
+
} finally {
|
|
560
|
+
clearTimeout(timeout);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
var instance2 = null;
|
|
565
|
+
function getMailPoller() {
|
|
566
|
+
if (!instance2) instance2 = new MailPoller();
|
|
567
|
+
return instance2;
|
|
568
|
+
}
|
|
569
|
+
async function ensureMailAddress(mindName) {
|
|
570
|
+
const config = readSystemsConfig();
|
|
571
|
+
if (!config) return;
|
|
572
|
+
try {
|
|
573
|
+
const res = await fetch(`${config.apiUrl}/api/mail/addresses/${encodeURIComponent(mindName)}`, {
|
|
574
|
+
method: "PUT",
|
|
575
|
+
headers: {
|
|
576
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
577
|
+
"Content-Type": "application/json"
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
if (!res.ok) {
|
|
581
|
+
console.error(`[mail] failed to ensure address for ${mindName}: HTTP ${res.status}`);
|
|
582
|
+
}
|
|
583
|
+
await res.text().catch(() => {
|
|
584
|
+
});
|
|
585
|
+
} catch (err) {
|
|
586
|
+
console.error(`[mail] failed to ensure address for ${mindName}:`, err);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
434
590
|
// src/lib/migrate-agents-to-minds.ts
|
|
435
591
|
import { execFileSync } from "child_process";
|
|
436
592
|
import { existsSync as existsSync3, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -718,10 +874,10 @@ var Scheduler = class {
|
|
|
718
874
|
}
|
|
719
875
|
}
|
|
720
876
|
};
|
|
721
|
-
var
|
|
877
|
+
var instance3 = null;
|
|
722
878
|
function getScheduler() {
|
|
723
|
-
if (!
|
|
724
|
-
return
|
|
879
|
+
if (!instance3) instance3 = new Scheduler();
|
|
880
|
+
return instance3;
|
|
725
881
|
}
|
|
726
882
|
|
|
727
883
|
// src/lib/token-budget.ts
|
|
@@ -882,10 +1038,10 @@ ${summary}`
|
|
|
882
1038
|
}
|
|
883
1039
|
}
|
|
884
1040
|
};
|
|
885
|
-
var
|
|
1041
|
+
var instance4 = null;
|
|
886
1042
|
function getTokenBudget() {
|
|
887
|
-
if (!
|
|
888
|
-
return
|
|
1043
|
+
if (!instance4) instance4 = new TokenBudget();
|
|
1044
|
+
return instance4;
|
|
889
1045
|
}
|
|
890
1046
|
|
|
891
1047
|
// src/web/middleware/auth.ts
|
|
@@ -1427,10 +1583,16 @@ var app3 = new Hono3().get("/:name/connectors", (c) => {
|
|
|
1427
1583
|
const configured = config.connectors ?? [];
|
|
1428
1584
|
const manager = getConnectorManager();
|
|
1429
1585
|
const runningStatus = manager.getConnectorStatus(name);
|
|
1430
|
-
const connectors = configured.map(
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1586
|
+
const connectors = configured.map(
|
|
1587
|
+
(type) => {
|
|
1588
|
+
const status = runningStatus.find((s) => s.type === type);
|
|
1589
|
+
return { type, running: status?.running ?? false };
|
|
1590
|
+
}
|
|
1591
|
+
);
|
|
1592
|
+
const systemsConfig = readSystemsConfig();
|
|
1593
|
+
if (systemsConfig && getMailPoller().isRunning()) {
|
|
1594
|
+
connectors.push({ type: "mail", running: true, auto: true });
|
|
1595
|
+
}
|
|
1434
1596
|
return c.json(connectors);
|
|
1435
1597
|
}).post("/:name/connectors/:type", requireAdmin, async (c) => {
|
|
1436
1598
|
const name = c.req.param("name");
|
|
@@ -2188,7 +2350,7 @@ var TypingMap = class {
|
|
|
2188
2350
|
dispose() {
|
|
2189
2351
|
clearInterval(this.sweepTimer);
|
|
2190
2352
|
this.channels.clear();
|
|
2191
|
-
if (
|
|
2353
|
+
if (instance5 === this) instance5 = void 0;
|
|
2192
2354
|
}
|
|
2193
2355
|
sweep() {
|
|
2194
2356
|
const now = Date.now();
|
|
@@ -2204,12 +2366,12 @@ var TypingMap = class {
|
|
|
2204
2366
|
}
|
|
2205
2367
|
}
|
|
2206
2368
|
};
|
|
2207
|
-
var
|
|
2369
|
+
var instance5;
|
|
2208
2370
|
function getTypingMap() {
|
|
2209
|
-
if (!
|
|
2210
|
-
|
|
2371
|
+
if (!instance5) {
|
|
2372
|
+
instance5 = new TypingMap();
|
|
2211
2373
|
}
|
|
2212
|
-
return
|
|
2374
|
+
return instance5;
|
|
2213
2375
|
}
|
|
2214
2376
|
|
|
2215
2377
|
// src/web/routes/minds.ts
|
|
@@ -2221,6 +2383,9 @@ async function startMindFull(name, baseName, variantName) {
|
|
|
2221
2383
|
const entry = findMind(baseName);
|
|
2222
2384
|
await getConnectorManager().startConnectors(baseName, dir, entry.port, getDaemonPort());
|
|
2223
2385
|
getScheduler().loadSchedules(baseName);
|
|
2386
|
+
ensureMailAddress(baseName).catch(
|
|
2387
|
+
(err) => console.error(`[mail] failed to ensure address for ${baseName}:`, err)
|
|
2388
|
+
);
|
|
2224
2389
|
const config = readVoluteConfig(dir);
|
|
2225
2390
|
if (config?.tokenBudget) {
|
|
2226
2391
|
getTokenBudget().setBudget(
|
|
@@ -3311,6 +3476,9 @@ var app10 = new Hono10().post("/restart", requireAdmin, (c) => {
|
|
|
3311
3476
|
});
|
|
3312
3477
|
});
|
|
3313
3478
|
});
|
|
3479
|
+
}).get("/info", (c) => {
|
|
3480
|
+
const config = readSystemsConfig();
|
|
3481
|
+
return c.json({ system: config?.system ?? null });
|
|
3314
3482
|
});
|
|
3315
3483
|
var system_default = app10;
|
|
3316
3484
|
|
|
@@ -4200,6 +4368,8 @@ async function startDaemon(opts) {
|
|
|
4200
4368
|
const connectors = initConnectorManager();
|
|
4201
4369
|
const scheduler = getScheduler();
|
|
4202
4370
|
scheduler.start(port, token);
|
|
4371
|
+
const mailPoller = getMailPoller();
|
|
4372
|
+
mailPoller.start(port, token);
|
|
4203
4373
|
const tokenBudget = getTokenBudget();
|
|
4204
4374
|
tokenBudget.start(port, token);
|
|
4205
4375
|
const registry = readRegistry();
|
|
@@ -4218,6 +4388,9 @@ async function startDaemon(opts) {
|
|
|
4218
4388
|
const dir = mindDir(entry.name);
|
|
4219
4389
|
await connectors.startConnectors(entry.name, dir, entry.port, port);
|
|
4220
4390
|
scheduler.loadSchedules(entry.name);
|
|
4391
|
+
ensureMailAddress(entry.name).catch(
|
|
4392
|
+
(err) => console.error(`[mail] failed to ensure address for ${entry.name}:`, err)
|
|
4393
|
+
);
|
|
4221
4394
|
const config = readVoluteConfig(dir);
|
|
4222
4395
|
if (config?.tokenBudget) {
|
|
4223
4396
|
tokenBudget.setBudget(
|
|
@@ -4266,6 +4439,7 @@ async function startDaemon(opts) {
|
|
|
4266
4439
|
console.error("[daemon] shutting down...");
|
|
4267
4440
|
scheduler.stop();
|
|
4268
4441
|
scheduler.saveState();
|
|
4442
|
+
mailPoller.stop();
|
|
4269
4443
|
tokenBudget.stop();
|
|
4270
4444
|
await connectors.stopAll();
|
|
4271
4445
|
await manager.stopAll();
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
promptLine
|
|
4
|
+
} from "./chunk-RVKR2R7F.js";
|
|
2
5
|
import {
|
|
3
6
|
parseArgs
|
|
4
7
|
} from "./chunk-D424ZQGI.js";
|
|
@@ -13,36 +16,6 @@ import {
|
|
|
13
16
|
import "./chunk-K3NQKI34.js";
|
|
14
17
|
|
|
15
18
|
// src/commands/env.ts
|
|
16
|
-
async function promptValue(key) {
|
|
17
|
-
process.stderr.write(`Enter value for ${key}: `);
|
|
18
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
19
|
-
return new Promise((resolve) => {
|
|
20
|
-
let value = "";
|
|
21
|
-
const onData = (buf) => {
|
|
22
|
-
for (const byte of buf) {
|
|
23
|
-
if (byte === 3) {
|
|
24
|
-
process.stderr.write("\n");
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
if (byte === 13 || byte === 10) {
|
|
28
|
-
process.stderr.write("\n");
|
|
29
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
30
|
-
process.stdin.removeListener("data", onData);
|
|
31
|
-
process.stdin.pause();
|
|
32
|
-
resolve(value);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
if (byte === 127 || byte === 8) {
|
|
36
|
-
value = value.slice(0, -1);
|
|
37
|
-
} else {
|
|
38
|
-
value += String.fromCharCode(byte);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
process.stdin.resume();
|
|
43
|
-
process.stdin.on("data", onData);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
19
|
function maskValue(value) {
|
|
47
20
|
if (value.length <= 6) return "***";
|
|
48
21
|
return `${value.slice(0, 3)}...${value.slice(-3)}`;
|
|
@@ -61,7 +34,7 @@ async function run(args) {
|
|
|
61
34
|
console.error("Usage: volute env set <KEY> [<VALUE>] [--mind <name>]");
|
|
62
35
|
process.exit(1);
|
|
63
36
|
}
|
|
64
|
-
const value = positional[2] ?? await
|
|
37
|
+
const value = positional[2] ?? await promptLine(`Enter value for ${key}: `);
|
|
65
38
|
let res;
|
|
66
39
|
if (flags.mind) {
|
|
67
40
|
res = await daemonFetch(
|